//===----------------------------------------------------------------------===//
#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"
#include "gtest/gtest.h"
#ifdef LLVM_ON_WIN32
-#include <Windows.h>
+#include <windows.h>
#include <winerror.h>
#endif
+#ifdef LLVM_ON_UNIX
+#include <sys/stat.h>
+#endif
+
using namespace llvm;
using namespace llvm::sys;
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) {
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 <ExpectedPathComponents.size(); ++i) {
+ EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
+ }
+}
+
+TEST(Support, RelativePathDotIterator) {
+ SmallString<64> Path(StringRef(".c/.d/../."));
+ typedef SmallVector<StringRef, 4> PathComponents;
+ PathComponents ExpectedPathComponents;
+ PathComponents ActualPathComponents;
+
+ StringRef(Path).split(ExpectedPathComponents, '/');
for (path::const_iterator I = path::begin(Path), E = path::end(Path); I != E;
++I) {
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 <ExpectedPathComponents.size(); ++i) {
+ EXPECT_EQ(ExpectedPathComponents[i].str(), ActualPathComponents[i].str());
+ }
+}
+
+TEST(Support, AbsolutePathDotIterator) {
+ SmallString<64> Path(StringRef("/.c/.d/../."));
+ typedef SmallVector<StringRef, 4> PathComponents;
+ PathComponents ExpectedPathComponents;
+ PathComponents ActualPathComponents;
+
+ StringRef(Path).split(ExpectedPathComponents, '/');
// The root path will also be a component when iterating
ExpectedPathComponents[0] = "/";
}
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<char> ref{reinterpret_cast<char const *>(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);
+ }
+}
+
+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: "<user_cache_dir>/(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);
}
-#endif
}
class FileSystemTest : public testing::Test {
/// 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.
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) {
"abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
- EXPECT_EQ(fs::createUniqueFile(Twine(Path270), FileDescriptor, TempPath),
+ EXPECT_EQ(fs::createUniqueFile(Path270, FileDescriptor, TempPath),
errc::invalid_argument);
// Relative path < 247 chars, no problem.
const char *Path216 =
"abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
- ASSERT_NO_ERROR(fs::createTemporaryFile(Twine(Path216), "", TempPath));
+ ASSERT_NO_ERROR(fs::createTemporaryFile(Path216, "", TempPath));
ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));
#endif
}
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().
size_t TmpLen = TestDirectory.size();
const char *OneDir = "\\123456789";
size_t OneDirLen = strlen(OneDir);
- ASSERT_LT(OneDirLen, 12);
+ ASSERT_LT(OneDirLen, 12U);
size_t NLevels = ((248 - TmpLen) / OneDirLen) + 1;
SmallString<260> LongDir(TestDirectory);
for (size_t I = 0; I < NLevels; ++I)
// TestDirectory so that the one we create ends up in the right place.
char PreviousDir[260];
size_t PreviousDirLen = ::GetCurrentDirectoryA(260, PreviousDir);
- ASSERT_GT(PreviousDirLen, 0);
- ASSERT_LT(PreviousDirLen, 260);
+ ASSERT_GT(PreviousDirLen, 0U);
+ ASSERT_LT(PreviousDirLen, 260U);
ASSERT_NE(::SetCurrentDirectoryA(TestDirectory.c_str()), 0);
LongDir.clear();
// Generate a relative directory name with absolute length > 248.
// While we're here, prove that .. and . handling works in these long paths.
const char *DotDotDirs = "\\..\\.\\b";
LongDir.append(DotDotDirs);
- ASSERT_NO_ERROR(fs::create_directory(Twine("b")));
+ ASSERT_NO_ERROR(fs::create_directory("b"));
ASSERT_EQ(fs::create_directory(Twine(LongDir), false), errc::file_exists);
// And clean up.
- ASSERT_NO_ERROR(fs::remove(Twine("b")));
+ ASSERT_NO_ERROR(fs::remove("b"));
ASSERT_NO_ERROR(fs::remove(
Twine(LongDir.substr(0, LongDir.size() - strlen(DotDotDirs)))));
ASSERT_NE(::SetCurrentDirectoryA(PreviousDir), 0);
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";
DEFINE(macho_bundle),
DEFINE(macho_dynamically_linked_shared_lib_stub),
DEFINE(macho_dsym_companion),
+ DEFINE(macho_kext_bundle),
DEFINE(windows_resource)
#undef DEFINE
};
}
#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,
- true,
- 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.
}
// Map it back in read-only
- fs::mapped_file_region mfr(Twine(TempPath),
- fs::mapped_file_region::readonly,
- 0,
- 0,
- EC);
+ int FD;
+ EC = fs::openFileForRead(Twine(TempPath), FD);
+ ASSERT_NO_ERROR(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(Twine(TempPath),
- 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);
- const char *Data = m.const_data();
- fs::mapped_file_region mfrrv(std::move(m));
- EXPECT_EQ(mfrrv.const_data(), Data);
+ ASSERT_EQ(close(FD), 0);
}
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