Migrate to null safety (#29)
Dropped support for archives as planned in https://github.com/dart-lang/test_descriptor/issues/25 - this also allows us to migrate before `package:archive`.
Closes https://github.com/dart-lang/test_descriptor/issues/25
diff --git a/.travis.yml b/.travis.yml
index 50bda8c..502b3eb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,6 @@
dart:
- dev
- - 2.8.1
dart_task:
- test
@@ -14,9 +13,6 @@
- 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<Descriptor> contents;
-
- /// Returns a `package:archive` [Archive] object that contains the contents of
- /// this file.
- Future<Archive> 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<Iterable<ArchiveFile>> _files(Iterable<Descriptor> 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<Descriptor> 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<String> read() async => throw UnsupportedError(
- 'ArchiveDescriptor.read() is not supported. Use Archive.readAsBytes() '
- 'instead.');
-
- @override
- Stream<List<int>> readAsBytes() => Stream.fromFuture(() async {
- return _encodeFunction()(await archive);
- }());
-
- @override
- Future<void> 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<int>);
- }));
-
- 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<int> 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<int>) _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 @@
/// Creates this entry within the [parent] directory, which defaults to
/// [sandbox].
- Future create([String parent]);
+ Future<void> 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<void> 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 @@
}
// Ignore broken symlinks.
return null;
- }).where((path) => path != null));
+ }).whereType<Descriptor>());
}
@override
- Future create([String parent]) async {
+ Future<void> 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<void> 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 @@
/// 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<List<int>> load(url, [String parents]) {
+ Stream<List<int>> 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 @@
FileDescriptor.protected(String name) : super(name);
@override
- Future create([String parent]) async {
+ Future<void> 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 @@
}
@override
- Future validate([String parent]) async {
+ Future<void> validate([String? parent]) async {
var fullPath = p.join(parent ?? sandbox, name);
var pretty = prettyPath(fullPath);
if (!(await File(fullPath).exists())) {
@@ -87,7 +87,7 @@
///
/// The [prettyPath] is a human-friendly representation of the path to the
/// descriptor.
- Future _validate(String prettyPath, List<int> binaryContents);
+ FutureOr<void> _validate(String prettyPath, List<int> binaryContents);
/// Reads and decodes the contents of this descriptor as a UTF-8 string.
///
@@ -113,8 +113,8 @@
Stream<List<int>> readAsBytes() => Stream.fromIterable([_contents]);
@override
- Future _validate(String prettPath, List<int> actualContents) async {
- if (const IterableEquality().equals(_contents, actualContents)) return null;
+ Future<void> _validate(String prettPath, List<int> 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 @@
Stream.fromIterable([utf8.encode(_contents)]);
@override
- Future _validate(String prettyPath, List<int> actualContents) {
+ void _validate(String prettyPath, List<int> actualContents) {
var actualContentsText = utf8.decode(actualContents);
- if (_contents == actualContentsText) return null;
+ if (_contents == actualContentsText) return;
fail(_textMismatchMessage(prettyPath, _contents, actualContentsText));
}
@@ -194,7 +194,7 @@
throw UnsupportedError("Matcher files can't be created or read.");
@override
- Future _validate(String prettyPath, List<int> actualContents) async {
+ Future<void> _validate(String prettyPath, List<int> 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 @@
NothingDescriptor(String name) : super(name);
@override
- Future create([String parent]) async {}
+ Future<void> create([String? parent]) async {}
@override
- Future validate([String parent]) async {
+ Future<void> 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 @@
/// in the constructor and validates the result. If exactly one succeeds,
/// `this` is considered valid.
@override
- Future validate([String parent]) async {
+ Future<void> validate([String? parent]) async {
var inSandbox = parent == null;
parent ??= sandbox;
var matchingEntries = await Directory(parent)
@@ -57,19 +57,22 @@
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<Future<Result<String>>>()
+ .toList());
var successes = results.where((result) => result.isValue).toList();
if (successes.isEmpty) {
@@ -77,7 +80,7 @@
} 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 @@
}
@override
- Future create([String parent]) {
+ Future<void> 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 @@
/// 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 @@
/// 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 @@
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/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 @@
/// 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<Descriptor> contents]) =>
+DirectoryDescriptor dir(String name, [Iterable<Descriptor>? contents]) =>
DirectoryDescriptor(name, contents ?? <Descriptor>[]);
/// Creates a new [NothingDescriptor] descriptor that asserts that no entry
@@ -72,21 +70,8 @@
/// A convenience method for creating a [PatternDescriptor] descriptor that
/// constructs a [DirectoryDescriptor] descriptor.
-PatternDescriptor dirPattern(Pattern name, [Iterable<Descriptor> contents]) =>
+PatternDescriptor dirPattern(Pattern name, [Iterable<Descriptor>? 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<Descriptor> 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<int>), 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 @@
});
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 @@
});
test('the directory is deleted after the test', () {
- String sandbox;
+ late String sandbox;
addTearDown(() {
expect(Directory(sandbox).existsSync(), isFalse);
});