X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=blobdiff_plain;f=unittests%2FSupport%2FPath.cpp;h=e325f9a1c6502b2a57e26cc7ecbd10e4320b92e4;hp=81d923427647dcb5a92c31e14bb1efeb32df5ef4;hb=790e8d91fffa01c1ae5a72d31615f20d78f20404;hpb=0be06cf3609cdf6bdc8f769348f6ffcaba560eec diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 81d92342764..e325f9a1c65 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Path.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" @@ -16,10 +17,14 @@ #include "gtest/gtest.h" #ifdef LLVM_ON_WIN32 -#include +#include #include #endif +#ifdef LLVM_ON_UNIX +#include +#endif + using namespace llvm; using namespace llvm::sys; @@ -146,6 +151,11 @@ TEST(Support, Path) { path::native(*i, temp_store); } + + SmallString<32> Relative("foo.cpp"); + ASSERT_NO_ERROR(sys::fs::make_absolute("/root", Relative)); + Relative[5] = '/'; // Fix up windows paths. + ASSERT_EQ("/root/foo.cpp", Relative); } TEST(Support, RelativePathIterator) { @@ -154,7 +164,27 @@ TEST(Support, RelativePathIterator) { PathComponents ExpectedPathComponents; PathComponents ActualPathComponents; - StringRef(Path).split(ExpectedPathComponents, "/"); + StringRef(Path).split(ExpectedPathComponents, '/'); + + for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; + ++I) { + ActualPathComponents.push_back(*I); + } + + ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size()); + + for (size_t i = 0; i Path(StringRef(".c/.d/../.")); + typedef SmallVector PathComponents; + PathComponents ExpectedPathComponents; + PathComponents ActualPathComponents; + + StringRef(Path).split(ExpectedPathComponents, '/'); for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; ++I) { @@ -174,7 +204,30 @@ TEST(Support, AbsolutePathIterator) { PathComponents ExpectedPathComponents; PathComponents ActualPathComponents; - StringRef(Path).split(ExpectedPathComponents, "/"); + StringRef(Path).split(ExpectedPathComponents, '/'); + + // The root path will also be a component when iterating + ExpectedPathComponents[0] = "/"; + + for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E; + ++I) { + ActualPathComponents.push_back(*I); + } + + ASSERT_EQ(ExpectedPathComponents.size(), ActualPathComponents.size()); + + for (size_t i = 0; i Path(StringRef("/.c/.d/../.")); + typedef SmallVector PathComponents; + PathComponents ExpectedPathComponents; + PathComponents ActualPathComponents; + + StringRef(Path).split(ExpectedPathComponents, '/'); // The root path will also be a component when iterating ExpectedPathComponents[0] = "/"; @@ -247,25 +300,134 @@ TEST(Support, AbsolutePathIteratorEnd) { } TEST(Support, HomeDirectory) { -#ifdef LLVM_ON_UNIX - // This test only makes sense on Unix if $HOME is set. - if (::getenv("HOME")) { + std::string expected; +#ifdef LLVM_ON_WIN32 + if (wchar_t const *path = ::_wgetenv(L"USERPROFILE")) { + auto pathLen = ::wcslen(path); + ArrayRef ref{reinterpret_cast(path), + pathLen * sizeof(wchar_t)}; + convertUTF16ToUTF8String(ref, expected); + } +#else + if (char const *path = ::getenv("HOME")) + expected = path; #endif + // Do not try to test it if we don't know what to expect. + // On Windows we use something better than env vars. + if (!expected.empty()) { SmallString<128> HomeDir; - EXPECT_TRUE(path::home_directory(HomeDir)); - EXPECT_FALSE(HomeDir.empty()); -#ifdef LLVM_ON_UNIX + auto status = path::home_directory(HomeDir); + EXPECT_TRUE(status); + EXPECT_EQ(expected, HomeDir); } -#endif } +TEST(Support, UserCacheDirectory) { + SmallString<13> CacheDir; + SmallString<20> CacheDir2; + auto Status = path::user_cache_directory(CacheDir, ""); + EXPECT_TRUE(Status ^ CacheDir.empty()); + + if (Status) { + EXPECT_TRUE(path::user_cache_directory(CacheDir2, "")); // should succeed + EXPECT_EQ(CacheDir, CacheDir2); // and return same paths + + EXPECT_TRUE(path::user_cache_directory(CacheDir, "A", "B", "file.c")); + auto It = path::rbegin(CacheDir); + EXPECT_EQ("file.c", *It); + EXPECT_EQ("B", *++It); + EXPECT_EQ("A", *++It); + auto ParentDir = *++It; + + // Test Unicode: "/(pi)r^2/aleth.0" + EXPECT_TRUE(path::user_cache_directory(CacheDir2, "\xCF\x80r\xC2\xB2", + "\xE2\x84\xB5.0")); + auto It2 = path::rbegin(CacheDir2); + EXPECT_EQ("\xE2\x84\xB5.0", *It2); + EXPECT_EQ("\xCF\x80r\xC2\xB2", *++It2); + auto ParentDir2 = *++It2; + + EXPECT_EQ(ParentDir, ParentDir2); + } +} + +TEST(Support, TempDirectory) { + SmallString<32> TempDir; + path::system_temp_directory(false, TempDir); + EXPECT_TRUE(!TempDir.empty()); + TempDir.clear(); + path::system_temp_directory(true, TempDir); + EXPECT_TRUE(!TempDir.empty()); +} + +static std::string path2regex(std::string Path) { + size_t Pos = 0; + while ((Pos = Path.find('\\', Pos)) != std::string::npos) { + Path.replace(Pos, 1, "\\\\"); + Pos += 2; + } + return Path; +} + +/// Helper for running temp dir test in separated process. See below. +#define EXPECT_TEMP_DIR(prepare, expected) \ + EXPECT_EXIT( \ + { \ + prepare; \ + SmallString<300> TempDir; \ + path::system_temp_directory(true, TempDir); \ + raw_os_ostream(std::cerr) << TempDir; \ + std::exit(0); \ + }, \ + ::testing::ExitedWithCode(0), path2regex(expected)) + +#ifdef LLVM_ON_WIN32 +TEST(SupportDeathTest, TempDirectoryOnWindows) { + // In this test we want to check how system_temp_directory responds to + // different values of specific env vars. To prevent corrupting env vars of + // the current process all checks are done in separated processes. + EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\OtherFolder"), "C:\\OtherFolder"); + EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:/Unix/Path/Seperators"), + "C:\\Unix\\Path\\Seperators"); + EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"Local Path"), ".+\\Local Path$"); + EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"F:\\TrailingSep\\"), "F:\\TrailingSep"); + EXPECT_TEMP_DIR( + _wputenv_s(L"TMP", L"C:\\2\x03C0r-\x00B5\x00B3\\\x2135\x2080"), + "C:\\2\xCF\x80r-\xC2\xB5\xC2\xB3\\\xE2\x84\xB5\xE2\x82\x80"); + + // Test $TMP empty, $TEMP set. + EXPECT_TEMP_DIR( + { + _wputenv_s(L"TMP", L""); + _wputenv_s(L"TEMP", L"C:\\Valid\\Path"); + }, + "C:\\Valid\\Path"); + + // All related env vars empty + EXPECT_TEMP_DIR( + { + _wputenv_s(L"TMP", L""); + _wputenv_s(L"TEMP", L""); + _wputenv_s(L"USERPROFILE", L""); + }, + "C:\\Temp"); + + // Test evn var / path with 260 chars. + SmallString<270> Expected{"C:\\Temp\\AB\\123456789"}; + while (Expected.size() < 260) + Expected.append("\\DirNameWith19Charss"); + ASSERT_EQ(260, Expected.size()); + EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str()); +} +#endif + class FileSystemTest : public testing::Test { protected: /// Unique temporary directory in which all created filesystem entities must /// be placed. It is removed at the end of each test (must be empty). SmallString<128> TestDirectory; - virtual void SetUp() { + void SetUp() override { ASSERT_NO_ERROR( fs::createUniqueDirectory("file-system-test", TestDirectory)); // We don't care about this specific file. @@ -273,9 +435,7 @@ protected: errs().flush(); } - virtual void TearDown() { - ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); - } + void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); } }; TEST_F(FileSystemTest, Unique) { @@ -417,6 +577,26 @@ TEST_F(FileSystemTest, CreateDir) { errc::file_exists); ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo")); +#ifdef LLVM_ON_UNIX + // Set a 0000 umask so that we can test our directory permissions. + mode_t OldUmask = ::umask(0000); + + fs::file_status Status; + ASSERT_NO_ERROR( + fs::create_directory(Twine(TestDirectory) + "baz500", false, + fs::perms::owner_read | fs::perms::owner_exe)); + ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz500", Status)); + ASSERT_EQ(Status.permissions() & fs::perms::all_all, + fs::perms::owner_read | fs::perms::owner_exe); + ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "baz777", false, + fs::perms::all_all)); + ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz777", Status)); + ASSERT_EQ(Status.permissions() & fs::perms::all_all, fs::perms::all_all); + + // Restore umask to be safe. + ::umask(OldUmask); +#endif + #ifdef LLVM_ON_WIN32 // Prove that create_directories() can handle a pathname > 248 characters, // which is the documented limit for CreateDirectory(). @@ -557,6 +737,7 @@ const char macho_dynamically_linked_shared_lib[] = const char macho_dynamic_linker[] = "\xfe\xed\xfa\xce..........\x00\x07"; const char macho_bundle[] = "\xfe\xed\xfa\xce..........\x00\x08"; const char macho_dsym_companion[] = "\xfe\xed\xfa\xce..........\x00\x0a"; +const char macho_kext_bundle[] = "\xfe\xed\xfa\xce..........\x00\x0b"; const char windows_resource[] = "\x00\x00\x00\x00\x020\x00\x00\x00\xff"; const char macho_dynamically_linked_shared_lib_stub[] = "\xfe\xed\xfa\xce..........\x00\x09"; @@ -587,6 +768,7 @@ TEST_F(FileSystemTest, Magic) { DEFINE(macho_bundle), DEFINE(macho_dynamically_linked_shared_lib_stub), DEFINE(macho_dsym_companion), + DEFINE(macho_kext_bundle), DEFINE(windows_resource) #undef DEFINE }; @@ -638,18 +820,31 @@ TEST_F(FileSystemTest, CarriageReturn) { } #endif +TEST_F(FileSystemTest, Resize) { + int FD; + SmallString<64> TempPath; + ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath)); + ASSERT_NO_ERROR(fs::resize_file(FD, 123)); + fs::file_status Status; + ASSERT_NO_ERROR(fs::status(FD, Status)); + ASSERT_EQ(Status.getSize(), 123U); +} + TEST_F(FileSystemTest, FileMapping) { // Create a temp file. int FileDescriptor; SmallString<64> TempPath; ASSERT_NO_ERROR( fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath)); + unsigned Size = 4096; + ASSERT_NO_ERROR(fs::resize_file(FileDescriptor, Size)); + // Map in temp file and add some content std::error_code EC; StringRef Val("hello there"); { fs::mapped_file_region mfr(FileDescriptor, - fs::mapped_file_region::readwrite, 4096, 0, EC); + fs::mapped_file_region::readwrite, Size, 0, EC); ASSERT_NO_ERROR(EC); std::copy(Val.begin(), Val.end(), mfr.data()); // Explicitly add a 0. @@ -661,14 +856,14 @@ TEST_F(FileSystemTest, FileMapping) { int FD; EC = fs::openFileForRead(Twine(TempPath), FD); ASSERT_NO_ERROR(EC); - fs::mapped_file_region mfr(FD, fs::mapped_file_region::readonly, 0, 0, EC); + fs::mapped_file_region mfr(FD, fs::mapped_file_region::readonly, Size, 0, EC); ASSERT_NO_ERROR(EC); // Verify content EXPECT_EQ(StringRef(mfr.const_data()), Val); // Unmap temp file - fs::mapped_file_region m(FD, fs::mapped_file_region::readonly, 0, 0, EC); + fs::mapped_file_region m(FD, fs::mapped_file_region::readonly, Size, 0, EC); ASSERT_NO_ERROR(EC); ASSERT_EQ(close(FD), 0); } @@ -709,4 +904,47 @@ TEST(Support, NormalizePath) { #undef EXPECT_PATH_IS } + +TEST(Support, RemoveLeadingDotSlash) { + StringRef Path1("././/foolz/wat"); + StringRef Path2("./////"); + + Path1 = path::remove_leading_dotslash(Path1); + EXPECT_EQ(Path1, "foolz/wat"); + Path2 = path::remove_leading_dotslash(Path2); + EXPECT_EQ(Path2, ""); +} + +static std::string remove_dots(StringRef path, + bool remove_dot_dot) { + SmallString<256> buffer(path); + path::remove_dots(buffer, remove_dot_dot); + return buffer.str(); +} + +TEST(Support, RemoveDots) { +#if defined(LLVM_ON_WIN32) + EXPECT_EQ("foolz\\wat", remove_dots(".\\.\\\\foolz\\wat", false)); + EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false)); + + EXPECT_EQ("a\\..\\b\\c", remove_dots(".\\a\\..\\b\\c", false)); + EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true)); + EXPECT_EQ("c", remove_dots(".\\.\\c", true)); + + SmallString<64> Path1(".\\.\\c"); + EXPECT_TRUE(path::remove_dots(Path1, true)); + EXPECT_EQ("c", Path1); +#else + EXPECT_EQ("foolz/wat", remove_dots("././/foolz/wat", false)); + EXPECT_EQ("", remove_dots("./////", false)); + + EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false)); + EXPECT_EQ("b/c", remove_dots("./a/../b/c", true)); + EXPECT_EQ("c", remove_dots("././c", true)); + + SmallString<64> Path1("././c"); + EXPECT_TRUE(path::remove_dots(Path1, true)); + EXPECT_EQ("c", Path1); +#endif +} } // anonymous namespace