Automated rollback of commit 82e68b75304438c96ff878a0c2b8d18b42002486.

Fixes #4322, #4306.

*** Reason for rollback ***

Introduces a deadlock (see https://github.com/bazelbuild/bazel/issues/4322)

*** Original change description ***

Make FileSystem operate on LocalPath instead of Path.

PiperOrigin-RevId: 179549866
diff --git a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
index 6fee2c1..259e0f2 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
@@ -25,7 +25,6 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.io.IOException;
@@ -59,7 +58,7 @@
     FileSystem myfs =
         new InMemoryFileSystem(BlazeClock.instance(), hf) {
           @Override
-          protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException {
+          protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
             try {
               barrierLatch.countDown();
               readyLatch.countDown();
@@ -73,8 +72,7 @@
           }
 
           @Override
-          protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction)
-              throws IOException {
+          protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
             return fastDigest ? super.getDigest(path, hashFunction) : null;
           }
         };
@@ -133,7 +131,7 @@
       FileSystem myFS =
           new InMemoryFileSystem(BlazeClock.instance(), hf) {
             @Override
-            protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction)
+            protected byte[] getFastDigest(Path path, HashFunction hashFunction)
                 throws IOException {
               // Digest functions have more than 3 bytes, usually at least 16.
               return malformed;
@@ -141,7 +139,6 @@
           };
       Path path = myFS.getPath("/file");
       FileSystemUtils.writeContentAsLatin1(path, "a");
-
       byte[] result = DigestUtils.getDigestOrFail(path, 1);
       assertThat(result).isEqualTo(path.getDigest());
       assertThat(result).isNotSameAs(malformed);
@@ -224,14 +221,13 @@
     FileSystem tracingFileSystem =
         new InMemoryFileSystem(BlazeClock.instance()) {
           @Override
-          protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction)
-              throws IOException {
+          protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
             getFastDigestCounter.incrementAndGet();
             return null;
           }
 
           @Override
-          protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException {
+          protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException {
             getDigestCounter.incrementAndGet();
             return super.getDigest(path, hashFunction);
           }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
index b7711aa..67d9b62 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
@@ -19,7 +19,7 @@
 import com.google.devtools.build.lib.clock.BlazeClock;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.io.IOException;
 import java.util.function.Function;
@@ -30,15 +30,15 @@
 /** {@link AnalysisTestCase} with custom filesystem that can throw on stat if desired. */
 @RunWith(JUnit4.class)
 public class AnalysisWithIOExceptionsTest extends AnalysisTestCase {
-  private static final Function<LocalPath, String> NULL_FUNCTION = (path) -> null;
+  private static final Function<Path, String> NULL_FUNCTION = (path) -> null;
 
-  private Function<LocalPath, String> crashMessage = NULL_FUNCTION;
+  private Function<Path, String> crashMessage = NULL_FUNCTION;
 
   @Override
   protected FileSystem createFileSystem() {
     return new InMemoryFileSystem(BlazeClock.instance()) {
       @Override
-      public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
         String crash = crashMessage.apply(path);
         if (crash != null) {
           throw new IOException(crash);
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java b/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java
index 023b41e..f9d0372 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java
@@ -24,7 +24,6 @@
 import com.google.devtools.build.lib.testutil.Suite;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.protobuf.ByteString;
@@ -55,23 +54,23 @@
   public final void setUp() throws Exception {
     calls = new HashMap<>();
     md5Overrides = new HashMap<>();
-    fs =
-        new InMemoryFileSystem() {
-          @Override
-          protected InputStream getInputStream(LocalPath path) throws IOException {
-            int c = calls.containsKey(path.toString()) ? calls.get(path.toString()) : 0;
-            c++;
-            calls.put(path.toString(), c);
-            return super.getInputStream(path);
-          }
+    fs = new InMemoryFileSystem() {
+        @Override
+        protected InputStream getInputStream(Path path) throws IOException {
+          int c = calls.containsKey(path.toString())
+              ? calls.get(path.toString()) : 0;
+          c++;
+          calls.put(path.toString(), c);
+          return super.getInputStream(path);
+        }
 
-          @Override
-          protected byte[] getDigest(LocalPath path, HashFunction hf) throws IOException {
-            assertThat(hf).isEqualTo(HashFunction.MD5);
-            byte[] override = md5Overrides.get(path.getPathString());
-            return override != null ? override : super.getDigest(path, hf);
-          }
-        };
+        @Override
+        protected byte[] getDigest(Path path, HashFunction hf) throws IOException {
+          assertThat(hf).isEqualTo(HashFunction.MD5);
+          byte[] override = md5Overrides.get(path.getPathString());
+          return override != null ? override : super.getDigest(path, hf);
+        }
+      };
     underTest = new SingleBuildFileCache("/", fs);
     Path root = fs.getRootDirectory();
     Path file = root.getChild("empty");
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
index 7f1f342..bbb8be7 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java
@@ -28,7 +28,6 @@
 import com.google.devtools.build.lib.packages.util.PackageFactoryTestBase;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.testutil.TestUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
@@ -1000,7 +999,7 @@
         scratch.file("/e/BUILD", "sh_library(name = 'e', data = glob(['*.txt']))");
     Path parentDir = buildFile.getParentDirectory();
     scratch.file("/e/data.txt");
-    throwOnReaddir = LocalPath.create(parentDir.getPathString());
+    throwOnReaddir = parentDir;
     try {
       packages.createPackage("e", buildFile);
     } catch (NoSuchPackageException expected) {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
index fa5e760..eb614fc 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java
@@ -39,7 +39,6 @@
 import com.google.devtools.build.lib.vfs.Dirent;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.io.FileNotFoundException;
@@ -81,7 +80,7 @@
 
   protected abstract PackageFactoryApparatus createPackageFactoryApparatus();
 
-  protected LocalPath throwOnReaddir = null;
+  protected Path throwOnReaddir = null;
 
   protected static AttributeMap attributes(Rule rule) {
     return RawAttributeMapper.of(rule);
@@ -123,8 +122,7 @@
     FileSystem fs =
         new InMemoryFileSystem() {
           @Override
-          public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks)
-              throws IOException {
+          public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
             if (path.equals(throwOnReaddir)) {
               throw new FileNotFoundException(path.getPathString());
             }
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
index 9f84f73..184f222 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
@@ -25,7 +25,6 @@
 import com.google.devtools.build.lib.skyframe.TransitiveTargetValue;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -49,16 +48,15 @@
 
   private static final String FS_ROOT = "/fsg";
 
-  private static final Function<LocalPath, String> NULL_FUNCTION =
-      new Function<LocalPath, String>() {
-        @Override
-        @Nullable
-        public String apply(LocalPath path) {
-          return null;
-        }
-      };
+  private static final Function<Path, String> NULL_FUNCTION = new Function<Path, String>() {
+    @Override
+    @Nullable
+    public String apply(Path path) {
+      return null;
+    }
+  };
 
-  private Function<LocalPath, String> crashMessage = NULL_FUNCTION;
+  private Function<Path, String> crashMessage = NULL_FUNCTION;
 
   @Before
   public final void initializeVisitor() throws Exception {
@@ -84,7 +82,7 @@
   protected FileSystem createFileSystem() {
     return new InMemoryFileSystem(BlazeClock.instance(), PathFragment.create(FS_ROOT)) {
       @Override
-      public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
         String crash = crashMessage.apply(path);
         if (crash != null) {
           throw new IOException(crash);
@@ -99,16 +97,15 @@
     reporter.removeHandler(failFastHandler); // expect errors
     final Path buildPath = scratch.file("pkg/BUILD",
         "sh_library(name = 'x')");
-    crashMessage =
-        new Function<LocalPath, String>() {
-          @Override
-          public String apply(LocalPath path) {
-            if (buildPath.getLocalPath().equals(path)) {
-              return "custom crash: " + buildPath;
-            }
-            return null;
-          }
-        };
+    crashMessage = new Function<Path, String>() {
+      @Override
+      public String apply(Path path) {
+        if (buildPath.equals(path)) {
+          return "custom crash: " + buildPath;
+        }
+        return null;
+      }
+    };
     assertThat(visitTransitively(Label.parseAbsolute("//pkg:x"))).isFalse();
     scratch.overwriteFile("pkg/BUILD",
         "# another comment to force reload",
@@ -129,16 +126,15 @@
         "sh_library(name = 'top', deps = ['//pkg:x'])");
     final Path buildPath = scratch.file("pkg/BUILD",
         "sh_library(name = 'x')");
-    crashMessage =
-        new Function<LocalPath, String>() {
-          @Override
-          public String apply(LocalPath path) {
-            if (buildPath.getLocalPath().equals(path)) {
-              return "custom crash: " + buildPath;
-            }
-            return null;
-          }
-        };
+    crashMessage = new Function<Path, String>() {
+      @Override
+      public String apply(Path path) {
+        if (buildPath.equals(path)) {
+          return "custom crash: " + buildPath;
+        }
+        return null;
+      }
+    };
     assertThat(visitTransitively(Label.parseAbsolute("//top:top"))).isFalse();
     assertContainsEvent("no such package 'pkg'");
     // The traditional label visitor does not propagate the original IOException message.
@@ -163,16 +159,15 @@
     final Path buildPath = scratch.file("top/BUILD",
         "sh_library(name = 'x')");
     buildPath.getParentDirectory().getRelative("pkg").createDirectory();
-    crashMessage =
-        new Function<LocalPath, String>() {
-          @Override
-          public String apply(LocalPath path) {
-            if (buildPath.getLocalPath().equals(path)) {
-              return "custom crash: " + buildPath;
-            }
-            return null;
-          }
-        };
+    crashMessage = new Function<Path, String>() {
+      @Override
+      public String apply(Path path) {
+        if (buildPath.equals(path)) {
+          return "custom crash: " + buildPath;
+        }
+        return null;
+      }
+    };
     assertThat(visitTransitively(Label.parseAbsolute("//top/pkg:x"))).isFalse();
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
index 04e41a3..6e83bbe 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
@@ -48,7 +48,6 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -77,8 +76,8 @@
 public class IncrementalLoadingTest {
   protected PackageCacheTester tester;
 
-  private LocalPath throwOnReaddir = null;
-  private LocalPath throwOnStat = null;
+  private Path throwOnReaddir = null;
+  private Path throwOnStat = null;
 
   @Before
   public final void createTester() throws Exception {
@@ -86,8 +85,7 @@
     FileSystem fs =
         new InMemoryFileSystem(clock) {
           @Override
-          public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks)
-              throws IOException {
+          public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
             if (path.equals(throwOnReaddir)) {
               throw new FileNotFoundException(path.getPathString());
             }
@@ -96,7 +94,7 @@
 
           @Nullable
           @Override
-          public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+          public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
             if (path.equals(throwOnStat)) {
               throw new IOException("bork " + path.getPathString());
             }
@@ -339,7 +337,7 @@
     Path buildFile = tester.addFile("e/BUILD", "sh_library(name = 'e', data = glob(['*.txt']))");
     Path parentDir = buildFile.getParentDirectory();
     tester.addFile("e/data.txt");
-    throwOnReaddir = parentDir.getLocalPath();
+    throwOnReaddir = parentDir;
     tester.sync();
     try {
       tester.getTarget("//e:e");
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
index 1eb8749..37a992d 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
@@ -21,7 +21,6 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryContentInfo;
@@ -42,15 +41,14 @@
   private static class Transformer {
     @SuppressWarnings("unused")
     @Nullable
-    public FileStatus stat(FileStatus stat, LocalPath path, boolean followSymlinks)
-        throws IOException {
+    public FileStatus stat(FileStatus stat, Path path, boolean followSymlinks) throws IOException {
       return stat;
     }
 
     @SuppressWarnings("unused")
     @Nullable
-    public Collection<Dirent> readdir(
-        Collection<Dirent> readdir, LocalPath path, boolean followSymlinks) throws IOException {
+    public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path, boolean followSymlinks)
+        throws IOException {
       return readdir;
     }
   }
@@ -61,14 +59,13 @@
   protected FileSystem createFileSystem() {
     return new InMemoryFileSystem(BlazeClock.instance(), PathFragment.create(FS_ROOT)) {
       @Override
-      public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
         FileStatus defaultResult = super.stat(path, followSymlinks);
         return transformer.stat(defaultResult, path, followSymlinks);
       }
 
       @Override
-      protected Collection<Dirent> readdir(LocalPath path, boolean followSymlinks)
-          throws IOException {
+      protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
         Collection<Dirent> defaultResult = super.readdir(path, followSymlinks);
         return transformer.readdir(defaultResult, path, followSymlinks);
       }
@@ -136,7 +133,7 @@
     return new Transformer() {
       @Nullable
       @Override
-      public FileStatus stat(FileStatus stat, LocalPath path, boolean followSymlinks)
+      public FileStatus stat(final FileStatus stat, Path path, boolean followSymlinks)
           throws IOException {
         if (path.getPathString().endsWith(badPathSuffix)) {
           return new InMemoryContentInfo(BlazeClock.instance()) {
@@ -203,8 +200,8 @@
     return new Transformer() {
       @Nullable
       @Override
-      public Collection<Dirent> readdir(
-          Collection<Dirent> readdir, LocalPath path, boolean followSymlinks) throws IOException {
+      public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path,
+          boolean followSymlinks) throws IOException {
         if (path.getPathString().endsWith(badPathSuffix)) {
           throw new IOException("Path ended in " + badPathSuffix + ", so readdir failed.");
         }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
index 289b420..68437db 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
@@ -26,7 +26,7 @@
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.skyframe.ErrorInfo;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -47,9 +47,10 @@
     boolean statThrowsIoException;
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (statThrowsIoException
-          && path.getPathString()
+          && path.asFragment()
+              .getPathString()
               .equals("/workspace/tools/build_rules/prelude_" + TestConstants.PRODUCT_NAME)) {
         throw new IOException("bork");
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
index de9a9ab..4004e85 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
@@ -40,7 +40,6 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -102,7 +101,7 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public byte[] getDigest(LocalPath path, HashFunction hf) throws IOException {
+          public byte[] getDigest(Path path, HashFunction hf) throws IOException {
             return path.getBaseName().equals("unreadable")
                 ? expectedDigest
                 : super.getDigest(path, hf);
@@ -159,7 +158,7 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public byte[] getDigest(LocalPath path, HashFunction hf) throws IOException {
+          public byte[] getDigest(Path path, HashFunction hf) throws IOException {
             throw exception;
           }
         });
@@ -183,7 +182,7 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+          public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
             if (path.getBaseName().equals("bad")) {
               throw exception;
             }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
index 6fd4837..ff83c4a 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
@@ -29,7 +29,6 @@
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -172,7 +171,7 @@
   /** InMemoryFileSystem that can pretend to do a fast digest. */
   protected class CustomInMemoryFs extends InMemoryFileSystem {
     @Override
-    protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException {
+    protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
       return fastDigest ? getDigest(path, hashFunction) : null;
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
index ee0f019..e28fef3 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -52,7 +52,6 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
@@ -445,7 +444,7 @@
     createFsAndRoot(
         new CustomInMemoryFs(manualClock) {
           @Override
-          protected byte[] getFastDigest(LocalPath path, HashFunction hf) throws IOException {
+          protected byte[] getFastDigest(Path path, HashFunction hf) throws IOException {
             return digest;
           }
         });
@@ -486,7 +485,7 @@
     createFsAndRoot(
         new CustomInMemoryFs(manualClock) {
           @Override
-          protected byte[] getFastDigest(LocalPath path, HashFunction hf) {
+          protected byte[] getFastDigest(Path path, HashFunction hf) {
             return path.getBaseName().equals("unreadable") ? expectedDigest : null;
           }
         });
@@ -831,7 +830,7 @@
     fs =
         new CustomInMemoryFs(manualClock) {
           @Override
-          protected byte[] getDigest(LocalPath path, HashFunction hf) throws IOException {
+          protected byte[] getDigest(Path path, HashFunction hf) throws IOException {
             digestCalls.incrementAndGet();
             return super.getDigest(path, hf);
           }
@@ -896,8 +895,8 @@
     // Our custom filesystem says "a" does not exist, so FileFunction shouldn't bother trying to
     // think about "a/b". Test for this by having a stat of "a/b" fail with an io error, and
     // observing that we don't encounter the error.
-    fs.stubStat(path("a").getLocalPath(), null);
-    fs.stubStatError(path("a/b").getLocalPath(), new IOException("ouch!"));
+    fs.stubStat(path("a"), null);
+    fs.stubStatError(path("a/b"), new IOException("ouch!"));
     assertThat(valueForPath(path("a/b")).exists()).isFalse();
   }
 
@@ -947,7 +946,7 @@
             return 0;
           }
         };
-    fs.stubStat(path("a").getLocalPath(), inconsistentParentFileStatus);
+    fs.stubStat(path("a"), inconsistentParentFileStatus);
     // Disable fast-path md5 so that we don't try try to md5 the "a" (since it actually physically
     // is a directory).
     fastDigest = false;
@@ -968,7 +967,7 @@
   public void testFilesystemInconsistencies_GetFastDigest() throws Exception {
     file("a");
     // Our custom filesystem says "a/b" exists but "a" does not exist.
-    fs.stubFastDigestError(path("a").getLocalPath(), new IOException("nope"));
+    fs.stubFastDigestError(path("a"), new IOException("nope"));
     SequentialBuildDriver driver = makeDriver();
     SkyKey skyKey = skyKey("a");
     EvaluationResult<FileValue> result =
@@ -986,7 +985,7 @@
     createFsAndRoot(
         new CustomInMemoryFs(manualClock) {
           @Override
-          protected boolean isReadable(LocalPath path) throws IOException {
+          protected boolean isReadable(Path path) throws IOException {
             if (path.getBaseName().equals("unreadable")) {
               throw new IOException("isReadable failed");
             }
@@ -1292,7 +1291,7 @@
   public void testInjectionOverIOException() throws Exception {
     Path foo = file("foo");
     SkyKey fooKey = skyKey("foo");
-    fs.stubStatError(foo.getLocalPath(), new IOException("bork"));
+    fs.stubStatError(foo, new IOException("bork"));
     BuildDriver driver = makeDriver();
     EvaluationResult<FileValue> result =
         driver.evaluate(
@@ -1307,7 +1306,7 @@
         .hasExceptionThat()
         .hasMessageThat()
         .isEqualTo("bork");
-    fs.stubbedStatErrors.remove(foo.getLocalPath());
+    fs.stubbedStatErrors.remove(foo);
     differencer.inject(
         fileStateSkyKey("foo"),
         FileStateValue.create(
@@ -1677,36 +1676,36 @@
 
   private class CustomInMemoryFs extends InMemoryFileSystem {
 
-    private final Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap();
-    private final Map<LocalPath, IOException> stubbedStatErrors = Maps.newHashMap();
-    private final Map<LocalPath, IOException> stubbedFastDigestErrors = Maps.newHashMap();
+    private final Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
+    private final Map<Path, IOException> stubbedStatErrors = Maps.newHashMap();
+    private final Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap();
 
     public CustomInMemoryFs(ManualClock manualClock) {
       super(manualClock);
     }
 
-    public void stubFastDigestError(LocalPath path, IOException error) {
+    public void stubFastDigestError(Path path, IOException error) {
       stubbedFastDigestErrors.put(path, error);
     }
 
     @Override
-    protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException {
+    protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
       if (stubbedFastDigestErrors.containsKey(path)) {
         throw stubbedFastDigestErrors.get(path);
       }
       return fastDigest ? getDigest(path) : null;
     }
 
-    public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) {
+    public void stubStat(Path path, @Nullable FileStatus stubbedResult) {
       stubbedStats.put(path, stubbedResult);
     }
 
-    public void stubStatError(LocalPath path, IOException error) {
+    public void stubStatError(Path path, IOException error) {
       stubbedStatErrors.put(path, error);
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStatErrors.containsKey(path)) {
         throw stubbedStatErrors.get(path);
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index a57f395..eb2d141 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -48,7 +48,6 @@
 import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
 import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -808,7 +807,7 @@
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (statThrowsRuntimeException) {
         throw new RuntimeException("bork");
       }
@@ -816,7 +815,7 @@
     }
 
     @Override
-    protected String readSymbolicLink(LocalPath path) throws IOException {
+    protected PathFragment readSymbolicLink(Path path) throws IOException {
       if (readlinkThrowsIoException) {
         throw new IOException("readlink failed");
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
index 3ac3dc4..13acf70 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
@@ -43,7 +43,6 @@
 import com.google.devtools.build.lib.vfs.Dirent;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.RootedPath;
@@ -630,7 +629,7 @@
   @Test
   public void testResilienceToFilesystemInconsistencies_DirectoryExistence() throws Exception {
     // Our custom filesystem says "pkgPath/BUILD" exists but "pkgPath" does not exist.
-    fs.stubStat(pkgPath.getLocalPath(), null);
+    fs.stubStat(pkgPath, null);
     RootedPath pkgRootedPath = RootedPath.toRootedPath(root, pkgPath);
     FileStateValue pkgDirFileStateValue = FileStateValue.create(pkgRootedPath, null);
     FileValue pkgDirValue =
@@ -655,7 +654,7 @@
     // Our custom filesystem says directory "pkgPath/foo/bar" contains a subdirectory "wiz" but a
     // direct stat on "pkgPath/foo/bar/wiz" says it does not exist.
     Path fooBarDir = pkgPath.getRelative("foo/bar");
-    fs.stubStat(fooBarDir.getRelative("wiz").getLocalPath(), null);
+    fs.stubStat(fooBarDir.getRelative("wiz"), null);
     RootedPath fooBarDirRootedPath = RootedPath.toRootedPath(root, fooBarDir);
     SkyValue fooBarDirListingValue =
         DirectoryListingStateValue.create(
@@ -684,7 +683,7 @@
         RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz/file"));
     final FileStatus realStat = fileRootedPath.asPath().stat();
     fs.stubStat(
-        fileRootedPath.asPath().getLocalPath(),
+        fileRootedPath.asPath(),
         new FileStatus() {
 
           @Override
@@ -761,18 +760,18 @@
 
   private static final class CustomInMemoryFs extends InMemoryFileSystem {
 
-    private Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap();
+    private Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
 
     public CustomInMemoryFs(ManualClock manualClock) {
       super(manualClock);
     }
 
-    public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) {
+    public void stubStat(Path path, @Nullable FileStatus stubbedResult) {
       stubbedStats.put(path, stubbedResult);
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
index 54f23f4..8773d54 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -39,7 +39,6 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -165,7 +164,7 @@
         return 0;
       }
     };
-    fs.stubStat(fooDir.getLocalPath(), inconsistentParentFileStatus);
+    fs.stubStat(fooDir, inconsistentParentFileStatus);
     RootedPath pkgRootedPath = RootedPath.toRootedPath(pkgRoot, fooDir);
     SkyValue fooDirValue = FileStateValue.create(pkgRootedPath, tsgm);
     differencer.inject(ImmutableMap.of(FileStateValue.key(pkgRootedPath), fooDirValue));
@@ -197,7 +196,7 @@
 
     // Our custom filesystem says "foo/bar/baz" does not exist but it also says that "foo/bar"
     // has a child directory "baz".
-    fs.stubStat(bazDir.getLocalPath(), null);
+    fs.stubStat(bazDir, null);
     RootedPath barDirRootedPath = RootedPath.toRootedPath(pkgRoot, barDir);
     FileStateValue barDirFileStateValue = FileStateValue.create(barDirRootedPath, tsgm);
     FileValue barDirFileValue = FileValue.value(barDirRootedPath, barDirFileStateValue,
@@ -226,7 +225,7 @@
     Path fooDir = fooBuildFile.getParentDirectory();
     Path barDir = fooDir.getRelative("bar");
     scratch.file("foo/bar/baz.sh");
-    fs.scheduleMakeUnreadableAfterReaddir(barDir.getLocalPath());
+    fs.scheduleMakeUnreadableAfterReaddir(barDir);
 
     SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo"));
     String expectedMessage = "Encountered error 'Directory is not readable'";
@@ -537,7 +536,7 @@
     scratch.file("foo/BUILD",
         "sh_library(name = 'foo', srcs = ['bar/baz.sh'])");
     Path barBuildFile = scratch.file("foo/bar/BUILD");
-    fs.stubStatError(barBuildFile.getLocalPath(), new IOException("nope"));
+    fs.stubStatError(barBuildFile, new IOException("nope"));
     SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo"));
     EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate(
         getSkyframeExecutor(), skyKey, /*keepGoing=*/false, reporter);
@@ -675,7 +674,7 @@
   public void testPackageLoadingErrorOnIOExceptionReadingBuildFile() throws Exception {
     Path fooBuildFilePath = scratch.file("foo/BUILD");
     IOException exn = new IOException("nope");
-    fs.throwExceptionOnGetInputStream(fooBuildFilePath.getLocalPath(), exn);
+    fs.throwExceptionOnGetInputStream(fooBuildFilePath, exn);
 
     SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo"));
     EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate(
@@ -693,7 +692,7 @@
     scratch.file("foo/BUILD", "load('//foo:bzl.bzl', 'x')");
     Path fooBzlFilePath = scratch.file("foo/bzl.bzl");
     IOException exn = new IOException("nope");
-    fs.throwExceptionOnGetInputStream(fooBzlFilePath.getLocalPath(), exn);
+    fs.throwExceptionOnGetInputStream(fooBzlFilePath, exn);
 
     SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo"));
     EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate(
@@ -740,49 +739,49 @@
       }
     }
 
-    private final Map<LocalPath, FileStatusOrException> stubbedStats = Maps.newHashMap();
-    private final Set<LocalPath> makeUnreadableAfterReaddir = Sets.newHashSet();
-    private final Map<LocalPath, IOException> pathsToErrorOnGetInputStream = Maps.newHashMap();
+    private final Map<Path, FileStatusOrException> stubbedStats = Maps.newHashMap();
+    private final Set<Path> makeUnreadableAfterReaddir = Sets.newHashSet();
+    private final Map<Path, IOException> pathsToErrorOnGetInputStream = Maps.newHashMap();
 
     public CustomInMemoryFs(ManualClock manualClock) {
       super(manualClock);
     }
 
-    public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) {
+    public void stubStat(Path path, @Nullable FileStatus stubbedResult) {
       stubbedStats.put(path, new FileStatusOrException.FileStatusImpl(stubbedResult));
     }
 
-    public void stubStatError(LocalPath path, IOException stubbedResult) {
+    public void stubStatError(Path path, IOException stubbedResult) {
       stubbedStats.put(path, new FileStatusOrException.ExceptionImpl(stubbedResult));
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path).get();
       }
       return super.stat(path, followSymlinks);
     }
 
-    public void scheduleMakeUnreadableAfterReaddir(LocalPath path) {
+    public void scheduleMakeUnreadableAfterReaddir(Path path) {
       makeUnreadableAfterReaddir.add(path);
     }
 
     @Override
-    public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) throws IOException {
+    public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
       Collection<Dirent> result = super.readdir(path, followSymlinks);
       if (makeUnreadableAfterReaddir.contains(path)) {
-        setReadable(path, false);
+        path.setReadable(false);
       }
       return result;
     }
 
-    public void throwExceptionOnGetInputStream(LocalPath path, IOException exn) {
+    public void throwExceptionOnGetInputStream(Path path, IOException exn) {
       pathsToErrorOnGetInputStream.put(path, exn);
     }
 
     @Override
-    protected InputStream getInputStream(LocalPath path) throws IOException {
+    protected InputStream getInputStream(Path path) throws IOException {
       IOException exnToThrow = pathsToErrorOnGetInputStream.get(path);
       if (exnToThrow != null) {
         throw exnToThrow;
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
index 13f5fa2..e271d90 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
@@ -45,7 +45,6 @@
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import java.io.FileNotFoundException;
@@ -242,59 +241,58 @@
 
   @Test
   public void testUpdateCacheError() throws Exception {
-    FileSystem fs =
-        new InMemoryFileSystem() {
-          @Override
-          public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
-            final FileStatus stat = super.stat(path, followSymlinks);
-            if (path.toString().endsWith("/out/foo")) {
-              return new FileStatus() {
-                private final FileStatus original = stat;
+    FileSystem fs = new InMemoryFileSystem() {
+      @Override
+      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+        final FileStatus stat = super.stat(path, followSymlinks);
+        if (path.toString().endsWith("/out/foo")) {
+          return new FileStatus() {
+            private final FileStatus original = stat;
 
-                @Override
-                public boolean isSymbolicLink() {
-                  return original.isSymbolicLink();
-                }
-
-                @Override
-                public boolean isFile() {
-                  return original.isFile();
-                }
-
-                @Override
-                public boolean isDirectory() {
-                  return original.isDirectory();
-                }
-
-                @Override
-                public boolean isSpecialFile() {
-                  return original.isSpecialFile();
-                }
-
-                @Override
-                public long getSize() throws IOException {
-                  return original.getSize();
-                }
-
-                @Override
-                public long getNodeId() throws IOException {
-                  return original.getNodeId();
-                }
-
-                @Override
-                public long getLastModifiedTime() throws IOException {
-                  throw new IOException();
-                }
-
-                @Override
-                public long getLastChangeTime() throws IOException {
-                  return original.getLastChangeTime();
-                }
-              };
+            @Override
+            public boolean isSymbolicLink() {
+              return original.isSymbolicLink();
             }
-            return stat;
-          }
-        };
+
+            @Override
+            public boolean isFile() {
+              return original.isFile();
+            }
+
+            @Override
+            public boolean isDirectory() {
+              return original.isDirectory();
+            }
+
+            @Override
+            public boolean isSpecialFile() {
+              return original.isSpecialFile();
+            }
+
+            @Override
+            public long getSize() throws IOException {
+              return original.getSize();
+            }
+
+            @Override
+            public long getNodeId() throws IOException {
+              return original.getNodeId();
+            }
+
+            @Override
+            public long getLastModifiedTime() throws IOException {
+              throw new IOException();
+            }
+
+            @Override
+            public long getLastChangeTime() throws IOException {
+              return original.getLastChangeTime();
+            }
+          };
+        }
+        return stat;
+      }
+    };
     Artifact foo = createDerivedArtifact(fs, "foo");
     registerAction(new TestAction(TestAction.NO_EFFECT, emptySet, ImmutableList.of(foo)));
     reporter.removeHandler(failFastHandler);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
index 8bca6bd..63e900f 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java
@@ -490,7 +490,7 @@
             return 0;
           }
         };
-    fs.stubStat(bazDir.getLocalPath(), inconsistentParentFileStatus);
+    fs.stubStat(bazDir, inconsistentParentFileStatus);
     Set<Label> labels = ImmutableSet.of(Label.parseAbsolute("//foo:foo"));
     getSkyframeExecutor()
         .getPackageManager()
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
index e3c54a5..f20cea8 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
@@ -36,8 +36,8 @@
 import com.google.devtools.build.lib.testutil.ManualClock;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
 import com.google.devtools.build.skyframe.DelegatingWalkableGraph;
 import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
@@ -240,18 +240,18 @@
 
   protected static class CustomInMemoryFs extends InMemoryFileSystem {
 
-    private Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap();
+    private Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
 
     public CustomInMemoryFs(ManualClock manualClock) {
       super(manualClock);
     }
 
-    public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) {
+    public void stubStat(Path path, @Nullable FileStatus stubbedResult) {
       stubbedStats.put(path, stubbedResult);
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
index 986c5a5..1e944d2 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
@@ -26,7 +26,6 @@
 import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.ModifiedFileSet;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -133,7 +132,7 @@
     reporter.removeHandler(failFastHandler);
     scratch.file("a/BUILD", "sh_library(name = 'b/c')");
     Path subpackageBuildFile = scratch.file("a/b/BUILD", "sh_library(name = 'c')");
-    fs.stubStatIOException(subpackageBuildFile.getLocalPath(), new IOException("nope"));
+    fs.stubStatIOException(subpackageBuildFile, new IOException("nope"));
     BuildFileNotFoundException exn =
         (BuildFileNotFoundException) getErrorFromTargetValue("//a:b/c");
     assertThat(exn).hasMessageThat().contains("nope");
@@ -141,18 +140,18 @@
 
   private static class CustomInMemoryFs extends InMemoryFileSystem {
 
-    private Map<LocalPath, IOException> stubbedStatExceptions = Maps.newHashMap();
+    private Map<Path, IOException> stubbedStatExceptions = Maps.newHashMap();
 
     public CustomInMemoryFs() {
       super(BlazeClock.instance());
     }
 
-    public void stubStatIOException(LocalPath path, IOException stubbedResult) {
+    public void stubStatIOException(Path path, IOException stubbedResult) {
       stubbedStatExceptions.put(path, stubbedResult);
     }
 
     @Override
-    public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStatExceptions.containsKey(path)) {
         throw stubbedStatExceptions.get(path);
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
index 1e52a54..3e156be 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
@@ -40,7 +40,6 @@
 import com.google.devtools.build.lib.events.NullEventHandler;
 import com.google.devtools.build.lib.vfs.FileStatus;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.LocalPath;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skyframe.EvaluationResult;
@@ -181,7 +180,7 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException {
+          public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
             if (path.getBaseName().equals("one")) {
               throw exception;
             }
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 be410fd..d4a079d 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
@@ -38,13 +38,11 @@
 
   @Test
   public void dotDParser_windowsPaths() throws Exception {
-    Path dotd =
-        scratch.file(
-            "C:/tmp/foo.d",
-            "bazel-out/hello-lib/cpp/hello-lib.o: \\",
-            " cpp/hello-lib.cc cpp/hello-lib.h c:\\mingw\\include\\stdio.h \\",
-            " c:\\mingw\\include\\_mingw.h \\",
-            " c:\\mingw\\lib\\gcc\\mingw32\\4.8.1\\include\\stdarg.h");
+    Path dotd = scratch.file("/tmp/foo.d",
+        "bazel-out/hello-lib/cpp/hello-lib.o: \\",
+        " cpp/hello-lib.cc cpp/hello-lib.h c:\\mingw\\include\\stdio.h \\",
+        " c:\\mingw\\include\\_mingw.h \\",
+        " c:\\mingw\\lib\\gcc\\mingw32\\4.8.1\\include\\stdarg.h");
 
     Set<Path> expected = Sets.newHashSet(
         root.getRelative("cpp/hello-lib.cc"),
@@ -58,11 +56,9 @@
 
   @Test
   public void dotDParser_windowsPathsWithSpaces() throws Exception {
-    Path dotd =
-        scratch.file(
-            "C:/tmp/foo.d",
-            "bazel-out/hello-lib/cpp/hello-lib.o: \\",
-            "C:\\Program\\ Files\\ (x86)\\LLVM\\stddef.h");
+    Path dotd = scratch.file("/tmp/foo.d",
+        "bazel-out/hello-lib/cpp/hello-lib.o: \\",
+        "C:\\Program\\ Files\\ (x86)\\LLVM\\stddef.h");
     assertThat(newDependencySet().read(dotd).getDependencies())
         .containsExactlyElementsIn(
             Sets.newHashSet(fileSystem.getPath("C:/Program Files (x86)/LLVM/stddef.h")));
@@ -73,14 +69,12 @@
     // This is (slightly simplified) actual output from clang. Yes, clang will happily mix
     // forward slashes and backslashes in a single path, not to mention using backslashes as
     // separators next to backslashes as escape characters.
-    Path dotd =
-        scratch.file(
-            "C:/tmp/foo.d",
-            "bazel-out/hello-lib/cpp/hello-lib.o: \\",
-            "cpp/hello-lib.cc cpp/hello-lib.h /mingw/include\\stdio.h \\",
-            "/mingw/include\\_mingw.h \\",
-            "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stddef.h \\",
-            "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stdarg.h");
+    Path dotd = scratch.file("/tmp/foo.d",
+        "bazel-out/hello-lib/cpp/hello-lib.o: \\",
+        "cpp/hello-lib.cc cpp/hello-lib.h /mingw/include\\stdio.h \\",
+        "/mingw/include\\_mingw.h \\",
+        "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stddef.h \\",
+        "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stdarg.h");
 
     Set<Path> expected = Sets.newHashSet(
         root.getRelative("cpp/hello-lib.cc"),
@@ -99,8 +93,10 @@
     Path file2 = fileSystem.getPath("C:/blah/blah/genhello/hello.h");
     Path file2DiffCase = fileSystem.getPath("C:/Blah/blah/Genhello/hello.h");
     String filename = "hello.o";
-    Path dotd =
-        scratch.file("C:/tmp/foo.d", filename + ": \\", " " + file1 + " \\", " " + file2 + " ");
+    Path dotd = scratch.file("/tmp/foo.d",
+        filename + ": \\",
+        " " + file1 + " \\",
+        " " + file2 + " ");
     assertThat(newDependencySet().read(dotd).getDependencies())
         .containsExactly(file1, file2DiffCase);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java
index 09ac79e..278a288 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java
@@ -379,7 +379,7 @@
   @Test
   public void testSymbolicFileLinkExists() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xFile);
       assertThat(someLink.exists()).isTrue();
       assertThat(someLink.statIfFound()).isNotNull();
@@ -389,7 +389,7 @@
   @Test
   public void testSymbolicFileLinkIsSymbolicLink() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xFile);
       assertThat(someLink.isSymbolicLink()).isTrue();
     }
@@ -398,7 +398,7 @@
   @Test
   public void testSymbolicFileLinkIsFile() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xFile);
       assertThat(someLink.isFile()).isTrue();
     }
@@ -407,7 +407,7 @@
   @Test
   public void testSymbolicFileLinkIsNotDirectory() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xFile);
       assertThat(someLink.isDirectory()).isFalse();
     }
@@ -416,7 +416,7 @@
   @Test
   public void testSymbolicDirLinkExists() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xEmptyDirectory);
       assertThat(someLink.exists()).isTrue();
       assertThat(someLink.statIfFound()).isNotNull();
@@ -426,7 +426,7 @@
   @Test
   public void testSymbolicDirLinkIsSymbolicLink() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xEmptyDirectory);
       assertThat(someLink.isSymbolicLink()).isTrue();
     }
@@ -435,7 +435,7 @@
   @Test
   public void testSymbolicDirLinkIsDirectory() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xEmptyDirectory);
       assertThat(someLink.isDirectory()).isTrue();
     }
@@ -444,7 +444,7 @@
   @Test
   public void testSymbolicDirLinkIsNotFile() throws Exception {
     Path someLink = absolutize("some-link");
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       someLink.createSymbolicLink(xEmptyDirectory);
       assertThat(someLink.isFile()).isFalse();
     }
@@ -1287,7 +1287,7 @@
     Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar");
     xNonEmptyDirectory.setWritable(false);
 
-    if (testFS.supportsSymbolicLinksNatively(xNonEmptyDirectoryBar.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xNonEmptyDirectoryBar)) {
       try {
         createSymbolicLink(xNonEmptyDirectoryBar, xNonEmptyDirectoryFoo);
         fail("No exception thrown.");
@@ -1335,19 +1335,19 @@
 
   @Test
   public void testResolveSymlinks() throws Exception {
-    if (testFS.supportsSymbolicLinksNatively(xLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xLink)) {
       createSymbolicLink(xLink, xFile);
       FileSystemUtils.createEmptyFile(xFile);
-      assertThat(testFS.resolveOneLink(xLink.getLocalPath())).isEqualTo(xFile.getPathString());
+      assertThat(testFS.resolveOneLink(xLink)).isEqualTo(xFile.asFragment());
       assertThat(xLink.resolveSymbolicLinks()).isEqualTo(xFile);
     }
   }
 
   @Test
   public void testResolveDanglingSymlinks() throws Exception {
-    if (testFS.supportsSymbolicLinksNatively(xLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xLink)) {
       createSymbolicLink(xLink, xNothing);
-      assertThat(testFS.resolveOneLink(xLink.getLocalPath())).isEqualTo(xNothing.getPathString());
+      assertThat(testFS.resolveOneLink(xLink)).isEqualTo(xNothing.asFragment());
       try {
         xLink.resolveSymbolicLinks();
         fail();
@@ -1358,15 +1358,15 @@
 
   @Test
   public void testResolveNonSymlinks() throws Exception {
-    if (testFS.supportsSymbolicLinksNatively(xFile.getLocalPath())) {
-      assertThat(testFS.resolveOneLink(xFile.getLocalPath())).isNull();
+    if (testFS.supportsSymbolicLinksNatively(xFile)) {
+      assertThat(testFS.resolveOneLink(xFile)).isNull();
       assertThat(xFile.resolveSymbolicLinks()).isEqualTo(xFile);
     }
   }
 
   @Test
   public void testCreateHardLink_Success() throws Exception {
-    if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) {
+    if (!testFS.supportsHardLinksNatively(xFile)) {
       return;
     }
     xFile.createHardLink(xLink);
@@ -1379,7 +1379,7 @@
 
   @Test
   public void testCreateHardLink_NeitherOriginalNorLinkExists() throws Exception {
-    if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) {
+    if (!testFS.supportsHardLinksNatively(xFile)) {
       return;
     }
 
@@ -1398,7 +1398,7 @@
   @Test
   public void testCreateHardLink_OriginalDoesNotExistAndLinkExists() throws Exception {
 
-    if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) {
+    if (!testFS.supportsHardLinksNatively(xFile)) {
       return;
     }
 
@@ -1419,7 +1419,7 @@
   @Test
   public void testCreateHardLink_BothOriginalAndLinkExist() throws Exception {
 
-    if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) {
+    if (!testFS.supportsHardLinksNatively(xFile)) {
       return;
     }
     /* Both original file and link file exist */
@@ -1437,7 +1437,6 @@
   }
 
   protected boolean isHardLinked(Path a, Path b) throws IOException {
-    return testFS.stat(a.getLocalPath(), false).getNodeId()
-        == testFS.stat(b.getLocalPath(), false).getNodeId();
+    return testFS.stat(a, false).getNodeId() == testFS.stat(b, false).getNodeId();
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
index 3b9a2dc..0e9b74e 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
@@ -851,8 +851,8 @@
     FileSystemUtils.createHardLink(linkPath, originalPath);
     assertThat(originalPath.exists()).isTrue();
     assertThat(linkPath.exists()).isTrue();
-    assertThat(fileSystem.stat(linkPath.getLocalPath(), false).getNodeId())
-        .isEqualTo(fileSystem.stat(originalPath.getLocalPath(), false).getNodeId());
+    assertThat(fileSystem.stat(linkPath, false).getNodeId())
+        .isEqualTo(fileSystem.stat(originalPath, false).getNodeId());
   }
 
   @Test
@@ -892,11 +892,11 @@
     assertThat(linkPath1.exists()).isTrue();
     assertThat(linkPath2.exists()).isTrue();
     assertThat(linkPath3.exists()).isTrue();
-    assertThat(fileSystem.stat(linkPath1.getLocalPath(), false).getNodeId())
-        .isEqualTo(fileSystem.stat(originalPath1.getLocalPath(), false).getNodeId());
-    assertThat(fileSystem.stat(linkPath2.getLocalPath(), false).getNodeId())
-        .isEqualTo(fileSystem.stat(originalPath2.getLocalPath(), false).getNodeId());
-    assertThat(fileSystem.stat(linkPath3.getLocalPath(), false).getNodeId())
-        .isEqualTo(fileSystem.stat(originalPath3.getLocalPath(), false).getNodeId());
+    assertThat(fileSystem.stat(linkPath1, false).getNodeId())
+        .isEqualTo(fileSystem.stat(originalPath1, false).getNodeId());
+    assertThat(fileSystem.stat(linkPath2, false).getNodeId())
+        .isEqualTo(fileSystem.stat(originalPath2, false).getNodeId());
+    assertThat(fileSystem.stat(linkPath3, false).getNodeId())
+        .isEqualTo(fileSystem.stat(originalPath3, false).getNodeId());
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
index 70fc86d..5b0958f 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java
@@ -54,18 +54,15 @@
 
   @Before
   public final void initializeFileSystem() throws Exception  {
-    fs =
-        new InMemoryFileSystem() {
-          @Override
-          public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks)
-              throws IOException {
-            if (throwOnReaddir != null
-                && path.getPathString().equals(throwOnReaddir.getPathString())) {
-              throw new FileNotFoundException(path.getPathString());
-            }
-            return super.readdir(path, followSymlinks);
-          }
-        };
+    fs = new InMemoryFileSystem() {
+      @Override
+      public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
+        if (path.equals(throwOnReaddir)) {
+          throw new FileNotFoundException(path.getPathString());
+        }
+        return super.readdir(path, followSymlinks);
+      }
+    };
     tmpPath = fs.getPath("/globtmp");
     for (String dir : ImmutableList.of("foo/bar/wiz",
                          "foo/barnacle/wiz",
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java
new file mode 100644
index 0000000..0807b4a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java
@@ -0,0 +1,78 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PathTrie}. */
+@RunWith(JUnit4.class)
+public class PathTrieTest {
+  @Test
+  public void empty() {
+    PathTrie<Integer> pathTrie = new PathTrie<>();
+    assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/x"))).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/x/y"))).isNull();
+  }
+
+  @Test
+  public void simpleBranches() {
+    PathTrie<Integer> pathTrie = new PathTrie<>();
+    pathTrie.put(PathFragment.create("/a"), 1);
+    pathTrie.put(PathFragment.create("/b"), 2);
+
+    assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(2);
+    assertThat(pathTrie.get(PathFragment.create("/b/c"))).isEqualTo(2);
+  }
+
+  @Test
+  public void nestedDirectories() {
+    PathTrie<Integer> pathTrie = new PathTrie<>();
+    pathTrie.put(PathFragment.create("/a/b/c"), 3);
+    assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/a"))).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/a/b"))).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3);
+    assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3);
+
+    pathTrie.put(PathFragment.create("/a"), 1);
+    assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/b"))).isNull();
+    assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3);
+    assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3);
+
+    pathTrie.put(PathFragment.ROOT_FRAGMENT, 0);
+    assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isEqualTo(0);
+    assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(0);
+    assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1);
+    assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1);
+  }
+
+  @Test
+  public void unrootedDirectoriesError() {
+    PathTrie<Integer> pathTrie = new PathTrie<>();
+    assertThrows(IllegalArgumentException.class, () -> pathTrie.put(PathFragment.create("a"), 1));
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java
index b49af40..6c545ac 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java
@@ -38,7 +38,7 @@
     assertThat(SearchPath.parse(fs, "/:/bin")).isEqualTo(searchPath);
     assertThat(SearchPath.parse(fs, ".:/:/bin")).isEqualTo(searchPath);
 
-    fs.getOutputStream(fs.getPath("/bin/exe").getLocalPath()).write(new byte[5]);
+    fs.getOutputStream(fs.getPath("/bin/exe")).write(new byte[5]);
 
     assertThat(SearchPath.which(searchPath, "exe")).isNull();
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java
index b9f6485..4720a6e 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java
@@ -78,7 +78,7 @@
     assertThat(linkPath.isDirectory(Symlinks.NOFOLLOW)).isFalse();
     assertThat(linkPath.isDirectory(Symlinks.FOLLOW)).isFalse();
 
-    if (testFS.supportsSymbolicLinksNatively(linkPath.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(linkPath)) {
       assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)).isEqualTo(newPath.toString().length());
       assertThat(linkPath.getFileSize()).isEqualTo(newPath.getFileSize(Symlinks.NOFOLLOW));
     }
@@ -196,7 +196,7 @@
       PathFragment relative = PathFragment.create(linkTarget);
       linkPath.delete();
       createSymbolicLink(linkPath, relative);
-      if (testFS.supportsSymbolicLinksNatively(linkPath.getLocalPath())) {
+      if (testFS.supportsSymbolicLinksNatively(linkPath)) {
         assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)).isEqualTo(linkTarget.length());
         assertThat(linkPath.readSymbolicLink()).isEqualTo(relative);
       }
@@ -224,7 +224,7 @@
 
     // The path may not be a symlink, neither on Darwin nor on Linux.
     String nonLinkEntry = null;
-    for (String child : testFS.getDirectoryEntries(rootPath.getLocalPath())) {
+    for (String child : testFS.getDirectoryEntries(rootPath)) {
       Path p = rootPath.getChild(child);
       if (!p.isSymbolicLink() && p.isDirectory()) {
         nonLinkEntry = p.getBaseName();
@@ -259,7 +259,7 @@
     Path link = absolutize("recursive-link");
     createSymbolicLink(link, link);
 
-    if (testFS.supportsSymbolicLinksNatively(link.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(link)) {
       try {
         link.resolveSymbolicLinks();
         fail();
@@ -276,7 +276,7 @@
     createSymbolicLink(link2, link1);
     createSymbolicLink(link1, link2);
 
-    if (testFS.supportsSymbolicLinksNatively(link1.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(link1)) {
       try {
         link1.resolveSymbolicLinks();
         fail();
@@ -288,7 +288,7 @@
 
   @Test
   public void testResolveSymbolicLinksENOENT() {
-    if (testFS.supportsSymbolicLinksNatively(xDanglingLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xDanglingLink)) {
       try {
         xDanglingLink.resolveSymbolicLinks();
         fail();
@@ -302,7 +302,7 @@
   public void testResolveSymbolicLinksENOTDIR() throws IOException {
     Path badLinkTarget = xFile.getChild("bad"); // parent is not a directory!
     Path badLink = absolutize("badLink");
-    if (testFS.supportsSymbolicLinksNatively(badLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(badLink)) {
       createSymbolicLink(badLink, badLinkTarget);
       try {
         badLink.resolveSymbolicLinks();
@@ -317,7 +317,7 @@
 
   @Test
   public void testResolveSymbolicLinksWithUplevelRefs() throws IOException {
-    if (testFS.supportsSymbolicLinksNatively(xLinkToFile.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xLinkToFile)) {
       // Create a series of links that refer to xFile as ./xFile,
       // ./../foo/xFile, ./../../bar/foo/xFile, etc.  They should all resolve
       // to xFile.
@@ -335,7 +335,7 @@
 
   @Test
   public void testReadSymbolicLink() throws IOException {
-    if (testFS.supportsSymbolicLinksNatively(xDanglingLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xDanglingLink)) {
       assertThat(xDanglingLink.readSymbolicLink().toString()).isEqualTo(xNothing.toString());
     }
 
@@ -364,7 +364,7 @@
       throws IOException {
     xEmptyDirectory.setWritable(false);
     Path xChildOfReadonlyDir = xEmptyDirectory.getChild("x");
-    if (testFS.supportsSymbolicLinksNatively(xChildOfReadonlyDir.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xChildOfReadonlyDir)) {
       try {
         xChildOfReadonlyDir.createSymbolicLink(xNothing);
         fail();
@@ -386,7 +386,7 @@
     assertThat(someLink.isSymbolicLink()).isTrue();
     assertThat(someLink.exists(Symlinks.NOFOLLOW)).isTrue(); // the link itself exists
     assertThat(someLink.exists()).isFalse(); // ...but the referent doesn't
-    if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(someLink)) {
       try {
         someLink.resolveSymbolicLinks();
       } catch (FileNotFoundException e) {
@@ -398,7 +398,7 @@
   @Test
   public void testCannotCreateSymbolicLinkWithoutParent() throws IOException {
     Path xChildOfMissingDir = xNothing.getChild("x");
-    if (testFS.supportsSymbolicLinksNatively(xChildOfMissingDir.getLocalPath())) {
+    if (testFS.supportsSymbolicLinksNatively(xChildOfMissingDir)) {
       try {
         xChildOfMissingDir.createSymbolicLink(xFile);
         fail();
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 a330197..1e877a5 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
@@ -54,8 +54,8 @@
   private UnionFileSystem createDefaultUnionFileSystem() {
     return new UnionFileSystem(
         ImmutableMap.of(
-            LocalPath.create("/in"), inDelegate,
-            LocalPath.create("/out"), outDelegate),
+            PathFragment.create("/in"), inDelegate,
+            PathFragment.create("/out"), outDelegate),
         defaultDelegate);
   }
 
@@ -76,9 +76,9 @@
   @Test
   public void testBasicDelegation() throws Exception {
     unionfs = createDefaultUnionFileSystem();
-    LocalPath fooPath = LocalPath.create("/foo");
-    LocalPath inPath = LocalPath.create("/in");
-    LocalPath outPath = LocalPath.create("/out/in.txt");
+    Path fooPath = unionfs.getPath("/foo");
+    Path inPath = unionfs.getPath("/in");
+    Path outPath = unionfs.getPath("/out/in.txt");
     assertThat(unionfs.getDelegate(inPath)).isSameAs(inDelegate);
     assertThat(unionfs.getDelegate(outPath)).isSameAs(outDelegate);
     assertThat(unionfs.getDelegate(fooPath)).isSameAs(defaultDelegate);
@@ -101,7 +101,7 @@
   @Test
   public void testDefaultFileSystemRequired() throws Exception {
     try {
-      new UnionFileSystem(ImmutableMap.of(), null);
+      new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of(), null);
       fail("Able to create a UnionFileSystem with no default!");
     } catch (NullPointerException expected) {
       // OK - should fail in this case.
@@ -114,16 +114,16 @@
   public void testPrefixDelegation() throws Exception {
     unionfs =
         new UnionFileSystem(
-            ImmutableMap.of(
-                LocalPath.create("/foo"), inDelegate,
-                LocalPath.create("/foo/bar"), outDelegate),
+            ImmutableMap.<PathFragment, FileSystem>of(
+                PathFragment.create("/foo"), inDelegate,
+                PathFragment.create("/foo/bar"), outDelegate),
             defaultDelegate);
 
-    assertThat(unionfs.getDelegate(LocalPath.create("/foo/foo.txt"))).isSameAs(inDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/foo.txt"))).isSameAs(outDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/../foo.txt"))).isSameAs(inDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/bar/foo.txt"))).isSameAs(defaultDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/../.."))).isSameAs(defaultDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/foo/foo.txt"))).isSameAs(inDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/foo.txt"))).isSameAs(outDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/../foo.txt"))).isSameAs(inDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/bar/foo.txt"))).isSameAs(defaultDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/../.."))).isSameAs(defaultDelegate);
   }
 
   // Checks that files cannot be modified when the filesystem is created
@@ -133,17 +133,17 @@
     unionfs =
         new UnionFileSystem(
             ImmutableMap.of(
-                LocalPath.create("/rw"), new XAttrInMemoryFs(BlazeClock.instance()),
-                LocalPath.create("/ro"),
+                PathFragment.create("/rw"), new XAttrInMemoryFs(BlazeClock.instance()),
+                PathFragment.create("/ro"),
                     new XAttrInMemoryFs(BlazeClock.instance()) {
                       @Override
-                      public boolean supportsModifications(LocalPath path) {
+                      public boolean supportsModifications(Path path) {
                         return false;
                       }
                     }),
             defaultDelegate);
-    LocalPath rwPath = LocalPath.create("/rw/foo.txt");
-    LocalPath roPath = LocalPath.create("/ro/foo.txt");
+    Path rwPath = unionfs.getPath("/rw/foo.txt");
+    Path roPath = unionfs.getPath("/ro/foo.txt");
     assertThat(unionfs.supportsModifications(rwPath)).isTrue();
     assertThat(unionfs.supportsModifications(roPath)).isFalse();
   }
@@ -152,18 +152,18 @@
   // delegate filesystems; i.e. they can be seen from the filesystem of the parent.
   @Test
   public void testDelegateRootDirectoryCreation() throws Exception {
-    LocalPath foo = LocalPath.create("/foo");
-    LocalPath bar = LocalPath.create("/bar");
-    LocalPath out = LocalPath.create("/out");
+    Path foo = unionfs.getPath("/foo");
+    Path bar = unionfs.getPath("/bar");
+    Path out = unionfs.getPath("/out");
     assertThat(unionfs.createDirectory(foo)).isTrue();
     assertThat(unionfs.createDirectory(bar)).isTrue();
     assertThat(unionfs.createDirectory(out)).isTrue();
-    LocalPath outFile = LocalPath.create("/out/in");
-    FileSystemUtils.writeContentAsLatin1(unionfs, outFile, "Out");
+    Path outFile = unionfs.getPath("/out/in");
+    FileSystemUtils.writeContentAsLatin1(outFile, "Out");
 
     // FileSystemTest.setUp() silently creates the test root on the filesystem...
     Path testDirUnderRoot = unionfs.getPath(workingDir.asFragment().subFragment(0, 1));
-    assertThat(unionfs.getDirectoryEntries(LocalPath.create("/")))
+    assertThat(unionfs.getDirectoryEntries(unionfs.getRootDirectory()))
         .containsExactly(
             foo.getBaseName(),
             bar.getBaseName(),
@@ -172,30 +172,31 @@
     assertThat(unionfs.getDirectoryEntries(out)).containsExactly(outFile.getBaseName());
 
     assertThat(defaultDelegate).isSameAs(unionfs.getDelegate(foo));
-    assertThat(unionfs.adjustPath(foo, defaultDelegate)).isEqualTo(foo);
+    assertThat(unionfs.adjustPath(foo, defaultDelegate).asFragment()).isEqualTo(foo.asFragment());
     assertThat(defaultDelegate).isSameAs(unionfs.getDelegate(bar));
     assertThat(outDelegate).isSameAs(unionfs.getDelegate(outFile));
     assertThat(outDelegate).isSameAs(unionfs.getDelegate(out));
 
     // As a fragment (i.e. without filesystem or root info), the path name should be preserved.
-    assertThat(unionfs.adjustPath(outFile, outDelegate)).isEqualTo(outFile);
+    assertThat(unionfs.adjustPath(outFile, outDelegate).asFragment())
+        .isEqualTo(outFile.asFragment());
   }
 
   // Ensure that the right filesystem is still chosen when paths contain "..".
   @Test
   public void testDelegationOfUpLevelReferences() throws Exception {
-    assertThat(unionfs.getDelegate(LocalPath.create("/in/../foo.txt"))).isSameAs(defaultDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/out/../in"))).isSameAs(inDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/out/../in/../out/foo.txt")))
+    assertThat(unionfs.getDelegate(unionfs.getPath("/in/../foo.txt"))).isSameAs(defaultDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/out/../in"))).isSameAs(inDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/out/../in/../out/foo.txt")))
         .isSameAs(outDelegate);
-    assertThat(unionfs.getDelegate(LocalPath.create("/in/./foo.txt"))).isSameAs(inDelegate);
+    assertThat(unionfs.getDelegate(unionfs.getPath("/in/./foo.txt"))).isSameAs(inDelegate);
   }
 
   // Basic *explicit* cross-filesystem symlink check.
   // Note: This does not work implicitly yet, as the next test illustrates.
   @Test
   public void testCrossDeviceSymlinks() throws Exception {
-    assertThat(unionfs.createDirectory(LocalPath.create("/out"))).isTrue();
+    assertThat(unionfs.createDirectory(unionfs.getPath("/out"))).isTrue();
 
     // Create an "/in" directory directly on the output delegate to bypass the
     // UnionFileSystem's mapping.
@@ -204,8 +205,8 @@
     outStream.write('i');
     outStream.close();
 
-    LocalPath outFoo = LocalPath.create("/out/foo");
-    unionfs.createSymbolicLink(outFoo, "../in/bar.txt");
+    Path outFoo = unionfs.getPath("/out/foo");
+    unionfs.createSymbolicLink(outFoo, PathFragment.create("../in/bar.txt"));
     assertThat(unionfs.stat(outFoo, false).isSymbolicLink()).isTrue();
 
     try {
@@ -215,13 +216,24 @@
       // OK
     }
 
-    LocalPath resolved = unionfs.resolveSymbolicLinks(outFoo);
-    InputStream barInput = unionfs.getInputStream(resolved);
+    Path resolved = unionfs.resolveSymbolicLinks(outFoo);
+    assertThat(resolved.getFileSystem()).isSameAs(unionfs);
+    InputStream barInput = resolved.getInputStream();
     int barChar = barInput.read();
     barInput.close();
     assertThat(barChar).isEqualTo('i');
   }
 
+  @Test
+  public void testNoDelegateLeakage() throws Exception {
+    assertThat(unionfs.getPath("/in/foo.txt").getFileSystem()).isSameAs(unionfs);
+    assertThat(unionfs.getPath("/in/foo/bar").getParentDirectory().getFileSystem())
+        .isSameAs(unionfs);
+    unionfs.createDirectory(unionfs.getPath("/out"));
+    unionfs.createDirectory(unionfs.getPath("/out/foo"));
+    unionfs.createDirectory(unionfs.getPath("/out/foo/bar"));
+  }
+
   // Write using the VFS through a UnionFileSystem and check that the file can
   // be read back in the same location using standard Java IO.
   // There is a similar test in UnixFileSystem, but this is essential to ensure
@@ -230,11 +242,12 @@
   public void testDelegateOperationsReflectOnLocalFilesystem() throws Exception {
     unionfs =
         new UnionFileSystem(
-            ImmutableMap.of(workingDir.getLocalPath().getParentDirectory(), new UnixFileSystem()),
+            ImmutableMap.<PathFragment, FileSystem>of(
+                workingDir.getParentDirectory().asFragment(), new UnixFileSystem()),
             defaultDelegate);
     // This is a child of the current tmpdir, and doesn't exist on its own.
     // It would be created in setup(), but of course, that didn't use a UnixFileSystem.
-    unionfs.createDirectory(workingDir.getLocalPath());
+    unionfs.createDirectory(workingDir);
     Path testFile = unionfs.getPath(workingDir.getRelative("test_file").asFragment());
     assertThat(testFile.asFragment().startsWith(workingDir.asFragment())).isTrue();
     String testString = "This is a test file";
@@ -243,7 +256,7 @@
       assertThat(new String(FileSystemUtils.readContentAsLatin1(testFile))).isEqualTo(testString);
     } finally {
       testFile.delete();
-      assertThat(unionfs.delete(workingDir.getLocalPath())).isTrue();
+      assertThat(unionfs.delete(workingDir)).isTrue();
     }
   }
 
@@ -252,7 +265,8 @@
   public void testCreateParentsAcrossMapping() throws Exception {
     unionfs =
         new UnionFileSystem(
-            ImmutableMap.of(LocalPath.create("/out/dir"), outDelegate), defaultDelegate);
+            ImmutableMap.<PathFragment, FileSystem>of(PathFragment.create("/out/dir"), outDelegate),
+            defaultDelegate);
     Path outDir = unionfs.getPath("/out/dir/biz/bang");
     FileSystemUtils.createDirectoryAndParents(outDir);
     assertThat(outDir.isDirectory()).isTrue();
@@ -264,7 +278,8 @@
     }
 
     @Override
-    public byte[] getxattr(LocalPath path, String name) {
+    public byte[] getxattr(Path path, String name) {
+      assertThat(path.getFileSystem()).isSameAs(this);
       return (name.equals(XATTR_KEY)) ? XATTR_VAL.getBytes(UTF_8) : null;
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java
index 16ae52c..2cdb401 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java
@@ -162,17 +162,6 @@
     assertThat(create("/..")).isEqualTo(create("/.."));
   }
 
-  @Test
-  public void testRootsUnix() {
-    assertThat(create("/").getDrive().getPathString()).isEqualTo("/");
-    assertThat(create("/usr").getDrive().getPathString()).isEqualTo("/");
-    assertThrows(IllegalArgumentException.class, () -> create("").getDrive());
-
-    assertThat(create("/").isRoot()).isTrue();
-    assertThat(create("/usr").isRoot()).isFalse();
-    assertThat(create("").isRoot()).isFalse();
-  }
-
   @Override
   protected OsPathPolicy getFilePathOs() {
     return new UnixOsPathPolicy();
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java
index 88c7b6f..ac5acef 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java
@@ -14,9 +14,9 @@
 package com.google.devtools.build.lib.vfs;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
 
 import com.google.common.testing.EqualsTester;
+import com.google.devtools.build.lib.testutil.MoreAsserts;
 import com.google.devtools.build.lib.vfs.LocalPath.OsPathPolicy;
 import com.google.devtools.build.lib.vfs.LocalPath.WindowsOsPathPolicy;
 import com.google.devtools.build.lib.vfs.LocalPath.WindowsOsPathPolicy.ShortPathResolver;
@@ -101,14 +101,8 @@
   @Test
   public void testisAbsoluteWindows() {
     assertThat(create("C:/").isAbsolute()).isTrue();
-  }
-
-  // We support unix paths to make test sharing easier
-  @Test
-  public void testUnixPathSupport() {
+    // test that msys paths turn into absolute paths
     assertThat(create("/").isAbsolute()).isTrue();
-    assertThat(create("/foo").isAbsolute()).isTrue();
-    assertThat(create("/foo").getParentDirectory().getPathString()).isEqualTo("/");
   }
 
   @Test
@@ -116,7 +110,22 @@
     assertThat(create("C:/foo").relativeTo(create("C:/"))).isEqualTo(create("foo"));
     // Case insensitivity test
     assertThat(create("C:/foo/bar").relativeTo(create("C:/FOO"))).isEqualTo(create("bar"));
-    assertThrows(IllegalArgumentException.class, () -> create("D:/foo").relativeTo(create("C:/")));
+    MoreAsserts.assertThrows(
+        IllegalArgumentException.class, () -> create("D:/foo").relativeTo(create("C:/")));
+  }
+
+  @Test
+  public void testAbsoluteUnixPathIsRelativeToWindowsUnixRoot() {
+    assertThat(create("/").getPathString()).isEqualTo("C:/fake/msys");
+    assertThat(create("/foo/bar").getPathString()).isEqualTo("C:/fake/msys/foo/bar");
+    assertThat(create("/foo/bar").getPathString()).isEqualTo("C:/fake/msys/foo/bar");
+  }
+
+  @Test
+  public void testAbsoluteUnixPathReferringToDriveIsRecognized() {
+    assertThat(create("/c/foo").getPathString()).isEqualTo("C:/foo");
+    assertThat(create("/c/foo").getPathString()).isEqualTo("C:/foo");
+    assertThat(create("/c:").getPathString()).isNotEqualTo("C:/foo");
   }
 
   @Test
@@ -152,16 +161,4 @@
     // Assert relative paths that look like short paths are untouched
     assertThat(create("progra~1").getPathString()).isEqualTo("progra~1");
   }
-
-  @Test
-  public void testRootsWindows() {
-    assertThat(create("C:/").getDrive().getPathString()).isEqualTo("C:/");
-    assertThat(create("C:/usr").getDrive().getPathString()).isEqualTo("C:/");
-    assertThrows(IllegalArgumentException.class, () -> create("").getDrive());
-
-    assertThat(create("C:/").isRoot()).isFalse();
-    assertThat(create("C:/usr").isRoot()).isFalse();
-    assertThat(create("/").isRoot()).isTrue();
-    assertThat(create("").isRoot()).isFalse();
-  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
index d50b243..8937c45 100644
--- a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
@@ -25,6 +25,7 @@
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.OS;
 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.jni.WindowsFileOperations;
@@ -99,9 +100,9 @@
     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();
-    assertThat(fs.isDirectory(juncBadPath.getLocalPath(), /* followSymlinks */ true)).isFalse();
-    assertThat(fs.isDirectory(juncBadPath.getLocalPath(), /* followSymlinks */ false)).isFalse();
+    //assertThat(fs.isSymbolicLink(juncBadPath)).isTrue();
+    assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ true)).isFalse();
+    assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ false)).isFalse();
 
     // Test deleting a dangling junction.
     assertThat(juncBadPath.delete()).isTrue();
@@ -332,21 +333,19 @@
     assertThat(fs.getPath(scratchRoot).createDirectory()).isTrue();
     // Create symlink with directory target, relative path.
     Path link1 = fs.getPath(scratchRoot).getRelative("link1");
-    fs.createSymbolicLink(link1.getLocalPath(), "..");
+    fs.createSymbolicLink(link1, PathFragment.create(".."));
     // Create symlink with directory target, absolute path.
     Path link2 = fs.getPath(scratchRoot).getRelative("link2");
-    fs.createSymbolicLink(
-        link2.getLocalPath(), fs.getPath(scratchRoot).getRelative("link1").getPathString());
+    fs.createSymbolicLink(link2, fs.getPath(scratchRoot).getRelative("link1").asFragment());
     // Create scratch files that'll be symlink targets.
     testUtil.scratchFile("foo.txt", "hello");
     testUtil.scratchFile("bar.txt", "hello");
     // Create symlink with file target, relative path.
     Path link3 = fs.getPath(scratchRoot).getRelative("link3");
-    fs.createSymbolicLink(link3.getLocalPath(), "foo.txt");
+    fs.createSymbolicLink(link3, PathFragment.create("foo.txt"));
     // Create symlink with file target, absolute path.
     Path link4 = fs.getPath(scratchRoot).getRelative("link4");
-    fs.createSymbolicLink(
-        link4.getLocalPath(), fs.getPath(scratchRoot).getRelative("bar.txt").getPathString());
+    fs.createSymbolicLink(link4, fs.getPath(scratchRoot).getRelative("bar.txt").asFragment());
     // Assert that link1 and link2 are true junctions and have the right contents.
     for (Path p : ImmutableList.of(link1, link2)) {
       assertThat(WindowsFileOperations.isJunction(p.getPathString())).isTrue();