Move UnixFileSystem to lib.unix, WindowsFileSystem to lib.windows

--
PiperOrigin-RevId: 148749485
MOS_MIGRATED_REVID=148749485
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 56958d9..b1efe10 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -6,16 +6,17 @@
 CROSS_PLATFORM_WINDOWS_TESTS = [
     "util/DependencySetWindowsTest.java",
     "vfs/PathFragmentWindowsTest.java",
-    "vfs/PathWindowsTest.java",
+    "windows/PathWindowsTest.java",
 ]
 
 # Tests for Windows-specific functionality that run on Windows.
 WINDOWS_ON_WINDOWS_TESTS = glob(
     ["windows/*.java"],
-    exclude = ["windows/MockSubprocess.java"],
-) + [
-    "vfs/WindowsFileSystemTest.java",
-]
+    exclude = [
+        "windows/MockSubprocess.java",
+        "windows/PathWindowsTest.java",
+    ],
+)
 
 # All Windows-specific tests. Use this to exclude Windows tests from globs.
 ALL_WINDOWS_TESTS = CROSS_PLATFORM_WINDOWS_TESTS + WINDOWS_ON_WINDOWS_TESTS
@@ -98,8 +99,10 @@
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages",
         "//src/main/java/com/google/devtools/build/lib:shell",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:guava-testlib",
         "//third_party:junit4",
@@ -191,6 +194,7 @@
         "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//src/main/java/com/google/devtools/common/options",
         "//third_party:guava",
         "//third_party:guava-testlib",
@@ -206,6 +210,7 @@
     ],
     deps = [
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:guava-testlib",
         "//third_party:junit4",
@@ -220,13 +225,19 @@
     data = [
         ":MockSubprocess_deploy.jar",
     ] + JNI_LIB,
+    jvm_flags = [
+        "-Dbazel.windows_unix_root=C:/fake/msys",
+    ],
     test_class = "com.google.devtools.build.lib.AllTests",
     deps = [
         ":test_runner",
         ":testutil",
         ":windows_testutil",
+        "//src/main/java/com/google/devtools/build/lib:clock",
+        "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
         "//src/main/java/com/google/devtools/build/lib:os_util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:junit4",
         "//third_party:truth",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
index b24f8d1..2b281f0 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
@@ -20,6 +20,7 @@
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
         "//src/main/java/com/google/devtools/build/lib:runtime",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java b/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
index 2002c62..6eac63e 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
@@ -22,11 +22,11 @@
 import com.google.devtools.build.lib.testutil.BlazeTestUtils;
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
index 3294d09..52cf1ba 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
@@ -63,7 +63,7 @@
   public static final String TEST_RULE_MODULE =
         "com.google.devtools.build.lib.bazel.rules.BazelRulesModule";
   public static final String TEST_REAL_UNIX_FILE_SYSTEM =
-      "com.google.devtools.build.lib.vfs.UnixFileSystem";
+      "com.google.devtools.build.lib.unix.UnixFileSystem";
 
   public static final ImmutableList<String> IGNORED_MESSAGE_PREFIXES = ImmutableList.<String>of();
 
diff --git a/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java b/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
index c2e0330..ea5039d 100644
--- a/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
@@ -23,7 +23,6 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
similarity index 84%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
rename to src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
index 46b200b..567b2f3 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
@@ -11,24 +11,24 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.google.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.unix;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.google.devtools.build.lib.unix.NativePosixFiles;
-
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.SymlinkAwareFileSystemTest;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import java.io.IOException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-
-/**
- * Tests for the {@link UnixFileSystem} class.
- */
+/** Tests for the {@link com.google.devtools.build.lib.unix.UnixFileSystem} class. */
 @RunWith(JUnit4.class)
 public class UnixFileSystemTest extends SymlinkAwareFileSystemTest {
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java b/src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
similarity index 96%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java
rename to src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
index fc4b253..bf8091a 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.google.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.unix;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -20,7 +20,8 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.testing.EqualsTester;
-
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
index 15e32e0c..c0099d2 100644
--- a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
@@ -19,14 +19,12 @@
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
-
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
+import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.Set;
-
 @RunWith(JUnit4.class)
 public class DependencySetWindowsTest {
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
similarity index 70%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
rename to src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
index e2317bf..15a1842 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
@@ -25,7 +25,7 @@
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
 import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -38,49 +38,47 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Tests for {@link Path}.
- */
+/** Tests for {@link Path} in combination with the native file system for the current platform. */
 @RunWith(JUnit4.class)
-public class UnixPathTest {
+public class NativePathTest {
 
-  private FileSystem unixFs;
+  private FileSystem fs;
   private File aDirectory;
   private File aFile;
   private File anotherFile;
   private File tmpDir;
 
-  protected FileSystem getUnixFileSystem() {
+  protected FileSystem getNativeFileSystem() {
     return FileSystems.getNativeFileSystem();
   }
 
   @Before
   public final void createFiles() throws Exception  {
-    unixFs = getUnixFileSystem();
+    fs = getNativeFileSystem();
     tmpDir = new File(TestUtils.tmpDir(), "tmpDir");
     tmpDir.mkdirs();
     aDirectory = new File(tmpDir, "a_directory");
     aDirectory.mkdirs();
     aFile = new File(tmpDir, "a_file");
-    new FileWriter(aFile).close();
+    new FileOutputStream(aFile).close();
     anotherFile = new File(aDirectory, "another_file.txt");
-    new FileWriter(anotherFile).close();
+    new FileOutputStream(anotherFile).close();
   }
 
   @Test
   public void testExists() {
-    assertTrue(unixFs.getPath(aDirectory.getPath()).exists());
-    assertTrue(unixFs.getPath(aFile.getPath()).exists());
-    assertFalse(unixFs.getPath("/does/not/exist").exists());
+    assertTrue(fs.getPath(aDirectory.getPath()).exists());
+    assertTrue(fs.getPath(aFile.getPath()).exists());
+    assertFalse(fs.getPath("/does/not/exist").exists());
   }
 
   @Test
   public void testDirectoryEntriesForDirectory() throws IOException {
     Collection<Path> entries =
-        unixFs.getPath(tmpDir.getPath()).getDirectoryEntries();
+        fs.getPath(tmpDir.getPath()).getDirectoryEntries();
     List<Path> expectedEntries = Arrays.asList(
-      unixFs.getPath(tmpDir.getPath() + "/a_file"),
-      unixFs.getPath(tmpDir.getPath() + "/a_directory"));
+      fs.getPath(tmpDir.getPath() + "/a_file"),
+      fs.getPath(tmpDir.getPath() + "/a_directory"));
 
     assertEquals(new HashSet<Object>(expectedEntries),
         new HashSet<Object>(entries));
@@ -89,7 +87,7 @@
   @Test
   public void testDirectoryEntriesForFileThrowsException() {
     try {
-      unixFs.getPath(aFile.getPath()).getDirectoryEntries();
+      fs.getPath(aFile.getPath()).getDirectoryEntries();
       fail("No exception thrown.");
     } catch (IOException x) {
       // The expected result.
@@ -98,42 +96,42 @@
 
   @Test
   public void testIsFileIsTrueForFile() {
-    assertTrue(unixFs.getPath(aFile.getPath()).isFile());
+    assertTrue(fs.getPath(aFile.getPath()).isFile());
   }
 
   @Test
   public void testIsFileIsFalseForDirectory() {
-    assertFalse(unixFs.getPath(aDirectory.getPath()).isFile());
+    assertFalse(fs.getPath(aDirectory.getPath()).isFile());
   }
 
   @Test
   public void testBaseName() {
-    assertEquals("base", unixFs.getPath("/foo/base").getBaseName());
+    assertEquals("base", fs.getPath("/foo/base").getBaseName());
   }
 
   @Test
   public void testBaseNameRunsAfterDotDotInterpretation() {
-    assertEquals("base", unixFs.getPath("/base/foo/..").getBaseName());
+    assertEquals("base", fs.getPath("/base/foo/..").getBaseName());
   }
 
   @Test
   public void testParentOfRootIsRoot() {
-    assertEquals(unixFs.getPath("/"), unixFs.getPath("/.."));
-    assertEquals(unixFs.getPath("/"), unixFs.getPath("/../../../../../.."));
-    assertEquals(unixFs.getPath("/foo"), unixFs.getPath("/../../../foo"));
+    assertEquals(fs.getPath("/"), fs.getPath("/.."));
+    assertEquals(fs.getPath("/"), fs.getPath("/../../../../../.."));
+    assertEquals(fs.getPath("/foo"), fs.getPath("/../../../foo"));
   }
 
   @Test
   public void testIsDirectory() {
-    assertTrue(unixFs.getPath(aDirectory.getPath()).isDirectory());
-    assertFalse(unixFs.getPath(aFile.getPath()).isDirectory());
-    assertFalse(unixFs.getPath("/does/not/exist").isDirectory());
+    assertTrue(fs.getPath(aDirectory.getPath()).isDirectory());
+    assertFalse(fs.getPath(aFile.getPath()).isDirectory());
+    assertFalse(fs.getPath("/does/not/exist").isDirectory());
   }
 
   @Test
   public void testListNonExistingDirectoryThrowsException() {
     try {
-      unixFs.getPath("/does/not/exist").getDirectoryEntries();
+      fs.getPath("/does/not/exist").getDirectoryEntries();
       fail("No exception thrown.");
     } catch (IOException ex) {
       // success!
@@ -152,22 +150,22 @@
 
   @Test
   public void testGlob() throws Exception {
-    Collection<Path> textFiles = UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+    Collection<Path> textFiles = UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*/*.txt")
         .globInterruptible();
     assertThat(textFiles).hasSize(1);
     Path onlyFile = textFiles.iterator().next();
-    assertEquals(unixFs.getPath(anotherFile.getPath()), onlyFile);
+    assertEquals(fs.getPath(anotherFile.getPath()), onlyFile);
 
     Collection<Path> onlyFiles =
-        UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+        UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*")
         .setExcludeDirectories(true)
         .globInterruptible();
     assertPathSet(onlyFiles, aFile.getPath());
 
     Collection<Path> directoriesToo =
-        UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+        UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*")
         .setExcludeDirectories(false)
         .globInterruptible();
@@ -176,16 +174,16 @@
 
   @Test
   public void testGetRelative() {
-    Path relative = unixFs.getPath("/foo").getChild("bar");
-    Path expected = unixFs.getPath("/foo/bar");
+    Path relative = fs.getPath("/foo").getChild("bar");
+    Path expected = fs.getPath("/foo/bar");
     assertEquals(expected, relative);
   }
 
   @Test
   public void testEqualsAndHash() {
-    Path path = unixFs.getPath("/foo/bar");
-    Path equalPath = unixFs.getPath("/foo/bar");
-    Path differentPath = unixFs.getPath("/foo/bar/baz");
+    Path path = fs.getPath("/foo/bar");
+    Path equalPath = fs.getPath("/foo/bar");
+    Path differentPath = fs.getPath("/foo/bar/baz");
     Object differentType = new Object();
 
     new EqualsTester().addEqualityGroup(path, equalPath).testEquals();
@@ -199,7 +197,7 @@
     for (int i = 0; i < 256; i++) {
       allLatin1Chars[i] = (char) i;
     }
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     String latin1String = new String(allLatin1Chars);
     FileSystemUtils.writeContentAsLatin1(path, latin1String);
     String fileContent = new String(FileSystemUtils.readContentAsLatin1(path));
@@ -207,9 +205,9 @@
   }
 
   /**
-   * Verify that the encoding implemented by
-   * {@link FileSystemUtils#writeContentAsLatin1(Path, String)}
-   * really is 8859-1 (latin1).
+   * Verify that the encoding implemented by {@link
+   * com.google.devtools.build.lib.vfs.FileSystemUtils#writeContentAsLatin1(Path, String)} really is
+   * 8859-1 (latin1).
    */
   @Test
   public void testVerifyLatin1() throws IOException {
@@ -217,7 +215,7 @@
     for( int i = 0; i < 256; i++) {
       allLatin1Chars[i] = (char)i;
     }
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     String latin1String = new String(allLatin1Chars);
     FileSystemUtils.writeContentAsLatin1(path, latin1String);
     byte[] bytes = FileSystemUtils.readContent(path);
@@ -228,7 +226,7 @@
   public void testBytesReadAndWrite() throws IOException {
     byte[] bytes = new byte[] { (byte) 0xdeadbeef, (byte) 0xdeadbeef>>8,
                                 (byte) 0xdeadbeef>>16, (byte) 0xdeadbeef>>24 };
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     FileSystemUtils.writeContent(path, bytes);
     byte[] content = FileSystemUtils.readContent(path);
     assertEquals(bytes.length, content.length);
@@ -239,7 +237,7 @@
 
   @Test
   public void testInputOutputStreams() throws IOException {
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     OutputStream out = path.getOutputStream();
     for (int i = 0; i < 256; i++) {
       out.write(i);
@@ -254,20 +252,8 @@
   }
 
   @Test
-  public void testAbsolutePathRoot() {
-    assertEquals("/", new Path(null).toString());
-  }
-
-  @Test
-  public void testAbsolutePath() {
-    Path segment = new Path(null, "bar.txt",
-      new Path(null, "foo", new Path(null)));
-    assertEquals("/foo/bar.txt", segment.toString());
-  }
-
-  @Test
   public void testDerivedSegmentEquality() {
-    Path absoluteSegment = unixFs.getRootDirectory();
+    Path absoluteSegment = fs.getRootDirectory();
 
     Path derivedNode = absoluteSegment.getChild("derivedSegment");
     Path otherDerivedNode = absoluteSegment.getChild("derivedSegment");
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
similarity index 87%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java
rename to src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
index d4719f6..5e6224c 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
@@ -18,28 +18,26 @@
 
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
-
+import java.io.IOException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-
 /**
- * A test for {@link Path} in the context of {@link UnixFileSystem}.
+ * A test for {@link Path}.
  */
 @RunWith(JUnit4.class)
-public class UnixPathGetParentTest {
+public class PathGetParentTest {
 
-  private FileSystem unixFs;
+  private FileSystem fs;
   private Path testRoot;
 
   @Before
   public final void createTestRoot() throws Exception  {
-    unixFs = FileSystems.getNativeFileSystem();
-    testRoot = unixFs.getPath(TestUtils.tmpDir()).getRelative("UnixPathGetParentTest");
+    fs = FileSystems.getNativeFileSystem();
+    testRoot = fs.getPath(TestUtils.tmpDir()).getRelative("UnixPathGetParentTest");
     FileSystemUtils.createDirectoryAndParents(testRoot);
   }
 
@@ -49,7 +47,7 @@
   }
 
   private Path getParent(String path) {
-    return unixFs.getPath(path).getParentDirectory();
+    return fs.getPath(path).getParentDirectory();
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
index 482ef8a..1976f05 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
@@ -25,12 +25,6 @@
 import com.google.common.testing.GcFinalization;
 import com.google.devtools.build.lib.util.BlazeClock;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ObjectInputStream;
@@ -38,6 +32,10 @@
 import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * A test for {@link Path}.
@@ -304,6 +302,18 @@
     }
   }
 
+  @Test
+  public void testAbsolutePathRoot() {
+    assertEquals("/", new Path(null).toString());
+  }
+
+  @Test
+  public void testAbsolutePath() {
+    Path segment = new Path(null, "bar.txt",
+      new Path(null, "foo", new Path(null)));
+    assertEquals("/foo/bar.txt", segment.toString());
+  }
+
   private void assertAsFragmentWorks(String expected) {
     assertEquals(new PathFragment(expected), filesystem.getPath(expected).asFragment());
   }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
index a860c9a..50329d6 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
@@ -25,17 +25,16 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.BlazeClock;
 import com.google.devtools.build.lib.util.Clock;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Tests for the UnionFileSystem, both of generic FileSystem functionality
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java b/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
index 1da3198..f7e6d6c 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
@@ -16,16 +16,15 @@
 import com.google.common.base.Verify;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.UnionFileSystem;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
 import com.google.devtools.build.lib.vfs.ZipFileSystem;
-
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
 import java.io.IOException;
 import java.util.Map;
 
@@ -58,7 +57,8 @@
       if (defaultNativeFileSystem == null) {
         try {
           defaultNativeFileSystem = (FileSystem)
-              Class.forName(TestConstants.TEST_REAL_UNIX_FILE_SYSTEM).newInstance();
+              Class.forName(TestConstants.TEST_REAL_UNIX_FILE_SYSTEM)
+                  .getDeclaredConstructor().newInstance();
         } catch (Exception e) {
           throw new IllegalStateException(e);
         }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java b/src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
similarity index 76%
rename from src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
rename to src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
index 9ef6bc7..388c3fd 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
@@ -11,18 +11,21 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package com.google.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.windows;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.devtools.build.lib.vfs.WindowsFileSystem.SHORT_NAME_MATCHER;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Path.PathFactory;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import com.google.devtools.build.lib.windows.WindowsFileSystem.WindowsPath;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -51,7 +54,7 @@
   }
 
   private FileSystem filesystem;
-  private Path root;
+  private WindowsPath root;
   private final MockShortPathResolver shortPathResolver = new MockShortPathResolver();
 
   @Before
@@ -63,7 +66,7 @@
             return WindowsFileSystem.getPathFactoryForTesting(shortPathResolver);
           }
         };
-    root = filesystem.getRootDirectory().getRelative("C:/");
+    root = (WindowsPath) filesystem.getRootDirectory().getRelative("C:/");
     root.createDirectory();
   }
 
@@ -163,7 +166,7 @@
   public void testChildRegistrationWithTranslatedPaths() {
     // Ensure the Path to "/usr" (actually "C:/fake/msys/usr") is created, path parents/children
     // properly registered.
-    Path usrPath = root.getRelative("/usr");
+    WindowsPath usrPath = (WindowsPath) root.getRelative("/usr");
     root.getRelative("dummy_path");
 
     // Assert that "usr" is not registered as a child of "/".
@@ -180,7 +183,7 @@
 
     // Assert that "usr" is registered as a child of "C:/fake/msys/".
     children.clear();
-    root.getRelative("C:/fake/msys")
+    ((WindowsPath) root.getRelative("C:/fake/msys"))
         .applyToChildren(
             new Predicate<Path>() {
               @Override
@@ -195,48 +198,6 @@
   }
 
   @Test
-  public void testShortNameMatcher() {
-    assertThat(SHORT_NAME_MATCHER.apply("abc")).isFalse(); // no ~ in the name
-    assertThat(SHORT_NAME_MATCHER.apply("abc~")).isFalse(); // no number after the ~
-    assertThat(SHORT_NAME_MATCHER.apply("~abc")).isFalse(); // no ~ followed by number
-    assertThat(SHORT_NAME_MATCHER.apply("too_long_path")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("too_long_path~1")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("abcd~1234")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("h~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~123456")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.a")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.ab")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abcd")).isFalse(); // too long for 8dot3
-  }
-
-  @Test
   public void testResolvesShortenedPaths() {
     shortPathResolver.resolutions.put("d:/progra~1", "program files");
     shortPathResolver.resolutions.put("d:/program files/micros~1", "microsoft something");
@@ -279,7 +240,7 @@
           }
         };
 
-    Path msRoot = root.getRelative("d:/progra~1/micros~1");
+    WindowsPath msRoot = (WindowsPath) root.getRelative("d:/progra~1/micros~1");
     assertThat(msRoot.getPathString()).isEqualTo("D:/program files/microsoft something");
     msRoot.applyToChildren(collector);
     // The path string has an upper-case drive letter because that's how path printing works.
@@ -287,7 +248,8 @@
 
     // Assert that the non-resolvable path was not cached.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    WindowsPath foo = (WindowsPath) msRoot.getRelative("foo");
+    foo.applyToChildren(collector);
     assertThat(children).containsExactly("D:/program files/microsoft something/foo/~bar_hello");
 
     // Pretend that a path we already failed to resolve once came into existence.
@@ -309,10 +271,10 @@
 
     // Assert that this time we cached the previously non-existent path.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    foo.applyToChildren(collector);
     // The path strings have upper-case drive letters because that's how path printing works.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    foo.applyToChildren(collector);
     assertThat(children)
         .containsExactly(
             "D:/program files/microsoft something/foo/~bar_hello",
diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
index 773daee..1fd4223 100644
--- a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
 import com.google.devtools.build.lib.windows.util.WindowsTestUtil;
 import java.io.File;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
similarity index 79%
rename from src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java
rename to src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
index ea08b22..75a5497 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.windows;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.windows.WindowsFileSystem.SHORT_NAME_MATCHER;
 import static org.junit.Assert.assertSame;
 
 import com.google.common.base.Function;
@@ -24,7 +25,10 @@
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.windows.WindowsFileOperations;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import com.google.devtools.build.lib.windows.WindowsFileSystem.WindowsPath;
 import com.google.devtools.build.lib.windows.util.WindowsTestUtil;
 import java.io.File;
 import java.io.IOException;
@@ -65,6 +69,48 @@
   }
 
   @Test
+  public void testShortNameMatcher() {
+    assertThat(SHORT_NAME_MATCHER.apply("abc")).isFalse(); // no ~ in the name
+    assertThat(SHORT_NAME_MATCHER.apply("abc~")).isFalse(); // no number after the ~
+    assertThat(SHORT_NAME_MATCHER.apply("~abc")).isFalse(); // no ~ followed by number
+    assertThat(SHORT_NAME_MATCHER.apply("too_long_path")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("too_long_path~1")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("abcd~1234")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("h~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~123456")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.a")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.ab")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abcd")).isFalse(); // too long for 8dot3
+  }
+
+  @Test
   public void testCanWorkWithJunctionSymlinks() throws Exception {
     testUtil.scratchFile("dir\\hello.txt", "hello");
     testUtil.scratchDir("non_existent");
@@ -76,26 +122,26 @@
     Path nonExistentPath = testUtil.createVfsPath(fs, "non_existent");
 
     // Test junction creation.
-    assertThat(fs.exists(juncPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(dirPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(nonExistentPath, /* followSymlinks */ false)).isTrue();
+    assertThat(juncPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(dirPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(nonExistentPath.exists(Symlinks.NOFOLLOW)).isTrue();
 
     // Test recognizing and dereferencing a directory junction.
-    assertThat(fs.isSymbolicLink(juncPath)).isTrue();
-    assertThat(fs.isDirectory(juncPath, /* followSymlinks */ true)).isTrue();
-    assertThat(fs.isDirectory(juncPath, /* followSymlinks */ false)).isFalse();
-    assertThat(fs.getDirectoryEntries(juncPath))
+    assertThat(juncPath.isSymbolicLink()).isTrue();
+    assertThat(juncPath.isDirectory(Symlinks.FOLLOW)).isTrue();
+    assertThat(juncPath.isDirectory(Symlinks.NOFOLLOW)).isFalse();
+    assertThat(juncPath.getDirectoryEntries())
         .containsExactly(testUtil.createVfsPath(fs, "junc\\hello.txt"));
 
     // Test deleting a directory junction.
-    assertThat(fs.delete(juncPath)).isTrue();
-    assertThat(fs.exists(juncPath, /* followSymlinks */ false)).isFalse();
+    assertThat(juncPath.delete()).isTrue();
+    assertThat(juncPath.exists(Symlinks.NOFOLLOW)).isFalse();
 
     // Test recognizing a dangling directory junction.
-    assertThat(fs.delete(nonExistentPath)).isTrue();
-    assertThat(fs.exists(nonExistentPath, /* followSymlinks */ false)).isFalse();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isTrue();
+    assertThat(nonExistentPath.delete()).isTrue();
+    assertThat(nonExistentPath.exists(Symlinks.NOFOLLOW)).isFalse();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isTrue();
     // TODO(bazel-team): fix https://github.com/bazelbuild/bazel/issues/1690 and uncomment the
     // assertion below.
     //assertThat(fs.isSymbolicLink(juncBadPath)).isTrue();
@@ -103,8 +149,8 @@
     assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ false)).isFalse();
 
     // Test deleting a dangling junction.
-    assertThat(fs.delete(juncBadPath)).isTrue();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isFalse();
+    assertThat(juncBadPath.delete()).isTrue();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isFalse();
   }
 
   @Test
@@ -254,7 +300,7 @@
     String longPrefix = "unresolvable.shortpath/foo/";
     String longPath = longPrefix + "will.exist/bar/hello.txt";
     testUtil.scratchDir(longPrefix);
-    final Path foo = fs.getPath(scratchRoot).getRelative(longPrefix);
+    final WindowsPath foo = (WindowsPath) fs.getPath(scratchRoot).getRelative(longPrefix);
 
     // Assert that we can create an unresolvable path.
     Path p = fs.getPath(scratchRoot).getRelative(shortPath);