| // 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:convert'; | |
| import 'package:path/path.dart' as p; | |
| import 'package:term_glyph/term_glyph.dart' as glyph; | |
| import 'package:test/test.dart'; | |
| import 'descriptor.dart'; | |
| import 'sandbox.dart'; | |
| /// A UTF-8 codec that allows malformed byte sequences. | |
| const utf8 = Utf8Codec(allowMalformed: true); | |
| /// 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'); | |
| /// Returns a human-readable description of a directory with the given [name] | |
| /// and [contents]. | |
| String describeDirectory(String name, List<Descriptor> contents) { | |
| if (contents.isEmpty) return name; | |
| final buffer = StringBuffer(); | |
| buffer.writeln(name); | |
| for (var entry in contents.take(contents.length - 1)) { | |
| final entryString = prefixLines( | |
| entry.describe(), | |
| '${glyph.verticalLine} ', | |
| first: '${glyph.teeRight}${glyph.horizontalLine}' | |
| '${glyph.horizontalLine} ', | |
| ); | |
| buffer.writeln(entryString); | |
| } | |
| final lastEntryString = prefixLines( | |
| contents.last.describe(), | |
| ' ', | |
| first: '${glyph.bottomLeftCorner}${glyph.horizontalLine}' | |
| '${glyph.horizontalLine} ', | |
| ); | |
| buffer.write(lastEntryString); | |
| return buffer.toString(); | |
| } | |
| /// 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, | |
| }) { | |
| single ??= first ?? last ?? prefix; | |
| first ??= prefix; | |
| last ??= prefix; | |
| final lines = text.split('\n'); | |
| if (lines.length == 1) return '$single$text'; | |
| final buffer = 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<List<T>> waitAndReportErrors<T>(Iterable<Future<T>> 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. | |
| future.catchError( | |
| // ignore: body_might_complete_normally_catch_error | |
| (Object error, StackTrace stackTrace) { | |
| if (!errored) { | |
| errored = true; | |
| throw error; // ignore: only_throw_errors | |
| } else { | |
| registerException(error, stackTrace); | |
| } | |
| }, | |
| ), | |
| ), | |
| ); | |
| } |