| // 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 org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertNotSame; | |
| import static org.junit.Assert.assertSame; | |
| import static org.junit.Assert.assertTrue; | |
| import static org.junit.Assert.fail; | |
| import com.google.common.collect.Lists; | |
| import com.google.common.testing.EqualsTester; | |
| import com.google.common.testing.GcFinalization; | |
| import com.google.devtools.build.lib.util.BlazeClock; | |
| import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.lang.ref.WeakReference; | |
| import java.util.Collections; | |
| import java.util.List; | |
| /** | |
| * A test for {@link Path}. | |
| */ | |
| @RunWith(JUnit4.class) | |
| public class PathTest { | |
| private FileSystem filesystem; | |
| private Path root; | |
| @Before | |
| public final void initializeFileSystem() throws Exception { | |
| filesystem = new InMemoryFileSystem(BlazeClock.instance()); | |
| root = filesystem.getRootDirectory(); | |
| Path first = root.getChild("first"); | |
| first.createDirectory(); | |
| } | |
| @Test | |
| public void testStartsWithWorksForSelf() { | |
| assertStartsWithReturns(true, "/first/child", "/first/child"); | |
| } | |
| @Test | |
| public void testStartsWithWorksForChild() { | |
| assertStartsWithReturns(true, | |
| "/first/child", "/first/child/grandchild"); | |
| } | |
| @Test | |
| public void testStartsWithWorksForDeepDescendant() { | |
| assertStartsWithReturns(true, | |
| "/first/child", "/first/child/grandchild/x/y/z"); | |
| } | |
| @Test | |
| public void testStartsWithFailsForParent() { | |
| assertStartsWithReturns(false, "/first/child", "/first"); | |
| } | |
| @Test | |
| public void testStartsWithFailsForSibling() { | |
| assertStartsWithReturns(false, "/first/child", "/first/child2"); | |
| } | |
| @Test | |
| public void testStartsWithFailsForLinkToDescendant() | |
| throws Exception { | |
| Path linkTarget = filesystem.getPath("/first/linked_to"); | |
| FileSystemUtils.createEmptyFile(linkTarget); | |
| Path second = filesystem.getPath("/second/"); | |
| second.createDirectory(); | |
| second.getChild("child_link").createSymbolicLink(linkTarget); | |
| assertStartsWithReturns(false, "/first", "/second/child_link"); | |
| } | |
| @Test | |
| public void testStartsWithFailsForNullPrefix() { | |
| try { | |
| filesystem.getPath("/first").startsWith(null); | |
| fail(); | |
| } catch (Exception e) { | |
| } | |
| } | |
| private void assertStartsWithReturns(boolean expected, | |
| String ancestor, | |
| String descendant) { | |
| Path parent = filesystem.getPath(ancestor); | |
| Path child = filesystem.getPath(descendant); | |
| assertEquals(expected, child.startsWith(parent)); | |
| } | |
| @Test | |
| public void testGetChildWorks() { | |
| assertGetChildWorks("second"); | |
| assertGetChildWorks("..."); | |
| assertGetChildWorks("...."); | |
| } | |
| private void assertGetChildWorks(String childName) { | |
| assertEquals(filesystem.getPath("/first/" + childName), | |
| filesystem.getPath("/first").getChild(childName)); | |
| } | |
| @Test | |
| public void testGetChildFailsForChildWithSlashes() { | |
| assertGetChildFails("second/third"); | |
| assertGetChildFails("./third"); | |
| assertGetChildFails("../third"); | |
| assertGetChildFails("second/.."); | |
| assertGetChildFails("second/."); | |
| assertGetChildFails("/third"); | |
| assertGetChildFails("third/"); | |
| } | |
| private void assertGetChildFails(String childName) { | |
| try { | |
| filesystem.getPath("/first").getChild(childName); | |
| fail(); | |
| } catch (IllegalArgumentException e) { | |
| // Expected. | |
| } | |
| } | |
| @Test | |
| public void testGetChildFailsForDotAndDotDot() { | |
| assertGetChildFails("."); | |
| assertGetChildFails(".."); | |
| } | |
| @Test | |
| public void testGetChildFailsForEmptyString() { | |
| assertGetChildFails(""); | |
| } | |
| @Test | |
| public void testRelativeToWorks() { | |
| assertRelativeToWorks("apple", "/fruit/apple", "/fruit"); | |
| assertRelativeToWorks("apple/jonagold", "/fruit/apple/jonagold", "/fruit"); | |
| } | |
| @Test | |
| public void testGetRelativeWithStringWorks() { | |
| assertGetRelativeWorks("/first/x/y", "y"); | |
| assertGetRelativeWorks("/y", "/y"); | |
| assertGetRelativeWorks("/first/x/x", "./x"); | |
| assertGetRelativeWorks("/first/y", "../y"); | |
| assertGetRelativeWorks("/", "../../../../.."); | |
| } | |
| @Test | |
| public void testAsFragmentWorks() { | |
| assertAsFragmentWorks("/"); | |
| assertAsFragmentWorks("//"); | |
| assertAsFragmentWorks("/first"); | |
| assertAsFragmentWorks("/first/x/y"); | |
| assertAsFragmentWorks("/first/x/y.foo"); | |
| } | |
| @Test | |
| public void testGetRelativeWithFragmentWorks() { | |
| Path dir = filesystem.getPath("/first/x"); | |
| assertEquals("/first/x/y", | |
| dir.getRelative(new PathFragment("y")).toString()); | |
| assertEquals("/first/x/x", | |
| dir.getRelative(new PathFragment("./x")).toString()); | |
| assertEquals("/first/y", | |
| dir.getRelative(new PathFragment("../y")).toString()); | |
| } | |
| @Test | |
| public void testGetRelativeWithAbsoluteFragmentWorks() { | |
| Path root = filesystem.getPath("/first/x"); | |
| assertEquals("/x/y", | |
| root.getRelative(new PathFragment("/x/y")).toString()); | |
| } | |
| @Test | |
| public void testGetRelativeWithAbsoluteStringWorks() { | |
| Path root = filesystem.getPath("/first/x"); | |
| assertEquals("/x/y", root.getRelative("/x/y").toString()); | |
| } | |
| @Test | |
| public void testComparableSortOrder() { | |
| Path zzz = filesystem.getPath("/zzz"); | |
| Path ZZZ = filesystem.getPath("/ZZZ"); | |
| Path abc = filesystem.getPath("/abc"); | |
| Path aBc = filesystem.getPath("/aBc"); | |
| Path AbC = filesystem.getPath("/AbC"); | |
| Path ABC = filesystem.getPath("/ABC"); | |
| List<Path> list = Lists.newArrayList(zzz, ZZZ, ABC, aBc, AbC, abc); | |
| Collections.sort(list); | |
| assertThat(list).containsExactly(ABC, AbC, ZZZ, aBc, abc, zzz).inOrder(); | |
| } | |
| @Test | |
| public void testParentOfRootIsRoot() { | |
| assertSame(root, root.getRelative("..")); | |
| assertSame(root.getRelative("dots"), | |
| root.getRelative("broken/../../dots")); | |
| } | |
| @Test | |
| public void testSingleSegmentEquivalence() { | |
| assertSame( | |
| root.getRelative("aSingleSegment"), | |
| root.getRelative("aSingleSegment")); | |
| } | |
| @Test | |
| public void testSiblingNonEquivalenceString() { | |
| assertNotSame( | |
| root.getRelative("aSingleSegment"), | |
| root.getRelative("aDifferentSegment")); | |
| } | |
| @Test | |
| public void testSiblingNonEquivalenceFragment() { | |
| assertNotSame( | |
| root.getRelative(new PathFragment("aSingleSegment")), | |
| root.getRelative(new PathFragment("aDifferentSegment"))); | |
| } | |
| @Test | |
| public void testHashCodeStableAcrossGarbageCollections() { | |
| Path parent = filesystem.getPath("/a"); | |
| PathFragment childFragment = new PathFragment("b"); | |
| Path child = parent.getRelative(childFragment); | |
| WeakReference<Path> childRef = new WeakReference<>(child); | |
| int childHashCode1 = childRef.get().hashCode(); | |
| assertEquals(childHashCode1, parent.getRelative(childFragment).hashCode()); | |
| child = null; | |
| GcFinalization.awaitClear(childRef); | |
| int childHashCode2 = parent.getRelative(childFragment).hashCode(); | |
| assertEquals(childHashCode1, childHashCode2); | |
| } | |
| @Test | |
| public void testSerialization() throws Exception { | |
| FileSystem oldFileSystem = Path.getFileSystemForSerialization(); | |
| try { | |
| Path.setFileSystemForSerialization(filesystem); | |
| Path root = filesystem.getPath("/"); | |
| Path p1 = filesystem.getPath("/foo"); | |
| Path p2 = filesystem.getPath("/foo/bar"); | |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |
| ObjectOutputStream oos = new ObjectOutputStream(bos); | |
| oos.writeObject(root); | |
| oos.writeObject(p1); | |
| oos.writeObject(p2); | |
| ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); | |
| ObjectInputStream ois = new ObjectInputStream(bis); | |
| Path dsRoot = (Path) ois.readObject(); | |
| Path dsP1 = (Path) ois.readObject(); | |
| Path dsP2 = (Path) ois.readObject(); | |
| new EqualsTester() | |
| .addEqualityGroup(root, dsRoot) | |
| .addEqualityGroup(p1, dsP1) | |
| .addEqualityGroup(p2, dsP2) | |
| .testEquals(); | |
| assertTrue(p2.startsWith(p1)); | |
| assertTrue(p2.startsWith(dsP1)); | |
| assertTrue(dsP2.startsWith(p1)); | |
| assertTrue(dsP2.startsWith(dsP1)); | |
| // Regression test for a very specific bug in compareTo involving our incorrect usage of | |
| // reference equality rather than logical equality. | |
| String relativePathStringA = "child/grandchildA"; | |
| String relativePathStringB = "child/grandchildB"; | |
| assertEquals( | |
| p1.getRelative(relativePathStringA).compareTo(p1.getRelative(relativePathStringB)), | |
| p1.getRelative(relativePathStringA).compareTo(dsP1.getRelative(relativePathStringB))); | |
| } finally { | |
| Path.setFileSystemForSerialization(oldFileSystem); | |
| } | |
| } | |
| private void assertAsFragmentWorks(String expected) { | |
| assertEquals(new PathFragment(expected), filesystem.getPath(expected).asFragment()); | |
| } | |
| private void assertGetRelativeWorks(String expected, String relative) { | |
| assertEquals(filesystem.getPath(expected), | |
| filesystem.getPath("/first/x").getRelative(relative)); | |
| } | |
| private void assertRelativeToWorks(String expected, String relative, String original) { | |
| assertEquals(new PathFragment(expected), | |
| filesystem.getPath(relative).relativeTo(filesystem.getPath(original))); | |
| } | |
| } |