From 13dbc20e9d5c354b670353220c3ab95642ae228c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 7 Nov 2022 08:13:29 -0800 Subject: [PATCH] update lints (#45) --- .github/workflows/test-package.yml | 2 +- CHANGELOG.md | 2 + README.md | 23 ++- analysis_options.yaml | 75 ++++----- example/example.dart | 25 +++ lib/src/directory_descriptor.dart | 90 ++++++----- lib/src/file_descriptor.dart | 43 ++--- lib/src/nothing_descriptor.dart | 6 +- lib/src/pattern_descriptor.dart | 60 +++---- lib/src/sandbox.dart | 4 +- lib/src/utils.dart | 74 ++++++--- lib/test_descriptor.dart | 13 +- pubspec.yaml | 10 +- test/directory_test.dart | 246 ++++++++++++++++++----------- test/file_test.dart | 107 +++++++++---- test/nothing_test.dart | 36 +++-- test/pattern_test.dart | 38 +++-- test/utils.dart | 24 +-- 18 files changed, 531 insertions(+), 347 deletions(-) create mode 100644 example/example.dart diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 38089b6..0de63d5 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -49,7 +49,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.12.0, dev] + sdk: [2.17.0, dev] steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d diff --git a/CHANGELOG.md b/CHANGELOG.md index abe849a..aad67bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.0.2-dev + ## 2.0.1 * Populate the pubspec `repository` field. diff --git a/README.md b/README.md index 94422ea..d50e776 100644 --- a/README.md +++ b/README.md @@ -21,25 +21,22 @@ define a filesystem structure that can be created using ```dart import 'dart:io'; +import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; void main() { - test("Directory.rename", () async { - await d.dir("parent", [ - d.file("sibling", "sibling-contents"), - d.dir("old-name", [ - d.file("child", "child-contents") - ]) + test('Directory.rename', () async { + await d.dir('parent', [ + d.file('sibling', 'sibling-contents'), + d.dir('old-name', [d.file('child', 'child-contents')]) ]).create(); - await new Directory("${d.sandbox}/parent/old-name") - .rename("${d.sandbox}/parent/new-name"); + await Directory('${d.sandbox}/parent/old-name') + .rename('${d.sandbox}/parent/new-name'); - await d.dir("parent", [ - d.file("sibling", "sibling-contents"), - d.dir("new-name", [ - d.file("child", "child-contents") - ]) + await d.dir('parent', [ + d.file('sibling', 'sibling-contents'), + d.dir('new-name', [d.file('child', 'child-contents')]) ]).validate(); }); } diff --git a/analysis_options.yaml b/analysis_options.yaml index 200a3d4..7806fc0 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,62 +1,57 @@ +# https://dart.dev/guides/language/analysis-options include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false + language: + strict-casts: true + strict-inference: true + strict-raw-types: true linter: rules: - - annotate_overrides + - always_declare_return_types + - avoid_bool_literals_in_conditional_expressions + - avoid_catching_errors + - avoid_classes_with_only_static_members - avoid_dynamic_calls - - avoid_function_literals_in_foreach_calls - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - - avoid_relative_lib_imports + - avoid_private_typedef_functions + - avoid_redundant_argument_values - avoid_returning_null + - avoid_returning_null_for_future + - avoid_returning_this - avoid_unused_constructor_parameters - - await_only_futures - - camel_case_types + - avoid_void_async - cancel_subscriptions - comment_references - - constant_identifier_names - - control_flow_in_finally - directives_ordering - - empty_catches - - empty_constructor_bodies - - empty_statements - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type - - library_names - - library_prefixes - - list_remove_unrelated_type + - join_return_with_assignment + - lines_longer_than_80_chars + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - - non_constant_identifier_names + - no_runtimeType_toString - omit_local_variable_types - only_throw_errors - - overridden_fields - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_adjacent_string_concatenation - - prefer_collection_literals - - prefer_conditional_assignment + - prefer_asserts_in_initializer_lists - prefer_const_constructors - - prefer_final_fields - - prefer_generic_function_type_aliases - - prefer_initializing_formals - - prefer_interpolation_to_compose_strings + - prefer_const_declarations + - prefer_expression_function_bodies + - prefer_final_locals + - prefer_relative_imports - prefer_single_quotes - - prefer_typing_uninitialized_variables - - slash_for_doc_comments + - sort_pub_dependencies - test_types_in_equals - throw_in_finally - - type_init_formals - - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_getters_setters + - type_annotate_public_apis + - unawaited_futures + - unnecessary_await_in_return - unnecessary_lambdas - - unnecessary_new - - unnecessary_null_aware_assignments + - unnecessary_parenthesis + - unnecessary_raw_strings - unnecessary_statements - - unnecessary_this + - use_if_null_to_convert_nulls_to_bools + - use_raw_strings + - use_string_buffers + - use_super_parameters + - require_trailing_commas diff --git a/example/example.dart b/example/example.dart new file mode 100644 index 0000000..ef5917e --- /dev/null +++ b/example/example.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2022, 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:io'; + +import 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; + +void main() { + test('Directory.rename', () async { + await d.dir('parent', [ + d.file('sibling', 'sibling-contents'), + d.dir('old-name', [d.file('child', 'child-contents')]) + ]).create(); + + await Directory('${d.sandbox}/parent/old-name') + .rename('${d.sandbox}/parent/new-name'); + + await d.dir('parent', [ + d.file('sibling', 'sibling-contents'), + d.dir('new-name', [d.file('child', 'child-contents')]) + ]).validate(); + }); +} diff --git a/lib/src/directory_descriptor.dart b/lib/src/directory_descriptor.dart index e4f7b80..c4f2694 100644 --- a/lib/src/directory_descriptor.dart +++ b/lib/src/directory_descriptor.dart @@ -29,14 +29,13 @@ class DirectoryDescriptor extends Descriptor { /// [sandbox]. Directory get io => Directory(p.join(sandbox, name)); - DirectoryDescriptor(String name, Iterable contents) - : contents = contents.toList(), - super(name); + DirectoryDescriptor(super.name, Iterable contents) + : contents = contents.toList(); /// Creates a directory descriptor named [name] that describes the physical /// directory at [path]. - factory DirectoryDescriptor.fromFilesystem(String name, String path) { - return DirectoryDescriptor( + factory DirectoryDescriptor.fromFilesystem(String name, String path) => + DirectoryDescriptor( name, Directory(path).listSync().map((entity) { // Ignore hidden files. @@ -44,32 +43,37 @@ class DirectoryDescriptor extends Descriptor { if (entity is Directory) { return DirectoryDescriptor.fromFilesystem( - p.basename(entity.path), entity.path); + p.basename(entity.path), + entity.path, + ); } else if (entity is File) { return FileDescriptor( - p.basename(entity.path), entity.readAsBytesSync()); + p.basename(entity.path), + entity.readAsBytesSync(), + ); } // Ignore broken symlinks. return null; - }).whereType()); - } + }).whereType(), + ); @override Future create([String? parent]) async { - var fullPath = p.join(parent ?? sandbox, name); + final fullPath = p.join(parent ?? sandbox, name); await Directory(fullPath).create(recursive: true); await Future.wait(contents.map((entry) => entry.create(fullPath))); } @override Future validate([String? parent]) async { - var fullPath = p.join(parent ?? sandbox, name); + final fullPath = p.join(parent ?? sandbox, name); if (!(await Directory(fullPath).exists())) { fail('Directory not found: "${prettyPath(fullPath)}".'); } await waitAndReportErrors( - contents.map((entry) => entry.validate(fullPath))); + contents.map((entry) => entry.validate(fullPath)), + ); } /// Treats this descriptor as a virtual filesystem and loads the binary @@ -80,35 +84,47 @@ class DirectoryDescriptor extends Descriptor { Stream> _load(String path, [String? parents]) { if (!p.url.isWithin('.', path)) { throw ArgumentError.value( - path, 'path', 'must be relative and beneath the base URL.'); + path, + 'path', + 'must be relative and beneath the base URL.', + ); } - return StreamCompleter.fromFuture(Future.sync(() { - var split = p.url.split(p.url.normalize(path)); - var file = split.length == 1; - var matchingEntries = contents.where((entry) { - return entry.name == split.first && - (file ? entry is FileDescriptor : entry is DirectoryDescriptor); - }).toList(); - - var type = file ? 'file' : 'directory'; - var parentsAndSelf = parents == null ? name : p.url.join(parents, name); - if (matchingEntries.isEmpty) { - fail('Couldn\'t find a $type descriptor named "${split.first}" within ' - '"$parentsAndSelf".'); - } else if (matchingEntries.length > 1) { - fail('Found multiple $type descriptors named "${split.first}" within ' - '"$parentsAndSelf".'); - } else { - var remainingPath = split.sublist(1); - if (remainingPath.isEmpty) { - return (matchingEntries.first as FileDescriptor).readAsBytes(); + return StreamCompleter.fromFuture( + Future.sync(() { + final split = p.url.split(p.url.normalize(path)); + final file = split.length == 1; + final matchingEntries = contents + .where( + (entry) => + entry.name == split.first && + (file + ? entry is FileDescriptor + : entry is DirectoryDescriptor), + ) + .toList(); + + final type = file ? 'file' : 'directory'; + final parentsAndSelf = + parents == null ? name : p.url.join(parents, name); + if (matchingEntries.isEmpty) { + fail( + 'Couldn\'t find a $type descriptor named "${split.first}" within ' + '"$parentsAndSelf".'); + } else if (matchingEntries.length > 1) { + fail('Found multiple $type descriptors named "${split.first}" within ' + '"$parentsAndSelf".'); } else { - return (matchingEntries.first as DirectoryDescriptor) - ._load(p.url.joinAll(remainingPath), parentsAndSelf); + final remainingPath = split.sublist(1); + if (remainingPath.isEmpty) { + return (matchingEntries.first as FileDescriptor).readAsBytes(); + } else { + return (matchingEntries.first as DirectoryDescriptor) + ._load(p.url.joinAll(remainingPath), parentsAndSelf); + } } - } - })); + }), + ); } @override diff --git a/lib/src/file_descriptor.dart b/lib/src/file_descriptor.dart index b9eae6e..2daedd5 100644 --- a/lib/src/file_descriptor.dart +++ b/lib/src/file_descriptor.dart @@ -33,8 +33,8 @@ abstract class FileDescriptor extends Descriptor { /// If [contents] isn't passed, [create] creates an empty file and [validate] /// verifies that the file is empty. /// - /// To match a [Matcher] against a file's binary contents, use [new - /// FileDescriptor.binaryMatcher] instead. + /// To match a [Matcher] against a file's binary contents, use + /// [FileDescriptor.binaryMatcher] instead. factory FileDescriptor(String name, Object? contents) { if (contents is String) return _StringFileDescriptor(name, contents); if (contents is List) { @@ -57,15 +57,15 @@ abstract class FileDescriptor extends Descriptor { _MatcherFileDescriptor(name, matcher, isBinary: true); /// A protected constructor that's only intended for subclasses. - FileDescriptor.protected(String name) : super(name); + FileDescriptor.protected(super.name); @override Future create([String? parent]) async { // Create the stream before we call [File.openWrite] because it may fail // fast (e.g. if this is a matcher file). - var file = File(p.join(parent ?? sandbox, name)).openWrite(); + final file = File(p.join(parent ?? sandbox, name)).openWrite(); try { - await readAsBytes().listen(file.add).asFuture(); + await readAsBytes().forEach(file.add); } finally { await file.close(); } @@ -73,8 +73,8 @@ abstract class FileDescriptor extends Descriptor { @override Future validate([String? parent]) async { - var fullPath = p.join(parent ?? sandbox, name); - var pretty = prettyPath(fullPath); + final fullPath = p.join(parent ?? sandbox, name); + final pretty = prettyPath(fullPath); if (!(await File(fullPath).exists())) { fail('File not found: "$pretty".'); } @@ -107,14 +107,14 @@ class _BinaryFileDescriptor extends FileDescriptor { /// The contents of this descriptor's file. final List _contents; - _BinaryFileDescriptor(String name, this._contents) : super.protected(name); + _BinaryFileDescriptor(super.name, this._contents) : super.protected(); @override Stream> readAsBytes() => Stream.fromIterable([_contents]); @override Future _validate(String prettPath, List actualContents) async { - if (const IterableEquality().equals(_contents, actualContents)) return; + if (const IterableEquality().equals(_contents, actualContents)) return; // TODO(nweiz): show a hex dump here if the data is small enough. fail('File "$prettPath" didn\'t contain the expected binary data.'); } @@ -124,7 +124,7 @@ class _StringFileDescriptor extends FileDescriptor { /// The contents of this descriptor's file. final String _contents; - _StringFileDescriptor(String name, this._contents) : super.protected(name); + _StringFileDescriptor(super.name, this._contents) : super.protected(); @override Future read() async => _contents; @@ -135,20 +135,23 @@ class _StringFileDescriptor extends FileDescriptor { @override void _validate(String prettyPath, List actualContents) { - var actualContentsText = utf8.decode(actualContents); + final actualContentsText = utf8.decode(actualContents); if (_contents == actualContentsText) return; fail(_textMismatchMessage(prettyPath, _contents, actualContentsText)); } String _textMismatchMessage( - String prettyPath, String expected, String actual) { + String prettyPath, + String expected, + String actual, + ) { final expectedLines = expected.split('\n'); final actualLines = actual.split('\n'); - var results = []; + final results = []; // Compare them line by line to see which ones match. - var length = math.max(expectedLines.length, actualLines.length); + final length = math.max(expectedLines.length, actualLines.length); for (var i = 0; i < length; i++) { if (i >= actualLines.length) { // Missing output. @@ -157,8 +160,8 @@ class _StringFileDescriptor extends FileDescriptor { // Unexpected extra output. results.add('X ${actualLines[i]}'); } else { - var expectedLine = expectedLines[i]; - var actualLine = actualLines[i]; + final expectedLine = expectedLines[i]; + final actualLine = actualLines[i]; if (expectedLine != actualLine) { // Mismatched lines. @@ -185,9 +188,9 @@ class _MatcherFileDescriptor extends FileDescriptor { /// contents. final bool _isBinary; - _MatcherFileDescriptor(String name, this._matcher, {bool isBinary = false}) + _MatcherFileDescriptor(super.name, this._matcher, {bool isBinary = false}) : _isBinary = isBinary, - super.protected(name); + super.protected(); @override Stream> readAsBytes() => @@ -197,7 +200,9 @@ class _MatcherFileDescriptor extends FileDescriptor { Future _validate(String prettyPath, List actualContents) async { try { expect( - _isBinary ? actualContents : utf8.decode(actualContents), _matcher); + _isBinary ? actualContents : utf8.decode(actualContents), + _matcher, + ); } on TestFailure catch (error) { fail('Invalid contents for file "$prettyPath":\n${error.message}'); } diff --git a/lib/src/nothing_descriptor.dart b/lib/src/nothing_descriptor.dart index 22e7bc8..da9e2a2 100644 --- a/lib/src/nothing_descriptor.dart +++ b/lib/src/nothing_descriptor.dart @@ -15,15 +15,15 @@ import 'utils.dart'; /// /// Calling [create] does nothing. class NothingDescriptor extends Descriptor { - NothingDescriptor(String name) : super(name); + NothingDescriptor(super.name); @override Future create([String? parent]) async {} @override Future validate([String? parent]) async { - var fullPath = p.join(parent ?? sandbox, name); - var pretty = prettyPath(fullPath); + final fullPath = p.join(parent ?? sandbox, name); + final pretty = prettyPath(fullPath); if (File(fullPath).existsSync()) { fail('Expected nothing to exist at "$pretty", but found a file.'); } else if (Directory(fullPath).existsSync()) { diff --git a/lib/src/pattern_descriptor.dart b/lib/src/pattern_descriptor.dart index ed1b556..d55afc0 100644 --- a/lib/src/pattern_descriptor.dart +++ b/lib/src/pattern_descriptor.dart @@ -13,11 +13,6 @@ import 'descriptor.dart'; import 'sandbox.dart'; import 'utils.dart'; -/// A function that takes a name for a [Descriptor] and returns a [Descriptor]. -/// This is used for [PatternDescriptor]s, where the name isn't known -/// ahead-of-time. -typedef _EntryCreator = Descriptor Function(String name); - /// A descriptor that matches filesystem entity names by [Pattern] rather than /// by exact [String]. /// @@ -29,7 +24,7 @@ class PatternDescriptor extends Descriptor { /// The function used to generate the [Descriptor] for filesystem entities /// matching [pattern]. - final _EntryCreator _fn; + final Descriptor Function(String) _fn; PatternDescriptor(this.pattern, Descriptor Function(String basename) child) : _fn = child, @@ -42,39 +37,44 @@ class PatternDescriptor extends Descriptor { /// `this` is considered valid. @override Future validate([String? parent]) async { - var inSandbox = parent == null; + final inSandbox = parent == null; parent ??= sandbox; - var matchingEntries = await Directory(parent) + final matchingEntries = await Directory(parent) .list() - .map((entry) => - entry is File ? entry.resolveSymbolicLinksSync() : entry.path) + .map( + (entry) => + entry is File ? entry.resolveSymbolicLinksSync() : entry.path, + ) .where((entry) => matchesAll(pattern, p.basename(entry))) .toList(); matchingEntries.sort(); - var location = inSandbox ? 'sandbox' : '"${prettyPath(parent)}"'; + final location = inSandbox ? 'sandbox' : '"${prettyPath(parent)}"'; if (matchingEntries.isEmpty) { fail('No entries found in $location matching $_patternDescription.'); } - var results = await Future.wait(matchingEntries - .map((entry) { - var basename = p.basename(entry); - return runZonedGuarded(() { - return Result.capture(Future.sync(() async { - await _fn(basename).validate(parent); - return basename; - })); - }, (_, __) { - // Validate may produce multiple errors, but we ignore all but the first - // to avoid cluttering the user with many different errors from many - // different un-matched entries. - }); - }) - .whereType>>() - .toList()); + final results = await Future.wait( + matchingEntries + .map((entry) { + final basename = p.basename(entry); + return runZonedGuarded( + () => Result.capture( + Future.sync(() async { + await _fn(basename).validate(parent); + return basename; + }), + ), (_, __) { + // Validate may produce multiple errors, but we ignore all but the + // first to avoid cluttering the user with many different errors + // from many different un-matched entries. + }); + }) + .whereType>>() + .toList(), + ); - var successes = results.where((result) => result.isValue).toList(); + final successes = results.where((result) => result.isValue).toList(); if (successes.isEmpty) { await waitAndReportErrors(results.map((result) => result.asFuture)); } else if (successes.length > 1) { @@ -91,8 +91,8 @@ class PatternDescriptor extends Descriptor { if (pattern is String) return '"$pattern"'; if (pattern is! RegExp) return '$pattern'; - var regExp = pattern as RegExp; - var flags = StringBuffer(); + final regExp = pattern as RegExp; + final flags = StringBuffer(); if (!regExp.isCaseSensitive) flags.write('i'); if (regExp.isMultiLine) flags.write('m'); return '/${regExp.pattern}/$flags'; diff --git a/lib/src/sandbox.dart b/lib/src/sandbox.dart index 1f5847f..128b6a9 100644 --- a/lib/src/sandbox.dart +++ b/lib/src/sandbox.dart @@ -16,12 +16,12 @@ String get sandbox { if (_sandbox != null) return _sandbox!; // Resolve symlinks so we don't end up with inconsistent paths on Mac OS where // /tmp is symlinked. - var sandbox = _sandbox = Directory.systemTemp + final sandbox = _sandbox = Directory.systemTemp .createTempSync('dart_test_') .resolveSymbolicLinksSync(); addTearDown(() async { - var sandbox = _sandbox!; + final sandbox = _sandbox!; _sandbox = null; await Directory(sandbox).delete(recursive: true); }); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 26fd3ab..f3542bd 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -12,11 +12,16 @@ import 'descriptor.dart'; import 'sandbox.dart'; /// A UTF-8 codec that allows malformed byte sequences. -final utf8 = const Utf8Codec(allowMalformed: true); +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: '| '); +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) => @@ -30,18 +35,24 @@ String bullet(Iterable strings) => strings.map(addBullet).join('\n'); String describeDirectory(String name, List contents) { if (contents.isEmpty) return name; - var buffer = StringBuffer(); + final buffer = StringBuffer(); buffer.writeln(name); for (var entry in contents.take(contents.length - 1)) { - var entryString = prefixLines(entry.describe(), '${glyph.verticalLine} ', - first: '${glyph.teeRight}${glyph.horizontalLine}' - '${glyph.horizontalLine} '); + final entryString = prefixLines( + entry.describe(), + '${glyph.verticalLine} ', + first: '${glyph.teeRight}${glyph.horizontalLine}' + '${glyph.horizontalLine} ', + ); buffer.writeln(entryString); } - var lastEntryString = prefixLines(contents.last.describe(), ' ', - first: '${glyph.bottomLeftCorner}${glyph.horizontalLine}' - '${glyph.horizontalLine} '); + final lastEntryString = prefixLines( + contents.last.describe(), + ' ', + first: '${glyph.bottomLeftCorner}${glyph.horizontalLine}' + '${glyph.horizontalLine} ', + ); buffer.write(lastEntryString); return buffer.toString(); } @@ -52,16 +63,21 @@ String describeDirectory(String name, List contents) { /// 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}) { +String prefixLines( + String text, + String prefix, { + String? first, + String? last, + String? single, +}) { single ??= first ?? last ?? prefix; first ??= prefix; last ??= prefix; - var lines = text.split('\n'); + final lines = text.split('\n'); if (lines.length == 1) return '$single$text'; - var buffer = StringBuffer('$first${lines.first}\n'); + final buffer = StringBuffer('$first${lines.first}\n'); for (var line in lines.skip(1).take(lines.length - 2)) { buffer.writeln('$prefix$line'); } @@ -90,16 +106,22 @@ bool matchesAll(Pattern pattern, String string) => /// first using [registerException] rather than silently ignoring them. Future> waitAndReportErrors(Iterable> 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((Object error, StackTrace stackTrace) { - if (!errored) { - errored = true; - throw error; // ignore: only_throw_errors - } else { - registerException(error, stackTrace); - } - }); - })); + 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); + } + }, + ), + ), + ); } diff --git a/lib/test_descriptor.dart b/lib/test_descriptor.dart index cf5e07f..b302910 100644 --- a/lib/test_descriptor.dart +++ b/lib/test_descriptor.dart @@ -30,9 +30,10 @@ export 'src/sandbox.dart' show sandbox; /// If [contents] isn't passed, [Descriptor.create] creates an empty file and /// [Descriptor.validate] verifies that the file is empty. /// -/// To match a [Matcher] against a file's binary contents, use [new -/// FileDescriptor.binaryMatcher] instead. -FileDescriptor file(String name, [contents]) => FileDescriptor(name, contents); +/// To match a [Matcher] against a file's binary contents, use +/// [FileDescriptor.binaryMatcher] instead. +FileDescriptor file(String name, [Object? contents]) => + FileDescriptor(name, contents); /// Creates a new [DirectoryDescriptor] descriptor with [name] and [contents]. /// @@ -60,12 +61,14 @@ NothingDescriptor nothing(String name) => NothingDescriptor(name); /// /// [Descriptor.create] is not supported for this descriptor. PatternDescriptor pattern( - Pattern name, Descriptor Function(String basename) child) => + Pattern name, + Descriptor Function(String basename) child, +) => PatternDescriptor(name, child); /// A convenience method for creating a [PatternDescriptor] descriptor that /// constructs a [FileDescriptor] descriptor. -PatternDescriptor filePattern(Pattern name, [contents]) => +PatternDescriptor filePattern(Pattern name, [Object? contents]) => pattern(name, (realName) => file(realName, contents)); /// A convenience method for creating a [PatternDescriptor] descriptor that diff --git a/pubspec.yaml b/pubspec.yaml index 0f3836c..1e01da0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,19 +1,17 @@ name: test_descriptor -version: 2.0.1 -description: An API for defining and verifying directory structures. +version: 2.0.2-dev +description: An API for defining and verifying files and directory structures. repository: https://github.com/dart-lang/test_descriptor environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' dependencies: async: ^2.5.0 collection: ^1.15.0 - matcher: ^0.12.10 - meta: ^1.3.0 path: ^1.8.0 term_glyph: ^1.2.0 test: ^1.16.0 dev_dependencies: - lints: ^1.0.0 + lints: ^2.0.0 diff --git a/test/directory_test.dart b/test/directory_test.dart index c669be8..b0b2a22 100644 --- a/test/directory_test.dart +++ b/test/directory_test.dart @@ -11,7 +11,6 @@ import 'dart:io'; import 'package:path/path.dart' as p; import 'package:term_glyph/term_glyph.dart' as term_glyph; import 'package:test/test.dart'; - import 'package:test_descriptor/test_descriptor.dart' as d; import 'utils.dart'; @@ -28,34 +27,40 @@ void main() { d.file('file2.txt', 'contents2') ]).create(); - expect(File(p.join(d.sandbox, 'dir', 'file1.txt')).readAsString(), - completion(equals('contents1'))); - expect(File(p.join(d.sandbox, 'dir', 'file2.txt')).readAsString(), - completion(equals('contents2'))); expect( - File(p.join(d.sandbox, 'dir', 'subdir', 'subfile1.txt')) - .readAsString(), - completion(equals('subcontents1'))); + File(p.join(d.sandbox, 'dir', 'file1.txt')).readAsString(), + completion(equals('contents1')), + ); + expect( + File(p.join(d.sandbox, 'dir', 'file2.txt')).readAsString(), + completion(equals('contents2')), + ); + expect( + File(p.join(d.sandbox, 'dir', 'subdir', 'subfile1.txt')).readAsString(), + completion(equals('subcontents1')), + ); expect( - File(p.join(d.sandbox, 'dir', 'subdir', 'subfile2.txt')) - .readAsString(), - completion(equals('subcontents2'))); + File(p.join(d.sandbox, 'dir', 'subdir', 'subfile2.txt')).readAsString(), + completion(equals('subcontents2')), + ); }); test('works if the directory already exists', () async { await d.dir('dir').create(); await d.dir('dir', [d.file('name.txt', 'contents')]).create(); - expect(File(p.join(d.sandbox, 'dir', 'name.txt')).readAsString(), - completion(equals('contents'))); + expect( + File(p.join(d.sandbox, 'dir', 'name.txt')).readAsString(), + completion(equals('contents')), + ); }); }); group('validate()', () { test('completes successfully if the filesystem matches the descriptor', () async { - var dirPath = p.join(d.sandbox, 'dir'); - var subdirPath = p.join(dirPath, 'subdir'); + final dirPath = p.join(d.sandbox, 'dir'); + final subdirPath = p.join(dirPath, 'subdir'); await Directory(subdirPath).create(recursive: true); await File(p.join(dirPath, 'file1.txt')).writeAsString('contents1'); await File(p.join(dirPath, 'file2.txt')).writeAsString('contents2'); @@ -75,85 +80,107 @@ void main() { }); test("fails if the directory doesn't exist", () async { - var dirPath = p.join(d.sandbox, 'dir'); + final dirPath = p.join(d.sandbox, 'dir'); await Directory(dirPath).create(); await File(p.join(dirPath, 'file1.txt')).writeAsString('contents1'); await File(p.join(dirPath, 'file2.txt')).writeAsString('contents2'); expect( - d.dir('dir', [ - d.dir('subdir', [ - d.file('subfile1.txt', 'subcontents1'), - d.file('subfile2.txt', 'subcontents2') - ]), - d.file('file1.txt', 'contents1'), - d.file('file2.txt', 'contents2') - ]).validate(), - throwsA(toString( - equals('Directory not found: "${p.join('dir', 'subdir')}".')))); + d.dir('dir', [ + d.dir('subdir', [ + d.file('subfile1.txt', 'subcontents1'), + d.file('subfile2.txt', 'subcontents2') + ]), + d.file('file1.txt', 'contents1'), + d.file('file2.txt', 'contents2') + ]).validate(), + throwsA( + toString( + equals('Directory not found: "${p.join('dir', 'subdir')}".'), + ), + ), + ); }); test('emits an error for each child that fails to validate', () async { - var dirPath = p.join(d.sandbox, 'dir'); - var subdirPath = p.join(dirPath, 'subdir'); + final dirPath = p.join(d.sandbox, 'dir'); + final subdirPath = p.join(dirPath, 'subdir'); await Directory(subdirPath).create(recursive: true); await File(p.join(dirPath, 'file1.txt')).writeAsString('contents1'); await File(p.join(subdirPath, 'subfile2.txt')) .writeAsString('subwrongtents2'); var errors = 0; - var controller = StreamController(); - runZonedGuarded(() { - d.dir('dir', [ - d.dir('subdir', [ - d.file('subfile1.txt', 'subcontents1'), - d.file('subfile2.txt', 'subcontents2') - ]), - d.file('file1.txt', 'contents1'), - d.file('file2.txt', 'contents2') - ]).validate(); - }, - expectAsync2((error, _) { + final controller = StreamController(); + runZonedGuarded( + () { + d.dir('dir', [ + d.dir('subdir', [ + d.file('subfile1.txt', 'subcontents1'), + d.file('subfile2.txt', 'subcontents2') + ]), + d.file('file1.txt', 'contents1'), + d.file('file2.txt', 'contents2') + ]).validate(); + }, + expectAsync2( + (error, _) { errors++; controller.add(error.toString()); if (errors == 3) controller.close(); - }, count: 3)); + }, + count: 3, + ), + ); expect( - controller.stream.toList(), - completion(allOf([ + controller.stream.toList(), + completion( + allOf([ contains( - 'File not found: "${p.join('dir', 'subdir', 'subfile1.txt')}".'), + 'File not found: "${p.join('dir', 'subdir', 'subfile1.txt')}".', + ), contains('File not found: "${p.join('dir', 'file2.txt')}".'), contains( - startsWith('File "${p.join('dir', 'subdir', 'subfile2.txt')}" ' - 'should contain:')), - ]))); + startsWith('File "${p.join('dir', 'subdir', 'subfile2.txt')}" ' + 'should contain:'), + ), + ]), + ), + ); }); }); group('load()', () { test('loads a file', () { - var dir = d.dir('dir', - [d.file('name.txt', 'contents'), d.file('other.txt', 'wrong')]); - expect(utf8.decodeStream(dir.load('name.txt')), - completion(equals('contents'))); + final dir = d.dir( + 'dir', + [d.file('name.txt', 'contents'), d.file('other.txt', 'wrong')], + ); + expect( + utf8.decodeStream(dir.load('name.txt')), + completion(equals('contents')), + ); }); test('loads a deeply-nested file', () { - var dir = d.dir('dir', [ - d.dir('subdir', - [d.file('name.txt', 'subcontents'), d.file('other.txt', 'wrong')]), + final dir = d.dir('dir', [ + d.dir( + 'subdir', + [d.file('name.txt', 'subcontents'), d.file('other.txt', 'wrong')], + ), d.dir('otherdir', [d.file('other.txt', 'wrong')]), d.file('name.txt', 'contents') ]); - expect(utf8.decodeStream(dir.load('subdir/name.txt')), - completion(equals('subcontents'))); + expect( + utf8.decodeStream(dir.load('subdir/name.txt')), + completion(equals('subcontents')), + ); }); test('fails to load a nested directory', () { - var dir = d.dir('dir', [ + final dir = d.dir('dir', [ d.dir('subdir', [ d.dir('subsubdir', [d.file('name.txt', 'subcontents')]) ]), @@ -161,52 +188,71 @@ void main() { ]); expect( - dir.load('subdir/subsubdir').toList(), - throwsA(toString(equals('Couldn\'t find a file descriptor named ' - '"subsubdir" within "dir/subdir".')))); + dir.load('subdir/subsubdir').toList(), + throwsA( + toString( + equals('Couldn\'t find a file descriptor named ' + '"subsubdir" within "dir/subdir".'), + ), + ), + ); }); test('fails to load an absolute path', () { - var dir = d.dir('dir', [d.file('name.txt', 'contents')]); + final dir = d.dir('dir', [d.file('name.txt', 'contents')]); expect(() => dir.load('/name.txt'), throwsArgumentError); }); test("fails to load '..'", () { - var dir = d.dir('dir', [d.file('name.txt', 'contents')]); + final dir = d.dir('dir', [d.file('name.txt', 'contents')]); expect(() => dir.load('..'), throwsArgumentError); }); test("fails to load a file that doesn't exist", () { - var dir = d.dir('dir', [ + final dir = d.dir('dir', [ d.dir('subdir', [d.file('name.txt', 'contents')]) ]); expect( - dir.load('subdir/not-name.txt').toList(), - throwsA(toString(equals('Couldn\'t find a file descriptor named ' - '"not-name.txt" within "dir/subdir".')))); + dir.load('subdir/not-name.txt').toList(), + throwsA( + toString( + equals('Couldn\'t find a file descriptor named ' + '"not-name.txt" within "dir/subdir".'), + ), + ), + ); }); test('fails to load a file that exists multiple times', () { - var dir = d.dir('dir', [ - d.dir('subdir', - [d.file('name.txt', 'contents'), d.file('name.txt', 'contents')]) + final dir = d.dir('dir', [ + d.dir( + 'subdir', + [d.file('name.txt', 'contents'), d.file('name.txt', 'contents')], + ) ]); expect( - dir.load('subdir/name.txt').toList(), - throwsA(toString(equals('Found multiple file descriptors named ' - '"name.txt" within "dir/subdir".')))); + dir.load('subdir/name.txt').toList(), + throwsA( + toString( + equals('Found multiple file descriptors named ' + '"name.txt" within "dir/subdir".'), + ), + ), + ); }); test('loads a file next to a subdirectory with the same name', () { - var dir = d.dir('dir', [ + final dir = d.dir('dir', [ d.file('name', 'contents'), d.dir('name', [d.file('subfile', 'contents')]) ]); expect( - utf8.decodeStream(dir.load('name')), completion(equals('contents'))); + utf8.decodeStream(dir.load('name')), + completion(equals('contents')), + ); }); }); @@ -222,18 +268,21 @@ void main() { }); test('lists the contents of the directory', () { - var dir = d.dir('dir', - [d.file('file1.txt', 'contents1'), d.file('file2.txt', 'contents2')]); + final dir = d.dir( + 'dir', + [d.file('file1.txt', 'contents1'), d.file('file2.txt', 'contents2')], + ); expect( - dir.describe(), - equals('dir\n' - '+-- file1.txt\n' - "'-- file2.txt")); + dir.describe(), + equals('dir\n' + '+-- file1.txt\n' + "'-- file2.txt"), + ); }); test('lists the contents of nested directories', () { - var dir = d.dir('dir', [ + final dir = d.dir('dir', [ d.file('file1.txt', 'contents1'), d.dir('subdir', [ d.file('subfile1.txt', 'subcontents1'), @@ -244,15 +293,16 @@ void main() { ]); expect( - dir.describe(), - equals('dir\n' - '+-- file1.txt\n' - '+-- subdir\n' - '| +-- subfile1.txt\n' - '| +-- subfile2.txt\n' - "| '-- subsubdir\n" - "| '-- subsubfile.txt\n" - "'-- file2.txt")); + dir.describe(), + equals('dir\n' + '+-- file1.txt\n' + '+-- subdir\n' + '| +-- subfile1.txt\n' + '| +-- subfile2.txt\n' + "| '-- subsubdir\n" + "| '-- subsubfile.txt\n" + "'-- file2.txt"), + ); }); test('with no contents returns the directory name', () { @@ -262,7 +312,7 @@ void main() { group('fromFilesystem()', () { test('creates a descriptor based on the physical filesystem', () async { - var dir = d.dir('dir', [ + final dir = d.dir('dir', [ d.dir('subdir', [ d.file('subfile1.txt', 'subcontents1'), d.file('subfile2.txt', 'subcontents2') @@ -272,7 +322,7 @@ void main() { ]); await dir.create(); - var descriptor = + final descriptor = d.DirectoryDescriptor.fromFilesystem('dir', p.join(d.sandbox, 'dir')); await descriptor.create(p.join(d.sandbox, 'dir2')); await dir.validate(p.join(d.sandbox, 'dir2')); @@ -288,13 +338,17 @@ void main() { d.file('.DS_Store', 'contents2') ]).create(); - var descriptor = d.DirectoryDescriptor.fromFilesystem( - 'dir2', p.join(d.sandbox, 'dir')); + final descriptor = d.DirectoryDescriptor.fromFilesystem( + 'dir2', + p.join(d.sandbox, 'dir'), + ); await descriptor.create(); await d.dir('dir2', [ - d.dir('subdir', - [d.file('subfile1.txt', 'subcontents1'), d.nothing('.hidden')]), + d.dir( + 'subdir', + [d.file('subfile1.txt', 'subcontents1'), d.nothing('.hidden')], + ), d.file('file1.txt', 'contents1'), d.nothing('.DS_Store') ]).validate(); diff --git a/test/file_test.dart b/test/file_test.dart index 13b7597..26a4528 100644 --- a/test/file_test.dart +++ b/test/file_test.dart @@ -18,28 +18,36 @@ void main() { test('creates a text file', () async { await d.file('name.txt', 'contents').create(); - expect(File(p.join(d.sandbox, 'name.txt')).readAsString(), - completion(equals('contents'))); + expect( + File(p.join(d.sandbox, 'name.txt')).readAsString(), + completion(equals('contents')), + ); }); test('creates a binary file', () async { await d.file('name.txt', [0, 1, 2, 3]).create(); - expect(File(p.join(d.sandbox, 'name.txt')).readAsBytes(), - completion(equals([0, 1, 2, 3]))); + expect( + File(p.join(d.sandbox, 'name.txt')).readAsBytes(), + completion(equals([0, 1, 2, 3])), + ); }); test('fails to create a matcher file', () async { expect( - d.file('name.txt', contains('foo')).create(), throwsUnsupportedError); + d.file('name.txt', contains('foo')).create(), + throwsUnsupportedError, + ); }); test('overwrites an existing file', () async { await d.file('name.txt', 'contents1').create(); await d.file('name.txt', 'contents2').create(); - expect(File(p.join(d.sandbox, 'name.txt')).readAsString(), - completion(equals('contents2'))); + expect( + File(p.join(d.sandbox, 'name.txt')).readAsString(), + completion(equals('contents2')), + ); }); }); @@ -72,62 +80,85 @@ void main() { test("fails if the text contents don't match", () async { await File(p.join(d.sandbox, 'name.txt')).writeAsString('wrong'); - expect(d.file('name.txt', 'contents').validate(), - throwsA(toString(startsWith('File "name.txt" should contain:')))); + expect( + d.file('name.txt', 'contents').validate(), + throwsA(toString(startsWith('File "name.txt" should contain:'))), + ); }); test("fails if the binary contents don't match", () async { await File(p.join(d.sandbox, 'name.txt')).writeAsBytes([5, 4, 3, 2]); expect( - d.file('name.txt', [0, 1, 2, 3]).validate(), - throwsA(toString(equals( - 'File "name.txt" didn\'t contain the expected binary data.')))); + d.file('name.txt', [0, 1, 2, 3]).validate(), + throwsA( + toString( + equals( + 'File "name.txt" didn\'t contain the expected binary data.', + ), + ), + ), + ); }); test("fails if the text contents don't match the matcher", () async { await File(p.join(d.sandbox, 'name.txt')).writeAsString('wrong'); expect( - d.file('name.txt', contains('ent')).validate(), - throwsA( - toString(startsWith('Invalid contents for file "name.txt":')))); + d.file('name.txt', contains('ent')).validate(), + throwsA( + toString(startsWith('Invalid contents for file "name.txt":')), + ), + ); }); test("fails if the binary contents don't match the matcher", () async { await File(p.join(d.sandbox, 'name.txt')).writeAsBytes([5, 4, 3, 2]); expect( - d.FileDescriptor.binaryMatcher('name.txt', contains(1)).validate(), - throwsA( - toString(startsWith('Invalid contents for file "name.txt":')))); + d.FileDescriptor.binaryMatcher('name.txt', contains(1)).validate(), + throwsA( + toString(startsWith('Invalid contents for file "name.txt":')), + ), + ); }); test("fails if invalid UTF-8 doesn't match a text matcher", () async { await File(p.join(d.sandbox, 'name.txt')).writeAsBytes([0xC3, 0x28]); expect( - d.file('name.txt', isEmpty).validate(), - throwsA(toString(allOf([ - startsWith('Invalid contents for file "name.txt":'), - contains('�') - ])))); + d.file('name.txt', isEmpty).validate(), + throwsA( + toString( + allOf([ + startsWith('Invalid contents for file "name.txt":'), + contains('�') + ]), + ), + ), + ); }); test("fails if there's no file", () { - expect(d.file('name.txt', 'contents').validate(), - throwsA(toString(equals('File not found: "name.txt".')))); + expect( + d.file('name.txt', 'contents').validate(), + throwsA(toString(equals('File not found: "name.txt".'))), + ); }); }); group('reading', () { test('read() returns the contents of a text file as a string', () { - expect(d.file('name.txt', 'contents').read(), - completion(equals('contents'))); + expect( + d.file('name.txt', 'contents').read(), + completion(equals('contents')), + ); }); test('read() returns the contents of a binary file as a string', () { - expect(d.file('name.txt', [0x68, 0x65, 0x6c, 0x6c, 0x6f]).read(), - completion(equals('hello'))); + expect( + d.file('name.txt', [0x68, 0x65, 0x6c, 0x6c, 0x6f]).read(), + completion(equals('hello')), + ); }); test('read() fails for a matcher file', () { @@ -136,19 +167,25 @@ void main() { test('readAsBytes() returns the contents of a text file as a byte stream', () { - expect(utf8.decodeStream(d.file('name.txt', 'contents').readAsBytes()), - completion(equals('contents'))); + expect( + utf8.decodeStream(d.file('name.txt', 'contents').readAsBytes()), + completion(equals('contents')), + ); }); test('readAsBytes() returns the contents of a binary file as a byte stream', () { - expect(byteStreamToList(d.file('name.txt', [0, 1, 2, 3]).readAsBytes()), - completion(equals([0, 1, 2, 3]))); + expect( + byteStreamToList(d.file('name.txt', [0, 1, 2, 3]).readAsBytes()), + completion(equals([0, 1, 2, 3])), + ); }); test('readAsBytes() fails for a matcher file', () { - expect(d.file('name.txt', contains('hi')).readAsBytes, - throwsUnsupportedError); + expect( + d.file('name.txt', contains('hi')).readAsBytes, + throwsUnsupportedError, + ); }); }); diff --git a/test/nothing_test.dart b/test/nothing_test.dart index a192087..23d91ff 100644 --- a/test/nothing_test.dart +++ b/test/nothing_test.dart @@ -28,25 +28,43 @@ void main() { test("fails if there's a file", () async { await d.file('name.txt', 'contents').create(); expect( - d.nothing('name.txt').validate(), - throwsA(toString(equals( - 'Expected nothing to exist at "name.txt", but found a file.')))); + d.nothing('name.txt').validate(), + throwsA( + toString( + equals( + 'Expected nothing to exist at "name.txt", but found a file.', + ), + ), + ), + ); }); test("fails if there's a directory", () async { await d.dir('dir').create(); expect( - d.nothing('dir').validate(), - throwsA(toString(equals( - 'Expected nothing to exist at "dir", but found a directory.')))); + d.nothing('dir').validate(), + throwsA( + toString( + equals( + 'Expected nothing to exist at "dir", but found a directory.', + ), + ), + ), + ); }); test("fails if there's a broken link", () async { await Link(p.join(d.sandbox, 'link')).create('nonexistent'); expect( - d.nothing('link').validate(), - throwsA(toString(equals( - 'Expected nothing to exist at "link", but found a link.')))); + d.nothing('link').validate(), + throwsA( + toString( + equals( + 'Expected nothing to exist at "link", but found a link.', + ), + ), + ), + ); }); }); } diff --git a/test/pattern_test.dart b/test/pattern_test.dart index eb7b815..3963fc0 100644 --- a/test/pattern_test.dart +++ b/test/pattern_test.dart @@ -15,14 +15,14 @@ void main() { test("succeeds if there's a file matching the pattern and the child", () async { await d.file('foo', 'blap').create(); - await d.filePattern(RegExp(r'f..'), 'blap').validate(); + await d.filePattern(RegExp('f..'), 'blap').validate(); }); test("succeeds if there's a directory matching the pattern and the child", () async { await d.dir('foo', [d.file('bar', 'baz')]).create(); - await d.dirPattern(RegExp(r'f..'), [d.file('bar', 'baz')]).validate(); + await d.dirPattern(RegExp('f..'), [d.file('bar', 'baz')]).validate(); }); test( @@ -32,29 +32,35 @@ void main() { await d.file('fee', 'blak').create(); await d.file('faa', 'blut').create(); - await d.filePattern(RegExp(r'f..'), 'blap').validate(); + await d.filePattern(RegExp('f..'), 'blap').validate(); }); test("fails if there's no file matching the pattern", () { expect( - d.filePattern(RegExp(r'f..'), 'bar').validate(), - throwsA( - toString(equals('No entries found in sandbox matching /f../.')))); + d.filePattern(RegExp('f..'), 'bar').validate(), + throwsA( + toString(equals('No entries found in sandbox matching /f../.')), + ), + ); }); test("fails if there's a file matching the pattern but not the entry", () async { await d.file('foo', 'bap').create(); - expect(d.filePattern(RegExp(r'f..'), 'bar').validate(), - throwsA(toString(startsWith('File "foo" should contain:')))); + expect( + d.filePattern(RegExp('f..'), 'bar').validate(), + throwsA(toString(startsWith('File "foo" should contain:'))), + ); }); test("fails if there's a dir matching the pattern but not the entry", () async { await d.dir('foo', [d.file('bar', 'bap')]).create(); - expect(d.dirPattern(RegExp(r'f..'), [d.file('bar', 'baz')]).validate(), - throwsA(toString(startsWith('File "foo/bar" should contain:')))); + expect( + d.dirPattern(RegExp('f..'), [d.file('bar', 'baz')]).validate(), + throwsA(toString(startsWith('File "foo/bar" should contain:'))), + ); }); test( @@ -64,9 +70,15 @@ void main() { await d.file('fee', 'bar').create(); await d.file('faa', 'bar').create(); expect( - d.filePattern(RegExp(r'f..'), 'bar').validate(), - throwsA(toString(startsWith( - 'Multiple valid entries found in sandbox matching /f../:')))); + d.filePattern(RegExp('f..'), 'bar').validate(), + throwsA( + toString( + startsWith( + 'Multiple valid entries found in sandbox matching /f../:', + ), + ), + ), + ); }); }); } diff --git a/test/utils.dart b/test/utils.dart index 5eb84f7..8b07e8c 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -5,18 +5,18 @@ import 'package:test/test.dart'; /// Converts a [Stream>] to a flat byte future. -Future> byteStreamToList(Stream> stream) { - return stream.fold([], (buffer, chunk) { - buffer.addAll(chunk); - return buffer; - }); -} +Future> byteStreamToList(Stream> stream) => + stream.fold([], (buffer, chunk) { + buffer.addAll(chunk); + return buffer; + }); /// Returns a matcher that verifies that the result of calling `toString()` /// matches [matcher]. -Matcher toString(matcher) { - return predicate((object) { - expect(object.toString(), matcher); - return true; - }, 'toString() matches $matcher'); -} +Matcher toString(Object? matcher) => predicate( + (object) { + expect(object.toString(), matcher); + return true; + }, + 'toString() matches $matcher', + );