Fix newly enforced package:pedantic lints (#23)

- annotate_overrides
- prefer_if_null_operators
- prefer_single_quotes
- use_function_type_syntax_for_parameters
diff --git a/lib/src/archive_descriptor.dart b/lib/src/archive_descriptor.dart
index 564958d..7d96ce8 100644
--- a/lib/src/archive_descriptor.dart
+++ b/lib/src/archive_descriptor.dart
@@ -33,6 +33,7 @@
     return archive;
   }
 
+  @override
   File get io => File(p.join(sandbox, name));
 
   /// Returns [ArchiveFile]s for each file in [descriptors].
@@ -42,7 +43,7 @@
       [String parent]) async {
     return (await waitAndReportErrors(descriptors.map((descriptor) async {
       var fullName =
-          parent == null ? descriptor.name : "$parent/${descriptor.name}";
+          parent == null ? descriptor.name : '$parent/${descriptor.name}';
 
       if (descriptor is FileDescriptor) {
         var bytes = await collectBytes(descriptor.readAsBytes());
@@ -57,8 +58,8 @@
         return await _files(descriptor.contents, fullName);
       } else {
         throw UnsupportedError(
-            "An archive can only be created from FileDescriptors and "
-            "DirectoryDescriptors.");
+            'An archive can only be created from FileDescriptors and '
+            'DirectoryDescriptors.');
       }
     })))
         .expand((files) => files);
@@ -68,6 +69,7 @@
       : contents = List.unmodifiable(contents),
         super(name);
 
+  @override
   Future create([String parent]) async {
     var path = p.join(parent ?? sandbox, name);
     var file = File(path).openWrite();
@@ -83,14 +85,17 @@
     }
   }
 
+  @override
   Future<String> read() async => throw UnsupportedError(
-      "ArchiveDescriptor.read() is not supported. Use Archive.readAsBytes() "
-      "instead.");
+      '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.
@@ -141,40 +146,41 @@
   /// Returns the function to use to encode this file to binary, based on its
   /// [name].
   List<int> Function(Archive) _encodeFunction() {
-    if (name.endsWith(".zip")) {
+    if (name.endsWith('.zip')) {
       return ZipEncoder().encode;
-    } else if (name.endsWith(".tar")) {
+    } else if (name.endsWith('.tar')) {
       return TarEncoder().encode;
-    } else if (name.endsWith(".tar.gz") ||
-        name.endsWith(".tar.gzip") ||
-        name.endsWith(".tgz")) {
+    } 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")) {
+    } else if (name.endsWith('.tar.bz2') || name.endsWith('.tar.bzip2')) {
       return (archive) => BZip2Encoder().encode(TarEncoder().encode(archive));
     } else {
-      throw UnsupportedError("Unknown file format $name.");
+      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")) {
+    if (name.endsWith('.zip')) {
       return ZipDecoder().decodeBytes;
-    } else if (name.endsWith(".tar")) {
+    } else if (name.endsWith('.tar')) {
       return TarDecoder().decodeBytes;
-    } else if (name.endsWith(".tar.gz") ||
-        name.endsWith(".tar.gzip") ||
-        name.endsWith(".tgz")) {
+    } 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")) {
+    } else if (name.endsWith('.tar.bz2') || name.endsWith('.tar.bzip2')) {
       return (archive) =>
           TarDecoder().decodeBytes(BZip2Decoder().decodeBytes(archive));
     } else {
-      throw UnsupportedError("Unknown file format $name.");
+      throw UnsupportedError('Unknown file format $name.');
     }
   }
 
+  @override
   String describe() => describeDirectory(name, contents);
 }
diff --git a/lib/src/directory_descriptor.dart b/lib/src/directory_descriptor.dart
index c505885..1a8fe65 100644
--- a/lib/src/directory_descriptor.dart
+++ b/lib/src/directory_descriptor.dart
@@ -41,7 +41,7 @@
         name,
         Directory(path).listSync().map((entity) {
           // Ignore hidden files.
-          if (p.basename(entity.path).startsWith(".")) return null;
+          if (p.basename(entity.path).startsWith('.')) return null;
 
           if (entity is Directory) {
             return DirectoryDescriptor.fromFilesystem(
@@ -55,12 +55,14 @@
         }).where((path) => path != null));
   }
 
+  @override
   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 {
     var fullPath = p.join(parent ?? sandbox, name);
     if (!(await Directory(fullPath).exists())) {
@@ -85,12 +87,12 @@
     } else if (url is Uri) {
       path = url.toString();
     } else {
-      throw ArgumentError.value(url, "url", "must be a Uri or a String.");
+      throw ArgumentError.value(url, 'url', 'must be a Uri or a String.');
     }
 
     if (!p.url.isWithin('.', path)) {
       throw ArgumentError.value(
-          url, "url", "must be relative and beneath the base URL.");
+          url, 'url', 'must be relative and beneath the base URL.');
     }
 
     return StreamCompleter.fromFuture(Future.sync(() {
@@ -121,5 +123,6 @@
     }));
   }
 
+  @override
   String describe() => describeDirectory(name, contents);
 }
diff --git a/lib/src/file_descriptor.dart b/lib/src/file_descriptor.dart
index b755ee5..3d634b6 100644
--- a/lib/src/file_descriptor.dart
+++ b/lib/src/file_descriptor.dart
@@ -59,6 +59,7 @@
   /// A protected constructor that's only intended for subclasses.
   FileDescriptor.protected(String name) : 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).
@@ -70,6 +71,7 @@
     }
   }
 
+  @override
   Future validate([String parent]) async {
     var fullPath = p.join(parent ?? sandbox, name);
     var pretty = prettyPath(fullPath);
@@ -97,6 +99,7 @@
   /// This isn't supported for matcher descriptors.
   Stream<List<int>> readAsBytes();
 
+  @override
   String describe() => name;
 }
 
@@ -106,8 +109,10 @@
 
   _BinaryFileDescriptor(String name, this._contents) : super.protected(name);
 
+  @override
   Stream<List<int>> readAsBytes() => Stream.fromIterable([_contents]);
 
+  @override
   Future _validate(String prettPath, List<int> actualContents) async {
     if (const IterableEquality().equals(_contents, actualContents)) return null;
     // TODO(nweiz): show a hex dump here if the data is small enough.
@@ -121,11 +126,14 @@
 
   _StringFileDescriptor(String name, this._contents) : super.protected(name);
 
+  @override
   Future<String> read() async => _contents;
 
+  @override
   Stream<List<int>> readAsBytes() =>
       Stream.fromIterable([utf8.encode(_contents)]);
 
+  @override
   Future _validate(String prettyPath, List<int> actualContents) {
     var actualContentsText = utf8.decode(actualContents);
     if (_contents == actualContentsText) return null;
@@ -163,8 +171,8 @@
     }
 
     return 'File "$prettyPath" should contain:\n'
-        "${addBar(expected)}\n"
-        "but actually contained:\n"
+        '${addBar(expected)}\n'
+        'but actually contained:\n'
         "${results.join('\n')}";
   }
 }
@@ -181,9 +189,11 @@
       : _isBinary = isBinary,
         super.protected(name);
 
+  @override
   Stream<List<int>> readAsBytes() =>
       throw UnsupportedError("Matcher files can't be created or read.");
 
+  @override
   Future _validate(String prettyPath, List<int> actualContents) async {
     try {
       expect(
diff --git a/lib/src/nothing_descriptor.dart b/lib/src/nothing_descriptor.dart
index 98abe61..5dd9b8a 100644
--- a/lib/src/nothing_descriptor.dart
+++ b/lib/src/nothing_descriptor.dart
@@ -18,8 +18,10 @@
 class NothingDescriptor extends Descriptor {
   NothingDescriptor(String name) : 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);
@@ -32,5 +34,6 @@
     }
   }
 
+  @override
   String describe() => 'nothing at "$name"';
 }
diff --git a/lib/src/pattern_descriptor.dart b/lib/src/pattern_descriptor.dart
index 3432876..9de3b99 100644
--- a/lib/src/pattern_descriptor.dart
+++ b/lib/src/pattern_descriptor.dart
@@ -31,7 +31,7 @@
   /// matching [pattern].
   final _EntryCreator _fn;
 
-  PatternDescriptor(this.pattern, Descriptor child(String basename))
+  PatternDescriptor(this.pattern, Descriptor Function(String basename) child)
       : _fn = child,
         super('$pattern');
 
@@ -40,6 +40,7 @@
   /// matching [pattern], then passes each of their names to `child` provided
   /// in the constructor and validates the result. If exactly one succeeds,
   /// `this` is considered valid.
+  @override
   Future validate([String parent]) async {
     var inSandbox = parent == null;
     parent ??= sandbox;
@@ -51,7 +52,7 @@
         .toList();
     matchingEntries.sort();
 
-    var location = inSandbox ? "sandbox" : '"${prettyPath(parent)}"';
+    var location = inSandbox ? 'sandbox' : '"${prettyPath(parent)}"';
     if (matchingEntries.isEmpty) {
       fail('No entries found in $location matching $_patternDescription.');
     }
@@ -80,7 +81,8 @@
     }
   }
 
-  String describe() => "entry matching $_patternDescription";
+  @override
+  String describe() => 'entry matching $_patternDescription';
 
   String get _patternDescription {
     if (pattern is String) return '"$pattern"';
@@ -93,6 +95,7 @@
     return '/${regExp.pattern}/$flags';
   }
 
+  @override
   Future create([String parent]) {
     throw UnsupportedError("Pattern descriptors don't support create().");
   }
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 5dad794..85d339f 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -16,15 +16,15 @@
 final utf8 = const 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) =>
-    prefixLines(text, "  ", first: "${glyph.bullet} ");
+    prefixLines(text, '  ', first: '${glyph.bullet} ');
 
 /// Converts [strings] to a bulleted list.
-String bullet(Iterable<String> strings) => strings.map(addBullet).join("\n");
+String bullet(Iterable<String> strings) => strings.map(addBullet).join('\n');
 
 /// Returns a human-readable description of a directory with the given [name]
 /// and [contents].
@@ -60,13 +60,13 @@
   single ??= first ?? last ?? prefix;
 
   var lines = text.split('\n');
-  if (lines.length == 1) return "$single$text";
+  if (lines.length == 1) return '$single$text';
 
-  var buffer = StringBuffer("$first${lines.first}\n");
+  var buffer = StringBuffer('$first${lines.first}\n');
   for (var line in lines.skip(1).take(lines.length - 2)) {
-    buffer.writeln("$prefix$line");
+    buffer.writeln('$prefix$line');
   }
-  buffer.write("$last${lines.last}");
+  buffer.write('$last${lines.last}');
   return buffer.toString();
 }
 
diff --git a/lib/test_descriptor.dart b/lib/test_descriptor.dart
index ea57645..343a274 100644
--- a/lib/test_descriptor.dart
+++ b/lib/test_descriptor.dart
@@ -43,7 +43,7 @@
 /// children exist. To ensure that a particular child doesn't exist, use
 /// [nothing].
 DirectoryDescriptor dir(String name, [Iterable<Descriptor> contents]) =>
-    DirectoryDescriptor(name, contents == null ? <Descriptor>[] : contents);
+    DirectoryDescriptor(name, contents ?? <Descriptor>[]);
 
 /// Creates a new [NothingDescriptor] descriptor that asserts that no entry
 /// named [name] exists.
@@ -61,7 +61,8 @@
 /// [child].
 ///
 /// [Descriptor.create] is not supported for this descriptor.
-PatternDescriptor pattern(Pattern name, Descriptor child(String basename)) =>
+PatternDescriptor pattern(
+        Pattern name, Descriptor Function(String basename) child) =>
     PatternDescriptor(name, child);
 
 /// A convenience method for creating a [PatternDescriptor] descriptor that
diff --git a/test/archive_test.dart b/test/archive_test.dart
index a413fb9..751afdc 100644
--- a/test/archive_test.dart
+++ b/test/archive_test.dart
@@ -16,250 +16,250 @@
 import 'utils.dart';
 
 void main() {
-  group("create()", () {
-    test("creates an empty archive", () async {
-      await d.archive("test.tar").create();
+  group('create()', () {
+    test('creates an empty archive', () async {
+      await d.archive('test.tar').create();
 
       var archive =
-          TarDecoder().decodeBytes(File(d.path("test.tar")).readAsBytesSync());
+          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")
+    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())
+          .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");
+      _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")
+    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())
+          .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");
+      _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")
+    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())
+          .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");
+      _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();
+    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");
+            .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 {
+      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();
+                '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");
+                .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 {
+      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();
+                '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");
+                .decodeBytes(File(d.path('test$extension')).readAsBytesSync()));
+            _expectFile(archive.files.single, 'file.txt', 'contents');
           });
         }
       });
     });
 
-    group("gracefully rejects", () {
-      test("an uncreatable descriptor", () async {
+    group('gracefully rejects', () {
+      test('an uncreatable descriptor', () async {
         await expectLater(
-            d.archive("test.tar", [d.filePattern(RegExp(r"^foo-"))]).create(),
+            d.archive('test.tar', [d.filePattern(RegExp(r'^foo-'))]).create(),
             throwsUnsupportedError);
-        await d.nothing("test.tar").validate();
+        await d.nothing('test.tar').validate();
       });
 
-      test("a non-file non-directory descriptor", () async {
+      test('a non-file non-directory descriptor', () async {
         await expectLater(
-            d.archive("test.tar", [d.nothing("file.txt")]).create(),
+            d.archive('test.tar', [d.nothing('file.txt')]).create(),
             throwsUnsupportedError);
-        await d.nothing("test.tar").validate();
+        await d.nothing('test.tar').validate();
       });
 
-      test("an unknown file extension", () async {
+      test('an unknown file extension', () async {
         await expectLater(
-            d.archive("test.asdf", [d.nothing("file.txt")]).create(),
+            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"))
+  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();
+        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('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(),
+      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();
+      test('fails if an invalid archive exists', () {
+        d.file('test.tar', 'not a valid tar file').create();
         expect(
-            d.archive("test.tar").validate(),
+            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('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()));
+      File(d.path('test.tar')).writeAsBytesSync(TarEncoder().encode(Archive()));
       expect(
-          d.archive("test.tar", [d.file("file.txt", "contents")]).validate(),
+          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"))));
+    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(),
+          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();
+    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"))));
+    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();
+            .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()
+      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")))));
+                    Archive()..addFile(_file('file.txt', 'contents')))));
 
             await d.archive(
-                "test$extension", [d.file("file.txt", "contents")]).validate();
+                '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()
+      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")))));
+                    Archive()..addFile(_file('file.txt', 'contents')))));
 
             await d.archive(
-                "test$extension", [d.file("file.txt", "contents")]).validate();
+                'test$extension', [d.file('file.txt', 'contents')]).validate();
           });
         }
       });
     });
 
-    test("gracefully rejects an unknown file format", () {
-      expect(d.archive("test.asdf").validate(), throwsUnsupportedError);
+    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('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")]);
+  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");
+    _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")
+  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");
+    _expectFile(files[0], 'file1.txt', 'contents 1');
+    _expectFile(files[1], 'file2.txt', 'contents 2');
   });
 
-  test("io refers to the file within the sandbox", () {
+  test('io refers to the file within the sandbox', () {
     expect(d.file('test.tar').io.path, equals(p.join(d.sandbox, 'test.tar')));
   });
 }
@@ -272,7 +272,7 @@
 
 /// Creates an [ArchiveFile] with the given [name] and [contents].
 ArchiveFile _file(String name, [String contents]) {
-  var bytes = utf8.encode(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.
diff --git a/test/directory_test.dart b/test/directory_test.dart
index b03edc3..71b998e 100644
--- a/test/directory_test.dart
+++ b/test/directory_test.dart
@@ -17,8 +17,8 @@
 import 'utils.dart';
 
 void main() {
-  group("create()", () {
-    test("creates a directory and its contents", () async {
+  group('create()', () {
+    test('creates a directory and its contents', () async {
       await d.dir('dir', [
         d.dir('subdir', [
           d.file('subfile1.txt', 'subcontents1'),
@@ -42,7 +42,7 @@
           completion(equals('subcontents2')));
     });
 
-    test("works if the directory already exists", () async {
+    test('works if the directory already exists', () async {
       await d.dir('dir').create();
       await d.dir('dir', [d.file('name.txt', 'contents')]).create();
 
@@ -51,8 +51,8 @@
     });
   });
 
-  group("validate()", () {
-    test("completes successfully if the filesystem matches the descriptor",
+  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');
@@ -93,7 +93,7 @@
               equals('Directory not found: "${p.join('dir', 'subdir')}".'))));
     });
 
-    test("emits an error for each child that fails to validate", () async {
+    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');
       await Directory(subdirPath).create(recursive: true);
@@ -132,15 +132,15 @@
     });
   });
 
-  group("load()", () {
-    test("loads a file", () {
+  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')));
     });
 
-    test("loads a deeply-nested file", () {
+    test('loads a deeply-nested file', () {
       var dir = d.dir('dir', [
         d.dir('subdir',
             [d.file('name.txt', 'subcontents'), d.file('other.txt', 'wrong')]),
@@ -152,7 +152,7 @@
           completion(equals('subcontents')));
     });
 
-    test("fails to load a nested directory", () {
+    test('fails to load a nested directory', () {
       var dir = d.dir('dir', [
         d.dir('subdir', [
           d.dir('subsubdir', [d.file('name.txt', 'subcontents')])
@@ -166,7 +166,7 @@
               '"subsubdir" within "dir/subdir".'))));
     });
 
-    test("fails to load an absolute path", () {
+    test('fails to load an absolute path', () {
       var dir = d.dir('dir', [d.file('name.txt', 'contents')]);
       expect(() => dir.load('/name.txt'), throwsArgumentError);
     });
@@ -187,7 +187,7 @@
               '"not-name.txt" within "dir/subdir".'))));
     });
 
-    test("fails to load a file that exists multiple times", () {
+    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')])
@@ -199,7 +199,7 @@
               '"name.txt" within "dir/subdir".'))));
     });
 
-    test("loads a file next to a subdirectory with the same name", () {
+    test('loads a file next to a subdirectory with the same name', () {
       var dir = d.dir('dir', [
         d.file('name', 'contents'),
         d.dir('name', [d.file('subfile', 'contents')])
@@ -210,7 +210,7 @@
     });
   });
 
-  group("describe()", () {
+  group('describe()', () {
     bool oldAscii;
     setUpAll(() {
       oldAscii = term_glyph.ascii;
@@ -221,18 +221,18 @@
       term_glyph.ascii = oldAscii;
     });
 
-    test("lists the contents of the directory", () {
+    test('lists the contents of the directory', () {
       var dir = d.dir('dir',
           [d.file('file1.txt', 'contents1'), d.file('file2.txt', 'contents2')]);
 
       expect(
           dir.describe(),
-          equals("dir\n"
-              "+-- file1.txt\n"
+          equals('dir\n'
+              '+-- file1.txt\n'
               "'-- file2.txt"));
     });
 
-    test("lists the contents of nested directories", () {
+    test('lists the contents of nested directories', () {
       var dir = d.dir('dir', [
         d.file('file1.txt', 'contents1'),
         d.dir('subdir', [
@@ -245,23 +245,23 @@
 
       expect(
           dir.describe(),
-          equals("dir\n"
-              "+-- file1.txt\n"
-              "+-- subdir\n"
-              "|   +-- subfile1.txt\n"
-              "|   +-- subfile2.txt\n"
+          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", () {
+    test('with no contents returns the directory name', () {
       expect(d.dir('dir').describe(), equals('dir'));
     });
   });
 
-  group("fromFilesystem()", () {
-    test("creates a descriptor based on the physical filesystem", () async {
+  group('fromFilesystem()', () {
+    test('creates a descriptor based on the physical filesystem', () async {
       var dir = d.dir('dir', [
         d.dir('subdir', [
           d.file('subfile1.txt', 'subcontents1'),
@@ -273,12 +273,12 @@
 
       await dir.create();
       var descriptor =
-          d.DirectoryDescriptor.fromFilesystem("dir", p.join(d.sandbox, 'dir'));
+          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'));
     });
 
-    test("ignores hidden files", () async {
+    test('ignores hidden files', () async {
       await d.dir('dir', [
         d.dir('subdir', [
           d.file('subfile1.txt', 'subcontents1'),
@@ -289,7 +289,7 @@
       ]).create();
 
       var descriptor = d.DirectoryDescriptor.fromFilesystem(
-          "dir2", p.join(d.sandbox, 'dir'));
+          'dir2', p.join(d.sandbox, 'dir'));
       await descriptor.create();
 
       await d.dir('dir2', [
@@ -301,7 +301,7 @@
     });
   });
 
-  test("io refers to the directory within the sandbox", () {
+  test('io refers to the directory within the sandbox', () {
     expect(d.file('dir').io.path, equals(p.join(d.sandbox, 'dir')));
   });
 }
diff --git a/test/file_test.dart b/test/file_test.dart
index a1e803b..8b111f9 100644
--- a/test/file_test.dart
+++ b/test/file_test.dart
@@ -13,7 +13,7 @@
 import 'utils.dart';
 
 void main() {
-  group("create()", () {
+  group('create()', () {
     test('creates a text file', () async {
       await d.file('name.txt', 'contents').create();
 
@@ -42,7 +42,7 @@
     });
   });
 
-  group("validate()", () {
+  group('validate()', () {
     test('succeeds if the filesystem matches a text descriptor', () async {
       await File(p.join(d.sandbox, 'name.txt')).writeAsString('contents');
       await d.file('name.txt', 'contents').validate();
@@ -118,40 +118,40 @@
     });
   });
 
-  group("reading", () {
-    test("read() returns the contents of a text file as a string", () {
+  group('reading', () {
+    test('read() returns the contents of a text file as a string', () {
       expect(d.file('name.txt', 'contents').read(),
           completion(equals('contents')));
     });
 
-    test("read() returns the contents of a binary file as a string", () {
+    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')));
     });
 
-    test("read() fails for a matcher file", () {
+    test('read() fails for a matcher file', () {
       expect(d.file('name.txt', contains('hi')).read, throwsUnsupportedError);
     });
 
-    test("readAsBytes() returns the contents of a text file as a byte stream",
+    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')));
     });
 
-    test("readAsBytes() returns the contents of a binary file as a byte stream",
+    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])));
     });
 
-    test("readAsBytes() fails for a matcher file", () {
+    test('readAsBytes() fails for a matcher file', () {
       expect(d.file('name.txt', contains('hi')).readAsBytes,
           throwsUnsupportedError);
     });
   });
 
-  test("io refers to the file within the sandbox", () {
+  test('io refers to the file within the sandbox', () {
     expect(d.file('name.txt').io.path, equals(p.join(d.sandbox, 'name.txt')));
   });
 }
diff --git a/test/nothing_test.dart b/test/nothing_test.dart
index d117260..a192087 100644
--- a/test/nothing_test.dart
+++ b/test/nothing_test.dart
@@ -14,13 +14,13 @@
 import 'utils.dart';
 
 void main() {
-  test("create() does nothing", () async {
+  test('create() does nothing', () async {
     await d.nothing('foo').create();
     expect(File(p.join(d.sandbox, 'foo')).exists(), completion(isFalse));
     expect(Directory(p.join(d.sandbox, 'foo')).exists(), completion(isFalse));
   });
 
-  group("validate()", () {
+  group('validate()', () {
     test("succeeds if nothing's there", () async {
       await d.nothing('foo').validate();
     });
diff --git a/test/pattern_test.dart b/test/pattern_test.dart
index d337b1f..eb7b815 100644
--- a/test/pattern_test.dart
+++ b/test/pattern_test.dart
@@ -11,7 +11,7 @@
 import 'utils.dart';
 
 void main() {
-  group("validate()", () {
+  group('validate()', () {
     test("succeeds if there's a file matching the pattern and the child",
         () async {
       await d.file('foo', 'blap').create();
@@ -26,8 +26,8 @@
     });
 
     test(
-        "succeeds if multiple files match the pattern but only one matches "
-        "the child entry", () async {
+        'succeeds if multiple files match the pattern but only one matches '
+        'the child entry', () async {
       await d.file('foo', 'blap').create();
       await d.file('fee', 'blak').create();
       await d.file('faa', 'blut').create();
@@ -58,15 +58,15 @@
     });
 
     test(
-        "fails if there are multiple files matching the pattern and the child "
-        "entry", () async {
+        'fails if there are multiple files matching the pattern and the child '
+        'entry', () async {
       await d.file('foo', 'bar').create();
       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../:"))));
+              'Multiple valid entries found in sandbox matching /f../:'))));
     });
   });
 }
diff --git a/test/sandbox_test.dart b/test/sandbox_test.dart
index c34c5c6..f20a8ff 100644
--- a/test/sandbox_test.dart
+++ b/test/sandbox_test.dart
@@ -12,11 +12,11 @@
 import 'package:test_descriptor/test_descriptor.dart' as d;
 
 void main() {
-  test("accessing the getter creates the directory", () {
+  test('accessing the getter creates the directory', () {
     expect(Directory(d.sandbox).existsSync(), isTrue);
   });
 
-  test("the directory is deleted after the test", () {
+  test('the directory is deleted after the test', () {
     String sandbox;
     addTearDown(() {
       expect(Directory(sandbox).existsSync(), isFalse);
@@ -25,7 +25,7 @@
     sandbox = d.sandbox;
   });
 
-  test("path() returns a path in the sandbox", () {
-    expect(d.path("foo"), equals(p.join(d.sandbox, "foo")));
+  test('path() returns a path in the sandbox', () {
+    expect(d.path('foo'), equals(p.join(d.sandbox, 'foo')));
   });
 }
diff --git a/test/utils.dart b/test/utils.dart
index d6359f1..3ea1e29 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -20,5 +20,5 @@
   return predicate((object) {
     expect(object.toString(), matcher);
     return true;
-  }, "toString() matches $matcher");
+  }, 'toString() matches $matcher');
 }