Allow malformed UTF-8. (dart-lang/test_descriptor#3)

diff --git a/pkgs/test_descriptor/CHANGELOG.md b/pkgs/test_descriptor/CHANGELOG.md
index 5b26ad3..77b3561 100644
--- a/pkgs/test_descriptor/CHANGELOG.md
+++ b/pkgs/test_descriptor/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.1
+
+* `FileDescriptor.validate()` now allows invalid UTF-8 files.
+
 ## 1.0.0
 
 * Initial version.
diff --git a/pkgs/test_descriptor/lib/src/file_descriptor.dart b/pkgs/test_descriptor/lib/src/file_descriptor.dart
index d648894..e10c2c6 100644
--- a/pkgs/test_descriptor/lib/src/file_descriptor.dart
+++ b/pkgs/test_descriptor/lib/src/file_descriptor.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert';
 import 'dart:io';
 import 'dart:math' as math;
 
@@ -87,7 +86,7 @@
   /// Reads and decodes the contents of this descriptor as a UTF-8 string.
   ///
   /// This isn't supported for matcher descriptors.
-  Future<String> read() => UTF8.decodeStream(readAsBytes());
+  Future<String> read() => utf8.decodeStream(readAsBytes());
 
   /// Reads the contents of this descriptor as a byte stream.
   ///
@@ -121,10 +120,10 @@
   Future<String> read() async => _contents;
 
   Stream<List<int>> readAsBytes() =>
-      new Stream.fromIterable([UTF8.encode(_contents)]);
+      new Stream.fromIterable([utf8.encode(_contents)]);
 
   Future _validate(String prettyPath, List<int> actualContents) {
-    var actualContentsText = UTF8.decode(actualContents);
+    var actualContentsText = utf8.decode(actualContents);
     if (_contents == actualContentsText) return null;
     throw fail(_textMismatchMessage(prettyPath, _contents, actualContentsText));
   }
@@ -184,7 +183,7 @@
   void _validate(String prettyPath, List<int> actualContents) {
     try {
       expect(
-          _isBinary ? actualContents : UTF8.decode(actualContents),
+          _isBinary ? actualContents : utf8.decode(actualContents),
           _matcher);
     } on TestFailure catch (error) {
       throw new TestFailure(
diff --git a/pkgs/test_descriptor/lib/src/utils.dart b/pkgs/test_descriptor/lib/src/utils.dart
index 385f4de..bd2d78c 100644
--- a/pkgs/test_descriptor/lib/src/utils.dart
+++ b/pkgs/test_descriptor/lib/src/utils.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
 
 import 'package:path/path.dart' as p;
 import 'package:term_glyph/term_glyph.dart' as glyph;
@@ -10,6 +11,9 @@
 
 import 'sandbox.dart';
 
+/// A UTF-8 codec that allows malformed byte sequences.
+final utf8 = const Utf8Codec(allowMalformed: true);
+
 /// Prepends a vertical bar to [text].
 String addBar(String text) => prefixLines(text, "${glyph.verticalLine} ",
     first: "${glyph.downEnd} ",
diff --git a/pkgs/test_descriptor/test/file_test.dart b/pkgs/test_descriptor/test/file_test.dart
index 08da999..4cdc184 100644
--- a/pkgs/test_descriptor/test/file_test.dart
+++ b/pkgs/test_descriptor/test/file_test.dart
@@ -66,6 +66,11 @@
           .validate();
     });
 
+    test('succeeds if invalid UTF-8 matches a text matcher', () async {
+      await new File(p.join(d.sandbox, 'name.txt')).writeAsBytes([0xC3, 0x28]);
+      await d.file('name.txt', isNot(isEmpty)).validate();
+    });
+
     test("fails if the text contents don't match", () async {
       await new File(p.join(d.sandbox, 'name.txt')).writeAsString('wrong');
 
@@ -99,6 +104,14 @@
               'Invalid contents for file "name.txt":'))));
     });
 
+    test("fails if invalid UTF-8 doesn't match a text matcher", () async {
+      await new File(p.join(d.sandbox, 'name.txt')).writeAsBytes([0xC3, 0x28]);
+      expect(d.file('name.txt', isEmpty).validate(), throwsA(toString(allOf([
+        startsWith('Invalid contents for file "name.txt":'),
+        contains('�')
+      ]))));
+    });
+
     test("fails if there's no file", () {
       expect(d.file('name.txt', 'contents').validate(),
           throwsA(toString(equals('File not found: "name.txt".'))));
diff --git a/pkgs/test_descriptor/test/utils.dart b/pkgs/test_descriptor/test/utils.dart
index d58f7ba..d6359f1 100644
--- a/pkgs/test_descriptor/test/utils.dart
+++ b/pkgs/test_descriptor/test/utils.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:test_descriptor/test_descriptor.dart' as d;
 import 'package:test/test.dart';
 
 /// Converts a [Stream<List<int>>] to a flat byte future.