Update code to use errno values (#44)

This updates all code that throws FileSystemException to properly set
the corresponding osError.errorCode, which allows us to update the
tests to expect the error code rather than the error message. This in turn
allows the tests to be more robust across locales and platforms.

To deal with the fact that different platforms have different errno values
(which will be exposed and tested via LocalFileSystem), this change
creates an ErrorCodes class with static getters for the set of errno
values that are common to all platforms. This code can be removed once
dart-lang/sdk#28860 is resolved and live.

Fixes #43
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dce9e49..4162b26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 2.2.0
+
+* Added `ErrorCodes` class, which holds errno values.
+
 #### 2.1.0
 
 * Add support for new `dart:io` API methods added in Dart SDK 1.23
diff --git a/lib/src/backends/chroot/chroot_directory.dart b/lib/src/backends/chroot/chroot_directory.dart
index 23d24c3..102a1c1 100644
--- a/lib/src/backends/chroot/chroot_directory.dart
+++ b/lib/src/backends/chroot/chroot_directory.dart
@@ -30,18 +30,18 @@
   Future<Directory> rename(String newPath) async {
     if (_isLink) {
       if (await fileSystem.type(path) != expectedType) {
-        throw new FileSystemException('Not a directory', path);
+        throw common.notADirectory(path);
       }
       FileSystemEntityType type = await fileSystem.type(newPath);
       if (type != FileSystemEntityType.NOT_FOUND) {
         if (type != expectedType) {
-          throw new FileSystemException('Not a directory', newPath);
+          throw common.notADirectory(newPath);
         }
         if (!(await fileSystem
             .directory(newPath)
             .list(followLinks: false)
             .isEmpty)) {
-          throw new FileSystemException('Directory not empty', newPath);
+          throw common.directoryNotEmpty(newPath);
         }
       }
       String target = await fileSystem.link(path).target();
@@ -58,18 +58,18 @@
   Directory renameSync(String newPath) {
     if (_isLink) {
       if (fileSystem.typeSync(path) != expectedType) {
-        throw new FileSystemException('Not a directory', path);
+        throw common.notADirectory(path);
       }
       FileSystemEntityType type = fileSystem.typeSync(newPath);
       if (type != FileSystemEntityType.NOT_FOUND) {
         if (type != expectedType) {
-          throw new FileSystemException('Not a directory', newPath);
+          throw common.notADirectory(newPath);
         }
         if (fileSystem
             .directory(newPath)
             .listSync(followLinks: false)
             .isNotEmpty) {
-          throw new FileSystemException('Directory not empty', newPath);
+          throw common.directoryNotEmpty(newPath);
         }
       }
       String target = fileSystem.link(path).targetSync();
@@ -99,9 +99,9 @@
     if (_isLink) {
       switch (await fileSystem.type(path)) {
         case FileSystemEntityType.NOT_FOUND:
-          throw new FileSystemException('No such file or directory', path);
+          throw common.noSuchFileOrDirectory(path);
         case FileSystemEntityType.FILE:
-          throw new FileSystemException('File exists', path);
+          throw common.fileExists(path);
         case FileSystemEntityType.DIRECTORY:
           // Nothing to do.
           return this;
@@ -118,9 +118,9 @@
     if (_isLink) {
       switch (fileSystem.typeSync(path)) {
         case FileSystemEntityType.NOT_FOUND:
-          throw new FileSystemException('No such file or directory', path);
+          throw common.noSuchFileOrDirectory(path);
         case FileSystemEntityType.FILE:
-          throw new FileSystemException('File exists', path);
+          throw common.fileExists(path);
         case FileSystemEntityType.DIRECTORY:
           // Nothing to do.
           return;
diff --git a/lib/src/backends/chroot/chroot_file.dart b/lib/src/backends/chroot/chroot_file.dart
index 74d8cc9..3e12c43 100644
--- a/lib/src/backends/chroot/chroot_file.dart
+++ b/lib/src/backends/chroot/chroot_file.dart
@@ -45,7 +45,7 @@
           };
           break;
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', newPath);
+          throw common.isADirectory(newPath);
         default:
           // Should never happen.
           throw new AssertionError();
@@ -55,9 +55,9 @@
     if (_isLink) {
       switch (await fileSystem.type(path)) {
         case FileSystemEntityType.NOT_FOUND:
-          throw new FileSystemException('No such file or directory', path);
+          throw common.noSuchFileOrDirectory(path);
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', path);
+          throw common.isADirectory(path);
         case FileSystemEntityType.FILE:
           await setUp();
           await fileSystem.delegate
@@ -94,7 +94,7 @@
           };
           break;
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', newPath);
+          throw common.isADirectory(newPath);
         default:
           // Should never happen.
           throw new AssertionError();
@@ -104,9 +104,9 @@
     if (_isLink) {
       switch (fileSystem.typeSync(path)) {
         case FileSystemEntityType.NOT_FOUND:
-          throw new FileSystemException('No such file or directory', path);
+          throw common.noSuchFileOrDirectory(path);
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', path);
+          throw common.isADirectory(path);
         case FileSystemEntityType.FILE:
           setUp();
           fileSystem.delegate
@@ -149,7 +149,7 @@
           // Nothing to do.
           return this;
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', path);
+          throw common.isADirectory(path);
         default:
           throw new AssertionError();
       }
@@ -181,7 +181,7 @@
           // Nothing to do.
           return;
         case FileSystemEntityType.DIRECTORY:
-          throw new FileSystemException('Is a directory', path);
+          throw common.isADirectory(path);
         default:
           throw new AssertionError();
       }
diff --git a/lib/src/backends/chroot/chroot_file_system.dart b/lib/src/backends/chroot/chroot_file_system.dart
index d3c861b..ae5135d 100644
--- a/lib/src/backends/chroot/chroot_file_system.dart
+++ b/lib/src/backends/chroot/chroot_file_system.dart
@@ -114,9 +114,9 @@
       case FileSystemEntityType.DIRECTORY:
         break;
       case FileSystemEntityType.NOT_FOUND:
-        throw new FileSystemException('No such file or directory');
+        throw common.noSuchFileOrDirectory(path);
       default:
-        throw new FileSystemException('Not a directory');
+        throw common.notADirectory(path);
     }
     assert(() {
       p.Context ctx = delegate.path;
@@ -299,7 +299,7 @@
         case FileSystemEntityType.FILE:
           breadcrumbs.clear();
           if (parts.isNotEmpty) {
-            throw new FileSystemException('Not a directory', currentPath);
+            throw common.notADirectory(currentPath);
           }
           break;
         case FileSystemEntityType.NOT_FOUND:
@@ -308,10 +308,6 @@
             return getCurrentPath();
           }
 
-          FileSystemException notFoundException() {
-            return new FileSystemException('No such file or directory', path);
-          }
-
           switch (notFound) {
             case _NotFoundBehavior.mkdir:
               if (parts.isNotEmpty) {
@@ -324,9 +320,9 @@
               if (parts.isEmpty) {
                 return returnEarly();
               }
-              throw notFoundException();
+              throw common.noSuchFileOrDirectory(path);
             case _NotFoundBehavior.throwError:
-              throw notFoundException();
+              throw common.noSuchFileOrDirectory(path);
           }
           break;
         case FileSystemEntityType.LINK:
@@ -334,8 +330,7 @@
             break;
           }
           if (!breadcrumbs.add(currentPath)) {
-            throw new FileSystemException(
-                'Too many levels of symbolic links', path);
+            throw common.tooManyLevelsOfSymbolicLinks(path);
           }
           String target = delegate.link(realPath).targetSync();
           if (ctx.isAbsolute(target)) {
diff --git a/lib/src/backends/chroot/chroot_file_system_entity.dart b/lib/src/backends/chroot/chroot_file_system_entity.dart
index 89b1487..ed777de 100644
--- a/lib/src/backends/chroot/chroot_file_system_entity.dart
+++ b/lib/src/backends/chroot/chroot_file_system_entity.dart
@@ -116,10 +116,9 @@
         String resolvedPath = fileSystem._resolve(p.basename(path),
             from: p.dirname(path), notFound: _NotFoundBehavior.allowAtTail);
         if (!recursive && await type(resolvedPath) != expectedType) {
-          String msg = expectedType == FileSystemEntityType.FILE
-              ? 'Is a directory'
-              : 'Not a directory';
-          throw new FileSystemException(msg, path);
+          throw expectedType == FileSystemEntityType.FILE
+              ? common.isADirectory(path)
+              : common.notADirectory(path);
         }
         await fileSystem.delegate.link(real(path)).delete();
       }
@@ -146,10 +145,9 @@
         String resolvedPath = fileSystem._resolve(p.basename(path),
             from: p.dirname(path), notFound: _NotFoundBehavior.allowAtTail);
         if (!recursive && type(resolvedPath) != expectedType) {
-          String msg = expectedType == FileSystemEntityType.FILE
-              ? 'Is a directory'
-              : 'Not a directory';
-          throw new FileSystemException(msg, path);
+          throw expectedType == FileSystemEntityType.FILE
+              ? common.isADirectory(path)
+              : common.notADirectory(path);
         }
         fileSystem.delegate.link(real(path)).deleteSync();
       }
diff --git a/lib/src/backends/memory/memory_directory.dart b/lib/src/backends/memory/memory_directory.dart
index 34793df..3a3cbb1 100644
--- a/lib/src/backends/memory/memory_directory.dart
+++ b/lib/src/backends/memory/memory_directory.dart
@@ -39,7 +39,7 @@
     );
     if (node.type != expectedType) {
       // There was an existing non-directory node at this object's path
-      throw new io.FileSystemException('File exists', path);
+      throw common.notADirectory(path);
     }
   }
 
@@ -73,7 +73,7 @@
         newPath,
         validateOverwriteExistingEntity: (_DirectoryNode existingNode) {
           if (existingNode.children.isNotEmpty) {
-            throw new io.FileSystemException('Directory not empty', newPath);
+            throw common.directoryNotEmpty(newPath);
           }
         },
       );
diff --git a/lib/src/backends/memory/memory_file.dart b/lib/src/backends/memory/memory_file.dart
index ded6bfa..6d9368e 100644
--- a/lib/src/backends/memory/memory_file.dart
+++ b/lib/src/backends/memory/memory_file.dart
@@ -51,7 +51,7 @@
     if (node.type != expectedType) {
       // There was an existing non-file entity at this object's path
       assert(node.type == FileSystemEntityType.DIRECTORY);
-      throw new io.FileSystemException('Is a directory', path);
+      throw common.isADirectory(path);
     }
     return node;
   }
@@ -66,10 +66,9 @@
         checkType: (_Node node) {
           FileSystemEntityType actualType = node.stat.type;
           if (actualType != expectedType) {
-            String msg = actualType == FileSystemEntityType.NOT_FOUND
-                ? 'No such file or directory'
-                : 'Is a directory';
-            throw new FileSystemException(msg, path);
+            throw actualType == FileSystemEntityType.NOT_FOUND
+                ? common.noSuchFileOrDirectory(path)
+                : common.isADirectory(path);
           }
         },
       );
@@ -231,7 +230,7 @@
     bool flush: false,
   }) {
     if (!_isWriteMode(mode)) {
-      throw new FileSystemException('Bad file descriptor', path);
+      throw common.badFileDescriptor(path);
     }
     _FileNode node = _resolvedBackingOrCreate;
     _truncateIfNecessary(node, mode);
diff --git a/lib/src/backends/memory/memory_file_system_entity.dart b/lib/src/backends/memory/memory_file_system_entity.dart
index b5fdcee..dfe23f9 100644
--- a/lib/src/backends/memory/memory_file_system_entity.dart
+++ b/lib/src/backends/memory/memory_file_system_entity.dart
@@ -261,7 +261,7 @@
     _Node node = _backing;
     if (!recursive) {
       if (node is _DirectoryNode && node.children.isNotEmpty) {
-        throw new io.FileSystemException('Directory not empty', path);
+        throw common.directoryNotEmpty(path);
       }
       (checkType ?? _defaultCheckType)(node);
     }
diff --git a/lib/src/backends/memory/memory_link.dart b/lib/src/backends/memory/memory_link.dart
index 2d63646..e141584 100644
--- a/lib/src/backends/memory/memory_link.dart
+++ b/lib/src/backends/memory/memory_link.dart
@@ -22,10 +22,9 @@
         newPath,
         checkType: (_Node node) {
           if (node.type != expectedType) {
-            throw new FileSystemException(
-                node.type == FileSystemEntityType.DIRECTORY
-                    ? 'Is a directory'
-                    : 'Invalid argument');
+            throw node.type == FileSystemEntityType.DIRECTORY
+                ? common.isADirectory(newPath)
+                : common.invalidArgument(newPath);
           }
         },
       );
@@ -50,7 +49,7 @@
     });
     if (preexisting) {
       // Per the spec, this is an error.
-      throw new io.FileSystemException('File exists', path);
+      throw common.fileExists(path);
     }
   }
 
@@ -82,7 +81,7 @@
     _Node node = _backing;
     if (node.type != expectedType) {
       // Note: this may change; https://github.com/dart-lang/sdk/issues/28204
-      throw new FileSystemException('No such file or directory', path);
+      throw common.noSuchFileOrDirectory(path);
     }
     return (node as _LinkNode).target;
   }
diff --git a/lib/src/backends/memory/utils.dart b/lib/src/backends/memory/utils.dart
index b62e079..72059af 100644
--- a/lib/src/backends/memory/utils.dart
+++ b/lib/src/backends/memory/utils.dart
@@ -27,14 +27,14 @@
 /// Throws a [io.FileSystemException] if [node] is null.
 void _checkExists(_Node node, _PathGenerator path) {
   if (node == null) {
-    throw new io.FileSystemException('No such file or directory', path());
+    throw common.noSuchFileOrDirectory(path());
   }
 }
 
 /// Throws a [io.FileSystemException] if [node] is not a directory.
 void _checkIsDir(_Node node, _PathGenerator path) {
   if (!_isDirectory(node)) {
-    throw new io.FileSystemException('Not a directory', path());
+    throw common.notADirectory(path());
   }
 }
 
@@ -46,23 +46,18 @@
   _PathGenerator path,
 ) {
   if (expectedType != actualType) {
-    String msg;
     switch (expectedType) {
       case FileSystemEntityType.DIRECTORY:
-        msg = 'Not a directory';
-        break;
+        throw common.notADirectory(path());
       case FileSystemEntityType.FILE:
         assert(actualType == FileSystemEntityType.DIRECTORY);
-        msg = 'Is a directory';
-        break;
+        throw common.isADirectory(path());
       case FileSystemEntityType.LINK:
-        msg = 'Invalid argument';
-        break;
+        throw common.invalidArgument(path());
       default:
         // Should not happen
         throw new AssertionError();
     }
-    throw new io.FileSystemException(msg, path());
   }
 }
 
@@ -111,8 +106,7 @@
   while (_isLink(node)) {
     link = node;
     if (!breadcrumbs.add(node)) {
-      throw new io.FileSystemException(
-          'Too many levels of symbolic links', path());
+      throw common.tooManyLevelsOfSymbolicLinks(path());
     }
     if (ledger != null) {
       if (_isAbsolute(link.target)) {
diff --git a/lib/src/common.dart b/lib/src/common.dart
index 24dc7be..87148c5 100644
--- a/lib/src/common.dart
+++ b/lib/src/common.dart
@@ -2,7 +2,8 @@
 // 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 'package:file/src/io.dart' as io;
+import 'interface.dart';
+import 'io.dart' as io;
 
 /// Gets the string path represented by the specified generic [path].
 String getPath(dynamic path) {
@@ -16,3 +17,49 @@
     throw new ArgumentError('Invalid type for "path": ${path?.runtimeType}');
   }
 }
+
+/// Returns a 'No such file or directory' [FileSystemException].
+FileSystemException noSuchFileOrDirectory(String path) {
+  return _fsException(path, 'No such file or directory', ErrorCodes.ENOENT);
+}
+
+/// Returns a 'Not a directory' [FileSystemException].
+FileSystemException notADirectory(String path) {
+  return _fsException(path, 'Not a directory', ErrorCodes.ENOTDIR);
+}
+
+/// Returns a 'Is a directory' [FileSystemException].
+FileSystemException isADirectory(String path) {
+  return _fsException(path, 'Is a directory', ErrorCodes.EISDIR);
+}
+
+/// Returns a 'Directory not empty' [FileSystemException].
+FileSystemException directoryNotEmpty(String path) {
+  return _fsException(path, 'Directory not empty', ErrorCodes.ENOTEMPTY);
+}
+
+/// Returns a 'File exists' [FileSystemException].
+FileSystemException fileExists(String path) {
+  return _fsException(path, 'File exists', ErrorCodes.EEXIST);
+}
+
+/// Returns a 'Invalid argument' [FileSystemException].
+FileSystemException invalidArgument(String path) {
+  return _fsException(path, 'Invalid argument', ErrorCodes.EINVAL);
+}
+
+/// Returns a 'Too many levels of symbolic links' [FileSystemException].
+FileSystemException tooManyLevelsOfSymbolicLinks(String path) {
+  // TODO(tvolkert): Switch to ErrorCodes.EMLINK
+  return _fsException(
+      path, 'Too many levels of symbolic links', ErrorCodes.ELOOP);
+}
+
+/// Returns a 'Bad file descriptor' [FileSystemException].
+FileSystemException badFileDescriptor(String path) {
+  return _fsException(path, 'Bad file descriptor', ErrorCodes.EBADF);
+}
+
+FileSystemException _fsException(String path, String msg, int errorCode) {
+  return new FileSystemException(msg, path, new OSError(msg, errorCode));
+}
diff --git a/lib/src/interface.dart b/lib/src/interface.dart
index db86876..4662e35 100644
--- a/lib/src/interface.dart
+++ b/lib/src/interface.dart
@@ -4,17 +4,10 @@
 
 library file.src.interface;
 
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:path/path.dart' as path;
-
-import 'io.dart' as io;
-
-export 'io.dart';
-
-part 'interface/directory.dart';
-part 'interface/file.dart';
-part 'interface/file_system.dart';
-part 'interface/file_system_entity.dart';
-part 'interface/link.dart';
+export 'interface/directory.dart';
+export 'interface/error_codes.dart';
+export 'interface/file.dart';
+export 'interface/file_system.dart';
+export 'interface/file_system_entity.dart';
+export 'interface/link.dart';
+export 'io.dart' hide Directory, File, FileSystemEntity, Link;
diff --git a/lib/src/interface/directory.dart b/lib/src/interface/directory.dart
index 10740ca..f19c67f 100644
--- a/lib/src/interface/directory.dart
+++ b/lib/src/interface/directory.dart
@@ -2,7 +2,10 @@
 // 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.
 
-part of file.src.interface;
+import 'dart:async';
+
+import 'file_system_entity.dart';
+import '../io.dart' as io;
 
 /// A reference to a directory on the file system.
 abstract class Directory implements FileSystemEntity, io.Directory {
diff --git a/lib/src/interface/error_codes.dart b/lib/src/interface/error_codes.dart
new file mode 100644
index 0000000..48fc5db
--- /dev/null
+++ b/lib/src/interface/error_codes.dart
@@ -0,0 +1,585 @@
+// Copyright (c) 2017, 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 'error_codes_internal.dart'
+    if (dart.library.io) 'error_codes_dart_io.dart';
+
+/// Operating system error codes.
+// TODO(tvolkert): Remove (https://github.com/dart-lang/sdk/issues/28860)
+class ErrorCodes {
+  ErrorCodes._();
+
+  /// Argument list too long
+  // ignore: non_constant_identifier_names
+  static int get E2BIG => _platform((_Codes codes) => codes.e2big);
+
+  /// Permission denied
+  // ignore: non_constant_identifier_names
+  static int get EACCES => _platform((_Codes codes) => codes.eacces);
+
+  /// Try again
+  // ignore: non_constant_identifier_names
+  static int get EAGAIN => _platform((_Codes codes) => codes.eagain);
+
+  /// Bad file number
+  // ignore: non_constant_identifier_names
+  static int get EBADF => _platform((_Codes codes) => codes.ebadf);
+
+  /// Device or resource busy
+  // ignore: non_constant_identifier_names
+  static int get EBUSY => _platform((_Codes codes) => codes.ebusy);
+
+  /// No child processes
+  // ignore: non_constant_identifier_names
+  static int get ECHILD => _platform((_Codes codes) => codes.echild);
+
+  /// Resource deadlock would occur
+  // ignore: non_constant_identifier_names
+  static int get EDEADLK => _platform((_Codes codes) => codes.edeadlk);
+
+  /// Math argument out of domain of func
+  // ignore: non_constant_identifier_names
+  static int get EDOM => _platform((_Codes codes) => codes.edom);
+
+  /// File exists
+  // ignore: non_constant_identifier_names
+  static int get EEXIST => _platform((_Codes codes) => codes.eexist);
+
+  /// Bad address
+  // ignore: non_constant_identifier_names
+  static int get EFAULT => _platform((_Codes codes) => codes.efault);
+
+  /// File too large
+  // ignore: non_constant_identifier_names
+  static int get EFBIG => _platform((_Codes codes) => codes.efbig);
+
+  /// Illegal byte sequence
+  // ignore: non_constant_identifier_names
+  static int get EILSEQ => _platform((_Codes codes) => codes.eilseq);
+
+  /// Interrupted system call
+  // ignore: non_constant_identifier_names
+  static int get EINTR => _platform((_Codes codes) => codes.eintr);
+
+  /// Invalid argument
+  // ignore: non_constant_identifier_names
+  static int get EINVAL => _platform((_Codes codes) => codes.einval);
+
+  /// I/O error
+  // ignore: non_constant_identifier_names
+  static int get EIO => _platform((_Codes codes) => codes.eio);
+
+  /// Is a directory
+  // ignore: non_constant_identifier_names
+  static int get EISDIR => _platform((_Codes codes) => codes.eisdir);
+
+  /// Too many levels of symbolic links
+  // ignore: non_constant_identifier_names
+  static int get ELOOP => _platform((_Codes codes) => codes.eloop);
+
+  /// Too many open files
+  // ignore: non_constant_identifier_names
+  static int get EMFILE => _platform((_Codes codes) => codes.emfile);
+
+  /// Too many links
+  // ignore: non_constant_identifier_names
+  static int get EMLINK => _platform((_Codes codes) => codes.emlink);
+
+  /// File name too long
+  // ignore: non_constant_identifier_names
+  static int get ENAMETOOLONG =>
+      _platform((_Codes codes) => codes.enametoolong);
+
+  /// File table overflow
+  // ignore: non_constant_identifier_names
+  static int get ENFILE => _platform((_Codes codes) => codes.enfile);
+
+  /// No such device
+  // ignore: non_constant_identifier_names
+  static int get ENODEV => _platform((_Codes codes) => codes.enodev);
+
+  /// No such file or directory
+  // ignore: non_constant_identifier_names
+  static int get ENOENT => _platform((_Codes codes) => codes.enoent);
+
+  /// Exec format error
+  // ignore: non_constant_identifier_names
+  static int get ENOEXEC => _platform((_Codes codes) => codes.enoexec);
+
+  /// No record locks available
+  // ignore: non_constant_identifier_names
+  static int get ENOLCK => _platform((_Codes codes) => codes.enolck);
+
+  /// Out of memory
+  // ignore: non_constant_identifier_names
+  static int get ENOMEM => _platform((_Codes codes) => codes.enomem);
+
+  /// No space left on device
+  // ignore: non_constant_identifier_names
+  static int get ENOSPC => _platform((_Codes codes) => codes.enospc);
+
+  /// Function not implemented
+  // ignore: non_constant_identifier_names
+  static int get ENOSYS => _platform((_Codes codes) => codes.enosys);
+
+  /// Not a directory
+  // ignore: non_constant_identifier_names
+  static int get ENOTDIR => _platform((_Codes codes) => codes.enotdir);
+
+  /// Directory not empty
+  // ignore: non_constant_identifier_names
+  static int get ENOTEMPTY => _platform((_Codes codes) => codes.enotempty);
+
+  /// Not a typewriter
+  // ignore: non_constant_identifier_names
+  static int get ENOTTY => _platform((_Codes codes) => codes.enotty);
+
+  /// No such device or address
+  // ignore: non_constant_identifier_names
+  static int get ENXIO => _platform((_Codes codes) => codes.enxio);
+
+  /// Operation not permitted
+  // ignore: non_constant_identifier_names
+  static int get EPERM => _platform((_Codes codes) => codes.eperm);
+
+  /// Broken pipe
+  // ignore: non_constant_identifier_names
+  static int get EPIPE => _platform((_Codes codes) => codes.epipe);
+
+  /// Math result not representable
+  // ignore: non_constant_identifier_names
+  static int get ERANGE => _platform((_Codes codes) => codes.erange);
+
+  /// Read-only file system
+  // ignore: non_constant_identifier_names
+  static int get EROFS => _platform((_Codes codes) => codes.erofs);
+
+  /// Illegal seek
+  // ignore: non_constant_identifier_names
+  static int get ESPIPE => _platform((_Codes codes) => codes.espipe);
+
+  /// No such process
+  // ignore: non_constant_identifier_names
+  static int get ESRCH => _platform((_Codes codes) => codes.esrch);
+
+  /// Cross-device link
+  // ignore: non_constant_identifier_names
+  static int get EXDEV => _platform((_Codes codes) => codes.exdev);
+
+  static int _platform(int getCode(_Codes codes)) {
+    _Codes codes = _platforms[operatingSystem];
+    return getCode(codes);
+  }
+}
+
+const Map<String, _Codes> _platforms = const <String, _Codes>{
+  'linux': const _LinuxCodes(),
+  'macos': const _MacOSCodes(),
+  'windows': const _WindowsCodes(),
+};
+
+abstract class _Codes {
+  int get e2big;
+  int get eacces;
+  int get eagain;
+  int get ebadf;
+  int get ebusy;
+  int get echild;
+  int get edeadlk;
+  int get edom;
+  int get eexist;
+  int get efault;
+  int get efbig;
+  int get eilseq;
+  int get eintr;
+  int get einval;
+  int get eio;
+  int get eisdir;
+  int get eloop;
+  int get emfile;
+  int get emlink;
+  int get enametoolong;
+  int get enfile;
+  int get enodev;
+  int get enoent;
+  int get enoexec;
+  int get enolck;
+  int get enomem;
+  int get enospc;
+  int get enosys;
+  int get enotdir;
+  int get enotempty;
+  int get enotty;
+  int get enxio;
+  int get eperm;
+  int get epipe;
+  int get erange;
+  int get erofs;
+  int get espipe;
+  int get esrch;
+  int get exdev;
+}
+
+class _LinuxCodes implements _Codes {
+  const _LinuxCodes();
+
+  @override
+  final int e2big = 7;
+
+  @override
+  final int eacces = 13;
+
+  @override
+  final int eagain = 11;
+
+  @override
+  final int ebadf = 9;
+
+  @override
+  final int ebusy = 16;
+
+  @override
+  final int echild = 10;
+
+  @override
+  final int edeadlk = 35;
+
+  @override
+  final int edom = 33;
+
+  @override
+  final int eexist = 17;
+
+  @override
+  final int efault = 14;
+
+  @override
+  final int efbig = 27;
+
+  @override
+  final int eilseq = 84;
+
+  @override
+  final int eintr = 4;
+
+  @override
+  final int einval = 22;
+
+  @override
+  final int eio = 5;
+
+  @override
+  final int eisdir = 21;
+
+  @override
+  final int eloop = 40;
+
+  @override
+  final int emfile = 24;
+
+  @override
+  final int emlink = 31;
+
+  @override
+  final int enametoolong = 36;
+
+  @override
+  final int enfile = 23;
+
+  @override
+  final int enodev = 19;
+
+  @override
+  final int enoent = 2;
+
+  @override
+  final int enoexec = 8;
+
+  @override
+  final int enolck = 37;
+
+  @override
+  final int enomem = 12;
+
+  @override
+  final int enospc = 28;
+
+  @override
+  final int enosys = 38;
+
+  @override
+  final int enotdir = 20;
+
+  @override
+  final int enotempty = 39;
+
+  @override
+  final int enotty = 25;
+
+  @override
+  final int enxio = 6;
+
+  @override
+  final int eperm = 1;
+
+  @override
+  final int epipe = 32;
+
+  @override
+  final int erange = 34;
+
+  @override
+  final int erofs = 30;
+
+  @override
+  final int espipe = 29;
+
+  @override
+  final int esrch = 3;
+
+  @override
+  final int exdev = 18;
+}
+
+class _MacOSCodes implements _Codes {
+  const _MacOSCodes();
+
+  @override
+  final int e2big = 7;
+
+  @override
+  final int eacces = 13;
+
+  @override
+  final int eagain = 35;
+
+  @override
+  final int ebadf = 9;
+
+  @override
+  final int ebusy = 16;
+
+  @override
+  final int echild = 10;
+
+  @override
+  final int edeadlk = 11;
+
+  @override
+  final int edom = 33;
+
+  @override
+  final int eexist = 17;
+
+  @override
+  final int efault = 14;
+
+  @override
+  final int efbig = 27;
+
+  @override
+  final int eilseq = 92;
+
+  @override
+  final int eintr = 4;
+
+  @override
+  final int einval = 22;
+
+  @override
+  final int eio = 5;
+
+  @override
+  final int eisdir = 21;
+
+  @override
+  final int eloop = 62;
+
+  @override
+  final int emfile = 24;
+
+  @override
+  final int emlink = 31;
+
+  @override
+  final int enametoolong = 63;
+
+  @override
+  final int enfile = 23;
+
+  @override
+  final int enodev = 19;
+
+  @override
+  final int enoent = 2;
+
+  @override
+  final int enoexec = 8;
+
+  @override
+  final int enolck = 77;
+
+  @override
+  final int enomem = 12;
+
+  @override
+  final int enospc = 28;
+
+  @override
+  final int enosys = 78;
+
+  @override
+  final int enotdir = 20;
+
+  @override
+  final int enotempty = 66;
+
+  @override
+  final int enotty = 25;
+
+  @override
+  final int enxio = 6;
+
+  @override
+  final int eperm = 1;
+
+  @override
+  final int epipe = 32;
+
+  @override
+  final int erange = 34;
+
+  @override
+  final int erofs = 30;
+
+  @override
+  final int espipe = 29;
+
+  @override
+  final int esrch = 3;
+
+  @override
+  final int exdev = 18;
+}
+
+class _WindowsCodes implements _Codes {
+  const _WindowsCodes();
+
+  @override
+  final int e2big = 7;
+
+  @override
+  final int eacces = 13;
+
+  @override
+  final int eagain = 11;
+
+  @override
+  final int ebadf = 9;
+
+  @override
+  final int ebusy = 16;
+
+  @override
+  final int echild = 10;
+
+  @override
+  final int edeadlk = 36;
+
+  @override
+  final int edom = 33;
+
+  @override
+  final int eexist = 17;
+
+  @override
+  final int efault = 14;
+
+  @override
+  final int efbig = 27;
+
+  @override
+  final int eilseq = 42;
+
+  @override
+  final int eintr = 4;
+
+  @override
+  final int einval = 22;
+
+  @override
+  final int eio = 5;
+
+  @override
+  final int eisdir = 21;
+
+  @override
+  final int eloop = -1;
+
+  @override
+  final int emfile = 24;
+
+  @override
+  final int emlink = 31;
+
+  @override
+  final int enametoolong = 38;
+
+  @override
+  final int enfile = 23;
+
+  @override
+  final int enodev = 19;
+
+  @override
+  final int enoent = 2;
+
+  @override
+  final int enoexec = 8;
+
+  @override
+  final int enolck = 39;
+
+  @override
+  final int enomem = 12;
+
+  @override
+  final int enospc = 28;
+
+  @override
+  final int enosys = 40;
+
+  @override
+  final int enotdir = 20;
+
+  @override
+  final int enotempty = 41;
+
+  @override
+  final int enotty = 25;
+
+  @override
+  final int enxio = 6;
+
+  @override
+  final int eperm = 1;
+
+  @override
+  final int epipe = 32;
+
+  @override
+  final int erange = 34;
+
+  @override
+  final int erofs = 30;
+
+  @override
+  final int espipe = 29;
+
+  @override
+  final int esrch = 3;
+
+  @override
+  final int exdev = 18;
+}
diff --git a/lib/src/interface/error_codes_dart_io.dart b/lib/src/interface/error_codes_dart_io.dart
new file mode 100644
index 0000000..3f0a97f
--- /dev/null
+++ b/lib/src/interface/error_codes_dart_io.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io' show Platform;
+
+/// If we have `dart:io` available, we pull the current operating system from
+/// the [Platform] class, so we'll get errno values that match our current
+/// operating system.
+final String operatingSystem = Platform.operatingSystem;
diff --git a/lib/src/interface/error_codes_internal.dart b/lib/src/interface/error_codes_internal.dart
new file mode 100644
index 0000000..0a9d7dc
--- /dev/null
+++ b/lib/src/interface/error_codes_internal.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, 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.
+
+/// In environments that don't have `dart:io`, we can't access the Platform
+/// class to determine what platform we're on, so we just pretend we're on
+/// Linux, meaning we'll get errno values that match Linux's errno.h.
+const String operatingSystem = 'linux';
diff --git a/lib/src/interface/file.dart b/lib/src/interface/file.dart
index 5523a78..ce200d5 100644
--- a/lib/src/interface/file.dart
+++ b/lib/src/interface/file.dart
@@ -2,7 +2,11 @@
 // 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.
 
-part of file.src.interface;
+import 'dart:async';
+import 'dart:convert';
+
+import 'file_system_entity.dart';
+import '../io.dart' as io;
 
 /// A reference to a file on the file system.
 abstract class File implements FileSystemEntity, io.File {
diff --git a/lib/src/interface/file_system.dart b/lib/src/interface/file_system.dart
index abd954e..084ba82 100644
--- a/lib/src/interface/file_system.dart
+++ b/lib/src/interface/file_system.dart
@@ -2,7 +2,15 @@
 // 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.
 
-part of file.src.interface;
+import 'dart:async';
+
+import 'package:path/path.dart' as path;
+
+import 'directory.dart';
+import 'file.dart';
+import 'file_system_entity.dart';
+import 'link.dart';
+import '../io.dart' as io;
 
 /// A generic representation of a file system.
 ///
diff --git a/lib/src/interface/file_system_entity.dart b/lib/src/interface/file_system_entity.dart
index a6152d5..53faa45 100644
--- a/lib/src/interface/file_system_entity.dart
+++ b/lib/src/interface/file_system_entity.dart
@@ -2,7 +2,11 @@
 // 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.
 
-part of file.src.interface;
+import 'dart:async';
+
+import 'directory.dart';
+import 'file_system.dart';
+import '../io.dart' as io;
 
 /// The common super class for [File], [Directory], and [Link] objects.
 abstract class FileSystemEntity implements io.FileSystemEntity {
diff --git a/lib/src/interface/link.dart b/lib/src/interface/link.dart
index 261b237..1288363 100644
--- a/lib/src/interface/link.dart
+++ b/lib/src/interface/link.dart
@@ -2,7 +2,10 @@
 // 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.
 
-part of file.src.interface;
+import 'dart:async';
+
+import 'file_system_entity.dart';
+import '../io.dart' as io;
 
 /// A reference to a symbolic link on the file system.
 abstract class Link implements FileSystemEntity, io.Link {
diff --git a/lib/src/testing/core_matchers.dart b/lib/src/testing/core_matchers.dart
index b6868e9..502afd1 100644
--- a/lib/src/testing/core_matchers.dart
+++ b/lib/src/testing/core_matchers.dart
@@ -30,31 +30,36 @@
 /// Returns a [Matcher] that successfully matches against an instance of
 /// [FileSystemException].
 ///
-/// If [message] is specified, matches will be limited to exceptions with a
-/// matching `message` (either in the exception itself or in the nested
-/// [OSError]).
+/// If [osErrorCode] is specified, matches will be limited to exceptions whose
+/// `osError.errorCode` also match the specified matcher.
 ///
-/// [message] may be a String, a predicate function, or a [Matcher]. If it is
-/// a String, it will be wrapped in an equality matcher.
-Matcher isFileSystemException([dynamic message]) =>
-    new _FileSystemException(message);
+/// [osErrorCode] may be an `int`, a predicate function, or a [Matcher]. If it
+/// is an `int`, it will be wrapped in an equality matcher.
+Matcher isFileSystemException([dynamic osErrorCode]) =>
+    new _FileSystemException(osErrorCode);
 
 /// Returns a matcher that successfully matches against a future or function
 /// that throws a [FileSystemException].
 ///
-/// If [message] is specified, matches will be limited to exceptions with a
-/// matching `message` (either in the exception itself or in the nested
-/// [OSError]).
+/// If [osErrorCode] is specified, matches will be limited to exceptions whose
+/// `osError.errorCode` also match the specified matcher.
 ///
-/// [message] may be a String, a predicate function, or a [Matcher]. If it is
-/// a String, it will be wrapped in an equality matcher.
-Matcher throwsFileSystemException([dynamic message]) =>
-    throwsA(isFileSystemException(message));
+/// [osErrorCode] may be an `int`, a predicate function, or a [Matcher]. If it
+/// is an `int`, it will be wrapped in an equality matcher.
+Matcher throwsFileSystemException([dynamic osErrorCode]) =>
+    throwsA(isFileSystemException(osErrorCode));
 
 /// Expects the specified [callback] to throw a [FileSystemException] with the
-/// specified [message].
-void expectFileSystemException(dynamic message, void callback()) {
-  expect(callback, throwsFileSystemException(message));
+/// specified [osErrorCode] (matched against the exception's
+/// `osError.errorCode`).
+///
+/// [osErrorCode] may be an `int`, a predicate function, or a [Matcher]. If it
+/// is an `int`, it will be wrapped in an equality matcher.
+///
+/// See also:
+///   - [ErrorCodes]
+void expectFileSystemException(dynamic osErrorCode, void callback()) {
+  expect(callback, throwsFileSystemException(osErrorCode));
 }
 
 /// Matcher that successfully matches against a [FileSystemEntity] that
@@ -64,23 +69,26 @@
 class _FileSystemException extends Matcher {
   final Matcher _matcher;
 
-  _FileSystemException(dynamic message)
-      : _matcher = message == null ? null : wrapMatcher(message);
+  _FileSystemException(dynamic osErrorCode)
+      : _matcher = osErrorCode == null ? null : wrapMatcher(osErrorCode);
 
   @override
   bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
     if (item is FileSystemException) {
       return (_matcher == null ||
-          _matcher.matches(item.message, matchState) ||
-          _matcher.matches(item.osError?.message, matchState));
+          _matcher.matches(item.osError?.errorCode, matchState));
     }
     return false;
   }
 
   @override
   Description describe(Description desc) {
-    desc.add('FileSystemException with message: ');
-    return _matcher.describe(desc);
+    if (_matcher == null) {
+      return desc.add('FileSystemException');
+    } else {
+      desc.add('FileSystemException with osError.errorCode: ');
+      return _matcher.describe(desc);
+    }
   }
 }
 
diff --git a/pubspec.yaml b/pubspec.yaml
index 5026759..3001c16 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: file
-version: 2.1.0
+version: 2.2.0
 authors:
 - Matan Lurey <matanl@google.com>
 - Yegor Jbanov <yjbanov@google.com>
diff --git a/test/chroot_test.dart b/test/chroot_test.dart
index f9e0d62..b911e19 100644
--- a/test/chroot_test.dart
+++ b/test/chroot_test.dart
@@ -104,7 +104,7 @@
           test('throwsIfSetToSymlinkToDirectoryOutsideJail', () {
             mem.directory('/bar').createSync();
             mem.link('/tmp/foo').createSync('/bar');
-            expectFileSystemException('No such file or directory', () {
+            expectFileSystemException(ErrorCodes.ENOENT, () {
               fs.currentDirectory = '/foo';
             });
           });
diff --git a/test/common_tests.dart b/test/common_tests.dart
index 206b9c5..86daea2 100644
--- a/test/common_tests.dart
+++ b/test/common_tests.dart
@@ -227,14 +227,14 @@
         });
 
         test('throwsIfSetToNonExistentPath', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.currentDirectory = ns('/foo');
           });
         });
 
         test('throwsIfHasNonExistentPathInComplexChain', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.currentDirectory = ns('/bar/../foo');
           });
         });
@@ -295,14 +295,14 @@
 
         test('throwsIfSetToFilePathSegmentAtTail', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.currentDirectory = ns('/foo');
           });
         });
 
         test('throwsIfSetToFilePathSegmentViaTraversal', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.currentDirectory = ns('/foo/bar/baz');
           });
         });
@@ -325,9 +325,12 @@
         test('throwsIfSetToLinkLoop', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Too many levels of symbolic links', () {
-            fs.currentDirectory = ns('/foo');
-          });
+          expectFileSystemException(
+            anyOf(ErrorCodes.EMLINK, ErrorCodes.ELOOP),
+            () {
+              fs.currentDirectory = ns('/foo');
+            },
+          );
         });
       });
 
@@ -395,14 +398,14 @@
         });
 
         test('throwsForDifferentPathsToNonExistentEntities', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.identicalSync(ns('/foo'), ns('/bar'));
           });
         });
 
         test('throwsForDifferentPathsToOneFileOneNonExistentEntity', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.identicalSync(ns('/foo'), ns('/bar'));
           });
         });
@@ -519,10 +522,7 @@
 
         test('throwsIfAlreadyExistsAsFile', () {
           fs.file(ns('/foo')).createSync();
-          // TODO(tvolkert): Change this to just be 'Not a directory'
-          // once Dart 1.22 is stable.
-          String pattern = '(File exists|Not a directory)';
-          expectFileSystemException(matches(pattern), () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.directory(ns('/foo')).createSync();
           });
         });
@@ -538,22 +538,24 @@
           fs.link(ns('/bar')).createSync(ns('/foo'));
           // TODO(tvolkert): Change this to just be 'Not a directory'
           // once Dart 1.22 is stable.
-          String pattern = '(File exists|Not a directory)';
-          expectFileSystemException(matches(pattern), () {
-            fs.directory(ns('/bar')).createSync();
-          });
+          expectFileSystemException(
+            anyOf(ErrorCodes.EEXIST, ErrorCodes.ENOTDIR),
+            () {
+              fs.directory(ns('/bar')).createSync();
+            },
+          );
         });
 
         test('throwsIfAlreadyExistsAsLinkToNotFoundAtTail', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).createSync();
           });
         });
 
         test('throwsIfAlreadyExistsAsLinkToNotFoundViaTraversal', () {
           fs.link(ns('/foo')).createSync(ns('/bar/baz'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).createSync();
           });
         });
@@ -562,7 +564,7 @@
           fs.directory(ns('/foo')).createSync();
           fs.directory(ns('/bar')).createSync();
           fs.link(ns('/bar/baz')).createSync(ns('/foo/qux'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/bar/baz')).createSync();
           });
         });
@@ -574,7 +576,7 @@
         });
 
         test('throwsIfAncestorDoesntExistRecursiveFalse', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo/bar')).createSync();
           });
         });
@@ -611,14 +613,14 @@
         test('throwsIfDestinationIsFile', () {
           fs.file(ns('/bar')).createSync();
           Directory src = fs.directory(ns('/foo'))..createSync();
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             src.renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfDestinationParentFolderDoesntExist', () {
           Directory src = fs.directory(ns('/foo'))..createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             src.renameSync(ns('/bar/baz'));
           });
         });
@@ -627,25 +629,24 @@
           fs.file(ns('/bar/baz')).createSync(recursive: true);
           Directory src = fs.directory(ns('/foo'))..createSync();
           // The error will be 'Directory not empty' on OS X, but it will be
-          // 'File exists' on Linux, so we just ignore it here in the test.
-          expectFileSystemException(null, () {
-            src.renameSync(ns('/bar'));
-          });
+          // 'File exists' on Linux.
+          expectFileSystemException(
+            anyOf(ErrorCodes.ENOTEMPTY, ErrorCodes.EEXIST),
+            () {
+              src.renameSync(ns('/bar'));
+            },
+          );
         });
 
         test('throwsIfSourceDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceIsFile', () {
           fs.file(ns('/foo')).createSync();
-          // The error message is usually 'No such file or directory', but
-          // it's occasionally 'Not a directory', 'Directory not empty',
-          // 'File exists', or 'Undefined error'.
-          // https://github.com/dart-lang/sdk/issues/28147
-          expectFileSystemException(null, () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.directory(ns('/foo')).renameSync(ns('/bar'));
           });
         });
@@ -665,7 +666,7 @@
         test('throwsIfDestinationIsLinkToNotFound', () {
           Directory src = fs.directory(ns('/foo'))..createSync();
           fs.link(ns('/bar')).createSync(ns('/baz'));
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             src.renameSync(ns('/bar'));
           });
         });
@@ -674,7 +675,7 @@
           Directory src = fs.directory(ns('/foo'))..createSync();
           fs.directory(ns('/bar')).createSync();
           fs.link(ns('/baz')).createSync(ns('/bar'));
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             src.renameSync(ns('/baz'));
           });
         });
@@ -723,7 +724,7 @@
         test('throwsIfNonEmptyDirectoryExistsAndRecursiveFalse', () {
           Directory dir = fs.directory(ns('/foo'))..createSync();
           fs.file(ns('/foo/bar')).createSync();
-          expectFileSystemException('Directory not empty', () {
+          expectFileSystemException(ErrorCodes.ENOTEMPTY, () {
             dir.deleteSync();
           });
         });
@@ -737,13 +738,13 @@
         });
 
         test('throwsIfDirectoryDoesntExistAndRecursiveFalse', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).deleteSync();
           });
         });
 
         test('throwsIfDirectoryDoesntExistAndRecursiveTrue', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).deleteSync(recursive: true);
           });
         });
@@ -756,7 +757,7 @@
 
         test('throwsIfPathReferencesFileAndRecursiveFalse', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.directory(ns('/foo')).deleteSync();
           });
         });
@@ -804,14 +805,14 @@
         test('throwsIfPathReferencesLinkToFileAndRecursiveFalse', () {
           fs.file(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.directory(ns('/bar')).deleteSync();
           });
         });
 
         test('throwsIfPathReferencesLinkToNotFoundAndRecursiveFalse', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('Not a directory', () {
+          expectFileSystemException(ErrorCodes.ENOTDIR, () {
             fs.directory(ns('/foo')).deleteSync();
           });
         });
@@ -826,26 +827,29 @@
           fs.link(ns('/foo')).createSync(ns('/bar'));
           fs.link(ns('/bar')).createSync(ns('/baz'));
           fs.link(ns('/baz'))..createSync(ns('/foo'));
-          expectFileSystemException('Too many levels of symbolic links', () {
-            fs.directory(ns('/foo')).resolveSymbolicLinksSync();
-          });
+          expectFileSystemException(
+            anyOf(ErrorCodes.EMLINK, ErrorCodes.ELOOP),
+            () {
+              fs.directory(ns('/foo')).resolveSymbolicLinksSync();
+            },
+          );
         });
 
         test('throwsIfPathNotFoundInTraversal', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo/bar')).resolveSymbolicLinksSync();
           });
         });
 
         test('throwsIfPathNotFoundAtTail', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).resolveSymbolicLinksSync();
           });
         });
 
         test('throwsIfPathNotFoundInMiddleThenBackedOut', () {
           fs.directory(ns('/foo/bar')).createSync(recursive: true);
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo/baz/../bar')).resolveSymbolicLinksSync();
           });
         });
@@ -955,7 +959,7 @@
         });
 
         test('throwsIfDirectoryDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/foo')).createTempSync();
           });
         });
@@ -985,7 +989,7 @@
 
         test('throwsWithNestedPathPrefixThatDoesntExist', () {
           Directory dir = fs.directory(ns('/foo'))..createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             dir.createTempSync('bar/baz');
           });
         });
@@ -1019,7 +1023,7 @@
         });
 
         test('throwsIfDirectoryDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.directory(ns('/bar')).listSync();
           });
         });
@@ -1122,7 +1126,7 @@
         });
 
         test('throwsIfAncestorDoesntExistRecursiveFalse', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo/bar')).createSync();
           });
         });
@@ -1134,7 +1138,7 @@
 
         test('throwsIfAlreadyExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).createSync();
           });
         });
@@ -1142,7 +1146,7 @@
         test('throwsIfAlreadyExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).createSync();
           });
         });
@@ -1165,10 +1169,10 @@
 
         test('throwsIfAlreadyExistsAsLinkToNotFoundViaTraversal', () {
           fs.link(ns('/foo')).createSync(ns('/bar/baz'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).createSync();
           });
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).createSync(recursive: true);
           });
         });
@@ -1176,7 +1180,7 @@
         /*
         test('throwsIfPathSegmentIsLinkToNotFoundAndRecursiveTrue', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo/baz')).createSync(recursive: true);
           });
         });
@@ -1210,7 +1214,7 @@
 
         test('throwsIfDestinationDoesntExistViaTraversal', () {
           File f = fs.file(ns('/foo'))..createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             f.renameSync(ns('/bar/baz'));
           });
         });
@@ -1226,7 +1230,7 @@
         test('throwsIfDestinationExistsAsDirectory', () {
           File f = fs.file(ns('/foo'))..createSync();
           fs.directory(ns('/bar')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             f.renameSync(ns('/bar'));
           });
         });
@@ -1247,7 +1251,7 @@
           File f = fs.file(ns('/foo'))..createSync();
           fs.directory(ns('/bar')).createSync();
           fs.link(ns('/baz')).createSync(ns('/bar'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             f.renameSync(ns('/baz'));
           });
         });
@@ -1262,14 +1266,14 @@
         });
 
         test('throwsIfSourceDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).renameSync(ns('/bar'));
           });
         });
@@ -1288,14 +1292,14 @@
         test('throwsIfSourceExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).renameSync(ns('/baz'));
           });
         });
 
         test('throwsIfSourceExistsAsLinkToNotFound', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).renameSync(ns('/baz'));
           });
         });
@@ -1320,7 +1324,7 @@
 
         test('throwsIfDestinationDoesntExistViaTraversal', () {
           File f = fs.file(ns('/foo'))..createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             f.copySync(ns('/bar/baz'));
           });
         });
@@ -1342,7 +1346,7 @@
         test('throwsIfDestinationExistsAsDirectory', () {
           File f = fs.file(ns('/foo'))..createSync();
           fs.directory(ns('/bar')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             f.copySync(ns('/bar'));
           });
         });
@@ -1370,20 +1374,20 @@
           File f = fs.file(ns('/foo'))..createSync();
           fs.directory(ns('/bar')).createSync();
           fs.link(ns('/baz')).createSync(ns('/bar'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             f.copySync(ns('/baz'));
           });
         });
 
         test('throwsIfSourceDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).copySync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).copySync(ns('/bar'));
           });
         });
@@ -1454,14 +1458,14 @@
 
       group('length', () {
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).lengthSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).lengthSync();
           });
         });
@@ -1505,14 +1509,14 @@
         });
 
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).lastAccessedSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).lastAccessedSync();
           });
         });
@@ -1532,14 +1536,14 @@
         final DateTime time = new DateTime(1999);
 
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).setLastAccessedSync(time);
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).setLastAccessedSync(time);
           });
         });
@@ -1569,14 +1573,14 @@
         });
 
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).lastModifiedSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).lastModifiedSync();
           });
         });
@@ -1596,14 +1600,14 @@
         final DateTime time = new DateTime(1999);
 
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).setLastModifiedSync(time);
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).setLastModifiedSync(time);
           });
         });
@@ -1626,7 +1630,7 @@
         void testIfDoesntExistAtTail(FileMode mode) {
           if (mode == FileMode.READ) {
             test('throwsIfDoesntExistAtTail', () {
-              expectFileSystemException('No such file or directory', () {
+              expectFileSystemException(ErrorCodes.ENOENT, () {
                 fs.file(ns('/bar')).openSync(mode: mode);
               });
             });
@@ -1641,7 +1645,7 @@
 
         void testThrowsIfDoesntExistViaTraversal(FileMode mode) {
           test('throwsIfDoesntExistViaTraversal', () {
-            expectFileSystemException('No such file or directory', () {
+            expectFileSystemException(ErrorCodes.ENOENT, () {
               fs.file(ns('/bar/baz')).openSync(mode: mode);
             });
           });
@@ -1668,28 +1672,28 @@
 
             test('succeedsIfClosedAfterClosed', () {
               raf.closeSync();
-              expectFileSystemException('File closed', () {
+              expectFileSystemException(null, () {
                 raf.closeSync();
               });
             });
 
             test('throwsIfReadAfterClose', () {
               raf.closeSync();
-              expectFileSystemException('File closed', () {
+              expectFileSystemException(null, () {
                 raf.readByteSync();
               });
             });
 
             test('throwsIfWriteAfterClose', () {
               raf.closeSync();
-              expectFileSystemException('File closed', () {
+              expectFileSystemException(null, () {
                 raf.writeByteSync(0xBAD);
               });
             });
 
             test('throwsIfTruncateAfterClose', () {
               raf.closeSync();
-              expectFileSystemException('File closed', () {
+              expectFileSystemException(null, () {
                 raf.truncateSync(0);
               });
             });
@@ -1707,19 +1711,19 @@
             if (mode == FileMode.WRITE_ONLY ||
                 mode == FileMode.WRITE_ONLY_APPEND) {
               test('throwsIfReadByte', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.readByteSync();
                 });
               });
 
               test('throwsIfRead', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.readSync(2);
                 });
               });
 
               test('throwsIfReadInto', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.readIntoSync(new List<int>(5));
                 });
               });
@@ -1777,19 +1781,19 @@
 
             if (mode == FileMode.READ) {
               test('throwsIfWriteByte', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.writeByteSync(0xBAD);
                 });
               });
 
               test('throwsIfWriteFrom', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.writeFromSync(<int>[1, 2, 3, 4]);
                 });
               });
 
               test('throwsIfWriteString', () {
-                expectFileSystemException('Bad file descriptor', () {
+                expectFileSystemException(ErrorCodes.EBADF, () {
                   raf.writeStringSync('This should throw.');
                 });
               });
@@ -1927,7 +1931,7 @@
               }
 
               test('throwsIfSetToNegativeNumber', () {
-                expectFileSystemException('Invalid argument', () {
+                expectFileSystemException(ErrorCodes.EINVAL, () {
                   raf.setPositionSync(-12);
                 });
               });
@@ -1935,7 +1939,7 @@
 
             if (mode == FileMode.READ) {
               test('throwsIfTruncate', () {
-                expectFileSystemException('Invalid argument', () {
+                expectFileSystemException(ErrorCodes.EINVAL, () {
                   raf.truncateSync(5);
                 });
               });
@@ -1963,7 +1967,7 @@
                 });
 
                 test('throwsIfSetToNegativeNumber', () {
-                  expectFileSystemException('Invalid argument', () {
+                  expectFileSystemException(ErrorCodes.EINVAL, () {
                     raf.truncateSync(-2);
                   });
                 });
@@ -1999,8 +2003,7 @@
       group('openRead', () {
         test('throwsIfDoesntExist', () {
           Stream<List<int>> stream = fs.file(ns('/foo')).openRead();
-          expect(stream.drain(),
-              throwsFileSystemException('No such file or directory'));
+          expect(stream.drain(), throwsFileSystemException(ErrorCodes.ENOENT));
         });
 
         test('succeedsIfExistsAsFile', () async {
@@ -2015,7 +2018,7 @@
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
           Stream<List<int>> stream = fs.file(ns('/foo')).openRead();
-          expect(stream.drain(), throwsFileSystemException('Is a directory'));
+          expect(stream.drain(), throwsFileSystemException(ErrorCodes.EISDIR));
         });
 
         test('succeedsIfExistsAsLinkToFile', () async {
@@ -2074,14 +2077,14 @@
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
           expect(fs.file(ns('/foo')).openWrite().close(),
-              throwsFileSystemException('Is a directory'));
+              throwsFileSystemException(ErrorCodes.EISDIR));
         });
 
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
           expect(fs.file(ns('/bar')).openWrite().close(),
-              throwsFileSystemException('Is a directory'));
+              throwsFileSystemException(ErrorCodes.EISDIR));
         });
 
         test('throwsIfModeIsRead', () {
@@ -2281,14 +2284,14 @@
 
       group('readAsBytes', () {
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).readAsBytesSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).readAsBytesSync();
           });
         });
@@ -2296,7 +2299,7 @@
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).readAsBytesSync();
           });
         });
@@ -2322,14 +2325,14 @@
 
       group('readAsString', () {
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).readAsStringSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).readAsStringSync();
           });
         });
@@ -2337,7 +2340,7 @@
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).readAsStringSync();
           });
         });
@@ -2370,14 +2373,14 @@
 
       group('readAsLines', () {
         test('throwsIfDoesntExist', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).readAsLinesSync();
           });
         });
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).readAsLinesSync();
           });
         });
@@ -2385,7 +2388,7 @@
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).readAsLinesSync();
           });
         });
@@ -2431,7 +2434,7 @@
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).writeAsBytesSync(<int>[1, 2, 3, 4]);
           });
         });
@@ -2439,7 +2442,7 @@
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).writeAsBytesSync(<int>[1, 2, 3, 4]);
           });
         });
@@ -2453,7 +2456,7 @@
 
         test('throwsIfFileModeRead', () {
           File f = fs.file(ns('/foo'))..createSync();
-          expectFileSystemException('Bad file descriptor', () {
+          expectFileSystemException(ErrorCodes.EBADF, () {
             f.writeAsBytesSync(<int>[1], mode: FileMode.READ);
           });
         });
@@ -2495,7 +2498,7 @@
 
         test('throwsIfExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).writeAsStringSync('Hello world');
           });
         });
@@ -2503,7 +2506,7 @@
         test('throwsIfExistsAsLinkToDirectory', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).writeAsStringSync('Hello world');
           });
         });
@@ -2517,7 +2520,7 @@
 
         test('throwsIfFileModeRead', () {
           File f = fs.file(ns('/foo'))..createSync();
-          expectFileSystemException('Bad file descriptor', () {
+          expectFileSystemException(ErrorCodes.EBADF, () {
             f.writeAsStringSync('Hello world', mode: FileMode.READ);
           });
         });
@@ -2633,13 +2636,13 @@
         });
 
         test('throwsIfDoesntExistAndRecursiveFalse', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).deleteSync();
           });
         });
 
         test('throwsIfDoesntExistAndRecursiveTrue', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.file(ns('/foo')).deleteSync(recursive: true);
           });
         });
@@ -2652,7 +2655,7 @@
 
         test('throwsIfExistsAsDirectoryAndRecursiveFalse', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/foo')).deleteSync();
           });
         });
@@ -2685,7 +2688,7 @@
         test('throwsIfExistsAsLinkToDirectoryAndRecursiveFalse', () {
           fs.directory(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.file(ns('/bar')).deleteSync();
           });
         });
@@ -2816,20 +2819,20 @@
         });
 
         test('throwsIfLinkDoesntExistAtTail', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).deleteSync();
           });
         });
 
         test('throwsIfLinkDoesntExistViaTraversal', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo/bar')).deleteSync();
           });
         });
 
         test('throwsIfPathReferencesFileAndRecursiveFalse', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Invalid argument', () {
+          expectFileSystemException(ErrorCodes.EINVAL, () {
             fs.link(ns('/foo')).deleteSync();
           });
         });
@@ -2845,10 +2848,12 @@
           fs.directory(ns('/foo')).createSync();
           // TODO(tvolkert): Change this to just be 'Is a directory'
           // once Dart 1.22 is stable.
-          String pattern = '(Invalid argument|Is a directory)';
-          expectFileSystemException(matches(pattern), () {
-            fs.link(ns('/foo')).deleteSync();
-          });
+          expectFileSystemException(
+            anyOf(ErrorCodes.EINVAL, ErrorCodes.EISDIR),
+            () {
+              fs.link(ns('/foo')).deleteSync();
+            },
+          );
         });
 
         test('succeedsIfPathReferencesDirectoryAndRecursiveTrue', () {
@@ -2938,7 +2943,7 @@
         });
 
         test('throwsIfLinkDoesntExistViaTraversalAndRecursiveFalse', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo/bar')).createSync('baz');
           });
         });
@@ -2954,28 +2959,28 @@
 
         test('throwsIfAlreadyExistsAsFile', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('File exists', () {
+          expectFileSystemException(ErrorCodes.EEXIST, () {
             fs.link(ns('/foo')).createSync(ns('/bar'));
           });
         });
 
         test('throwsIfAlreadyExistsAsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('File exists', () {
+          expectFileSystemException(ErrorCodes.EEXIST, () {
             fs.link(ns('/foo')).createSync(ns('/bar'));
           });
         });
 
         test('throwsIfAlreadyExistsWithSameTarget', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('File exists', () {
+          expectFileSystemException(ErrorCodes.EEXIST, () {
             fs.link(ns('/foo')).createSync(ns('/bar'));
           });
         });
 
         test('throwsIfAlreadyExistsWithDifferentTarget', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
-          expectFileSystemException('File exists', () {
+          expectFileSystemException(ErrorCodes.EEXIST, () {
             fs.link(ns('/foo')).createSync(ns('/baz'));
           });
         });
@@ -2988,20 +2993,20 @@
         });
 
         test('throwsIfLinkDoesntExistAtTail', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).updateSync(ns('/bar'));
           });
         });
 
         test('throwsIfLinkDoesntExistViaTraversal', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo/bar')).updateSync(ns('/baz'));
           });
         });
 
         test('throwsIfPathReferencesFile', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Invalid argument', () {
+          expectFileSystemException(ErrorCodes.EINVAL, () {
             fs.link(ns('/foo')).updateSync(ns('/bar'));
           });
         });
@@ -3010,10 +3015,12 @@
           fs.directory(ns('/foo')).createSync();
           // TODO(tvolkert): Change this to just be 'Is a directory'
           // once Dart 1.22 is stable.
-          String pattern = '(Invalid argument|Is a directory)';
-          expectFileSystemException(matches(pattern), () {
-            fs.link(ns('/foo')).updateSync(ns('/bar'));
-          });
+          expectFileSystemException(
+            anyOf(ErrorCodes.EINVAL, ErrorCodes.EISDIR),
+            () {
+              fs.link(ns('/foo')).updateSync(ns('/bar'));
+            },
+          );
         });
 
         test('succeedsIfNewTargetSameAsOldTarget', () {
@@ -3049,27 +3056,27 @@
 
       group('target', () {
         test('throwsIfLinkDoesntExistAtTail', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).targetSync();
           });
         });
 
         test('throwsIfLinkDoesntExistViaTraversal', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo/bar')).targetSync();
           });
         });
 
         test('throwsIfPathReferencesFile', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).targetSync();
           });
         });
 
         test('throwsIfPathReferencesDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).targetSync();
           });
         });
@@ -3106,27 +3113,27 @@
         });
 
         test('throwsIfSourceDoesntExistAtTail', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo')).renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceDoesntExistViaTraversal', () {
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             fs.link(ns('/foo/bar')).renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceIsFile', () {
           fs.file(ns('/foo')).createSync();
-          expectFileSystemException('Invalid argument', () {
+          expectFileSystemException(ErrorCodes.EINVAL, () {
             fs.link(ns('/foo')).renameSync(ns('/bar'));
           });
         });
 
         test('throwsIfSourceIsDirectory', () {
           fs.directory(ns('/foo')).createSync();
-          expectFileSystemException('Is a directory', () {
+          expectFileSystemException(ErrorCodes.EISDIR, () {
             fs.link(ns('/foo')).renameSync(ns('/bar'));
           });
         });
@@ -3189,7 +3196,7 @@
 
         test('throwsIfDestinationDoesntExistViaTraversal', () {
           Link l = fs.link(ns('/foo'))..createSync(ns('/bar'));
-          expectFileSystemException('No such file or directory', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
             l.renameSync(ns('/baz/qux'));
           });
         });
@@ -3197,7 +3204,7 @@
         test('throwsIfDestinationExistsAsFile', () {
           Link l = fs.link(ns('/foo'))..createSync(ns('/bar'));
           fs.file(ns('/baz')).createSync();
-          expectFileSystemException('Invalid argument', () {
+          expectFileSystemException(ErrorCodes.EINVAL, () {
             l.renameSync(ns('/baz'));
           });
         });
@@ -3205,7 +3212,7 @@
         test('throwsIfDestinationExistsAsDirectory', () {
           Link l = fs.link(ns('/foo'))..createSync(ns('/bar'));
           fs.directory(ns('/baz')).createSync();
-          expectFileSystemException('Invalid argument', () {
+          expectFileSystemException(ErrorCodes.EINVAL, () {
             l.renameSync(ns('/baz'));
           });
         });