// 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. library command_line_config; import 'dart:io'; import '../../../pkg/path/lib/path.dart' as path; import '../../../pkg/unittest/lib/unittest.dart'; import '../../pub/utils.dart'; /// Gets a "special" string (ANSI escape or Unicode). On Windows, returns /// something else since those aren't supported. String _getSpecial(String color, [String onWindows = '']) { // No ANSI escapes on windows. if (Platform.operatingSystem == 'windows') return onWindows; return color; } /// Pretty Unicode characters! final _checkbox = _getSpecial('\u2713', 'PASS'); final _ballotX = _getSpecial('\u2717', 'FAIL'); final _lambda = _getSpecial('\u03bb', ''); final _green = _getSpecial('\u001b[32m'); final _red = _getSpecial('\u001b[31m'); final _magenta = _getSpecial('\u001b[35m'); final _none = _getSpecial('\u001b[0m'); /// A custom unittest configuration for running the pub tests from the /// command-line and generating human-friendly output. class CommandLineConfiguration extends Configuration { void onInit() { // Do nothing. Overridden to prevent the base class from printing. } void onTestResult(TestCase testCase) { var result; switch (testCase.result) { case PASS: result = '$_green$_checkbox$_none'; break; case FAIL: result = '$_red$_ballotX$_none'; break; case ERROR: result = '$_magenta?$_none'; break; } print('$result ${testCase.description}'); if (testCase.message != '') { print(_indent(testCase.message)); } _printStackTrace(testCase.stackTrace); currentTestCase = null; } void onSummary(int passed, int failed, int errors, List results, String uncaughtError) { var success = false; if (uncaughtError != null) { print('Top-level uncaught error: $uncaughtError'); } else if (errors != 0) { print('${_green}$passed${_none} passed, ${_red}$failed${_none} failed, ' '${_magenta}$errors${_none} errors.'); } else if (failed != 0) { print('${_green}$passed${_none} passed, ${_red}$failed${_none} ' 'failed.'); } else if (passed == 0) { print('No tests found.'); } else { print('All ${_green}$passed${_none} tests passed!'); success = true; } } void onDone(bool success) { if (!success) exit(1); } void _printStackTrace(String stackTrace) { if (stackTrace == null || stackTrace == '') return; print(''); // Parse out each stack entry. var stack = []; for (var line in stackTrace.split('\n')) { if (line.trim() == '') continue; stack.add(new _StackFrame(line)); } if (stack.length == 0) return; // Figure out the longest path so we know how much to pad. int longest = stack.map((frame) => frame.location.length).max(); // Print out the stack trace nicely formatted. for (var frame in stack) { print(' ${_padLeft(frame.location, longest)} ${frame.member}'); } print(''); } String _padLeft(String string, int length) { if (string.length >= length) return string; var result = new StringBuffer(); result.add(string); for (var i = 0; i < length - string.length; i++) { result.add(' '); } return result.toString(); } String _indent(String str) { // TODO(nweiz): Use this simpler code once issue 2980 is fixed. // return str.replaceAll(new RegExp("^", multiLine: true), " "); return str.split("\n").map((line) => " $line").join("\n"); } } class _StackFrame { static final fileRegExp = new RegExp( r'#\d+\s+(.*) \(file://(/.+):(\d+):(\d+)\)'); static final coreRegExp = new RegExp(r'#\d+\s+(.*) \((.+):(\d+):(\d+)\)'); /// If `true`, then this stack frame is for a library built into Dart and /// not a regular file path. final bool isCore; /// The path to the library or the library name if a core library. String library; /// The line number. final String line; /// The column number. final String column; /// The member where the error occurred. final String member; /// A formatted description of the code location. String get location => '$library $line:$column'; _StackFrame._(this.isCore, this.library, this.line, this.column, this.member); factory _StackFrame(String text) { var match = fileRegExp.firstMatch(text); var isCore = false; if (match == null) { match = coreRegExp.firstMatch(text); if (match == null) throw "Couldn't parse stack trace line '$text'."; isCore = true; } var library = match[2]; if (!isCore) { // Make the library path relative to the entrypoint. library = path.relative(library); } var member = match[1].replaceAll("", _lambda); return new _StackFrame._(isCore, library, match[3], match[4], member); } }