#include "llvm/Support/Path.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Duration.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Testing/Support/Error.h"
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#ifdef _WIN32
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Windows/WindowsSupport.h"
#include <windows.h>
#include <winerror.h>
#endif
#ifdef LLVM_ON_UNIX
#include <pwd.h>
#include <sys/stat.h>
#endif
usingnamespacellvm;
usingnamespacellvm::sys;
#define ASSERT_NO_ERROR(x) …
#define ASSERT_ERROR(x) …
namespace {
void checkSeparators(StringRef Path) { … }
struct FileDescriptorCloser { … };
TEST(is_style_Style, Works) { … }
TEST(is_separator, Works) { … }
TEST(get_separator, Works) { … }
TEST(is_absolute_gnu, Works) { … }
TEST(Support, Path) { … }
TEST(Support, PathRoot) { … }
TEST(Support, FilenameParent) { … }
static std::vector<StringRef>
GetComponents(StringRef Path, path::Style S = path::Style::native) { … }
TEST(Support, PathIterator) { … }
TEST(Support, AbsolutePathIteratorEnd) { … }
#ifdef _WIN32
std::string getEnvWin(const wchar_t *Var) {
std::string expected;
if (wchar_t const *path = ::_wgetenv(Var)) {
auto pathLen = ::wcslen(path);
ArrayRef<char> ref{reinterpret_cast<char const *>(path),
pathLen * sizeof(wchar_t)};
convertUTF16ToUTF8String(ref, expected);
SmallString<32> Buf(expected);
path::make_preferred(Buf);
expected.assign(Buf.begin(), Buf.end());
}
return expected;
}
#else
class WithEnv { … };
#endif
TEST(Support, HomeDirectory) { … }
#if defined(LLVM_ON_UNIX) && !defined(__APPLE__)
TEST(Support, HomeDirectoryWithNoEnv) { … }
TEST(Support, ConfigDirectoryWithEnv) { … }
TEST(Support, ConfigDirectoryNoEnv) { … }
TEST(Support, CacheDirectoryWithEnv) { … }
TEST(Support, CacheDirectoryNoEnv) { … }
#endif
#ifdef __APPLE__
TEST(Support, ConfigDirectory) {
SmallString<128> Fallback;
ASSERT_TRUE(path::home_directory(Fallback));
path::append(Fallback, "Library/Preferences");
SmallString<128> ConfigDir;
EXPECT_TRUE(path::user_config_directory(ConfigDir));
EXPECT_EQ(Fallback, ConfigDir);
}
#endif
#ifdef _WIN32
TEST(Support, ConfigDirectory) {
std::string Expected = getEnvWin(L"LOCALAPPDATA");
if (Expected.empty())
GTEST_SKIP();
SmallString<128> CacheDir;
EXPECT_TRUE(path::user_config_directory(CacheDir));
EXPECT_EQ(Expected, CacheDir);
}
TEST(Support, CacheDirectory) {
std::string Expected = getEnvWin(L"LOCALAPPDATA");
if (Expected.empty())
GTEST_SKIP();
SmallString<128> CacheDir;
EXPECT_TRUE(path::cache_directory(CacheDir));
EXPECT_EQ(Expected, CacheDir);
}
#endif
TEST(Support, TempDirectory) { … }
#ifdef _WIN32
static std::string path2regex(std::string Path) {
size_t Pos = 0;
bool Forward = path::get_separator()[0] == '/';
while ((Pos = Path.find('\\', Pos)) != std::string::npos) {
if (Forward) {
Path.replace(Pos, 1, "/");
Pos += 1;
} else {
Path.replace(Pos, 1, "\\\\");
Pos += 2;
}
}
return Path;
}
#define EXPECT_TEMP_DIR …
TEST(SupportDeathTest, TempDirectoryOnWindows) {
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\OtherFolder"), "C:\\OtherFolder");
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:/Unix/Path/Separators"),
"C:\\Unix\\Path\\Separators");
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");
EXPECT_TEMP_DIR(
{
_wputenv_s(L"TMP", L"");
_wputenv_s(L"TEMP", L"C:\\Valid\\Path");
},
"C:\\Valid\\Path");
EXPECT_TEMP_DIR(
{
_wputenv_s(L"TMP", L"");
_wputenv_s(L"TEMP", L"");
_wputenv_s(L"USERPROFILE", L"");
},
"C:\\Temp");
SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};
while (Expected.size() < 260)
Expected.append("\\DirNameWith19Charss");
ASSERT_EQ(260U, Expected.size());
EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str());
}
#endif
class FileSystemTest : public testing::Test { … };
TEST_F(FileSystemTest, Unique) { … }
TEST_F(FileSystemTest, RealPath) { … }
TEST_F(FileSystemTest, ExpandTilde) { … }
#ifdef LLVM_ON_UNIX
TEST_F(FileSystemTest, RealPathNoReadPerm) { … }
TEST_F(FileSystemTest, RemoveDirectoriesNoExePerm) { … }
#endif
TEST_F(FileSystemTest, TempFileKeepDiscard) { … }
TEST_F(FileSystemTest, TempFileDiscardDiscard) { … }
TEST_F(FileSystemTest, TempFiles) { … }
TEST_F(FileSystemTest, TempFileCollisions) { … }
TEST_F(FileSystemTest, CreateDir) { … }
TEST_F(FileSystemTest, DirectoryIteration) { … }
TEST_F(FileSystemTest, DirectoryNotExecutable) { … }
#ifdef LLVM_ON_UNIX
TEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) { … }
#endif
#ifdef _WIN32
TEST_F(FileSystemTest, UTF8ToUTF16DirectoryIteration) {
const char *Pi = "\xcf\x80";
std::string RootDir = (TestDirectory + "/" + Pi).str();
ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/a"));
ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/b"));
std::error_code EC;
unsigned Count = 0;
for (fs::directory_iterator I(Twine(RootDir), EC), E; I != E;
I.increment(EC)) {
ASSERT_NO_ERROR(EC);
StringRef DirName = path::filename(I->path());
EXPECT_TRUE(DirName == "a" || DirName == "b");
++Count;
}
EXPECT_EQ(Count, 2U);
ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/a"));
ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/b"));
ASSERT_NO_ERROR(fs::remove(Twine(RootDir)));
}
#endif
TEST_F(FileSystemTest, Remove) { … }
#ifdef _WIN32
TEST_F(FileSystemTest, CarriageReturn) {
SmallString<128> FilePathname(TestDirectory);
std::error_code EC;
path::append(FilePathname, "test");
{
raw_fd_ostream File(FilePathname, EC, sys::fs::OF_TextWithCRLF);
ASSERT_NO_ERROR(EC);
File << '\n';
}
{
auto Buf = MemoryBuffer::getFile(FilePathname.str());
EXPECT_TRUE((bool)Buf);
EXPECT_EQ(Buf.get()->getBuffer(), "\r\n");
}
{
raw_fd_ostream File(FilePathname, EC, sys::fs::OF_None);
ASSERT_NO_ERROR(EC);
File << '\n';
}
{
auto Buf = MemoryBuffer::getFile(FilePathname.str());
EXPECT_TRUE((bool)Buf);
EXPECT_EQ(Buf.get()->getBuffer(), "\n");
}
ASSERT_NO_ERROR(fs::remove(Twine(FilePathname)));
}
#endif
TEST_F(FileSystemTest, Resize) { … }
TEST_F(FileSystemTest, ResizeBeforeMapping) { … }
TEST_F(FileSystemTest, MD5) { … }
TEST_F(FileSystemTest, FileMapping) { … }
TEST(Support, NormalizePath) { … }
TEST(Support, RemoveLeadingDotSlash) { … }
static std::string remove_dots(StringRef path, bool remove_dot_dot,
path::Style style) { … }
TEST(Support, RemoveDots) { … }
TEST(Support, ReplacePathPrefix) { … }
TEST_F(FileSystemTest, OpenFileForRead) { … }
TEST_F(FileSystemTest, OpenDirectoryAsFileForRead) { … }
TEST_F(FileSystemTest, OpenDirectoryAsFileForWrite) { … }
static void createFileWithData(const Twine &Path, bool ShouldExistBefore,
fs::CreationDisposition Disp, StringRef Data) { … }
static void verifyFileContents(const Twine &Path, StringRef Contents) { … }
TEST_F(FileSystemTest, CreateNew) { … }
TEST_F(FileSystemTest, CreateAlways) { … }
TEST_F(FileSystemTest, OpenExisting) { … }
TEST_F(FileSystemTest, OpenAlways) { … }
TEST_F(FileSystemTest, AppendSetsCorrectFileOffset) { … }
static void verifyRead(int FD, StringRef Data, bool ShouldSucceed) { … }
static void verifyWrite(int FD, StringRef Data, bool ShouldSucceed) { … }
TEST_F(FileSystemTest, ReadOnlyFileCantWrite) { … }
TEST_F(FileSystemTest, WriteOnlyFileCantRead) { … }
TEST_F(FileSystemTest, ReadWriteFileCanReadOrWrite) { … }
TEST_F(FileSystemTest, readNativeFile) { … }
TEST_F(FileSystemTest, readNativeFileToEOF) { … }
TEST_F(FileSystemTest, readNativeFileSlice) { … }
TEST_F(FileSystemTest, is_local) { … }
TEST_F(FileSystemTest, getUmask) { … }
TEST_F(FileSystemTest, RespectUmask) { … }
TEST_F(FileSystemTest, set_current_path) { … }
TEST_F(FileSystemTest, permissions) { … }
#ifdef _WIN32
TEST_F(FileSystemTest, widenPath) {
const std::wstring LongPathPrefix(L"\\\\?\\");
std::string Input("C:\\foldername\\");
const std::string Pi("\xcf\x80");
const size_t NumChars = MAX_PATH - Input.size() - 1;
for (size_t i = 0; i < NumChars; ++i)
Input += Pi;
EXPECT_GT(Input.size(), (size_t)MAX_PATH);
SmallVector<wchar_t, MAX_PATH + 16> Result;
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),
LongPathPrefix.size()) != 0);
EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);
Input += Pi;
SmallVector<wchar_t, MAX_PATH + 16> Expected;
ASSERT_NO_ERROR(windows::UTF8ToUTF16(Input, Expected));
Expected.insert(Expected.begin(), LongPathPrefix.begin(),
LongPathPrefix.end());
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_EQ(Result, Expected);
SmallString<MAX_PATH + 16> InputForward(Input);
path::make_preferred(InputForward, path::Style::windows_slash);
ASSERT_NO_ERROR(windows::widenPath(InputForward, Result));
EXPECT_EQ(Result, Expected);
SmallString<MAX_PATH + 16> PrefixedPath("\\\\?\\C:\\foldername");
ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));
path::make_preferred(PrefixedPath, path::Style::windows_slash);
ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));
EXPECT_EQ(Result, Expected);
PrefixedPath.assign("/\\?/C:/foldername");
ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));
ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));
EXPECT_EQ(Result, Expected);
const std::string ShareName("\\\\sharename\\");
const std::string FileName("\\filename");
const char DirChar = 'x';
std::string DirName(MAX_PATH - ShareName.size() - FileName.size() - 1,
DirChar);
Input = ShareName + DirName + FileName;
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),
LongPathPrefix.size()) != 0);
EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);
DirName += DirChar;
Input = ShareName + DirName + FileName;
ASSERT_NO_ERROR(windows::UTF8ToUTF16(StringRef(Input).substr(2), Expected));
const std::wstring UNCPrefix(LongPathPrefix + L"UNC\\");
Expected.insert(Expected.begin(), UNCPrefix.begin(), UNCPrefix.end());
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_EQ(Result, Expected);
std::replace(Input.begin(), Input.end(), '\\', '/');
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_EQ(Result, Expected);
Input = ShareName + DirName + "\\.\\foo\\.\\.." + FileName;
ASSERT_NO_ERROR(windows::widenPath(Input, Result));
EXPECT_EQ(Result, Expected);
}
#endif
#ifdef _WIN32
TEST_F(FileSystemTest, FileLocker) {
using namespace std::chrono;
int FD;
std::error_code EC;
SmallString<64> TempPath;
EC = fs::createTemporaryFile("test", "temp", FD, TempPath);
ASSERT_NO_ERROR(EC);
FileRemover Cleanup(TempPath);
raw_fd_ostream Stream(TempPath, EC);
EC = fs::tryLockFile(FD);
ASSERT_NO_ERROR(EC);
EC = fs::unlockFile(FD);
ASSERT_NO_ERROR(EC);
if (auto L = Stream.lock()) {
ASSERT_ERROR(fs::tryLockFile(FD));
ASSERT_NO_ERROR(L->unlock());
ASSERT_NO_ERROR(fs::tryLockFile(FD));
ASSERT_NO_ERROR(fs::unlockFile(FD));
} else {
ADD_FAILURE();
handleAllErrors(L.takeError(), [&](ErrorInfoBase &EIB) {});
}
ASSERT_NO_ERROR(fs::tryLockFile(FD));
ASSERT_NO_ERROR(fs::unlockFile(FD));
{
Expected<fs::FileLocker> L1 = Stream.lock();
ASSERT_THAT_EXPECTED(L1, Succeeded());
raw_fd_ostream Stream2(FD, false);
Expected<fs::FileLocker> L2 = Stream2.tryLockFor(250ms);
ASSERT_THAT_EXPECTED(L2, Failed());
ASSERT_NO_ERROR(L1->unlock());
Expected<fs::FileLocker> L3 = Stream.tryLockFor(0ms);
ASSERT_THAT_EXPECTED(L3, Succeeded());
}
ASSERT_NO_ERROR(fs::tryLockFile(FD));
ASSERT_NO_ERROR(fs::unlockFile(FD));
}
#endif
TEST_F(FileSystemTest, CopyFile) { … }
}