From 8a9be26492651afd8c71e5da98751e36d948b4e5 Mon Sep 17 00:00:00 2001 From: Laszlo Csomor Date: Mon, 10 Jul 2017 13:30:04 +0200 Subject: Windows, Android BusyBox: create JunctionCreator Introduce the JunctionCreator classes that the Android BusyBox can use to work around path length limitations on Windows. See https://github.com/bazelbuild/bazel/issues/3264 Change-Id: Ia5ee39f0635dcc2690ffb1755dc56d21e7bc7536 PiperOrigin-RevId: 161378422 --- .../java/com/google/devtools/build/android/BUILD | 1 + .../google/devtools/build/android/junctions/BUILD | 36 ++++ .../android/junctions/NoopJunctionCreatorTest.java | 49 ++++++ .../junctions/WindowsJunctionCreatorTest.java | 186 +++++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 src/test/java/com/google/devtools/build/android/junctions/BUILD create mode 100644 src/test/java/com/google/devtools/build/android/junctions/NoopJunctionCreatorTest.java create mode 100644 src/test/java/com/google/devtools/build/android/junctions/WindowsJunctionCreatorTest.java (limited to 'src/test/java/com/google') diff --git a/src/test/java/com/google/devtools/build/android/BUILD b/src/test/java/com/google/devtools/build/android/BUILD index 29e8b2a04a..ab7dd8982a 100644 --- a/src/test/java/com/google/devtools/build/android/BUILD +++ b/src/test/java/com/google/devtools/build/android/BUILD @@ -3,6 +3,7 @@ filegroup( srcs = glob(["**"]) + [ "//src/test/java/com/google/devtools/build/android/idlclass:srcs", "//src/test/java/com/google/devtools/build/android/dexer:srcs", + "//src/test/java/com/google/devtools/build/android/junctions:srcs", "//src/test/java/com/google/devtools/build/android/resources:srcs", "//src/test/java/com/google/devtools/build/android/testing/manifestmerge:srcs", "//src/test/java/com/google/devtools/build/android/ziputils:srcs", diff --git a/src/test/java/com/google/devtools/build/android/junctions/BUILD b/src/test/java/com/google/devtools/build/android/junctions/BUILD new file mode 100644 index 0000000000..ad4ae01400 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/junctions/BUILD @@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:private"]) + +package_group( + name = "android-tests", + packages = [ + "//src/test/java/com/google/devtools/build/android/...", + ], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = [":android-tests"], +) + +java_test( + name = "JunctionCreatorTest", + size = "small", + srcs = select({ + "//src:windows": ["WindowsJunctionCreatorTest.java"], + "//src:windows_msvc": ["WindowsJunctionCreatorTest.java"], + "//src:windows_msys": ["WindowsJunctionCreatorTest.java"], + "//conditions:default": ["NoopJunctionCreatorTest.java"], + }), + test_class = select({ + "//src:windows": "com.google.devtools.build.android.junctions.WindowsJunctionCreatorTest", + "//src:windows_msvc": "com.google.devtools.build.android.junctions.WindowsJunctionCreatorTest", + "//src:windows_msys": "com.google.devtools.build.android.junctions.WindowsJunctionCreatorTest", + "//conditions:default": "com.google.devtools.build.android.junctions.NoopJunctionCreatorTest", + }), + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/junctions", + "//third_party:junit4", + "//third_party:truth", + ], +) diff --git a/src/test/java/com/google/devtools/build/android/junctions/NoopJunctionCreatorTest.java b/src/test/java/com/google/devtools/build/android/junctions/NoopJunctionCreatorTest.java new file mode 100644 index 0000000000..a99d568e8f --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/junctions/NoopJunctionCreatorTest.java @@ -0,0 +1,49 @@ +// 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.android.junctions; + +import static com.google.common.truth.Truth.assertThat; + +import java.nio.file.FileSystems; +import java.nio.file.Path; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for NoopJunctionCreator. */ +@RunWith(JUnit4.class) +public class NoopJunctionCreatorTest { + private Path tmproot = null; + + @Before + public void acquireTmpRoot() { + String tmpEnv = System.getenv("TEST_TMPDIR"); + assertThat(tmpEnv).isNotNull(); + tmproot = FileSystems.getDefault().getPath(tmpEnv); + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) tmproot).isNotNull(); + } + + @Test + public void testNoopJunctionCreator() throws Exception { + JunctionCreator jc = new NoopJunctionCreator(); + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) jc.create(null)).isNull(); + + Path p = tmproot.resolve("foo"); + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) jc.create(p)).isSameAs(p); + } +} diff --git a/src/test/java/com/google/devtools/build/android/junctions/WindowsJunctionCreatorTest.java b/src/test/java/com/google/devtools/build/android/junctions/WindowsJunctionCreatorTest.java new file mode 100644 index 0000000000..22ea0f22a1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/junctions/WindowsJunctionCreatorTest.java @@ -0,0 +1,186 @@ +// 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.android.junctions; + +import static com.google.common.truth.Truth.assertThat; + +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for WindowsJunctionCreator. */ +@RunWith(JUnit4.class) +public class WindowsJunctionCreatorTest { + private Path tmproot = null; + + @Before + public void acquireTmpRoot() { + String tmpEnv = System.getenv("TEST_TMPDIR"); + assertThat(tmpEnv).isNotNull(); + tmproot = FileSystems.getDefault().getPath(tmpEnv); + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) tmproot).isNotNull(); + } + + @Test + public void testNullInput() throws Exception { + try (JunctionCreator jc = new WindowsJunctionCreator(tmproot.resolve("foo"))) { + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) jc.create(null)).isNull(); + } + } + + @Test + public void testFileInput() throws Exception { + Path dir = tmproot.resolve("foo"); // [tmproot]/foo/ + Path file = dir.resolve("bar"); // [tmproot]/foo/bar + Path juncroot = tmproot.resolve("junc"); // [tmproot]/junc/ + Path junc = juncroot.resolve("0"); // [tmproot]/junc/0 -> [tmproot]/foo/bar + Path fileViaJunc = junc.resolve("bar"); // [tmproot]/junc/0/bar + try { + Files.createDirectories(dir); + Files.createDirectories(juncroot); + + // Create a scratch file and assert its existence. + Files.write(file, "hello".getBytes()); + assertThat(file.toFile().exists()).isTrue(); + + // Assert that the junction doesn't exist yet. + assertThat(junc.toFile().exists()).isFalse(); + assertThat(fileViaJunc.toFile().exists()).isFalse(); + + try (JunctionCreator jc = new WindowsJunctionCreator(juncroot)) { + // Assert creation of a junction for a file (more precisely, for its parent directory). + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) jc.create(file)).isEqualTo(fileViaJunc); + // Assert that the junction now exists. + assertThat(junc.toFile().exists()).isTrue(); + // Sanity check: the `file` should still exist. + assertThat(file.toFile().exists()).isTrue(); + // Assert that the junction indeed points to `dir`, by asserting the existence of `file` + // through the junction. + assertThat(fileViaJunc.toFile().exists()).isTrue(); + } + // Assert that WindowsJunctionCreator.close cleaned up the junction and root directory, but + // not the files in the directory that the junction pointed to. + assertThat(junc.toFile().exists()).isFalse(); + assertThat(juncroot.toFile().exists()).isFalse(); + // Assert that the `file` and `directory` still exist. Deleting the junction should not have + // affected them. + assertThat(file.toFile().exists()).isTrue(); + assertThat(dir.toFile().exists()).isTrue(); + } finally { + file.toFile().delete(); + dir.toFile().delete(); + } + } + + @Test + public void testJunctionCaching() throws Exception { + Path dir1 = tmproot.resolve("foo"); // [tmproot]/foo/ + Path dir2 = dir1.resolve("foo"); // [tmproot]/foo/foo/ + Path file1 = dir1.resolve("bar"); // [tmproot]/foo/bar + Path file2 = dir1.resolve("baz"); // [tmproot]/foo/baz + Path file3 = dir2.resolve("bar"); // [tmproot]/foo/foo/bar + Path juncroot = tmproot.resolve("junc"); // [tmproot]/junc/ + Path junc0 = juncroot.resolve("0"); // [tmproot]/junc/0 -> [tmproot]/foo/ + Path junc1 = juncroot.resolve("1"); // [tmproot]/junc/1 -> [tmproot]/foo/foo/ + Path file1junc = junc0.resolve("bar"); // [tmproot]/junc/0/bar + Path file2junc = junc0.resolve("baz"); // [tmproot]/junc/0/baz + Path file3junc = junc1.resolve("bar"); // [tmproot]/junc/1/bar + + try { + Files.createDirectories(dir2); + Files.createDirectories(juncroot); + + // Create scratch files and assert their existence. + Files.write(file1, "i am file1".getBytes()); + Files.write(file2, "i am file2".getBytes()); + Files.write(file3, "i am file3".getBytes()); + assertThat(file1.toFile().exists()).isTrue(); + assertThat(file2.toFile().exists()).isTrue(); + assertThat(file3.toFile().exists()).isTrue(); + + // Assert that the junctions don't exist yet. + assertThat(junc0.toFile().exists()).isFalse(); + assertThat(junc1.toFile().exists()).isFalse(); + assertThat(file1junc.toFile().exists()).isFalse(); + assertThat(file2junc.toFile().exists()).isFalse(); + assertThat(file3junc.toFile().exists()).isFalse(); + + try (JunctionCreator jc = new WindowsJunctionCreator(juncroot)) { + Path dir1juncActual = jc.create(dir1); + Path file1juncActual = jc.create(file1); + Path dir2juncActual = jc.create(dir2); + Path file3juncActual = jc.create(file3); + Path file2juncActual = jc.create(file2); + + // Assert that the junctions now exists. + assertThat(dir1juncActual.toFile().exists()).isTrue(); + assertThat(dir2juncActual.toFile().exists()).isTrue(); + assertThat(file1juncActual.toFile().exists()).isTrue(); + assertThat(file2juncActual.toFile().exists()).isTrue(); + assertThat(file3juncActual.toFile().exists()).isTrue(); + + // Assert that the junctions were chached. + // Cast Path to Object to disambiguate which assertThat-overload to use. + assertThat((Object) dir1juncActual).isEqualTo(junc0); + assertThat((Object) dir2juncActual).isEqualTo(junc1); + assertThat((Object) file1juncActual).isEqualTo(file1junc); + assertThat((Object) file2juncActual).isEqualTo(file2junc); + assertThat((Object) file3juncActual).isEqualTo(file3junc); + assertThat((Object) file1juncActual.getParent()).isEqualTo(junc0); + assertThat((Object) file2juncActual.getParent()).isEqualTo(junc0); + assertThat((Object) file3juncActual.getParent()).isEqualTo(junc1); + + // Assert that the directory junctions indeed point where they should, by asserting the + // existence of `file1`, `file2`, and `file3` through them. + assertThat(dir1juncActual.resolve("bar").toFile().exists()).isTrue(); + assertThat(dir1juncActual.resolve("baz").toFile().exists()).isTrue(); + assertThat(dir2juncActual.resolve("bar").toFile().exists()).isTrue(); + + // Assert that the file junctions indeed point where they should, by asserting the contents + // we can read from them. + assertThat(Files.readAllBytes(file1junc)).isEqualTo("i am file1".getBytes()); + assertThat(Files.readAllBytes(file2junc)).isEqualTo("i am file2".getBytes()); + assertThat(Files.readAllBytes(file3junc)).isEqualTo("i am file3".getBytes()); + } + + // Assert that WindowsJunctionCreator.close cleaned up the junction and root directory, but + // not the files in the directory that the junction pointed to. + assertThat(junc0.toFile().exists()).isFalse(); + assertThat(junc1.toFile().exists()).isFalse(); + assertThat(file1junc.toFile().exists()).isFalse(); + assertThat(file2junc.toFile().exists()).isFalse(); + assertThat(file3junc.toFile().exists()).isFalse(); + assertThat(juncroot.toFile().exists()).isFalse(); + + // Assert that the original files (and consequently the directories) still exist. Deleting the + // junction should not have affected them. + assertThat(file1.toFile().exists()).isTrue(); + assertThat(file2.toFile().exists()).isTrue(); + assertThat(file3.toFile().exists()).isTrue(); + } finally { + file3.toFile().delete(); + file2.toFile().delete(); + file1.toFile().delete(); + dir2.toFile().delete(); + dir1.toFile().delete(); + } + } +} -- cgit v1.2.3