diff --git a/.travis.yml b/.travis.yml index 50bda8c..502b3eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: dart dart: - dev - - 2.8.1 dart_task: - test @@ -14,9 +13,6 @@ matrix: - dart: dev dart_task: dartanalyzer: --fatal-infos --fatal-warnings . - - dart: 2.8.1 - dart_task: - dartanalyzer: --fatal-warnings . branches: only: [master] diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4156d..2c2bc32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ -## 1.2.1-dev +## 2.0.0-nullsafety -* Update minimum Dart SDK to `2.7.0`. +* Migrate to null safety. * Fix outdated URLs in `README.md`. +* BREAKING: Removed archive support. ## 1.2.0 diff --git a/lib/src/archive_descriptor.dart b/lib/src/archive_descriptor.dart deleted file mode 100644 index 7d96ce8..0000000 --- a/lib/src/archive_descriptor.dart +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) 2019, 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 'dart:io'; - -import 'package:archive/archive.dart'; -import 'package:async/async.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; - -import 'descriptor.dart'; -import 'directory_descriptor.dart'; -import 'file_descriptor.dart'; -import 'sandbox.dart'; -import 'utils.dart'; - -/// A [Descriptor] describing files in a Tar or Zip archive. -/// -/// The format is determined by the descriptor's file extension. -@sealed -class ArchiveDescriptor extends Descriptor implements FileDescriptor { - /// Descriptors for entries in this archive. - final List contents; - - /// Returns a `package:archive` [Archive] object that contains the contents of - /// this file. - Future get archive async { - var archive = Archive(); - (await _files(contents)).forEach(archive.addFile); - return archive; - } - - @override - File get io => File(p.join(sandbox, name)); - - /// Returns [ArchiveFile]s for each file in [descriptors]. - /// - /// If [parent] is passed, it's used as the parent directory for filenames. - Future> _files(Iterable descriptors, - [String parent]) async { - return (await waitAndReportErrors(descriptors.map((descriptor) async { - var fullName = - parent == null ? descriptor.name : '$parent/${descriptor.name}'; - - if (descriptor is FileDescriptor) { - var bytes = await collectBytes(descriptor.readAsBytes()); - return [ - ArchiveFile(fullName, bytes.length, bytes) - // Setting the mode and mod time are necessary to work around - // brendan-duncan/archive#76. - ..mode = 428 - ..lastModTime = DateTime.now().millisecondsSinceEpoch ~/ 1000 - ]; - } else if (descriptor is DirectoryDescriptor) { - return await _files(descriptor.contents, fullName); - } else { - throw UnsupportedError( - 'An archive can only be created from FileDescriptors and ' - 'DirectoryDescriptors.'); - } - }))) - .expand((files) => files); - } - - ArchiveDescriptor(String name, Iterable contents) - : contents = List.unmodifiable(contents), - super(name); - - @override - Future create([String parent]) async { - var path = p.join(parent ?? sandbox, name); - var file = File(path).openWrite(); - try { - try { - await readAsBytes().listen(file.add).asFuture(); - } finally { - await file.close(); - } - } catch (_) { - await File(path).delete(); - rethrow; - } - } - - @override - Future read() async => throw UnsupportedError( - 'ArchiveDescriptor.read() is not supported. Use Archive.readAsBytes() ' - 'instead.'); - - @override - Stream> readAsBytes() => Stream.fromFuture(() async { - return _encodeFunction()(await archive); - }()); - - @override - Future validate([String parent]) async { - // Access this first so we eaerly throw an error for a path with an invalid - // extension. - var decoder = _decodeFunction(); - - var fullPath = p.join(parent ?? sandbox, name); - var pretty = prettyPath(fullPath); - if (!(await File(fullPath).exists())) { - fail('File not found: "$pretty".'); - } - - var bytes = await File(fullPath).readAsBytes(); - Archive archive; - try { - archive = decoder(bytes); - } catch (_) { - // Catch every error to work around brendan-duncan/archive#77. - fail('File "$pretty" is not a valid archive.'); - } - - // Because validators expect to validate against a real filesystem, we have - // to extract the archive to a temp directory and run validation on that. - var tempDir = await Directory.systemTemp - .createTempSync('dart_test_') - .resolveSymbolicLinks(); - - try { - await waitAndReportErrors(archive.files.map((file) async { - var path = p.join(tempDir, file.name); - await Directory(p.dirname(path)).create(recursive: true); - await File(path).writeAsBytes(file.content as List); - })); - - await waitAndReportErrors(contents.map((entry) async { - try { - await entry.validate(tempDir); - } on TestFailure catch (error) { - // Replace the temporary directory with the path to the archive to - // make the error more user-friendly. - fail(error.message.replaceAll(tempDir, pretty)); - } - })); - } finally { - await Directory(tempDir).delete(recursive: true); - } - } - - /// Returns the function to use to encode this file to binary, based on its - /// [name]. - List Function(Archive) _encodeFunction() { - if (name.endsWith('.zip')) { - return ZipEncoder().encode; - } else if (name.endsWith('.tar')) { - return TarEncoder().encode; - } else if (name.endsWith('.tar.gz') || - name.endsWith('.tar.gzip') || - name.endsWith('.tgz')) { - return (archive) => GZipEncoder().encode(TarEncoder().encode(archive)); - } else if (name.endsWith('.tar.bz2') || name.endsWith('.tar.bzip2')) { - return (archive) => BZip2Encoder().encode(TarEncoder().encode(archive)); - } else { - throw UnsupportedError('Unknown file format $name.'); - } - } - - /// Returns the function to use to decode this file from binary, based on its - /// [name]. - Archive Function(List) _decodeFunction() { - if (name.endsWith('.zip')) { - return ZipDecoder().decodeBytes; - } else if (name.endsWith('.tar')) { - return TarDecoder().decodeBytes; - } else if (name.endsWith('.tar.gz') || - name.endsWith('.tar.gzip') || - name.endsWith('.tgz')) { - return (archive) => - TarDecoder().decodeBytes(GZipDecoder().decodeBytes(archive)); - } else if (name.endsWith('.tar.bz2') || name.endsWith('.tar.bzip2')) { - return (archive) => - TarDecoder().decodeBytes(BZip2Decoder().decodeBytes(archive)); - } else { - throw UnsupportedError('Unknown file format $name.'); - } - } - - @override - String describe() => describeDirectory(name, contents); -} diff --git a/lib/src/descriptor.dart b/lib/src/descriptor.dart index cd8f7cb..0d6c30c 100644 --- a/lib/src/descriptor.dart +++ b/lib/src/descriptor.dart @@ -15,11 +15,11 @@ abstract class Descriptor { /// Creates this entry within the [parent] directory, which defaults to /// [sandbox]. - Future create([String parent]); + Future create([String? parent]); /// Validates that the physical file system under [parent] (which defaults to /// [sandbox]) contains an entry that matches this descriptor. - Future validate([String parent]); + Future validate([String? parent]); /// Returns a human-friendly tree-style description of this descriptor. String describe(); diff --git a/lib/src/directory_descriptor.dart b/lib/src/directory_descriptor.dart index 1a8fe65..0927667 100644 --- a/lib/src/directory_descriptor.dart +++ b/lib/src/directory_descriptor.dart @@ -52,18 +52,18 @@ class DirectoryDescriptor extends Descriptor { } // Ignore broken symlinks. return null; - }).where((path) => path != null)); + }).whereType()); } @override - Future create([String parent]) async { + Future create([String? parent]) async { var 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 { + Future validate([String? parent]) async { var fullPath = p.join(parent ?? sandbox, name); if (!(await Directory(fullPath).exists())) { fail('Directory not found: "${prettyPath(fullPath)}".'); @@ -80,7 +80,7 @@ class DirectoryDescriptor extends Descriptor { /// The [parents] parameter should only be passed by subclasses of /// [DirectoryDescriptor] that are recursively calling [load]. It's the /// URL-format path of the directories that have been loaded so far. - Stream> load(url, [String parents]) { + Stream> load(url, [String? parents]) { String path; if (url is String) { path = url; diff --git a/lib/src/file_descriptor.dart b/lib/src/file_descriptor.dart index 3d634b6..f5d9216 100644 --- a/lib/src/file_descriptor.dart +++ b/lib/src/file_descriptor.dart @@ -60,7 +60,7 @@ abstract class FileDescriptor extends Descriptor { FileDescriptor.protected(String name) : super(name); @override - Future create([String parent]) async { + 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(); @@ -72,7 +72,7 @@ abstract class FileDescriptor extends Descriptor { } @override - Future validate([String parent]) async { + Future validate([String? parent]) async { var fullPath = p.join(parent ?? sandbox, name); var pretty = prettyPath(fullPath); if (!(await File(fullPath).exists())) { @@ -87,7 +87,7 @@ abstract class FileDescriptor extends Descriptor { /// /// The [prettyPath] is a human-friendly representation of the path to the /// descriptor. - Future _validate(String prettyPath, List binaryContents); + FutureOr _validate(String prettyPath, List binaryContents); /// Reads and decodes the contents of this descriptor as a UTF-8 string. /// @@ -113,8 +113,8 @@ class _BinaryFileDescriptor extends FileDescriptor { Stream> readAsBytes() => Stream.fromIterable([_contents]); @override - Future _validate(String prettPath, List actualContents) async { - if (const IterableEquality().equals(_contents, actualContents)) return null; + Future _validate(String prettPath, List actualContents) async { + 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.'); } @@ -134,9 +134,9 @@ class _StringFileDescriptor extends FileDescriptor { Stream.fromIterable([utf8.encode(_contents)]); @override - Future _validate(String prettyPath, List actualContents) { + void _validate(String prettyPath, List actualContents) { var actualContentsText = utf8.decode(actualContents); - if (_contents == actualContentsText) return null; + if (_contents == actualContentsText) return; fail(_textMismatchMessage(prettyPath, _contents, actualContentsText)); } @@ -194,7 +194,7 @@ class _MatcherFileDescriptor extends FileDescriptor { throw UnsupportedError("Matcher files can't be created or read."); @override - Future _validate(String prettyPath, List actualContents) async { + Future _validate(String prettyPath, List actualContents) async { try { expect( _isBinary ? actualContents : utf8.decode(actualContents), _matcher); diff --git a/lib/src/nothing_descriptor.dart b/lib/src/nothing_descriptor.dart index e70e890..22e7bc8 100644 --- a/lib/src/nothing_descriptor.dart +++ b/lib/src/nothing_descriptor.dart @@ -18,10 +18,10 @@ class NothingDescriptor extends Descriptor { NothingDescriptor(String name) : super(name); @override - Future create([String parent]) async {} + Future create([String? parent]) async {} @override - Future validate([String parent]) async { + Future validate([String? parent]) async { var fullPath = p.join(parent ?? sandbox, name); var pretty = prettyPath(fullPath); if (File(fullPath).existsSync()) { diff --git a/lib/src/pattern_descriptor.dart b/lib/src/pattern_descriptor.dart index 4c1760d..ed1b556 100644 --- a/lib/src/pattern_descriptor.dart +++ b/lib/src/pattern_descriptor.dart @@ -41,7 +41,7 @@ class PatternDescriptor extends Descriptor { /// in the constructor and validates the result. If exactly one succeeds, /// `this` is considered valid. @override - Future validate([String parent]) async { + Future validate([String? parent]) async { var inSandbox = parent == null; parent ??= sandbox; var matchingEntries = await Directory(parent) @@ -57,19 +57,22 @@ class PatternDescriptor extends Descriptor { 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. - }); - }).toList()); + 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()); var successes = results.where((result) => result.isValue).toList(); if (successes.isEmpty) { @@ -77,7 +80,7 @@ class PatternDescriptor extends Descriptor { } else if (successes.length > 1) { fail('Multiple valid entries found in $location matching ' '$_patternDescription:\n' - '${bullet(successes.map((result) => result.asValue.value))}'); + '${bullet(successes.map((result) => result.asValue!.value))}'); } } @@ -96,7 +99,7 @@ class PatternDescriptor extends Descriptor { } @override - Future create([String parent]) { + Future create([String? parent]) { throw UnsupportedError("Pattern descriptors don't support create()."); } } diff --git a/lib/src/sandbox.dart b/lib/src/sandbox.dart index a09145e..1f5847f 100644 --- a/lib/src/sandbox.dart +++ b/lib/src/sandbox.dart @@ -13,23 +13,23 @@ import 'package:test/test.dart'; /// created the first time [sandbox] is accessed for each test case, and /// automatically deleted after the test finishes running. String get sandbox { - if (_sandbox != null) return _sandbox; + if (_sandbox != null) return _sandbox!; // Resolve symlinks so we don't end up with inconsistent paths on Mac OS where // /tmp is symlinked. - _sandbox = Directory.systemTemp + var sandbox = _sandbox = Directory.systemTemp .createTempSync('dart_test_') .resolveSymbolicLinksSync(); addTearDown(() async { - var sandbox = _sandbox; + var sandbox = _sandbox!; _sandbox = null; await Directory(sandbox).delete(recursive: true); }); - return _sandbox; + return sandbox; } -String _sandbox; +String? _sandbox; /// Whether [sandbox] has been created. bool get sandboxExists => _sandbox != null; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 85d339f..d9d8017 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -54,10 +54,10 @@ String describeDirectory(String name, List contents) { /// 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? first, String? last, String? single}) { + single ??= first ?? last ?? prefix; first ??= prefix; last ??= prefix; - single ??= first ?? last ?? prefix; var lines = text.split('\n'); if (lines.length == 1) return '$single$text'; @@ -94,10 +94,10 @@ Future> waitAndReportErrors(Iterable> futures) { 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 stackTrace) { + return future.catchError((Object error, StackTrace stackTrace) { if (!errored) { errored = true; - throw error; + throw error; // ignore: only_throw_errors } else { registerException(error, stackTrace); } diff --git a/lib/test_descriptor.dart b/lib/test_descriptor.dart index 343a274..cf5e07f 100644 --- a/lib/test_descriptor.dart +++ b/lib/test_descriptor.dart @@ -5,7 +5,6 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'src/archive_descriptor.dart'; import 'src/descriptor.dart'; import 'src/directory_descriptor.dart'; import 'src/file_descriptor.dart'; @@ -13,7 +12,6 @@ import 'src/nothing_descriptor.dart'; import 'src/pattern_descriptor.dart'; import 'src/sandbox.dart'; -export 'src/archive_descriptor.dart'; export 'src/descriptor.dart'; export 'src/directory_descriptor.dart'; export 'src/file_descriptor.dart'; @@ -42,7 +40,7 @@ FileDescriptor file(String name, [contents]) => FileDescriptor(name, contents); /// children of the physical diretory, but it *doesn't* require that no other /// children exist. To ensure that a particular child doesn't exist, use /// [nothing]. -DirectoryDescriptor dir(String name, [Iterable contents]) => +DirectoryDescriptor dir(String name, [Iterable? contents]) => DirectoryDescriptor(name, contents ?? []); /// Creates a new [NothingDescriptor] descriptor that asserts that no entry @@ -72,21 +70,8 @@ PatternDescriptor filePattern(Pattern name, [contents]) => /// A convenience method for creating a [PatternDescriptor] descriptor that /// constructs a [DirectoryDescriptor] descriptor. -PatternDescriptor dirPattern(Pattern name, [Iterable contents]) => +PatternDescriptor dirPattern(Pattern name, [Iterable? contents]) => pattern(name, (realName) => dir(realName, contents)); -/// Creates a new [ArchiveDescriptor] with [name] and [contents]. -/// -/// [Descriptor.create] creates an archive with the given files and directories -/// within it, and [Descriptor.validate] validates that the archive contains the -/// given contents. It *doesn't* require that no other children exist. To ensure -/// that a particular child doesn't exist, use [nothing]. -/// -/// The type of the archive is determined by [name]'s file extension. It -/// supports `.zip`, `.tar`, `.tar.gz`/`.tar.gzip`/`.tgz`, and -/// `.tar.bz2`/`.tar.bzip2` files. -ArchiveDescriptor archive(String name, [Iterable contents]) => - ArchiveDescriptor(name, contents ?? []); - /// Returns [path] within the [sandbox] directory. String path(String path) => p.join(sandbox, path); diff --git a/pubspec.yaml b/pubspec.yaml index 290888f..8ebd785 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,20 +1,19 @@ name: test_descriptor -version: 1.2.1-dev +version: 2.0.0-nullsafety description: An API for defining and verifying directory structures. homepage: https://github.com/dart-lang/test_descriptor environment: - sdk: '>=2.8.1 <3.0.0' + sdk: '>=2.12.0-0 <3.0.0' dependencies: - archive: '^2.0.0' - async: '>=1.13.0 <3.0.0' - collection: '^1.5.0' - matcher: '^0.12.0' - meta: '^1.1.7' - path: '^1.0.0' - test: '^1.6.0' - term_glyph: '^1.0.0' + async: ^2.5.0-nullsafety + collection: ^1.15.0-nullsafety + matcher: ^0.12.10-nullsafety + meta: ^1.3.0-nullsafety + path: ^1.8.0-nullsafety + term_glyph: ^ 1.2.0-nullsafety + test: ^1.16.0-nullsafety dev_dependencies: - pedantic: ^1.0.0 + pedantic: ^1.10.0-nullsafety diff --git a/test/archive_test.dart b/test/archive_test.dart deleted file mode 100644 index 751afdc..0000000 --- a/test/archive_test.dart +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) 2019, 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. - -@TestOn('vm') - -import 'dart:convert'; -import 'dart:io'; - -import 'package:archive/archive.dart'; -import 'package:async/async.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; -import 'package:test_descriptor/test_descriptor.dart' as d; - -import 'utils.dart'; - -void main() { - group('create()', () { - test('creates an empty archive', () async { - await d.archive('test.tar').create(); - - var archive = - TarDecoder().decodeBytes(File(d.path('test.tar')).readAsBytesSync()); - expect(archive.files, isEmpty); - }); - - test('creates an archive with files', () async { - await d.archive('test.tar', [ - d.file('file1.txt', 'contents 1'), - d.file('file2.txt', 'contents 2') - ]).create(); - - var files = TarDecoder() - .decodeBytes(File(d.path('test.tar')).readAsBytesSync()) - .files; - expect(files.length, equals(2)); - _expectFile(files[0], 'file1.txt', 'contents 1'); - _expectFile(files[1], 'file2.txt', 'contents 2'); - }); - - test('creates an archive with files in a directory', () async { - await d.archive('test.tar', [ - d.dir('dir', [ - d.file('file1.txt', 'contents 1'), - d.file('file2.txt', 'contents 2') - ]) - ]).create(); - - var files = TarDecoder() - .decodeBytes(File(d.path('test.tar')).readAsBytesSync()) - .files; - expect(files.length, equals(2)); - _expectFile(files[0], 'dir/file1.txt', 'contents 1'); - _expectFile(files[1], 'dir/file2.txt', 'contents 2'); - }); - - test('creates an archive with files in a nested directory', () async { - await d.archive('test.tar', [ - d.dir('dir', [ - d.dir('subdir', [ - d.file('file1.txt', 'contents 1'), - d.file('file2.txt', 'contents 2') - ]) - ]) - ]).create(); - - var files = TarDecoder() - .decodeBytes(File(d.path('test.tar')).readAsBytesSync()) - .files; - expect(files.length, equals(2)); - _expectFile(files[0], 'dir/subdir/file1.txt', 'contents 1'); - _expectFile(files[1], 'dir/subdir/file2.txt', 'contents 2'); - }); - - group('creates a file in', () { - test('zip format', () async { - await d.archive('test.zip', [d.file('file.txt', 'contents')]).create(); - - var archive = ZipDecoder() - .decodeBytes(File(d.path('test.zip')).readAsBytesSync()); - _expectFile(archive.files.single, 'file.txt', 'contents'); - }); - - group('gzip tar format', () { - for (var extension in ['.tar.gz', '.tar.gzip', '.tgz']) { - test('with $extension', () async { - await d.archive( - 'test$extension', [d.file('file.txt', 'contents')]).create(); - - var archive = TarDecoder().decodeBytes(GZipDecoder() - .decodeBytes(File(d.path('test$extension')).readAsBytesSync())); - _expectFile(archive.files.single, 'file.txt', 'contents'); - }); - } - }); - - group('bzip2 tar format', () { - for (var extension in ['.tar.bz2', '.tar.bzip2']) { - test('with $extension', () async { - await d.archive( - 'test$extension', [d.file('file.txt', 'contents')]).create(); - - var archive = TarDecoder().decodeBytes(BZip2Decoder() - .decodeBytes(File(d.path('test$extension')).readAsBytesSync())); - _expectFile(archive.files.single, 'file.txt', 'contents'); - }); - } - }); - }); - - group('gracefully rejects', () { - test('an uncreatable descriptor', () async { - await expectLater( - d.archive('test.tar', [d.filePattern(RegExp(r'^foo-'))]).create(), - throwsUnsupportedError); - await d.nothing('test.tar').validate(); - }); - - test('a non-file non-directory descriptor', () async { - await expectLater( - d.archive('test.tar', [d.nothing('file.txt')]).create(), - throwsUnsupportedError); - await d.nothing('test.tar').validate(); - }); - - test('an unknown file extension', () async { - await expectLater( - d.archive('test.asdf', [d.nothing('file.txt')]).create(), - throwsUnsupportedError); - }); - }); - }); - - group('validate()', () { - group('with an empty archive', () { - test('succeeds if an empty archive exists', () async { - File(d.path('test.tar')) - .writeAsBytesSync(TarEncoder().encode(Archive())); - await d.archive('test.tar').validate(); - }); - - test('succeeds if a non-empty archive exists', () async { - File(d.path('test.tar')).writeAsBytesSync( - TarEncoder().encode(Archive()..addFile(_file('file.txt')))); - await d.archive('test.tar').validate(); - }); - - test('fails if no archive exists', () { - expect(d.archive('test.tar').validate(), - throwsA(toString(startsWith('File not found: "test.tar".')))); - }); - - test('fails if an invalid archive exists', () { - d.file('test.tar', 'not a valid tar file').create(); - expect( - d.archive('test.tar').validate(), - throwsA(toString( - startsWith('File "test.tar" is not a valid archive.')))); - }); - }); - - test('succeeds if an archive contains a matching file', () async { - File(d.path('test.tar')).writeAsBytesSync(TarEncoder() - .encode(Archive()..addFile(_file('file.txt', 'contents')))); - await d.archive('test.tar', [d.file('file.txt', 'contents')]).validate(); - }); - - test("fails if an archive doesn't contain a file", () async { - File(d.path('test.tar')).writeAsBytesSync(TarEncoder().encode(Archive())); - expect( - d.archive('test.tar', [d.file('file.txt', 'contents')]).validate(), - throwsA( - toString(startsWith('File not found: "test.tar/file.txt".')))); - }); - - test('fails if an archive contains a non-matching file', () async { - File(d.path('test.tar')).writeAsBytesSync(TarEncoder() - .encode(Archive()..addFile(_file('file.txt', 'wrong contents')))); - expect( - d.archive('test.tar', [d.file('file.txt', 'contents')]).validate(), - throwsA(toString( - startsWith('File "test.tar/file.txt" should contain:')))); - }); - - test('succeeds if an archive contains a file matching a pattern', () async { - File(d.path('test.tar')).writeAsBytesSync(TarEncoder() - .encode(Archive()..addFile(_file('file.txt', 'contents')))); - await d.archive('test.tar', - [d.filePattern(RegExp(r'f..e\.txt'), 'contents')]).validate(); - }); - - group('validates a file in', () { - test('zip format', () async { - File(d.path('test.zip')).writeAsBytesSync(ZipEncoder() - .encode(Archive()..addFile(_file('file.txt', 'contents')))); - - await d - .archive('test.zip', [d.file('file.txt', 'contents')]).validate(); - }); - - group('gzip tar format', () { - for (var extension in ['.tar.gz', '.tar.gzip', '.tgz']) { - test('with $extension', () async { - File(d.path('test$extension')).writeAsBytesSync(GZipEncoder() - .encode(TarEncoder().encode( - Archive()..addFile(_file('file.txt', 'contents'))))); - - await d.archive( - 'test$extension', [d.file('file.txt', 'contents')]).validate(); - }); - } - }); - - group('bzip2 tar format', () { - for (var extension in ['.tar.bz2', '.tar.bzip2']) { - test('with $extension', () async { - File(d.path('test$extension')).writeAsBytesSync(BZip2Encoder() - .encode(TarEncoder().encode( - Archive()..addFile(_file('file.txt', 'contents'))))); - - await d.archive( - 'test$extension', [d.file('file.txt', 'contents')]).validate(); - }); - } - }); - }); - - test('gracefully rejects an unknown file format', () { - expect(d.archive('test.asdf').validate(), throwsUnsupportedError); - }); - }); - - test('read() is unsupported', () { - expect(d.archive('test.tar').read(), throwsUnsupportedError); - }); - - test('readAsBytes() returns the contents of the archive', () async { - var descriptor = d.archive('test.tar', - [d.file('file1.txt', 'contents 1'), d.file('file2.txt', 'contents 2')]); - - var files = TarDecoder() - .decodeBytes(await collectBytes(descriptor.readAsBytes())) - .files; - expect(files.length, equals(2)); - _expectFile(files[0], 'file1.txt', 'contents 1'); - _expectFile(files[1], 'file2.txt', 'contents 2'); - }); - - test('archive returns the in-memory contents', () async { - var archive = await d.archive('test.tar', [ - d.file('file1.txt', 'contents 1'), - d.file('file2.txt', 'contents 2') - ]).archive; - - var files = archive.files; - expect(files.length, equals(2)); - _expectFile(files[0], 'file1.txt', 'contents 1'); - _expectFile(files[1], 'file2.txt', 'contents 2'); - }); - - test('io refers to the file within the sandbox', () { - expect(d.file('test.tar').io.path, equals(p.join(d.sandbox, 'test.tar'))); - }); -} - -/// Asserts that [file] has the given [name] and [contents]. -void _expectFile(ArchiveFile file, String name, String contents) { - expect(file.name, equals(name)); - expect(utf8.decode(file.content as List), equals(contents)); -} - -/// Creates an [ArchiveFile] with the given [name] and [contents]. -ArchiveFile _file(String name, [String contents]) { - var bytes = utf8.encode(contents ?? ''); - return ArchiveFile(name, bytes.length, bytes) - // Setting the mode and mod time are necessary to work around - // brendan-duncan/archive#76. - ..mode = 428 - ..lastModTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; -} diff --git a/test/directory_test.dart b/test/directory_test.dart index e135086..c669be8 100644 --- a/test/directory_test.dart +++ b/test/directory_test.dart @@ -211,7 +211,7 @@ void main() { }); group('describe()', () { - bool oldAscii; + late bool oldAscii; setUpAll(() { oldAscii = term_glyph.ascii; term_glyph.ascii = true; diff --git a/test/sandbox_test.dart b/test/sandbox_test.dart index f20a8ff..991bd7f 100644 --- a/test/sandbox_test.dart +++ b/test/sandbox_test.dart @@ -17,7 +17,7 @@ void main() { }); test('the directory is deleted after the test', () { - String sandbox; + late String sandbox; addTearDown(() { expect(Directory(sandbox).existsSync(), isFalse); });