Add `FileSystem.systemTempDirectory` (#55)

This provides API parity with dart:io's `Directory.systemTemp`
getter.
diff --git a/lib/src/backends/chroot/chroot_file_system.dart b/lib/src/backends/chroot/chroot_file_system.dart
index dad728f..2e1b299 100644
--- a/lib/src/backends/chroot/chroot_file_system.dart
+++ b/lib/src/backends/chroot/chroot_file_system.dart
@@ -34,6 +34,7 @@
   final FileSystem delegate;
   final String root;
 
+  String _systemTemp;
   String _cwd;
 
   /// Creates a new `ChrootFileSystem` backed by the specified [delegate] file
@@ -62,6 +63,15 @@
   @override
   Link link(path) => new _ChrootLink(this, common.getPath(path));
 
+  /// Gets the system temp directory. This directory will be created on-demand
+  /// in the local root of the file system. Once created, its location is fixed
+  /// for the life of the process.
+  @override
+  Directory get systemTempDirectory {
+    _systemTemp ??= directory(_localRoot).createTempSync('.tmp_').path;
+    return directory(_systemTemp)..createSync();
+  }
+
   /// Gets the current working directory for this file system. Note that this
   /// does *not* proxy to the underlying file system's current directory in
   /// any way; the state of this file system's current directory is local to
diff --git a/lib/src/backends/local/local_file_system.dart b/lib/src/backends/local/local_file_system.dart
index bc23038..a8a64d4 100644
--- a/lib/src/backends/local/local_file_system.dart
+++ b/lib/src/backends/local/local_file_system.dart
@@ -17,6 +17,13 @@
   @override
   Link link(path) => new _LocalLink(this, shim.newLink(path));
 
+  /// Gets the directory provided by the operating system for creating temporary
+  /// files and directories in. The location of the system temp directory is
+  /// platform-dependent, and may be set by an environment variable.
+  @override
+  Directory get systemTempDirectory =>
+      new _LocalDirectory(this, shim.systemTemp());
+
   @override
   Directory get currentDirectory => directory(shim.currentDirectory.path);
 
diff --git a/lib/src/backends/memory/memory_file_system.dart b/lib/src/backends/memory/memory_file_system.dart
index 3985537..277cc7e 100644
--- a/lib/src/backends/memory/memory_file_system.dart
+++ b/lib/src/backends/memory/memory_file_system.dart
@@ -44,6 +44,7 @@
 /// as [#28078](https://github.com/dart-lang/sdk/issues/28078) is resolved.
 class MemoryFileSystem extends FileSystem {
   _RootNode _root;
+  String _systemTemp;
   String _cwd = _separator;
 
   MemoryFileSystem() {
@@ -59,6 +60,15 @@
   @override
   Link link(path) => new _MemoryLink(this, common.getPath(path));
 
+  /// Gets the system temp directory. This directory will be created on-demand
+  /// in the root of the file system. Once created, its location is fixed for
+  /// the life of the process.
+  @override
+  Directory get systemTempDirectory {
+    _systemTemp ??= directory(_separator).createTempSync('.tmp_').path;
+    return directory(_systemTemp)..createSync();
+  }
+
   @override
   Directory get currentDirectory => directory(_cwd);
 
diff --git a/lib/src/interface/file_system.dart b/lib/src/interface/file_system.dart
index 1ed7099..3cdf740 100644
--- a/lib/src/interface/file_system.dart
+++ b/lib/src/interface/file_system.dart
@@ -24,6 +24,12 @@
   /// [path] can be either a [`String`], a [`Uri`], or a [`FileSystemEntity`].
   Link link(path);
 
+  /// Gets the system temp directory.
+  ///
+  /// It is left to file system implementations to decide how to define the
+  /// "system temp directory".
+  Directory get systemTempDirectory;
+
   /// Creates a directory object pointing to the current working directory.
   Directory get currentDirectory;
 
diff --git a/lib/src/io/shim_dart_io.dart b/lib/src/io/shim_dart_io.dart
index 356b3f8..782b171 100644
--- a/lib/src/io/shim_dart_io.dart
+++ b/lib/src/io/shim_dart_io.dart
@@ -7,6 +7,7 @@
 io.Directory newDirectory(path) => new io.Directory(common.getPath(path));
 io.File newFile(path) => new io.File(common.getPath(path));
 io.Link newLink(path) => new io.Link(common.getPath(path));
+io.Directory systemTemp() => io.Directory.systemTemp;
 io.Directory get currentDirectory => io.Directory.current;
 set currentDirectory(dynamic path) => io.Directory.current = path;
 Future<io.FileStat> stat(String path) => io.FileStat.stat(path);
diff --git a/lib/src/io/shim_internal.dart b/lib/src/io/shim_internal.dart
index b3de276..4b7ff32 100644
--- a/lib/src/io/shim_internal.dart
+++ b/lib/src/io/shim_internal.dart
@@ -8,6 +8,7 @@
 io.Directory newDirectory(_) => _requiresIO();
 io.File newFile(_) => _requiresIO();
 io.Link newLink(_) => _requiresIO();
+io.Directory systemTemp() => _requiresIO();
 io.Directory get currentDirectory => _requiresIO();
 set currentDirectory(dynamic _) => _requiresIO();
 Future<io.FileStat> stat(String _) => _requiresIO();
diff --git a/test/common_tests.dart b/test/common_tests.dart
index d65c73b..99ed345 100644
--- a/test/common_tests.dart
+++ b/test/common_tests.dart
@@ -120,6 +120,14 @@
         });
       });
 
+      group('systemTempDirectory', () {
+        test('existsAsDirectory', () {
+          var tmp = fs.systemTempDirectory;
+          expect(tmp, isDirectory);
+          expect(tmp.existsSync(), isTrue);
+        });
+      });
+
       group('currentDirectory', () {
         test('defaultsToRoot', () {
           expect(fs.currentDirectory.path, root);