diff options
author | 2018-02-08 15:32:00 -0800 | |
---|---|---|
committer | 2018-02-08 15:34:11 -0800 | |
commit | a729b9b4c3d7844a7d44934bf3365f92633c0a60 (patch) | |
tree | 6329f4baf5b0b83ea6e3bd577b78b8d49afea9f1 /src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java | |
parent | 0ab46f0dd95f735056add4dd8a90a76944b81d00 (diff) |
Replace path implementation.
Path and PathFragment have been replaced with String-based implementations. They are pretty similar, but each method is dissimilar enough that I did not feel sharing code was appropriate.
A summary of changes:
PATH
====
* Subsumes LocalPath (deleted, its tests repurposed)
* Use a simple string to back Path
* Path instances are no longer interned; Reference equality will no longer work
* Always normalized (same as before)
* Some operations will now be slower, like instance compares (which were previously just a reference check)
* Multiple identical paths will now consume more memory since they are not interned
PATH FRAGMENT
=============
* Use a simple string to back PathFragment
* No more segment arrays with interned strings
* Always normalized
* Remove isNormalized
* Replace some isNormalizied uses with containsUpLevelReferences() to check if path fragments try to escape their scope
* To check if user input is normalized, supply static methods on PathFragment to validate the string before constructing a PathFragment
* Because PathFragments are always normalized, we have to replace checks for literal "." from PathFragment#getPathString to PathFragment#getSafePathString. The latter returns "." for the empty string.
* The previous implementation supported efficient segment semantics (segment count, iterating over segments). This is now expensive since we do longer have a segment array.
ARTIFACT
========
* Remove Path instance. It is instead dynamically constructed on request. This is necessary to avoid this CL becoming a memory regression.
RELNOTES: None
PiperOrigin-RevId: 185062932
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java new file mode 100644 index 0000000000..7494683391 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/vfs/PathAbstractTest.java @@ -0,0 +1,141 @@ +// Copyright 2017 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 java.util.stream.Collectors.toList; + +import com.google.common.collect.Lists; +import com.google.common.testing.EqualsTester; +import com.google.devtools.build.lib.clock.BlazeClock; +import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +/** Tests for {@link Path}. */ +public abstract class PathAbstractTest { + + private FileSystem fileSystem; + private boolean isCaseSensitive; + + @Before + public void setup() { + fileSystem = new InMemoryFileSystem(BlazeClock.instance()); + isCaseSensitive = OsPathPolicy.getFilePathOs().isCaseSensitive(); + } + + @Test + public void testStripsTrailingSlash() { + // compare string forms + assertThat(create("/foo/bar/").getPathString()).isEqualTo("/foo/bar"); + // compare fragment forms + assertThat(create("/foo/bar/")).isEqualTo(create("/foo/bar")); + } + + @Test + public void testBasename() throws Exception { + assertThat(create("/foo/bar").getBaseName()).isEqualTo("bar"); + assertThat(create("/foo/").getBaseName()).isEqualTo("foo"); + assertThat(create("/foo").getBaseName()).isEqualTo("foo"); + assertThat(create("/").getBaseName()).isEmpty(); + } + + @Test + public void testNormalStringsDoNotAllocate() { + String normal1 = "/a/b/hello.txt"; + assertThat(create(normal1).getPathString()).isSameAs(normal1); + + // Sanity check our testing strategy + String notNormal = "/a/../b"; + assertThat(create(notNormal).getPathString()).isNotSameAs(notNormal); + } + + @Test + public void testComparableSortOrder() { + List<Path> list = + Lists.newArrayList( + create("/zzz"), + create("/ZZZ"), + create("/ABC"), + create("/aBc"), + create("/AbC"), + create("/abc")); + Collections.sort(list); + List<String> result = list.stream().map(Path::getPathString).collect(toList()); + + if (isCaseSensitive) { + assertThat(result).containsExactly("/ABC", "/AbC", "/ZZZ", "/aBc", "/abc", "/zzz").inOrder(); + } else { + // Partial ordering among case-insensitive items guaranteed by Collections.sort stability + assertThat(result).containsExactly("/ABC", "/aBc", "/AbC", "/abc", "/zzz", "/ZZZ").inOrder(); + } + } + + @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(); + + assertThat(p2.startsWith(p1)).isTrue(); + assertThat(p2.startsWith(dsP1)).isTrue(); + assertThat(dsP2.startsWith(p1)).isTrue(); + assertThat(dsP2.startsWith(dsP1)).isTrue(); + + // 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"; + assertThat( + p1.getRelative(relativePathStringA).compareTo(dsP1.getRelative(relativePathStringB))) + .isEqualTo( + p1.getRelative(relativePathStringA).compareTo(p1.getRelative(relativePathStringB))); + } finally { + Path.setFileSystemForSerialization(oldFileSystem); + } + } + + protected Path create(String path) { + return Path.create(path, fileSystem); + } +} |