blob: 385f4de3a7aaae66c6b2203a9bec1529b3143978 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'package:path/path.dart' as p;
import 'package:term_glyph/term_glyph.dart' as glyph;
import 'package:test/test.dart';
import 'sandbox.dart';
/// Prepends a vertical bar to [text].
String addBar(String text) => prefixLines(text, "${glyph.verticalLine} ",
first: "${glyph.downEnd} ",
last: "${glyph.upEnd} ",
single: "| ");
/// Indents [text], and adds a bullet at the beginning.
String addBullet(String text) =>
prefixLines(text, " ", first: "${glyph.bullet} ");
/// Converts [strings] to a bulleted list.
String bullet(Iterable<String> strings) => strings.map(addBullet).join("\n");
/// Prepends each line in [text] with [prefix].
///
/// If [first] or [last] is passed, the first and last lines, respectively, are
/// prefixed with those instead. If [single] is passed, it's used if there's
/// only a single line; otherwise, [first], [last], or [prefix] is used, in that
/// order of precedence.
String prefixLines(String text, String prefix, {String first, String last,
String single}) {
first ??= prefix;
last ??= prefix;
single ??= first ?? last ?? prefix;
var lines = text.split('\n');
if (lines.length == 1) return "$single$text";
var buffer = new StringBuffer("$first${lines.first}\n");
for (var line in lines.skip(1).take(lines.length - 2)) {
buffer.writeln("$prefix$line");
}
buffer.write("$last${lines.last}");
return buffer.toString();
}
/// Returns a representation of [path] that's easy for humans to read.
///
/// This may not be a valid path relative to [p.current].
String prettyPath(String path) {
if (sandboxExists && p.isWithin(sandbox, path)) {
return p.relative(path, from: sandbox);
} else if (p.isWithin(p.current, path)) {
return p.relative(path);
} else {
return path;
}
}
/// Returns whether [pattern] matches all of [string].
bool matchesAll(Pattern pattern, String string) =>
pattern.matchAsPrefix(string)?.end == string.length;
/// Like [Future.wait] with `eagerError: true`, but reports errors after the
/// first using [registerException] rather than silently ignoring them.
Future waitAndReportErrors(Iterable<Future> futures) {
var errored = false;
return Future.wait(futures.map((future) {
// Avoid async/await so that we synchronously add error handlers for the
// futures to keep them from top-leveling.
return future.catchError((error, stackTrace) {
if (!errored) {
errored = true;
throw error;
} else {
registerException(error, stackTrace);
}
});
}));
}