Create a unit test config for the pub tests that's prettier.

In particular, it:

- Shows test results incrementally.
- Cleans up the stack traces.
- Doesn't show the failures and their stack traces twice.

Right now, it's opt in. You need to:
- Add initConfig() to the top of a test suite.
- Pass "--human" when you run the test.

At some point, I can add the initConfig() calls to every test suite but I figured we could just add them manually for now.

Review URL: https://codereview.chromium.org//11867012

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@17313 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
rnystrom@google.com 2013-01-18 21:11:43 +00:00
parent 4dcfe405c0
commit 4573dcd11d
2 changed files with 149 additions and 1 deletions

View file

@ -0,0 +1,139 @@
// 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/unittest/lib/unittest.dart';
import '../../pub/utils.dart';
const _GREEN = '\u001b[32m';
const _RED = '\u001b[31m';
const _MAGENTA = '\u001b[35m';
const _NONE = '\u001b[0m';
/// Pretty Unicode characters!
const _CHECKBOX = '\u2713';
const _BALLOT_X = '\u2717';
/// 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$_BALLOT_X$_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<TestCase> 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;
// Parse out each stack entry.
var regexp = new RegExp(r'#\d+\s+(.*) \(file:///([^)]+)\)');
var stack = [];
for (var line in stackTrace.split('\n')) {
if (line.trim() == '') continue;
var match = regexp.firstMatch(line);
if (match == null) throw "Couldn't clean up stack trace line '$line'.";
stack.add(new Pair(match[2], match[1]));
}
if (stack.length == 0) return;
// Find the common prefixes of the paths.
var common = 0;
while (true) {
var matching = true;
// TODO(bob): Handle empty stack.
var c = stack[0].first[common];
for (var pair in stack) {
if (pair.first.length <= common || pair.first[common] != c) {
matching = false;
break;
}
}
if (!matching) break;
common++;
}
// Remove them.
if (common > 0) {
for (var pair in stack) {
pair.first = pair.first.substring(common);
}
}
// Figure out the longest path so we know how much to pad.
int longest = stack.mappedBy((pair) => pair.first.length).max();
// Print out the stack trace nicely formatted.
for (var pair in stack) {
var path = pair.first;
path = path.replaceFirst(':', ' ');
print(' ${_padLeft(path, longest)} ${pair.last}');
}
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 Strings.join(str.split("\n").mappedBy((line) => " $line"), "\n");
}
}

View file

@ -29,6 +29,16 @@ import '../../pub/system_cache.dart';
import '../../pub/utils.dart';
import '../../pub/validator.dart';
import '../../pub/yaml/yaml.dart';
import 'command_line_config.dart';
/// This should be called at the top of a test file to set up an appropriate
/// test configuration for the machine running the tests.
initConfig() {
// If we aren't running on the bots, use the human-friendly config.
if (new Options().arguments.contains('--human')) {
configure(new CommandLineConfiguration());
}
}
/// Creates a new [FileDescriptor] with [name] and [contents].
FileDescriptor file(Pattern name, String contents) =>
@ -456,7 +466,6 @@ integration(String description, body()) {
/// once per test case.
void _run() {
var createdSandboxDir;
var asyncDone = expectAsync0(() {});
Future cleanup() {