Make MemoryFileSystem support Windows-style paths (#82)

Fixes #68
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4072f42..232542c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
 #### 4.0.1
 
 * General library cleanup
+* Add `style` support in `MemoryFileSystem`, so that callers can choose to
+  have a memory file system with windows-like paths. [#68]
+  (https://github.com/google/file.dart/issues/68)
 
 #### 4.0.0
 
diff --git a/lib/src/backends/chroot/chroot_file_system.dart b/lib/src/backends/chroot/chroot_file_system.dart
index 54a4892..7389716 100644
--- a/lib/src/backends/chroot/chroot_file_system.dart
+++ b/lib/src/backends/chroot/chroot_file_system.dart
@@ -262,6 +262,10 @@
     bool followLinks: true,
     _NotFoundBehavior notFound: _NotFoundBehavior.allow,
   }) {
+    if (path.isEmpty) {
+      throw common.noSuchFileOrDirectory(path);
+    }
+
     p.Context ctx = this.path;
     String root = _localRoot;
     List<String> parts, ledger;
diff --git a/lib/src/backends/memory.dart b/lib/src/backends/memory.dart
index c2ab6d2..428bc54 100644
--- a/lib/src/backends/memory.dart
+++ b/lib/src/backends/memory.dart
@@ -3,3 +3,4 @@
 // BSD-style license that can be found in the LICENSE file.
 
 export 'memory/memory_file_system.dart' show MemoryFileSystem;
+export 'memory/style.dart' show FileSystemStyle, StyleableFileSystem;
diff --git a/lib/src/backends/memory/common.dart b/lib/src/backends/memory/common.dart
index fa7c6a1..d227c1c 100644
--- a/lib/src/backends/memory/common.dart
+++ b/lib/src/backends/memory/common.dart
@@ -4,9 +4,6 @@
 
 import 'package:file/src/common.dart' as common;
 
-/// The file separator.
-const String separator = '/';
-
 /// Generates a path to use in error messages.
 typedef dynamic PathGenerator();
 
diff --git a/lib/src/backends/memory/memory_directory.dart b/lib/src/backends/memory/memory_directory.dart
index fb8e303..0585c40 100644
--- a/lib/src/backends/memory/memory_directory.dart
+++ b/lib/src/backends/memory/memory_directory.dart
@@ -14,6 +14,7 @@
 import 'memory_file_system_entity.dart';
 import 'memory_link.dart';
 import 'node.dart';
+import 'style.dart';
 import 'utils.dart' as utils;
 
 /// Internal implementation of [Directory].
@@ -30,7 +31,10 @@
   io.FileSystemEntityType get expectedType => io.FileSystemEntityType.DIRECTORY;
 
   @override
-  Uri get uri => new Uri.directory(path);
+  Uri get uri {
+    return new Uri.directory(path,
+        windows: fileSystem.style == FileSystemStyle.windows);
+  }
 
   @override
   bool existsSync() => backingOrNull?.stat?.type == expectedType;
@@ -121,7 +125,9 @@
     List<_PendingListTask> tasks = <_PendingListTask>[
       new _PendingListTask(
         node,
-        path.endsWith(separator) ? path.substring(0, path.length - 1) : path,
+        path.endsWith(fileSystem.path.separator)
+            ? path.substring(0, path.length - 1)
+            : path,
         new Set<LinkNode>(),
       ),
     ];
diff --git a/lib/src/backends/memory/memory_file_system.dart b/lib/src/backends/memory/memory_file_system.dart
index 905ee7a..935ef7b 100644
--- a/lib/src/backends/memory/memory_file_system.dart
+++ b/lib/src/backends/memory/memory_file_system.dart
@@ -14,6 +14,7 @@
 import 'memory_file_stat.dart';
 import 'memory_link.dart';
 import 'node.dart';
+import 'style.dart';
 import 'utils.dart' as utils;
 
 const String _thisDir = '.';
@@ -21,8 +22,6 @@
 
 /// An implementation of [FileSystem] that exists entirely in memory with an
 /// internal representation loosely based on the Filesystem Hierarchy Standard.
-/// Notably, this means that this implementation will not look like a Windows
-/// file system even if it's being run on a Windows host operating system.
 ///
 /// [MemoryFileSystem] is suitable for mocking and tests, as well as for
 /// caching or staging before writing or reading to a live system.
@@ -30,12 +29,15 @@
 /// This implementation of the [FileSystem] interface does not directly use
 /// any `dart:io` APIs; it merely uses the library's enum values and interfaces.
 /// As such, it is suitable for use in the browser.
-abstract class MemoryFileSystem implements FileSystem {
+abstract class MemoryFileSystem implements StyleableFileSystem {
   /// Creates a new `MemoryFileSystem`.
   ///
   /// The file system will be empty, and the current directory will be the
   /// root directory.
-  factory MemoryFileSystem() = _MemoryFileSystem;
+  ///
+  /// If [style] is specified, the file system will use the specified path
+  /// style. The default is [FileSystemStyle.posix].
+  factory MemoryFileSystem({FileSystemStyle style}) = _MemoryFileSystem;
 }
 
 /// Internal implementation of [MemoryFileSystem].
@@ -43,21 +45,22 @@
     implements MemoryFileSystem, NodeBasedFileSystem {
   RootNode _root;
   String _systemTemp;
-  String _cwd = separator;
+  p.Context _context;
 
-  /// Creates a new `MemoryFileSystem`.
-  ///
-  /// The file system will be empty, and the current directory will be the
-  /// root directory.
-  _MemoryFileSystem() {
+  _MemoryFileSystem({this.style: FileSystemStyle.posix})
+      : assert(style != null) {
     _root = new RootNode(this);
+    _context = style.contextFor(style.root);
   }
 
   @override
+  final FileSystemStyle style;
+
+  @override
   RootNode get root => _root;
 
   @override
-  String get cwd => _cwd;
+  String get cwd => _context.current;
 
   @override
   Directory directory(dynamic path) => new MemoryDirectory(this, getPath(path));
@@ -69,19 +72,19 @@
   Link link(dynamic path) => new MemoryLink(this, getPath(path));
 
   @override
-  p.Context get path => new p.Context(style: p.Style.posix, current: _cwd);
+  p.Context get path => _context;
 
   /// 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;
+    _systemTemp ??= directory(style.root).createTempSync('.tmp_').path;
     return directory(_systemTemp)..createSync();
   }
 
   @override
-  Directory get currentDirectory => directory(_cwd);
+  Directory get currentDirectory => directory(cwd);
 
   @override
   set currentDirectory(dynamic path) {
@@ -98,8 +101,8 @@
     Node node = findNode(value);
     checkExists(node, () => value);
     utils.checkIsDir(node, () => value);
-    assert(utils.isAbsolute(value));
-    _cwd = value;
+    assert(_context.isAbsolute(value));
+    _context = style.contextFor(value);
   }
 
   @override
@@ -154,7 +157,7 @@
   /// Gets the node backing for the current working directory. Note that this
   /// can return null if the directory has been deleted or moved from under our
   /// feet.
-  DirectoryNode get _current => findNode(_cwd);
+  DirectoryNode get _current => findNode(cwd);
 
   @override
   Node findNode(
@@ -169,13 +172,15 @@
       throw new ArgumentError.notNull('path');
     }
 
-    if (utils.isAbsolute(path)) {
+    if (_context.isAbsolute(path)) {
       reference = _root;
+      path = path.substring(style.drive.length);
     } else {
       reference ??= _current;
     }
 
-    List<String> parts = path.split(separator)..removeWhere(utils.isEmpty);
+    List<String> parts = path.split(style.separator)
+      ..removeWhere(utils.isEmpty);
     DirectoryNode directory = reference.directory;
     Node child = directory;
 
@@ -200,7 +205,9 @@
         pathWithSymlinks.add(basename);
       }
 
-      PathGenerator subpath = utils.subpath(parts, 0, i);
+      // Generates a subpath for the current segment.
+      String subpath() => parts.sublist(0, i + 1).join(_context.separator);
+
       if (utils.isLink(child) && (i < finalSegment || followTailLink)) {
         if (visitLinks || segmentVisitor == null) {
           if (segmentVisitor != null) {
diff --git a/lib/src/backends/memory/memory_file_system_entity.dart b/lib/src/backends/memory/memory_file_system_entity.dart
index c699311..16a451a 100644
--- a/lib/src/backends/memory/memory_file_system_entity.dart
+++ b/lib/src/backends/memory/memory_file_system_entity.dart
@@ -12,6 +12,7 @@
 import 'common.dart';
 import 'memory_directory.dart';
 import 'node.dart';
+import 'style.dart';
 import 'utils.dart' as utils;
 
 /// Validator function for use with `_renameSync`. This will be invoked if the
@@ -89,7 +90,10 @@
   }
 
   @override
-  Uri get uri => new Uri.file(path);
+  Uri get uri {
+    return new Uri.file(path,
+        windows: fileSystem.style == FileSystemStyle.windows);
+  }
 
   @override
   Future<bool> exists() async => existsSync();
@@ -99,16 +103,21 @@
 
   @override
   String resolveSymbolicLinksSync() {
+    if (path.isEmpty) {
+      throw common.noSuchFileOrDirectory(path);
+    }
     List<String> ledger = <String>[];
     if (isAbsolute) {
-      ledger.add('');
+      ledger.add(fileSystem.style.drive);
     }
     Node node = fileSystem.findNode(path,
         pathWithSymlinks: ledger, followTailLink: true);
     checkExists(node, () => path);
-    String resolved = ledger.join(separator);
-    if (!utils.isAbsolute(resolved)) {
-      resolved = fileSystem.cwd + separator + resolved;
+    String resolved = ledger.join(fileSystem.path.separator);
+    if (resolved == fileSystem.style.drive) {
+      resolved = fileSystem.style.root;
+    } else if (!fileSystem.path.isAbsolute(resolved)) {
+      resolved = fileSystem.cwd + fileSystem.path.separator + resolved;
     }
     return fileSystem.path.normalize(resolved);
   }
@@ -137,12 +146,12 @@
       throw new UnsupportedError('Watching not supported in MemoryFileSystem');
 
   @override
-  bool get isAbsolute => utils.isAbsolute(path);
+  bool get isAbsolute => fileSystem.path.isAbsolute(path);
 
   @override
   FileSystemEntity get absolute {
     String absolutePath = path;
-    if (!utils.isAbsolute(absolutePath)) {
+    if (!fileSystem.path.isAbsolute(absolutePath)) {
       absolutePath = fileSystem.path.join(fileSystem.cwd, absolutePath);
     }
     return clone(absolutePath);
diff --git a/lib/src/backends/memory/node.dart b/lib/src/backends/memory/node.dart
index 351b652..1bea575 100644
--- a/lib/src/backends/memory/node.dart
+++ b/lib/src/backends/memory/node.dart
@@ -7,6 +7,7 @@
 
 import 'common.dart';
 import 'memory_file_stat.dart';
+import 'style.dart';
 
 /// Visitor callback for use with [NodeBasedFileSystem.findNode].
 ///
@@ -36,7 +37,7 @@
 
 /// A [FileSystem] whose internal structure is made up of a tree of [Node]
 /// instances, rooted at a single node.
-abstract class NodeBasedFileSystem implements FileSystem {
+abstract class NodeBasedFileSystem implements StyleableFileSystem {
   /// The root node.
   RootNode get root;
 
diff --git a/lib/src/backends/memory/style.dart b/lib/src/backends/memory/style.dart
new file mode 100644
index 0000000..ba78937
--- /dev/null
+++ b/lib/src/backends/memory/style.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2018, 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 'package:file/file.dart';
+import 'package:path/path.dart' as p;
+
+/// Class that represents the path style that a memory file system should
+/// adopt.
+///
+/// This is primarily useful if you want to test how your code will behave
+/// when faced with particular paths or particular path separator characters.
+/// For instance, you may want to test that your code will work on Windows,
+/// while still using a memory file system in order to gain hermeticity in your
+/// tests.
+abstract class FileSystemStyle {
+  const FileSystemStyle._();
+
+  /// Mimics the Unix file system style.
+  ///
+  /// * This style does not have the notion of drives
+  /// * All file system paths are rooted at `/`
+  /// * The path separator is `/`
+  ///
+  /// An example path in this style is `/path/to/file`.
+  static const FileSystemStyle posix = const _Posix();
+
+  /// Mimics the Windows file system style.
+  ///
+  /// * This style mounts its root folder on a single root drive (`C:`)
+  /// * All file system paths are rooted at `C:\`
+  /// * The path separator is `\`
+  ///
+  /// An example path in this style is `C:\path\to\file`.
+  static const FileSystemStyle windows = const _Windows();
+
+  /// The drive upon which the root directory is mounted.
+  ///
+  /// While real-world file systems that have the notion of drives will support
+  /// multiple drives per system, memory file system will only support one
+  /// root drive.
+  ///
+  /// This will be the empty string for styles that don't have the notion of
+  /// drives (e.g. [posix]).
+  String get drive;
+
+  /// The String that represents the delineation between a directory and its
+  /// children.
+  String get separator;
+
+  /// The string that represents the root of the file system.
+  ///
+  /// Memory file system is always single-rooted.
+  String get root => '$drive$separator';
+
+  /// Gets an object useful for manipulating paths in this style.
+  ///
+  /// Relative path manipulations will be relative to the specified [path].
+  p.Context contextFor(String path);
+}
+
+class _Posix extends FileSystemStyle {
+  const _Posix() : super._();
+
+  @override
+  String get drive => '';
+
+  @override
+  String get separator {
+    return p.Style.posix.separator; // ignore: deprecated_member_use
+  }
+
+  @override
+  p.Context contextFor(String path) =>
+      new p.Context(style: p.Style.posix, current: path);
+}
+
+class _Windows extends FileSystemStyle {
+  const _Windows() : super._();
+
+  @override
+  String get drive => 'C:';
+
+  @override
+  String get separator {
+    return p.Style.windows.separator; // ignore: deprecated_member_use
+  }
+
+  @override
+  p.Context contextFor(String path) =>
+      new p.Context(style: p.Style.windows, current: path);
+}
+
+/// A file system that supports different styles.
+abstract class StyleableFileSystem implements FileSystem {
+  /// The style used by this file system.
+  FileSystemStyle get style;
+}
diff --git a/lib/src/backends/memory/utils.dart b/lib/src/backends/memory/utils.dart
index 5decab3..51ffe2c 100644
--- a/lib/src/backends/memory/utils.dart
+++ b/lib/src/backends/memory/utils.dart
@@ -18,9 +18,6 @@
 /// Checks if `node.type` returns [io.FileSystemEntityType.LINK].
 bool isLink(Node node) => node?.type == io.FileSystemEntityType.LINK;
 
-/// Tells whether the specified path represents an absolute path.
-bool isAbsolute(String path) => path.startsWith(separator);
-
 /// Validator function that is expected to throw a [FileSystemException] if
 /// the node does not represent the type that is expected in any given context.
 typedef void TypeChecker(Node node);
@@ -62,12 +59,6 @@
     mode == io.FileMode.WRITE_ONLY ||
     mode == io.FileMode.WRITE_ONLY_APPEND;
 
-/// Returns a [PathGenerator] that generates a subpath of the constituent
-/// [parts] (from [start]..[end], inclusive).
-PathGenerator subpath(List<String> parts, int start, int end) {
-  return () => parts.sublist(start, end + 1).join(separator);
-}
-
 /// Tells whether the given string is empty.
 bool isEmpty(String str) => str.isEmpty;
 
@@ -103,12 +94,12 @@
       throw common.tooManyLevelsOfSymbolicLinks(path());
     }
     if (ledger != null) {
-      if (isAbsolute(link.target)) {
+      if (link.fs.path.isAbsolute(link.target)) {
         ledger.clear();
       } else if (ledger.isNotEmpty) {
         ledger.removeLast();
       }
-      ledger.addAll(link.target.split(separator));
+      ledger.addAll(link.target.split(link.fs.path.separator));
     }
     node = link.getReferent(
       tailVisitor: (DirectoryNode parent, String childName, Node child) {
diff --git a/test/common_tests.dart b/test/common_tests.dart
index 6243e50..d1746c6 100644
--- a/test/common_tests.dart
+++ b/test/common_tests.dart
@@ -130,6 +130,7 @@
     String ns(String path) {
       p.Context posix = new p.Context(style: p.Style.posix);
       List<String> parts = posix.split(path);
+      parts[0] = root;
       path = fs.path.joinAll(parts);
       String rootPrefix = fs.path.rootPrefix(path);
       assert(rootPrefix.isNotEmpty);
@@ -161,7 +162,7 @@
 
         test('succeedsWithUriArgument', () {
           fs.directory(ns('/foo')).createSync();
-          Uri uri = Uri.parse('file://${ns('/foo')}${fs.path.separator}');
+          Uri uri = fs.path.toUri(ns('/foo'));
           expect(fs.directory(uri), exists);
         });
 
@@ -185,7 +186,7 @@
 
         test('succeedsWithUriArgument', () {
           fs.file(ns('/foo')).createSync();
-          Uri uri = Uri.parse('file://${ns('/foo')}');
+          Uri uri = fs.path.toUri(ns('/foo'));
           expect(fs.file(uri), exists);
         });
 
@@ -210,7 +211,7 @@
         test('succeedsWithUriArgument', () {
           fs.file(ns('/foo')).createSync();
           fs.link(ns('/bar')).createSync(ns('/foo'));
-          Uri uri = Uri.parse('file://${ns('/bar')}');
+          Uri uri = fs.path.toUri(ns('/bar'));
           expect(fs.link(uri), exists);
         });
 
@@ -303,8 +304,10 @@
         });
 
         test('staysAtRootIfSetToParentOfRoot', () {
-          fs.currentDirectory = '../../../../../../../../../..';
-          expect(fs.currentDirectory.path, '/');
+          fs.currentDirectory =
+              new List<String>.filled(20, '..').join(fs.path.separator);
+          String cwd = fs.currentDirectory.path;
+          expect(cwd, fs.path.rootPrefix(cwd));
         });
 
         test('removesTrailingSlashIfSet', () {
@@ -445,7 +448,8 @@
         });
 
         test('isDirectoryForAncestorOfRoot', () {
-          FileSystemEntityType type = fs.typeSync('../../../../../../../..');
+          FileSystemEntityType type = fs.typeSync(
+              new List<String>.filled(20, '..').join(fs.path.separator));
           expect(type, FileSystemEntityType.DIRECTORY);
         });
 
@@ -486,8 +490,7 @@
 
     group('Directory', () {
       test('uri', () {
-        expect(fs.directory(ns('/foo')).uri.toString(),
-            'file://${ns('/foo')}${fs.path.separator}');
+        expect(fs.directory(ns('/foo')).uri, fs.path.toUri(ns('/foo') + '/'));
         expect(fs.directory('foo').uri.toString(), 'foo/');
       });
 
@@ -843,6 +846,12 @@
           expect(fs.directory(ns('/')).resolveSymbolicLinksSync(), ns('/'));
         });
 
+        test('throwsIfPathIsEmpty', () {
+          expectFileSystemException(ErrorCodes.ENOENT, () {
+            fs.directory('').resolveSymbolicLinksSync();
+          });
+        });
+
         test('throwsIfLoopInLinkChain', () {
           fs.link(ns('/foo')).createSync(ns('/bar'));
           fs.link(ns('/bar')).createSync(ns('/baz'));
@@ -891,11 +900,15 @@
 
         test('handlesRelativeLinks', () {
           fs.directory(ns('/foo/bar/baz')).createSync(recursive: true);
-          fs.link(ns('/foo/qux')).createSync('bar/baz');
-          expect(fs.directory(ns('/foo/qux')).resolveSymbolicLinksSync(),
-              ns('/foo/bar/baz'));
-          expect(fs.directory('foo/qux').resolveSymbolicLinksSync(),
-              ns('/foo/bar/baz'));
+          fs.link(ns('/foo/qux')).createSync(fs.path.join('bar', 'baz'));
+          expect(
+            fs.directory(ns('/foo/qux')).resolveSymbolicLinksSync(),
+            ns('/foo/bar/baz'),
+          );
+          expect(
+            fs.directory(fs.path.join('foo', 'qux')).resolveSymbolicLinksSync(),
+            ns('/foo/bar/baz'),
+          );
         });
 
         test('handlesAbsoluteLinks', () {
@@ -921,7 +934,7 @@
 
         test('handlesParentAndThisFolderReferences', () {
           fs.directory(ns('/foo/bar/baz')).createSync(recursive: true);
-          fs.link(ns('/foo/bar/baz/qux')).createSync('../..');
+          fs.link(ns('/foo/bar/baz/qux')).createSync(fs.path.join('..', '..'));
           String resolved = fs
               .directory(ns('/foo/./bar/baz/../baz/qux/bar'))
               .resolveSymbolicLinksSync();
@@ -935,7 +948,9 @@
         });
 
         test('handlesComplexPathWithMultipleLinks', () {
-          fs.link(ns('/foo/bar/baz')).createSync('../../qux', recursive: true);
+          fs
+              .link(ns('/foo/bar/baz'))
+              .createSync(fs.path.join('..', '..', 'qux'), recursive: true);
           fs.link(ns('/qux')).createSync('quux');
           fs.link(ns('/quux/quuz')).createSync(ns('/foo'), recursive: true);
           String resolved = fs
@@ -960,16 +975,22 @@
       });
 
       group('parent', () {
+        String root;
+
+        setUp(() {
+          root = fs.path.style.name == 'windows' ? r'C:\' : '/';
+        });
+
         test('returnsCovariantType', () {
-          expect(fs.directory('/').parent, isDirectory);
+          expect(fs.directory(root).parent, isDirectory);
         });
 
         test('returnsRootForRoot', () {
-          expect(fs.directory('/').parent.path, '/');
+          expect(fs.directory(root).parent.path, root);
         });
 
         test('succeedsForNonRoot', () {
-          expect(fs.directory('/foo/bar').parent.path, '/foo');
+          expect(fs.directory(ns('/foo/bar')).parent.path, ns('/foo'));
         });
       });
 
@@ -1021,10 +1042,12 @@
         setUp(() {
           dir = fs.currentDirectory = fs.directory(ns('/foo'))..createSync();
           fs.file('bar').createSync();
-          fs.file('baz/qux').createSync(recursive: true);
-          fs.link('quux').createSync('baz/qux');
-          fs.link('baz/quuz').createSync('../quux');
-          fs.link('baz/grault').createSync('.');
+          fs.file(fs.path.join('baz', 'qux')).createSync(recursive: true);
+          fs.link('quux').createSync(fs.path.join('baz', 'qux'));
+          fs
+              .link(fs.path.join('baz', 'quuz'))
+              .createSync(fs.path.join('..', 'quux'));
+          fs.link(fs.path.join('baz', 'grault')).createSync('.');
           fs.currentDirectory = ns('/');
         });
 
@@ -1135,7 +1158,7 @@
 
     group('File', () {
       test('uri', () {
-        expect(fs.file(ns('/foo')).uri.toString(), 'file://${ns('/foo')}');
+        expect(fs.file(ns('/foo')).uri, fs.path.toUri(ns('/foo')));
         expect(fs.file('foo').uri.toString(), 'foo');
       });
 
@@ -2745,19 +2768,19 @@
         test('whenTargetIsDirectory', () {
           fs.directory(ns('/foo')).createSync();
           Link l = fs.link(ns('/bar'))..createSync(ns('/foo'));
-          expect(l.uri.toString(), 'file://${ns('/bar')}');
+          expect(l.uri, fs.path.toUri(ns('/bar')));
           expect(fs.link('bar').uri.toString(), 'bar');
         });
 
         test('whenTargetIsFile', () {
           fs.file(ns('/foo')).createSync();
           Link l = fs.link(ns('/bar'))..createSync(ns('/foo'));
-          expect(l.uri.toString(), 'file://${ns('/bar')}');
+          expect(l.uri, fs.path.toUri(ns('/bar')));
           expect(fs.link('bar').uri.toString(), 'bar');
         });
 
         test('whenLinkDoesntExist', () {
-          expect(fs.link(ns('/foo')).uri.toString(), 'file://${ns('/foo')}');
+          expect(fs.link(ns('/foo')).uri, fs.path.toUri(ns('/foo')));
           expect(fs.link('foo').uri.toString(), 'foo');
         });
       });
diff --git a/test/memory_test.dart b/test/memory_test.dart
index b968231..2a78372 100644
--- a/test/memory_test.dart
+++ b/test/memory_test.dart
@@ -8,7 +8,7 @@
 import 'common_tests.dart';
 
 void main() {
-  group('MemoryFileSystem', () {
+  group('MemoryFileSystem unix style', () {
     MemoryFileSystem fs;
 
     setUp(() {
@@ -36,4 +36,35 @@
       });
     });
   });
+
+  group('MemoryFileSystem windows style', () {
+    MemoryFileSystem fs;
+
+    setUp(() {
+      fs = new MemoryFileSystem(style: FileSystemStyle.windows);
+    });
+
+    runCommonTests(
+      () => fs,
+      root: () => fs.style.root,
+      skip: <String>[
+        'File > open', // Not yet implemented
+      ],
+    );
+
+    group('toString', () {
+      test('File', () {
+        expect(fs.file('C:\\foo').toString(), "MemoryFile: 'C:\\foo'");
+      });
+
+      test('Directory', () {
+        expect(
+            fs.directory('C:\\foo').toString(), "MemoryDirectory: 'C:\\foo'");
+      });
+
+      test('Link', () {
+        expect(fs.link('C:\\foo').toString(), "MemoryLink: 'C:\\foo'");
+      });
+    });
+  });
 }