| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test_progress; | 5 library test_progress; |
| 6 | 6 |
| 7 import "dart:io"; | 7 import "dart:io"; |
| 8 import "dart:io" as io; | 8 import "dart:io" as io; |
| 9 import "http_server.dart" as http_server; | 9 import "http_server.dart" as http_server; |
| 10 import "status_file_parser.dart"; | 10 import "status_file_parser.dart"; |
| 11 import "test_runner.dart"; | 11 import "test_runner.dart"; |
| 12 import "test_suite.dart"; | 12 import "test_suite.dart"; |
| 13 import "utils.dart"; | 13 import "utils.dart"; |
| 14 | 14 |
| 15 class ProgressIndicator { | 15 String _pad(String s, int length) { |
| 16 ProgressIndicator(this._startTime, this._printTiming) | 16 StringBuffer buffer = new StringBuffer(); |
| 17 : _tests = [], _failureSummary = []; | 17 for (int i = s.length; i < length; i++) { |
| 18 buffer.add(' '); |
| 19 } |
| 20 buffer.add(s); |
| 21 return buffer.toString(); |
| 22 } |
| 18 | 23 |
| 19 factory ProgressIndicator.fromName(String name, | 24 String _padTime(int time) { |
| 20 Date startTime, | 25 if (time == 0) { |
| 21 bool printTiming) { | 26 return '00'; |
| 22 switch (name) { | 27 } else if (time < 10) { |
| 23 case 'compact': | 28 return '0$time'; |
| 24 return new CompactProgressIndicator(startTime, printTiming); | 29 } else { |
| 25 case 'color': | 30 return '$time'; |
| 26 return new ColorProgressIndicator(startTime, printTiming); | 31 } |
| 27 case 'line': | 32 } |
| 28 return new LineProgressIndicator(startTime, printTiming); | 33 |
| 29 case 'verbose': | 34 String _timeString(Duration d) { |
| 30 return new VerboseProgressIndicator(startTime, printTiming); | 35 var min = d.inMinutes; |
| 31 case 'silent': | 36 var sec = d.inSeconds % 60; |
| 32 return new SilentProgressIndicator(startTime, printTiming); | 37 return '${_padTime(min)}:${_padTime(sec)}'; |
| 33 case 'status': | 38 } |
| 34 return new StatusProgressIndicator(startTime, printTiming); | 39 |
| 35 case 'buildbot': | 40 class Formatter { |
| 36 return new BuildbotProgressIndicator(startTime, printTiming); | 41 const Formatter(); |
| 37 case 'diff': | 42 String passed(msg) => msg; |
| 38 return new DiffProgressIndicator(startTime, printTiming); | 43 String failed(msg) => msg; |
| 39 default: | 44 } |
| 40 assert(false); | 45 |
| 41 break; | 46 class ColorFormatter extends Formatter { |
| 47 static int BOLD = 1; |
| 48 static int GREEN = 32; |
| 49 static int RED = 31; |
| 50 static int NONE = 0; |
| 51 static String ESCAPE = decodeUtf8([27]); |
| 52 |
| 53 String passed(String msg) => _color(msg, GREEN); |
| 54 String failed(String msg) => _color(msg, RED); |
| 55 |
| 56 static String _color(String msg, int color) { |
| 57 return "$ESCAPE[${color}m$msg$ESCAPE[0m"; |
| 58 } |
| 59 } |
| 60 |
| 61 |
| 62 List<String> _buildFailureOutput(TestCase test, |
| 63 [Formatter formatter = const Formatter()]) { |
| 64 List<String> output = new List<String>(); |
| 65 output.add(''); |
| 66 output.add(formatter.failed('FAILED: ${test.configurationString}' |
| 67 ' ${test.displayName}')); |
| 68 StringBuffer expected = new StringBuffer(); |
| 69 expected.add('Expected: '); |
| 70 for (var expectation in test.expectedOutcomes) { |
| 71 expected.add('$expectation '); |
| 72 } |
| 73 output.add(expected.toString()); |
| 74 output.add('Actual: ${test.lastCommandOutput.result}'); |
| 75 if (!test.lastCommandOutput.hasTimedOut && test.info != null) { |
| 76 if (test.lastCommandOutput.incomplete && !test.info.hasCompileError) { |
| 77 output.add('Unexpected compile-time error.'); |
| 78 } else { |
| 79 if (test.info.hasCompileError) { |
| 80 output.add('Compile-time error expected.'); |
| 81 } |
| 82 if (test.info.hasRuntimeError) { |
| 83 output.add('Runtime error expected.'); |
| 84 } |
| 42 } | 85 } |
| 43 } | 86 } |
| 87 if (!test.lastCommandOutput.diagnostics.isEmpty) { |
| 88 String prefix = 'diagnostics:'; |
| 89 for (var s in test.lastCommandOutput.diagnostics) { |
| 90 output.add('$prefix ${s}'); |
| 91 prefix = ' '; |
| 92 } |
| 93 } |
| 94 if (!test.lastCommandOutput.stdout.isEmpty) { |
| 95 output.add(''); |
| 96 output.add('stdout:'); |
| 97 if (test.lastCommandOutput.command.isPixelTest) { |
| 98 output.add('DRT pixel test failed! stdout is not printed because it ' |
| 99 'contains binary data!'); |
| 100 } else { |
| 101 output.add(decodeUtf8(test.lastCommandOutput.stdout)); |
| 102 } |
| 103 } |
| 104 if (!test.lastCommandOutput.stderr.isEmpty) { |
| 105 output.add(''); |
| 106 output.add('stderr:'); |
| 107 output.add(decodeUtf8(test.lastCommandOutput.stderr)); |
| 108 } |
| 109 if (test is BrowserTestCase) { |
| 110 // Additional command for rerunning the steps locally after the fact. |
| 111 var command = |
| 112 test.configuration["_servers_"].httpServerCommandline(); |
| 113 output.add('To retest, run: $command'); |
| 114 } |
| 115 for (Command c in test.commands) { |
| 116 output.add(''); |
| 117 String message = (c == test.commands.last |
| 118 ? "Command line" : "Compilation command"); |
| 119 output.add('$message: $c'); |
| 120 } |
| 121 return output; |
| 122 } |
| 44 | 123 |
| 45 void testAdded() { _foundTests++; } | |
| 46 | 124 |
| 47 void start(TestCase test) { | 125 class EventListener { |
| 48 _printStartProgress(test); | 126 void testAdded() { } |
| 127 void start(TestCase test) { } |
| 128 void done(TestCase test) { } |
| 129 void allTestsKnown() { } |
| 130 void allDone() { } |
| 131 } |
| 132 |
| 133 class ExitCodeSetter extends EventListener { |
| 134 void done(TestCase test) { |
| 135 if (test.lastCommandOutput.unexpectedOutput) { |
| 136 io.exitCode = 1; |
| 137 } |
| 49 } | 138 } |
| 139 } |
| 50 | 140 |
| 141 class FlakyLogWriter extends EventListener { |
| 51 void done(TestCase test) { | 142 void done(TestCase test) { |
| 52 if (test.isFlaky && test.lastCommandOutput.result != PASS) { | 143 if (test.isFlaky && test.lastCommandOutput.result != PASS) { |
| 53 var buf = new StringBuffer(); | 144 var buf = new StringBuffer(); |
| 54 for (var l in _buildFailureOutput(test)) { | 145 for (var l in _buildFailureOutput(test)) { |
| 55 buf.add("$l\n"); | 146 buf.add("$l\n"); |
| 56 } | 147 } |
| 57 _appendToFlakyFile(buf.toString()); | 148 _appendToFlakyFile(buf.toString()); |
| 58 } | 149 } |
| 150 } |
| 151 |
| 152 void _appendToFlakyFile(String msg) { |
| 153 var file = new File(TestUtils.flakyFileName()); |
| 154 var fd = file.openSync(FileMode.APPEND); |
| 155 fd.writeStringSync(msg); |
| 156 fd.closeSync(); |
| 157 } |
| 158 } |
| 159 |
| 160 class SummaryPrinter extends EventListener { |
| 161 void allTestsKnown() { |
| 162 if (SummaryReport.total > 0) { |
| 163 SummaryReport.printReport(); |
| 164 } |
| 165 } |
| 166 } |
| 167 |
| 168 class TimingPrinter extends EventListener { |
| 169 List<TestCase> _tests = <TestCase>[]; |
| 170 Date _startTime; |
| 171 |
| 172 TimingPrinter(this._startTime); |
| 173 |
| 174 void done(TestCase testCase) { |
| 175 _tests.add(testCase); |
| 176 } |
| 177 |
| 178 void allDone() { |
| 179 // TODO: We should take all the commands into account |
| 180 Duration d = (new Date.now()).difference(_startTime); |
| 181 print('\n--- Total time: ${_timeString(d)} ---'); |
| 182 _tests.sort((a, b) { |
| 183 Duration aDuration = a.lastCommandOutput.time; |
| 184 Duration bDuration = b.lastCommandOutput.time; |
| 185 return bDuration.inMilliseconds - aDuration.inMilliseconds; |
| 186 }); |
| 187 for (int i = 0; i < 20 && i < _tests.length; i++) { |
| 188 var name = _tests[i].displayName; |
| 189 var duration = _tests[i].lastCommandOutput.time; |
| 190 var configuration = _tests[i].configurationString; |
| 191 print('${duration} - $configuration $name'); |
| 192 } |
| 193 } |
| 194 } |
| 195 |
| 196 class StatusFileUpdatePrinter extends EventListener { |
| 197 var statusToConfigs = new Map<String, List<String>>(); |
| 198 var _failureSummary = <String>[]; |
| 199 |
| 200 void done(TestCase test) { |
| 201 if (test.lastCommandOutput.unexpectedOutput) { |
| 202 _printFailureOutput(test); |
| 203 } |
| 204 } |
| 205 |
| 206 void allDone() { |
| 207 _printFailureSummary(); |
| 208 } |
| 209 |
| 210 |
| 211 void _printFailureOutput(TestCase test) { |
| 212 String status = '${test.displayName}: ${test.lastCommandOutput.result}'; |
| 213 List<String> configs = |
| 214 statusToConfigs.putIfAbsent(status, () => <String>[]); |
| 215 configs.add(test.configurationString); |
| 216 if (test.lastCommandOutput.hasTimedOut) { |
| 217 print('\n${test.displayName} timed out on ${test.configurationString}'); |
| 218 } |
| 219 } |
| 220 |
| 221 String _extractRuntime(String configuration) { |
| 222 // Extract runtime from a configuration, for example, |
| 223 // 'none-vm-checked release_ia32'. |
| 224 List<String> runtime = configuration.split(' ')[0].split('-'); |
| 225 return '${runtime[0]}-${runtime[1]}'; |
| 226 } |
| 227 |
| 228 void _printFailureSummary() { |
| 229 var groupedStatuses = new Map<String, List<String>>(); |
| 230 statusToConfigs.forEach((String status, List<String> configs) { |
| 231 var runtimeToConfiguration = new Map<String, List<String>>(); |
| 232 for (String config in configs) { |
| 233 String runtime = _extractRuntime(config); |
| 234 var runtimeConfigs = |
| 235 runtimeToConfiguration.putIfAbsent(runtime, () => <String>[]); |
| 236 runtimeConfigs.add(config); |
| 237 } |
| 238 runtimeToConfiguration.forEach((String runtime, |
| 239 List<String> runtimeConfigs) { |
| 240 runtimeConfigs.sort((a, b) => a.compareTo(b)); |
| 241 List<String> statuses = |
| 242 groupedStatuses.putIfAbsent('$runtime: $runtimeConfigs', |
| 243 () => <String>[]); |
| 244 statuses.add(status); |
| 245 }); |
| 246 }); |
| 247 |
| 248 print('\n\nNecessary status file updates:'); |
| 249 groupedStatuses.forEach((String config, List<String> statuses) { |
| 250 print(''); |
| 251 print('$config:'); |
| 252 statuses.sort((a, b) => a.compareTo(b)); |
| 253 for (String status in statuses) { |
| 254 print(' $status'); |
| 255 } |
| 256 }); |
| 257 } |
| 258 } |
| 259 |
| 260 class SkippedCompilationsPrinter extends EventListener { |
| 261 int _skippedCompilations = 0; |
| 262 |
| 263 void done(TestCase test) { |
| 59 for (var commandOutput in test.commandOutputs.values) { | 264 for (var commandOutput in test.commandOutputs.values) { |
| 60 if (commandOutput.compilationSkipped) | 265 if (commandOutput.compilationSkipped) |
| 61 _skippedCompilations++; | 266 _skippedCompilations++; |
| 62 } | 267 } |
| 63 | 268 } |
| 64 if (test.lastCommandOutput.unexpectedOutput) { | 269 |
| 65 _failedTests++; | 270 void allDone() { |
| 66 _printFailureOutput(test); | |
| 67 } else { | |
| 68 _passedTests++; | |
| 69 } | |
| 70 _printDoneProgress(test); | |
| 71 // If we need to print timing information we hold on to all completed | |
| 72 // tests. | |
| 73 if (_printTiming) _tests.add(test); | |
| 74 } | |
| 75 | |
| 76 void allTestsKnown() { | |
| 77 if (!_allTestsKnown) SummaryReport.printReport(); | |
| 78 _allTestsKnown = true; | |
| 79 } | |
| 80 | |
| 81 void _printSkippedCompilationInfo() { | |
| 82 if (_skippedCompilations > 0) { | 271 if (_skippedCompilations > 0) { |
| 83 print('\n$_skippedCompilations compilations were skipped because ' | 272 print('\n$_skippedCompilations compilations were skipped because ' |
| 84 'the previous output was already up to date\n'); | 273 'the previous output was already up to date\n'); |
| 85 } | 274 } |
| 86 } | 275 } |
| 87 | 276 } |
| 88 void _printTimingInformation() { | 277 |
| 89 if (_printTiming) { | 278 class LeftOverTempDirPrinter extends EventListener { |
| 90 // TODO: We should take all the commands into account | 279 final MIN_NUMBER_OF_TEMP_DIRS = 50; |
| 91 Duration d = (new Date.now()).difference(_startTime); | 280 |
| 92 print('\n--- Total time: ${_timeString(d)} ---'); | 281 Path _tempDir() { |
| 93 _tests.sort((a, b) { | 282 // Dir will be located in the system temporary directory. |
| 94 Duration aDuration = a.lastCommandOutput.time; | 283 var dir = new Directory('').createTempSync(); |
| 95 Duration bDuration = b.lastCommandOutput.time; | 284 var path = new Path(dir.path).directoryPath; |
| 96 return bDuration.inMilliseconds - aDuration.inMilliseconds; | 285 dir.deleteSync(); |
| 97 }); | 286 return path; |
| 98 for (int i = 0; i < 20 && i < _tests.length; i++) { | 287 } |
| 99 var name = _tests[i].displayName; | 288 |
| 100 var duration = _tests[i].lastCommandOutput.time; | 289 void allDone() { |
| 101 var configuration = _tests[i].configurationString; | 290 var tempDirs = []; |
| 102 print('${duration} - $configuration $name'); | 291 var systemTempDir = _tempDir(); |
| 103 } | 292 var lister = new Directory.fromPath(systemTempDir).list(); |
| 293 lister.onDir = (path) => tempDirs.add(path); |
| 294 lister.onDone = (_) { |
| 295 if (tempDirs.length > MIN_NUMBER_OF_TEMP_DIRS) { |
| 296 DebugLogger.warning("There are ${tempDirs.length} directories " |
| 297 "in the system tempdir ('$systemTempDir')! " |
| 298 "Maybe left over directories?\n"); |
| 299 } |
| 300 }; |
| 301 } |
| 302 } |
| 303 |
| 304 class LineProgressIndicator extends EventListener { |
| 305 void done(TestCase test) { |
| 306 var status = 'pass'; |
| 307 if (test.lastCommandOutput.unexpectedOutput) { |
| 308 status = 'fail'; |
| 309 } |
| 310 print('Done ${test.configurationString} ${test.displayName}: $status'); |
| 311 } |
| 312 } |
| 313 |
| 314 class TestFailurePrinter extends EventListener { |
| 315 var _failureSummary = <String>[]; |
| 316 var _formatter; |
| 317 |
| 318 TestFailurePrinter([this._formatter = const Formatter()]); |
| 319 |
| 320 void done(TestCase test) { |
| 321 if (test.lastCommandOutput.unexpectedOutput) { |
| 322 _printFailureOutput(test); |
| 104 } | 323 } |
| 105 } | 324 } |
| 106 | 325 |
| 107 void allDone() { | 326 void allDone() { |
| 108 _printFailureSummary(); | 327 _printFailureSummary(); |
| 109 _printStatus(); | 328 } |
| 110 _printSkippedCompilationInfo(); | |
| 111 _printTimingInformation(); | |
| 112 stdout.close(); | |
| 113 stderr.close(); | |
| 114 if (_failedTests > 0) { | |
| 115 io.exitCode = 1; | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 void _printStartProgress(TestCase test) {} | |
| 120 void _printDoneProgress(TestCase test) {} | |
| 121 | |
| 122 String _pad(String s, int length) { | |
| 123 StringBuffer buffer = new StringBuffer(); | |
| 124 for (int i = s.length; i < length; i++) { | |
| 125 buffer.add(' '); | |
| 126 } | |
| 127 buffer.add(s); | |
| 128 return buffer.toString(); | |
| 129 } | |
| 130 | |
| 131 String _padTime(int time) { | |
| 132 if (time == 0) { | |
| 133 return '00'; | |
| 134 } else if (time < 10) { | |
| 135 return '0$time'; | |
| 136 } else { | |
| 137 return '$time'; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 String _timeString(Duration d) { | |
| 142 var min = d.inMinutes; | |
| 143 var sec = d.inSeconds % 60; | |
| 144 return '${_padTime(min)}:${_padTime(sec)}'; | |
| 145 } | |
| 146 | |
| 147 String _header(String header) => header; | |
| 148 | 329 |
| 149 void _printFailureOutput(TestCase test) { | 330 void _printFailureOutput(TestCase test) { |
| 150 var failureOutput = _buildFailureOutput(test); | 331 var failureOutput = _buildFailureOutput(test, _formatter); |
| 151 for (var line in failureOutput) { | 332 for (var line in failureOutput) { |
| 152 print(line); | 333 print(line); |
| 153 } | 334 } |
| 335 print(''); |
| 154 _failureSummary.addAll(failureOutput); | 336 _failureSummary.addAll(failureOutput); |
| 155 } | 337 } |
| 156 | 338 |
| 157 List<String> _buildFailureOutput(TestCase test) { | |
| 158 List<String> output = new List<String>(); | |
| 159 output.add(''); | |
| 160 output.add(_header('FAILED: ${test.configurationString}' | |
| 161 ' ${test.displayName}')); | |
| 162 StringBuffer expected = new StringBuffer(); | |
| 163 expected.add('Expected: '); | |
| 164 for (var expectation in test.expectedOutcomes) { | |
| 165 expected.add('$expectation '); | |
| 166 } | |
| 167 output.add(expected.toString()); | |
| 168 output.add('Actual: ${test.lastCommandOutput.result}'); | |
| 169 if (!test.lastCommandOutput.hasTimedOut && test.info != null) { | |
| 170 if (test.lastCommandOutput.incomplete && !test.info.hasCompileError) { | |
| 171 output.add('Unexpected compile-time error.'); | |
| 172 } else { | |
| 173 if (test.info.hasCompileError) { | |
| 174 output.add('Compile-time error expected.'); | |
| 175 } | |
| 176 if (test.info.hasRuntimeError) { | |
| 177 output.add('Runtime error expected.'); | |
| 178 } | |
| 179 } | |
| 180 } | |
| 181 if (!test.lastCommandOutput.diagnostics.isEmpty) { | |
| 182 String prefix = 'diagnostics:'; | |
| 183 for (var s in test.lastCommandOutput.diagnostics) { | |
| 184 output.add('$prefix ${s}'); | |
| 185 prefix = ' '; | |
| 186 } | |
| 187 } | |
| 188 if (!test.lastCommandOutput.stdout.isEmpty) { | |
| 189 output.add(''); | |
| 190 output.add('stdout:'); | |
| 191 if (test.lastCommandOutput.command.isPixelTest) { | |
| 192 output.add('DRT pixel test failed! stdout is not printed because it ' | |
| 193 'contains binary data!'); | |
| 194 } else { | |
| 195 output.add(decodeUtf8(test.lastCommandOutput.stdout)); | |
| 196 } | |
| 197 } | |
| 198 if (!test.lastCommandOutput.stderr.isEmpty) { | |
| 199 output.add(''); | |
| 200 output.add('stderr:'); | |
| 201 output.add(decodeUtf8(test.lastCommandOutput.stderr)); | |
| 202 } | |
| 203 if (test is BrowserTestCase) { | |
| 204 // Additional command for rerunning the steps locally after the fact. | |
| 205 var command = | |
| 206 test.configuration["_servers_"].httpServerCommandline(); | |
| 207 output.add('To retest, run: $command'); | |
| 208 } | |
| 209 for (Command c in test.commands) { | |
| 210 output.add(''); | |
| 211 String message = (c == test.commands.last | |
| 212 ? "Command line" : "Compilation command"); | |
| 213 output.add('$message: $c'); | |
| 214 } | |
| 215 return output; | |
| 216 } | |
| 217 | |
| 218 void _printFailureSummary() { | 339 void _printFailureSummary() { |
| 219 for (String line in _failureSummary) { | 340 for (String line in _failureSummary) { |
| 220 print(line); | 341 print(line); |
| 221 } | 342 } |
| 222 print(''); | 343 print(''); |
| 223 } | 344 } |
| 345 } |
| 346 |
| 347 class ProgressIndicator extends EventListener { |
| 348 ProgressIndicator(this._startTime); |
| 349 |
| 350 factory ProgressIndicator.fromName(String name, |
| 351 Date startTime, |
| 352 Formatter formatter) { |
| 353 switch (name) { |
| 354 case 'compact': |
| 355 return new CompactProgressIndicator(startTime, formatter); |
| 356 case 'line': |
| 357 return new LineProgressIndicator(); |
| 358 case 'verbose': |
| 359 return new VerboseProgressIndicator(startTime); |
| 360 case 'status': |
| 361 return new ProgressIndicator(startTime); |
| 362 case 'buildbot': |
| 363 return new BuildbotProgressIndicator(startTime); |
| 364 default: |
| 365 assert(false); |
| 366 break; |
| 367 } |
| 368 } |
| 369 |
| 370 void testAdded() { _foundTests++; } |
| 371 |
| 372 void start(TestCase test) { |
| 373 _printStartProgress(test); |
| 374 } |
| 375 |
| 376 void done(TestCase test) { |
| 377 if (test.lastCommandOutput.unexpectedOutput) { |
| 378 _failedTests++; |
| 379 } else { |
| 380 _passedTests++; |
| 381 } |
| 382 _printDoneProgress(test); |
| 383 } |
| 384 |
| 385 void allTestsKnown() { |
| 386 _allTestsKnown = true; |
| 387 } |
| 388 |
| 389 void allDone() { |
| 390 _printStatus(); |
| 391 } |
| 392 |
| 393 void _printStartProgress(TestCase test) {} |
| 394 void _printDoneProgress(TestCase test) {} |
| 224 | 395 |
| 225 void _printStatus() { | 396 void _printStatus() { |
| 226 if (_failedTests == 0) { | 397 if (_failedTests == 0) { |
| 227 print('\n==='); | 398 print('\n==='); |
| 228 print('=== All tests succeeded'); | 399 print('=== All tests succeeded'); |
| 229 print('===\n'); | 400 print('===\n'); |
| 230 } else { | 401 } else { |
| 231 var pluralSuffix = _failedTests != 1 ? 's' : ''; | 402 var pluralSuffix = _failedTests != 1 ? 's' : ''; |
| 232 print('\n==='); | 403 print('\n==='); |
| 233 print('=== ${_failedTests} test$pluralSuffix failed'); | 404 print('=== ${_failedTests} test$pluralSuffix failed'); |
| 234 print('===\n'); | 405 print('===\n'); |
| 235 } | 406 } |
| 236 } | 407 } |
| 237 | 408 |
| 238 void _appendToFlakyFile(String msg) { | |
| 239 var file = new File(TestUtils.flakyFileName()); | |
| 240 var fd = file.openSync(FileMode.APPEND); | |
| 241 fd.writeStringSync(msg); | |
| 242 fd.closeSync(); | |
| 243 } | |
| 244 | |
| 245 int get numFailedTests => _failedTests; | 409 int get numFailedTests => _failedTests; |
| 246 | 410 |
| 247 int _completedTests() => _passedTests + _failedTests; | 411 int _completedTests() => _passedTests + _failedTests; |
| 248 | 412 |
| 249 int _foundTests = 0; | 413 int _foundTests = 0; |
| 250 int _passedTests = 0; | 414 int _passedTests = 0; |
| 251 int _failedTests = 0; | 415 int _failedTests = 0; |
| 252 int _skippedCompilations = 0; | |
| 253 bool _allTestsKnown = false; | 416 bool _allTestsKnown = false; |
| 254 Date _startTime; | 417 Date _startTime; |
| 255 bool _printTiming; | |
| 256 List<TestCase> _tests; | |
| 257 List<String> _failureSummary; | |
| 258 } | |
| 259 | |
| 260 | |
| 261 class SilentProgressIndicator extends ProgressIndicator { | |
| 262 SilentProgressIndicator(Date startTime, bool printTiming) | |
| 263 : super(startTime, printTiming); | |
| 264 void testAdded() { } | |
| 265 void start(TestCase test) { } | |
| 266 void done(TestCase test) { } | |
| 267 void _printStartProgress(TestCase test) { } | |
| 268 void _printDoneProgress(TestCase test) { } | |
| 269 void allTestsKnown() { } | |
| 270 void allDone() { } | |
| 271 } | 418 } |
| 272 | 419 |
| 273 abstract class CompactIndicator extends ProgressIndicator { | 420 abstract class CompactIndicator extends ProgressIndicator { |
| 274 CompactIndicator(Date startTime, bool printTiming) | 421 CompactIndicator(Date startTime) |
| 275 : super(startTime, printTiming); | 422 : super(startTime); |
| 276 | 423 |
| 277 void allDone() { | 424 void allDone() { |
| 278 stdout.write('\n'.charCodes); | 425 stdout.write('\n'.charCodes); |
| 279 _printFailureSummary(); | |
| 280 _printSkippedCompilationInfo(); | |
| 281 _printTimingInformation(); | |
| 282 if (_failedTests > 0) { | 426 if (_failedTests > 0) { |
| 283 // We may have printed many failure logs, so reprint the summary data. | 427 // We may have printed many failure logs, so reprint the summary data. |
| 284 _printProgress(); | 428 _printProgress(); |
| 285 print(''); | 429 print(''); |
| 286 } | 430 } |
| 287 stdout.close(); | 431 stdout.close(); |
| 288 stderr.close(); | 432 stderr.close(); |
| 289 if (_failedTests > 0) { | |
| 290 io.exitCode = 1; | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 void allTestsKnown() { | |
| 295 if (!_allTestsKnown && SummaryReport.total > 0) { | |
| 296 // Clear progress indicator before printing summary report. | |
| 297 stdout.write( | |
| 298 '\r \r'.charCodes); | |
| 299 SummaryReport.printReport(); | |
| 300 } | |
| 301 _allTestsKnown = true; | |
| 302 } | 433 } |
| 303 | 434 |
| 304 void _printStartProgress(TestCase test) => _printProgress(); | 435 void _printStartProgress(TestCase test) => _printProgress(); |
| 305 void _printDoneProgress(TestCase test) => _printProgress(); | 436 void _printDoneProgress(TestCase test) => _printProgress(); |
| 306 | 437 |
| 307 void _printProgress(); | 438 void _printProgress(); |
| 308 } | 439 } |
| 309 | 440 |
| 310 | 441 |
| 311 class CompactProgressIndicator extends CompactIndicator { | 442 class CompactProgressIndicator extends CompactIndicator { |
| 312 CompactProgressIndicator(Date startTime, bool printTiming) | 443 Formatter _formatter; |
| 313 : super(startTime, printTiming); | 444 |
| 445 CompactProgressIndicator(Date startTime, this._formatter) |
| 446 : super(startTime); |
| 314 | 447 |
| 315 void _printProgress() { | 448 void _printProgress() { |
| 316 var percent = ((_completedTests() / _foundTests) * 100).toInt().toString(); | 449 var percent = ((_completedTests() / _foundTests) * 100).toInt().toString(); |
| 317 var progressPadded = _pad(_allTestsKnown ? percent : '--', 3); | 450 var progressPadded = _pad(_allTestsKnown ? percent : '--', 3); |
| 318 var passedPadded = _pad(_passedTests.toString(), 5); | 451 var passedPadded = _pad(_passedTests.toString(), 5); |
| 319 var failedPadded = _pad(_failedTests.toString(), 5); | 452 var failedPadded = _pad(_failedTests.toString(), 5); |
| 320 Duration d = (new Date.now()).difference(_startTime); | 453 Duration d = (new Date.now()).difference(_startTime); |
| 321 var progressLine = | 454 var progressLine = |
| 322 '\r[${_timeString(d)} | $progressPadded% | ' | 455 '\r[${_timeString(d)} | $progressPadded% | ' |
| 323 '+$passedPadded | -$failedPadded]'; | 456 '+${_formatter.passed(passedPadded)} | ' |
| 457 '-${_formatter.failed(failedPadded)}]'; |
| 324 stdout.write(progressLine.charCodes); | 458 stdout.write(progressLine.charCodes); |
| 325 } | 459 } |
| 326 } | 460 } |
| 327 | 461 |
| 328 | 462 |
| 329 class ColorProgressIndicator extends CompactIndicator { | |
| 330 ColorProgressIndicator(Date startTime, bool printTiming) | |
| 331 : super(startTime, printTiming); | |
| 332 | |
| 333 static int BOLD = 1; | |
| 334 static int GREEN = 32; | |
| 335 static int RED = 31; | |
| 336 static int NONE = 0; | |
| 337 | |
| 338 addColorWrapped(List<int> codes, String string, int color) { | |
| 339 codes.add(27); | |
| 340 codes.addAll('[${color}m'.charCodes); | |
| 341 codes.addAll(encodeUtf8(string)); | |
| 342 codes.add(27); | |
| 343 codes.addAll('[0m'.charCodes); | |
| 344 } | |
| 345 | |
| 346 void _printProgress() { | |
| 347 var percent = ((_completedTests() / _foundTests) * 100).toInt().toString(); | |
| 348 var progressPadded = _pad(_allTestsKnown ? percent : '--', 3); | |
| 349 var passedPadded = _pad(_passedTests.toString(), 5); | |
| 350 var failedPadded = _pad(_failedTests.toString(), 5); | |
| 351 Duration d = (new Date.now()).difference(_startTime); | |
| 352 var progressLine = []; | |
| 353 progressLine.addAll('\r[${_timeString(d)} | $progressPadded% | '.charCodes); | |
| 354 addColorWrapped(progressLine, '+$passedPadded ', GREEN); | |
| 355 progressLine.addAll('| '.charCodes); | |
| 356 var failedColor = (_failedTests != 0) ? RED : NONE; | |
| 357 addColorWrapped(progressLine, '-$failedPadded', failedColor); | |
| 358 progressLine.addAll(']'.charCodes); | |
| 359 stdout.write(progressLine); | |
| 360 } | |
| 361 | |
| 362 String _header(String header) { | |
| 363 var result = []; | |
| 364 addColorWrapped(result, header, BOLD); | |
| 365 return decodeUtf8(result); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 | |
| 370 class LineProgressIndicator extends ProgressIndicator { | |
| 371 LineProgressIndicator(Date startTime, bool printTiming) | |
| 372 : super(startTime, printTiming); | |
| 373 | |
| 374 void _printStartProgress(TestCase test) { | |
| 375 } | |
| 376 | |
| 377 void _printDoneProgress(TestCase test) { | |
| 378 var status = 'pass'; | |
| 379 if (test.lastCommandOutput.unexpectedOutput) { | |
| 380 status = 'fail'; | |
| 381 } | |
| 382 print('Done ${test.configurationString} ${test.displayName}: $status'); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 | |
| 387 class VerboseProgressIndicator extends ProgressIndicator { | 463 class VerboseProgressIndicator extends ProgressIndicator { |
| 388 VerboseProgressIndicator(Date startTime, bool printTiming) | 464 VerboseProgressIndicator(Date startTime) |
| 389 : super(startTime, printTiming); | 465 : super(startTime); |
| 390 | 466 |
| 391 void _printStartProgress(TestCase test) { | 467 void _printStartProgress(TestCase test) { |
| 392 print('Starting ${test.configurationString} ${test.displayName}...'); | 468 print('Starting ${test.configurationString} ${test.displayName}...'); |
| 393 } | 469 } |
| 394 | 470 |
| 395 void _printDoneProgress(TestCase test) { | 471 void _printDoneProgress(TestCase test) { |
| 396 var status = 'pass'; | 472 var status = 'pass'; |
| 397 if (test.lastCommandOutput.unexpectedOutput) { | 473 if (test.lastCommandOutput.unexpectedOutput) { |
| 398 status = 'fail'; | 474 status = 'fail'; |
| 399 } | 475 } |
| 400 print('Done ${test.configurationString} ${test.displayName}: $status'); | 476 print('Done ${test.configurationString} ${test.displayName}: $status'); |
| 401 } | 477 } |
| 402 } | 478 } |
| 403 | 479 |
| 404 | 480 |
| 405 class StatusProgressIndicator extends ProgressIndicator { | |
| 406 StatusProgressIndicator(Date startTime, bool printTiming) | |
| 407 : super(startTime, printTiming); | |
| 408 | |
| 409 void _printStartProgress(TestCase test) { | |
| 410 } | |
| 411 | |
| 412 void _printDoneProgress(TestCase test) { | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 | |
| 417 class BuildbotProgressIndicator extends ProgressIndicator { | 481 class BuildbotProgressIndicator extends ProgressIndicator { |
| 418 static String stepName; | 482 static String stepName; |
| 419 | 483 |
| 420 BuildbotProgressIndicator(Date startTime, bool printTiming) | 484 BuildbotProgressIndicator(Date startTime) : super(startTime); |
| 421 : super(startTime, printTiming); | |
| 422 | |
| 423 void _printStartProgress(TestCase test) { | |
| 424 } | |
| 425 | 485 |
| 426 void _printDoneProgress(TestCase test) { | 486 void _printDoneProgress(TestCase test) { |
| 427 var status = 'pass'; | 487 var status = 'pass'; |
| 428 if (test.lastCommandOutput.unexpectedOutput) { | 488 if (test.lastCommandOutput.unexpectedOutput) { |
| 429 status = 'fail'; | 489 status = 'fail'; |
| 430 } | 490 } |
| 431 var percent = ((_completedTests() / _foundTests) * 100).toInt().toString(); | 491 var percent = ((_completedTests() / _foundTests) * 100).toInt().toString(); |
| 432 print('Done ${test.configurationString} ${test.displayName}: $status'); | 492 print('Done ${test.configurationString} ${test.displayName}: $status'); |
| 433 print('@@@STEP_CLEAR@@@'); | 493 print('@@@STEP_CLEAR@@@'); |
| 434 print('@@@STEP_TEXT@ $percent% +$_passedTests -$_failedTests @@@'); | 494 print('@@@STEP_TEXT@ $percent% +$_passedTests -$_failedTests @@@'); |
| 435 } | 495 } |
| 436 | 496 |
| 437 void _printFailureSummary() { | 497 void allDone() { |
| 438 if (!_failureSummary.isEmpty && stepName != null) { | 498 if (_failedTests > 0) { |
| 439 print('@@@STEP_FAILURE@@@'); | 499 print('@@@STEP_FAILURE@@@'); |
| 440 print('@@@BUILD_STEP $stepName failures@@@'); | 500 print('@@@BUILD_STEP $stepName failures@@@'); |
| 441 } | 501 } |
| 442 super._printFailureSummary(); | 502 super.allDone(); |
| 443 } | 503 } |
| 444 } | 504 } |
| 445 | 505 |
| 446 class DiffProgressIndicator extends ColorProgressIndicator { | |
| 447 Map<String, List<String>> statusToConfigs = new Map<String, List<String>>(); | |
| 448 | |
| 449 DiffProgressIndicator(Date startTime, bool printTiming) | |
| 450 : super(startTime, printTiming); | |
| 451 | |
| 452 void _printFailureOutput(TestCase test) { | |
| 453 String status = '${test.displayName}: ${test.lastCommandOutput.result}'; | |
| 454 List<String> configs = | |
| 455 statusToConfigs.putIfAbsent(status, () => <String>[]); | |
| 456 configs.add(test.configurationString); | |
| 457 if (test.lastCommandOutput.hasTimedOut) { | |
| 458 print('\n${test.displayName} timed out on ${test.configurationString}'); | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 String _extractRuntime(String configuration) { | |
| 463 // Extract runtime from a configuration, for example, | |
| 464 // 'none-vm-checked release_ia32'. | |
| 465 List<String> runtime = configuration.split(' ')[0].split('-'); | |
| 466 return '${runtime[0]}-${runtime[1]}'; | |
| 467 } | |
| 468 | |
| 469 void _printFailureSummary() { | |
| 470 Map<String, List<String>> groupedStatuses = new Map<String, List<String>>(); | |
| 471 statusToConfigs.forEach((String status, List<String> configs) { | |
| 472 Map<String, List<String>> runtimeToConfiguration = | |
| 473 new Map<String, List<String>>(); | |
| 474 for (String config in configs) { | |
| 475 String runtime = _extractRuntime(config); | |
| 476 List<String> runtimeConfigs = | |
| 477 runtimeToConfiguration.putIfAbsent(runtime, () => <String>[]); | |
| 478 runtimeConfigs.add(config); | |
| 479 } | |
| 480 runtimeToConfiguration.forEach((String runtime, | |
| 481 List<String> runtimeConfigs) { | |
| 482 runtimeConfigs.sort((a, b) => a.compareTo(b)); | |
| 483 List<String> statuses = | |
| 484 groupedStatuses.putIfAbsent('$runtime: $runtimeConfigs', | |
| 485 () => <String>[]); | |
| 486 statuses.add(status); | |
| 487 }); | |
| 488 }); | |
| 489 groupedStatuses.forEach((String config, List<String> statuses) { | |
| 490 print(''); | |
| 491 print(''); | |
| 492 print('$config:'); | |
| 493 statuses.sort((a, b) => a.compareTo(b)); | |
| 494 for (String status in statuses) { | |
| 495 print(' $status'); | |
| 496 } | |
| 497 }); | |
| 498 _printStatus(); | |
| 499 } | |
| 500 } | |
| OLD | NEW |