diff options
author | 2017-01-05 10:14:09 +0000 | |
---|---|---|
committer | 2017-01-05 15:17:37 +0000 | |
commit | 2459be34c81b6ccac01e05c416b3912a072fb870 (patch) | |
tree | c4e9f8729d8b195ebb07ebae0e117a1c534e559a /src/test/cpp/util | |
parent | 823091f7516abf7d854021edc765daf1467f1647 (diff) |
Bazel client: implement PathExists on Windows
Checking if a path exists is surprisingly hard on
Windows. The most convenient API functions are
PathFileExists and GetFileAttributes but neither
of them follows junctions. To check if a junction
is dangling, we have to resolve it all the way.
This change adds a JunctionResolver class to
file_windows, which can resolve junctions (if they
aren't dangling) and non-junctions (in this case
just checks their existence).
See https://github.com/bazelbuild/bazel/issues/2107
See https://github.com/bazelbuild/bazel/issues/2181
--
PiperOrigin-RevId: 143645274
MOS_MIGRATED_REVID=143645274
Diffstat (limited to 'src/test/cpp/util')
-rw-r--r-- | src/test/cpp/util/file_windows_test.cc | 91 |
1 files changed, 84 insertions, 7 deletions
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc index fe23a92b67..66c1af481f 100644 --- a/src/test/cpp/util/file_windows_test.cc +++ b/src/test/cpp/util/file_windows_test.cc @@ -11,8 +11,9 @@ // 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. +#include <stdio.h> #include <string.h> -#include <windows.h> // SetEnvironmentVariableA +#include <windows.h> #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" @@ -24,7 +25,9 @@ namespace blaze_util { -void ReinitMsysRootForTesting(); // defined in file_windows.cc +using std::string; + +void ResetMsysRootForTesting(); // defined in file_windows.cc TEST(FileTest, TestDirname) { ASSERT_EQ("", Dirname("")); @@ -98,7 +101,7 @@ TEST(FileTest, IsRootDirectory) { TEST(FileTest, TestAsWindowsPath) { SetEnvironmentVariableA("BAZEL_SH", "c:\\msys\\some\\long\\path\\bash.exe"); - ReinitMsysRootForTesting(); + ResetMsysRootForTesting(); std::wstring actual; ASSERT_TRUE(AsWindowsPath("", &actual)); @@ -141,21 +144,95 @@ TEST(FileTest, TestMsysRootRetrieval) { std::wstring actual; SetEnvironmentVariableA("BAZEL_SH", "c:/foo/msys/bar/qux.exe"); - ReinitMsysRootForTesting(); + ResetMsysRootForTesting(); ASSERT_TRUE(AsWindowsPath("/blah", &actual)); ASSERT_EQ(std::wstring(L"c:\\foo\\msys\\blah"), actual); SetEnvironmentVariableA("BAZEL_SH", "c:/foo/MSYS64/bar/qux.exe"); - ReinitMsysRootForTesting(); + ResetMsysRootForTesting(); ASSERT_TRUE(AsWindowsPath("/blah", &actual)); ASSERT_EQ(std::wstring(L"c:\\foo\\msys64\\blah"), actual); SetEnvironmentVariableA("BAZEL_SH", "c:/qux.exe"); - ReinitMsysRootForTesting(); + ResetMsysRootForTesting(); ASSERT_FALSE(AsWindowsPath("/blah", &actual)); SetEnvironmentVariableA("BAZEL_SH", nullptr); - ReinitMsysRootForTesting(); + ResetMsysRootForTesting(); +} + +static void RunCommand(const string& cmdline) { + STARTUPINFOA startupInfo = {sizeof(STARTUPINFO)}; + PROCESS_INFORMATION processInfo; + // command line maximum size is 32K + // Source (on 2017-01-04): + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx + char mutable_cmdline[0x8000]; + strncpy(mutable_cmdline, cmdline.c_str(), 0x8000); + BOOL ok = CreateProcessA( + /* lpApplicationName */ NULL, + /* lpCommandLine */ mutable_cmdline, + /* lpProcessAttributes */ NULL, + /* lpThreadAttributes */ NULL, + /* bInheritHandles */ TRUE, + /* dwCreationFlags */ 0, + /* lpEnvironment */ NULL, + /* lpCurrentDirectory */ NULL, + /* lpStartupInfo */ &startupInfo, + /* lpProcessInformation */ &processInfo); + ASSERT_TRUE(ok); + + // Wait 1 second for the process to finish. + ASSERT_EQ(WAIT_OBJECT_0, WaitForSingleObject(processInfo.hProcess, 1000)); + + DWORD exit_code = 1; + ASSERT_TRUE(GetExitCodeProcess(processInfo.hProcess, &exit_code)); + ASSERT_EQ(0, exit_code); +} + +TEST(FileTest, TestPathExistsWindows) { + ASSERT_FALSE(PathExists("")); + ASSERT_TRUE(PathExists(".")); + ASSERT_FALSE(PathExists("non.existent")); + + char buf[MAX_PATH] = {0}; + DWORD len = GetEnvironmentVariableA("TEST_TMPDIR", buf, MAX_PATH); + ASSERT_GT(len, 0); + string tmpdir(buf); + ASSERT_TRUE(PathExists(tmpdir)); + + // Create a fake msys root. We'll also use it as a junction target. + string fake_msys_root(tmpdir + "/fake_msys"); + ASSERT_EQ(0, mkdir(fake_msys_root.c_str())); + ASSERT_TRUE(PathExists(fake_msys_root)); + + // Set the BAZEL_SH root so we can resolve MSYS paths. + SetEnvironmentVariableA("BAZEL_SH", + (fake_msys_root + "/fake_bash.exe").c_str()); + ResetMsysRootForTesting(); + + // Assert existence check for MSYS paths. + ASSERT_FALSE(PathExists("/this/should/not/exist/mkay")); + ASSERT_TRUE(PathExists("/")); + + // Create a junction pointing to an existing directory. + RunCommand(string("cmd.exe /C mklink /J \"") + tmpdir + "/junc1\" \"" + + fake_msys_root + "\" >NUL 2>NUL"); + ASSERT_TRUE(PathExists(fake_msys_root)); + ASSERT_TRUE(PathExists(JoinPath(tmpdir, "junc1"))); + + // Create a junction pointing to a non-existent directory. + RunCommand(string("cmd.exe /C mklink /J \"") + tmpdir + "/junc2\" \"" + + fake_msys_root + "/i.dont.exist\" >NUL 2>NUL"); + ASSERT_FALSE(PathExists(JoinPath(fake_msys_root, "i.dont.exist"))); + ASSERT_FALSE(PathExists(JoinPath(tmpdir, "junc2"))); + + // Clean up. + ASSERT_EQ(0, rmdir(JoinPath(tmpdir, "junc1").c_str())); + ASSERT_EQ(0, rmdir(JoinPath(tmpdir, "junc2").c_str())); + ASSERT_EQ(0, rmdir(fake_msys_root.c_str())); + ASSERT_FALSE(PathExists(JoinPath(tmpdir, "junc1"))); + ASSERT_FALSE(PathExists(JoinPath(tmpdir, "junc2"))); } } // namespace blaze_util |