2592 lines · cpp
1//===- llvm/unittest/Support/Path.cpp - Path tests ------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Support/Path.h"10#include "llvm/ADT/STLExtras.h"11#include "llvm/ADT/ScopeExit.h"12#include "llvm/ADT/SmallVector.h"13#include "llvm/BinaryFormat/Magic.h"14#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX15#include "llvm/Support/Compiler.h"16#include "llvm/Support/ConvertUTF.h"17#include "llvm/Support/Duration.h"18#include "llvm/Support/Errc.h"19#include "llvm/Support/ErrorHandling.h"20#include "llvm/Support/FileSystem.h"21#include "llvm/Support/FileUtilities.h"22#include "llvm/Support/MemoryBuffer.h"23#include "llvm/Support/raw_ostream.h"24#include "llvm/TargetParser/Host.h"25#include "llvm/TargetParser/Triple.h"26#include "llvm/Testing/Support/Error.h"27#include "llvm/Testing/Support/SupportHelpers.h"28#include "gmock/gmock.h"29#include "gtest/gtest.h"30 31#ifdef _WIN3232#include "llvm/ADT/ArrayRef.h"33#include "llvm/Support/Chrono.h"34#include "llvm/Support/Windows/WindowsSupport.h"35#include <windows.h>36#include <winerror.h>37#endif38 39#ifdef LLVM_ON_UNIX40#include <pwd.h>41#include <sys/stat.h>42#endif43 44using namespace llvm;45using namespace llvm::sys;46 47#define ASSERT_NO_ERROR(x) \48 if (std::error_code ASSERT_NO_ERROR_ec = x) { \49 SmallString<128> MessageStorage; \50 raw_svector_ostream Message(MessageStorage); \51 Message << #x ": did not return errc::success.\n" \52 << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \53 << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \54 GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \55 } else { \56 }57 58#define ASSERT_ERROR(x) \59 if (!x) { \60 SmallString<128> MessageStorage; \61 raw_svector_ostream Message(MessageStorage); \62 Message << #x ": did not return a failure error code.\n"; \63 GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \64 }65 66namespace {67 68void checkSeparators(StringRef Path) {69#ifdef _WIN3270 char UndesiredSeparator = sys::path::get_separator()[0] == '/' ? '\\' : '/';71 ASSERT_EQ(Path.find(UndesiredSeparator), StringRef::npos);72#endif73}74 75struct FileDescriptorCloser {76 explicit FileDescriptorCloser(int FD) : FD(FD) {}77 ~FileDescriptorCloser() { ::close(FD); }78 int FD;79};80 81TEST(is_style_Style, Works) {82 using namespace llvm::sys::path;83 // Check platform-independent results.84 EXPECT_TRUE(is_style_posix(Style::posix));85 EXPECT_TRUE(is_style_windows(Style::windows));86 EXPECT_TRUE(is_style_windows(Style::windows_slash));87 EXPECT_FALSE(is_style_posix(Style::windows));88 EXPECT_FALSE(is_style_posix(Style::windows_slash));89 EXPECT_FALSE(is_style_windows(Style::posix));90 91 // Check platform-dependent results.92#if defined(_WIN32)93 EXPECT_FALSE(is_style_posix(Style::native));94 EXPECT_TRUE(is_style_windows(Style::native));95#else96 EXPECT_TRUE(is_style_posix(Style::native));97 EXPECT_FALSE(is_style_windows(Style::native));98#endif99}100 101TEST(is_separator, Works) {102 EXPECT_TRUE(path::is_separator('/'));103 EXPECT_FALSE(path::is_separator('\0'));104 EXPECT_FALSE(path::is_separator('-'));105 EXPECT_FALSE(path::is_separator(' '));106 107 EXPECT_TRUE(path::is_separator('\\', path::Style::windows));108 EXPECT_TRUE(path::is_separator('\\', path::Style::windows_slash));109 EXPECT_FALSE(path::is_separator('\\', path::Style::posix));110 111 EXPECT_EQ(path::is_style_windows(path::Style::native),112 path::is_separator('\\'));113}114 115TEST(get_separator, Works) {116 EXPECT_EQ(path::get_separator(path::Style::posix), "/");117 EXPECT_EQ(path::get_separator(path::Style::windows_backslash), "\\");118 EXPECT_EQ(path::get_separator(path::Style::windows_slash), "/");119}120 121TEST(is_absolute_gnu, Works) {122 // Test tuple <Path, ExpectedPosixValue, ExpectedWindowsValue>.123 const std::tuple<StringRef, bool, bool> Paths[] = {124 std::make_tuple("", false, false),125 std::make_tuple("/", true, true),126 std::make_tuple("/foo", true, true),127 std::make_tuple("\\", false, true),128 std::make_tuple("\\foo", false, true),129 std::make_tuple("foo", false, false),130 std::make_tuple("c", false, false),131 std::make_tuple("c:", false, true),132 std::make_tuple("c:\\", false, true),133 std::make_tuple("!:", false, true),134 std::make_tuple("xx:", false, false),135 std::make_tuple("c:abc\\", false, true),136 std::make_tuple(":", false, false)};137 138 for (const auto &Path : Paths) {139 EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::posix),140 std::get<1>(Path));141 EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::windows),142 std::get<2>(Path));143 144 constexpr int Native = is_style_posix(path::Style::native) ? 1 : 2;145 EXPECT_EQ(path::is_absolute_gnu(std::get<0>(Path), path::Style::native),146 std::get<Native>(Path));147 }148}149 150TEST(Support, Path) {151 SmallVector<StringRef, 40> paths;152 paths.push_back("");153 paths.push_back(".");154 paths.push_back("..");155 paths.push_back("foo");156 paths.push_back("/");157 paths.push_back("/foo");158 paths.push_back("foo/");159 paths.push_back("/foo/");160 paths.push_back("foo/bar");161 paths.push_back("/foo/bar");162 paths.push_back("//net");163 paths.push_back("//net/");164 paths.push_back("//net/foo");165 paths.push_back("///foo///");166 paths.push_back("///foo///bar");167 paths.push_back("/.");168 paths.push_back("./");169 paths.push_back("/..");170 paths.push_back("../");171 paths.push_back("foo/.");172 paths.push_back("foo/..");173 paths.push_back("foo/./");174 paths.push_back("foo/./bar");175 paths.push_back("foo/..");176 paths.push_back("foo/../");177 paths.push_back("foo/../bar");178 paths.push_back("c:");179 paths.push_back("c:/");180 paths.push_back("c:foo");181 paths.push_back("c:/foo");182 paths.push_back("c:foo/");183 paths.push_back("c:/foo/");184 paths.push_back("c:/foo/bar");185 paths.push_back("prn:");186 paths.push_back("c:\\");187 paths.push_back("c:foo");188 paths.push_back("c:\\foo");189 paths.push_back("c:foo\\");190 paths.push_back("c:\\foo\\");191 paths.push_back("c:\\foo/");192 paths.push_back("c:/foo\\bar");193 paths.push_back(":");194 195 for (SmallVector<StringRef, 40>::const_iterator i = paths.begin(),196 e = paths.end();197 i != e;198 ++i) {199 SCOPED_TRACE(*i);200 SmallVector<StringRef, 5> ComponentStack;201 for (sys::path::const_iterator ci = sys::path::begin(*i),202 ce = sys::path::end(*i);203 ci != ce;204 ++ci) {205 EXPECT_FALSE(ci->empty());206 ComponentStack.push_back(*ci);207 }208 209 SmallVector<StringRef, 5> ReverseComponentStack;210 for (sys::path::reverse_iterator ci = sys::path::rbegin(*i),211 ce = sys::path::rend(*i);212 ci != ce;213 ++ci) {214 EXPECT_FALSE(ci->empty());215 ReverseComponentStack.push_back(*ci);216 }217 std::reverse(ReverseComponentStack.begin(), ReverseComponentStack.end());218 EXPECT_THAT(ComponentStack, testing::ContainerEq(ReverseComponentStack));219 220 // Crash test most of the API - since we're iterating over all of our paths221 // here there isn't really anything reasonable to assert on in the results.222 (void)path::has_root_path(*i);223 (void)path::root_path(*i);224 (void)path::has_root_name(*i);225 (void)path::root_name(*i);226 (void)path::has_root_directory(*i);227 (void)path::root_directory(*i);228 (void)path::has_parent_path(*i);229 (void)path::parent_path(*i);230 (void)path::has_filename(*i);231 (void)path::filename(*i);232 (void)path::has_stem(*i);233 (void)path::stem(*i);234 (void)path::has_extension(*i);235 (void)path::extension(*i);236 (void)path::is_absolute(*i);237 (void)path::is_absolute_gnu(*i);238 (void)path::is_relative(*i);239 240 SmallString<128> temp_store;241 temp_store = *i;242 ASSERT_NO_ERROR(fs::make_absolute(temp_store));243 temp_store = *i;244 path::remove_filename(temp_store);245 246 temp_store = *i;247 path::replace_extension(temp_store, "ext");248 StringRef filename(temp_store.begin(), temp_store.size()), stem, ext;249 stem = path::stem(filename);250 ext = path::extension(filename);251 EXPECT_EQ(*sys::path::rbegin(filename), (stem + ext).str());252 253 path::native(*i, temp_store);254 }255 256 {257 SmallString<32> Relative("foo.cpp");258 path::make_absolute("/root", Relative);259 Relative[5] = '/'; // Fix up windows paths.260 ASSERT_EQ("/root/foo.cpp", Relative);261 }262 263 {264 SmallString<32> Relative("foo.cpp");265 path::make_absolute("//root", Relative);266 Relative[6] = '/'; // Fix up windows paths.267 ASSERT_EQ("//root/foo.cpp", Relative);268 }269}270 271TEST(Support, PathRoot) {272 ASSERT_EQ(path::root_name("//net/hello", path::Style::posix).str(), "//net");273 ASSERT_EQ(path::root_name("c:/hello", path::Style::posix).str(), "");274 ASSERT_EQ(path::root_name("c:/hello", path::Style::windows).str(), "c:");275 ASSERT_EQ(path::root_name("/hello", path::Style::posix).str(), "");276 277 ASSERT_EQ(path::root_directory("/goo/hello", path::Style::posix).str(), "/");278 ASSERT_EQ(path::root_directory("c:/hello", path::Style::windows).str(), "/");279 ASSERT_EQ(path::root_directory("d/file.txt", path::Style::posix).str(), "");280 ASSERT_EQ(path::root_directory("d/file.txt", path::Style::windows).str(), "");281 282 SmallVector<StringRef, 40> paths;283 paths.push_back("");284 paths.push_back(".");285 paths.push_back("..");286 paths.push_back("foo");287 paths.push_back("/");288 paths.push_back("/foo");289 paths.push_back("foo/");290 paths.push_back("/foo/");291 paths.push_back("foo/bar");292 paths.push_back("/foo/bar");293 paths.push_back("//net");294 paths.push_back("//net/");295 paths.push_back("//net/foo");296 paths.push_back("///foo///");297 paths.push_back("///foo///bar");298 paths.push_back("/.");299 paths.push_back("./");300 paths.push_back("/..");301 paths.push_back("../");302 paths.push_back("foo/.");303 paths.push_back("foo/..");304 paths.push_back("foo/./");305 paths.push_back("foo/./bar");306 paths.push_back("foo/..");307 paths.push_back("foo/../");308 paths.push_back("foo/../bar");309 paths.push_back("c:");310 paths.push_back("c:/");311 paths.push_back("c:foo");312 paths.push_back("c:/foo");313 paths.push_back("c:foo/");314 paths.push_back("c:/foo/");315 paths.push_back("c:/foo/bar");316 paths.push_back("prn:");317 paths.push_back("c:\\");318 paths.push_back("c:foo");319 paths.push_back("c:\\foo");320 paths.push_back("c:foo\\");321 paths.push_back("c:\\foo\\");322 paths.push_back("c:\\foo/");323 paths.push_back("c:/foo\\bar");324 325 for (StringRef p : paths) {326 ASSERT_EQ(327 path::root_name(p, path::Style::posix).str() + path::root_directory(p, path::Style::posix).str(),328 path::root_path(p, path::Style::posix).str());329 330 ASSERT_EQ(331 path::root_name(p, path::Style::windows).str() + path::root_directory(p, path::Style::windows).str(),332 path::root_path(p, path::Style::windows).str());333 }334}335 336TEST(Support, FilenameParent) {337 EXPECT_EQ("/", path::filename("/"));338 EXPECT_EQ("", path::parent_path("/"));339 340 EXPECT_EQ("\\", path::filename("c:\\", path::Style::windows));341 EXPECT_EQ("c:", path::parent_path("c:\\", path::Style::windows));342 343 EXPECT_EQ("/", path::filename("///"));344 EXPECT_EQ("", path::parent_path("///"));345 346 EXPECT_EQ("\\", path::filename("c:\\\\", path::Style::windows));347 EXPECT_EQ("c:", path::parent_path("c:\\\\", path::Style::windows));348 349 EXPECT_EQ("bar", path::filename("/foo/bar"));350 EXPECT_EQ("/foo", path::parent_path("/foo/bar"));351 352 EXPECT_EQ("foo", path::filename("/foo"));353 EXPECT_EQ("/", path::parent_path("/foo"));354 355 EXPECT_EQ("foo", path::filename("foo"));356 EXPECT_EQ("", path::parent_path("foo"));357 358 EXPECT_EQ(".", path::filename("foo/"));359 EXPECT_EQ("foo", path::parent_path("foo/"));360 361 EXPECT_EQ("//net", path::filename("//net"));362 EXPECT_EQ("", path::parent_path("//net"));363 364 EXPECT_EQ("/", path::filename("//net/"));365 EXPECT_EQ("//net", path::parent_path("//net/"));366 367 EXPECT_EQ("foo", path::filename("//net/foo"));368 EXPECT_EQ("//net/", path::parent_path("//net/foo"));369 370 // These checks are just to make sure we do something reasonable with the371 // paths below. They are not meant to prescribe the one true interpretation of372 // these paths. Other decompositions (e.g. "//" -> "" + "//") are also373 // possible.374 EXPECT_EQ("/", path::filename("//"));375 EXPECT_EQ("", path::parent_path("//"));376 377 EXPECT_EQ("\\", path::filename("\\\\", path::Style::windows));378 EXPECT_EQ("", path::parent_path("\\\\", path::Style::windows));379 380 EXPECT_EQ("\\", path::filename("\\\\\\", path::Style::windows));381 EXPECT_EQ("", path::parent_path("\\\\\\", path::Style::windows));382}383 384static std::vector<StringRef>385GetComponents(StringRef Path, path::Style S = path::Style::native) {386 return {path::begin(Path, S), path::end(Path)};387}388 389TEST(Support, PathIterator) {390 EXPECT_THAT(GetComponents("/foo"), testing::ElementsAre("/", "foo"));391 EXPECT_THAT(GetComponents("/"), testing::ElementsAre("/"));392 EXPECT_THAT(GetComponents("//"), testing::ElementsAre("/"));393 EXPECT_THAT(GetComponents("///"), testing::ElementsAre("/"));394 EXPECT_THAT(GetComponents("c/d/e/foo.txt"),395 testing::ElementsAre("c", "d", "e", "foo.txt"));396 EXPECT_THAT(GetComponents(".c/.d/../."),397 testing::ElementsAre(".c", ".d", "..", "."));398 EXPECT_THAT(GetComponents("/c/d/e/foo.txt"),399 testing::ElementsAre("/", "c", "d", "e", "foo.txt"));400 EXPECT_THAT(GetComponents("/.c/.d/../."),401 testing::ElementsAre("/", ".c", ".d", "..", "."));402 EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),403 testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));404 EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows_slash),405 testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));406 EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));407 EXPECT_THAT(GetComponents("//net/c/foo.txt"),408 testing::ElementsAre("//net", "/", "c", "foo.txt"));409}410 411TEST(Support, AbsolutePathIteratorEnd) {412 // Trailing slashes are converted to '.' unless they are part of the root path.413 SmallVector<std::pair<StringRef, path::Style>, 4> Paths;414 Paths.emplace_back("/foo/", path::Style::native);415 Paths.emplace_back("/foo//", path::Style::native);416 Paths.emplace_back("//net/foo/", path::Style::native);417 Paths.emplace_back("c:\\foo\\", path::Style::windows);418 419 for (auto &Path : Paths) {420 SCOPED_TRACE(Path.first);421 StringRef LastComponent = *path::rbegin(Path.first, Path.second);422 EXPECT_EQ(".", LastComponent);423 }424 425 SmallVector<std::pair<StringRef, path::Style>, 3> RootPaths;426 RootPaths.emplace_back("/", path::Style::native);427 RootPaths.emplace_back("//net/", path::Style::native);428 RootPaths.emplace_back("c:\\", path::Style::windows);429 RootPaths.emplace_back("//net//", path::Style::native);430 RootPaths.emplace_back("c:\\\\", path::Style::windows);431 432 for (auto &Path : RootPaths) {433 SCOPED_TRACE(Path.first);434 StringRef LastComponent = *path::rbegin(Path.first, Path.second);435 EXPECT_EQ(1u, LastComponent.size());436 EXPECT_TRUE(path::is_separator(LastComponent[0], Path.second));437 }438}439 440#ifdef _WIN32441std::string getEnvWin(const wchar_t *Var) {442 std::string expected;443 if (wchar_t const *path = ::_wgetenv(Var)) {444 auto pathLen = ::wcslen(path);445 ArrayRef<char> ref{reinterpret_cast<char const *>(path),446 pathLen * sizeof(wchar_t)};447 convertUTF16ToUTF8String(ref, expected);448 SmallString<32> Buf(expected);449 path::make_preferred(Buf);450 expected.assign(Buf.begin(), Buf.end());451 }452 return expected;453}454#else455// RAII helper to set and restore an environment variable.456class WithEnv {457 const char *Var;458 std::optional<std::string> OriginalValue;459 460public:461 WithEnv(const char *Var, const char *Value) : Var(Var) {462 if (const char *V = ::getenv(Var))463 OriginalValue.emplace(V);464 if (Value)465 ::setenv(Var, Value, 1);466 else467 ::unsetenv(Var);468 }469 ~WithEnv() {470 if (OriginalValue)471 ::setenv(Var, OriginalValue->c_str(), 1);472 else473 ::unsetenv(Var);474 }475};476#endif477 478TEST(Support, HomeDirectory) {479 std::string expected;480#ifdef _WIN32481 expected = getEnvWin(L"USERPROFILE");482#else483 if (char const *path = ::getenv("HOME"))484 expected = path;485#endif486 // Do not try to test it if we don't know what to expect.487 // On Windows we use something better than env vars.488 if (expected.empty())489 GTEST_SKIP();490 SmallString<128> HomeDir;491 auto status = path::home_directory(HomeDir);492 EXPECT_TRUE(status);493 EXPECT_EQ(expected, HomeDir);494}495 496// Apple has their own solution for this.497#if defined(LLVM_ON_UNIX) && !defined(__APPLE__)498TEST(Support, HomeDirectoryWithNoEnv) {499 WithEnv Env("HOME", nullptr);500 501 // Don't run the test if we have nothing to compare against.502 struct passwd *pw = getpwuid(getuid());503 if (!pw || !pw->pw_dir)504 GTEST_SKIP();505 std::string PwDir = pw->pw_dir;506 507 SmallString<128> HomeDir;508 EXPECT_TRUE(path::home_directory(HomeDir));509 EXPECT_EQ(PwDir, HomeDir);510}511 512TEST(Support, ConfigDirectoryWithEnv) {513 WithEnv Env("XDG_CONFIG_HOME", "/xdg/config");514 515 SmallString<128> ConfigDir;516 EXPECT_TRUE(path::user_config_directory(ConfigDir));517 EXPECT_EQ("/xdg/config", ConfigDir);518}519 520TEST(Support, ConfigDirectoryNoEnv) {521 WithEnv Env("XDG_CONFIG_HOME", nullptr);522 523 SmallString<128> Fallback;524 ASSERT_TRUE(path::home_directory(Fallback));525 path::append(Fallback, ".config");526 527 SmallString<128> CacheDir;528 EXPECT_TRUE(path::user_config_directory(CacheDir));529 EXPECT_EQ(Fallback, CacheDir);530}531 532TEST(Support, CacheDirectoryWithEnv) {533 WithEnv Env("XDG_CACHE_HOME", "/xdg/cache");534 535 SmallString<128> CacheDir;536 EXPECT_TRUE(path::cache_directory(CacheDir));537 EXPECT_EQ("/xdg/cache", CacheDir);538}539 540TEST(Support, CacheDirectoryNoEnv) {541 WithEnv Env("XDG_CACHE_HOME", nullptr);542 543 SmallString<128> Fallback;544 ASSERT_TRUE(path::home_directory(Fallback));545 path::append(Fallback, ".cache");546 547 SmallString<128> CacheDir;548 EXPECT_TRUE(path::cache_directory(CacheDir));549 EXPECT_EQ(Fallback, CacheDir);550}551#endif552 553#ifdef __APPLE__554TEST(Support, ConfigDirectory) {555 SmallString<128> Fallback;556 ASSERT_TRUE(path::home_directory(Fallback));557 path::append(Fallback, "Library/Preferences");558 559 SmallString<128> ConfigDir;560 EXPECT_TRUE(path::user_config_directory(ConfigDir));561 EXPECT_EQ(Fallback, ConfigDir);562}563#endif564 565#ifdef _WIN32566TEST(Support, ConfigDirectory) {567 std::string Expected = getEnvWin(L"LOCALAPPDATA");568 // Do not try to test it if we don't know what to expect.569 if (Expected.empty())570 GTEST_SKIP();571 SmallString<128> CacheDir;572 EXPECT_TRUE(path::user_config_directory(CacheDir));573 EXPECT_EQ(Expected, CacheDir);574}575 576TEST(Support, CacheDirectory) {577 std::string Expected = getEnvWin(L"LOCALAPPDATA");578 // Do not try to test it if we don't know what to expect.579 if (Expected.empty())580 GTEST_SKIP();581 SmallString<128> CacheDir;582 EXPECT_TRUE(path::cache_directory(CacheDir));583 EXPECT_EQ(Expected, CacheDir);584}585#endif586 587TEST(Support, TempDirectory) {588 SmallString<32> TempDir;589 path::system_temp_directory(false, TempDir);590 EXPECT_TRUE(!TempDir.empty());591 TempDir.clear();592 path::system_temp_directory(true, TempDir);593 EXPECT_TRUE(!TempDir.empty());594}595 596#ifdef _WIN32597static std::string path2regex(std::string Path) {598 size_t Pos = 0;599 bool Forward = path::get_separator()[0] == '/';600 while ((Pos = Path.find('\\', Pos)) != std::string::npos) {601 if (Forward) {602 Path.replace(Pos, 1, "/");603 Pos += 1;604 } else {605 Path.replace(Pos, 1, "\\\\");606 Pos += 2;607 }608 }609 return Path;610}611 612/// Helper for running temp dir test in separated process. See below.613#define EXPECT_TEMP_DIR(prepare, expected) \614 EXPECT_EXIT( \615 { \616 prepare; \617 SmallString<300> TempDir; \618 path::system_temp_directory(true, TempDir); \619 raw_os_ostream(std::cerr) << TempDir; \620 std::exit(0); \621 }, \622 ::testing::ExitedWithCode(0), path2regex(expected))623 624TEST(SupportDeathTest, TempDirectoryOnWindows) {625 // In this test we want to check how system_temp_directory responds to626 // different values of specific env vars. To prevent corrupting env vars of627 // the current process all checks are done in separated processes.628 EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\OtherFolder"), "C:\\OtherFolder");629 EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:/Unix/Path/Separators"),630 "C:\\Unix\\Path\\Separators");631 EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"Local Path"), ".+\\Local Path$");632 EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"F:\\TrailingSep\\"), "F:\\TrailingSep");633 EXPECT_TEMP_DIR(634 _wputenv_s(L"TMP", L"C:\\2\x03C0r-\x00B5\x00B3\\\x2135\x2080"),635 "C:\\2\xCF\x80r-\xC2\xB5\xC2\xB3\\\xE2\x84\xB5\xE2\x82\x80");636 637 // Test $TMP empty, $TEMP set.638 EXPECT_TEMP_DIR(639 {640 _wputenv_s(L"TMP", L"");641 _wputenv_s(L"TEMP", L"C:\\Valid\\Path");642 },643 "C:\\Valid\\Path");644 645 // All related env vars empty646 EXPECT_TEMP_DIR(647 {648 _wputenv_s(L"TMP", L"");649 _wputenv_s(L"TEMP", L"");650 _wputenv_s(L"USERPROFILE", L"");651 },652 "C:\\Temp");653 654 // Test evn var / path with 260 chars.655 SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};656 while (Expected.size() < 260)657 Expected.append("\\DirNameWith19Charss");658 ASSERT_EQ(260U, Expected.size());659 EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str());660}661#endif662 663class FileSystemTest : public testing::Test {664protected:665 /// Unique temporary directory in which all created filesystem entities must666 /// be placed. It is removed at the end of each test (must be empty).667 SmallString<128> TestDirectory;668 SmallString<128> NonExistantFile;669 670 void SetUp() override {671 ASSERT_NO_ERROR(672 fs::createUniqueDirectory("file-system-test", TestDirectory));673 // We don't care about this specific file.674 errs() << "Test Directory: " << TestDirectory << '\n';675 errs().flush();676 NonExistantFile = TestDirectory;677 678 // Even though this value is hardcoded, is a 128-bit GUID, so we should be679 // guaranteed that this file will never exist.680 sys::path::append(NonExistantFile, "1B28B495C16344CB9822E588CD4C3EF0");681 }682 683 void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); }684};685 686TEST_F(FileSystemTest, Unique) {687 // Create a temp file.688 int FileDescriptor;689 SmallString<64> TempPath;690 ASSERT_NO_ERROR(691 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));692 693 // The same file should return an identical unique id.694 fs::UniqueID F1, F2;695 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F1));696 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F2));697 ASSERT_EQ(F1, F2);698 699 // Different files should return different unique ids.700 int FileDescriptor2;701 SmallString<64> TempPath2;702 ASSERT_NO_ERROR(703 fs::createTemporaryFile("prefix", "temp", FileDescriptor2, TempPath2));704 705 fs::UniqueID D;706 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D));707 ASSERT_NE(D, F1);708 ::close(FileDescriptor2);709 710 ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));711 712#ifndef _WIN32713 // Two paths representing the same file on disk should still provide the714 // same unique id. We can test this by making a hard link.715 // FIXME: Our implementation of getUniqueID on Windows doesn't consider hard716 // links to be the same file.717 ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));718 fs::UniqueID D2;719 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D2));720 ASSERT_EQ(D2, F1);721#endif722 723 ::close(FileDescriptor);724 725 SmallString<128> Dir1;726 ASSERT_NO_ERROR(727 fs::createUniqueDirectory("dir1", Dir1));728 ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F1));729 ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F2));730 ASSERT_EQ(F1, F2);731 732 SmallString<128> Dir2;733 ASSERT_NO_ERROR(734 fs::createUniqueDirectory("dir2", Dir2));735 ASSERT_NO_ERROR(fs::getUniqueID(Dir2.c_str(), F2));736 ASSERT_NE(F1, F2);737 ASSERT_NO_ERROR(fs::remove(Dir1));738 ASSERT_NO_ERROR(fs::remove(Dir2));739 ASSERT_NO_ERROR(fs::remove(TempPath2));740 ASSERT_NO_ERROR(fs::remove(TempPath));741}742 743TEST_F(FileSystemTest, RealPath) {744 ASSERT_NO_ERROR(745 fs::create_directories(Twine(TestDirectory) + "/test1/test2/test3"));746 ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/test1/test2/test3"));747 748 SmallString<64> RealBase;749 SmallString<64> Expected;750 SmallString<64> Actual;751 752 // TestDirectory itself might be under a symlink or have been specified with753 // a different case than the existing temp directory. In such cases real_path754 // on the concatenated path will differ in the TestDirectory portion from755 // how we specified it. Make sure to compare against the real_path of the756 // TestDirectory, and not just the value of TestDirectory.757 ASSERT_NO_ERROR(fs::real_path(TestDirectory, RealBase));758 checkSeparators(RealBase);759 path::native(Twine(RealBase) + "/test1/test2", Expected);760 761 ASSERT_NO_ERROR(fs::real_path(762 Twine(TestDirectory) + "/././test1/../test1/test2/./test3/..", Actual));763 checkSeparators(Actual);764 765 EXPECT_EQ(Expected, Actual);766 767 SmallString<64> HomeDir;768 769 // This can fail if $HOME is not set and getpwuid fails.770 bool Result = llvm::sys::path::home_directory(HomeDir);771 if (Result) {772 checkSeparators(HomeDir);773 ASSERT_NO_ERROR(fs::real_path(HomeDir, Expected));774 checkSeparators(Expected);775 ASSERT_NO_ERROR(fs::real_path("~", Actual, true));776 EXPECT_EQ(Expected, Actual);777 ASSERT_NO_ERROR(fs::real_path("~/", Actual, true));778 EXPECT_EQ(Expected, Actual);779 }780 781 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/test1"));782}783 784TEST_F(FileSystemTest, ExpandTilde) {785 SmallString<64> Expected;786 SmallString<64> Actual;787 SmallString<64> HomeDir;788 789 // This can fail if $HOME is not set and getpwuid fails.790 bool Result = llvm::sys::path::home_directory(HomeDir);791 if (Result) {792 fs::expand_tilde(HomeDir, Expected);793 794 fs::expand_tilde("~", Actual);795 EXPECT_EQ(Expected, Actual);796 797#ifdef _WIN32798 Expected += "\\foo";799 fs::expand_tilde("~\\foo", Actual);800#else801 Expected += "/foo";802 fs::expand_tilde("~/foo", Actual);803#endif804 805 EXPECT_EQ(Expected, Actual);806 }807}808 809#ifdef LLVM_ON_UNIX810TEST_F(FileSystemTest, RealPathNoReadPerm) {811 SmallString<64> Expanded;812 813 ASSERT_NO_ERROR(814 fs::create_directories(Twine(TestDirectory) + "/noreadperm"));815 ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/noreadperm"));816 817 fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::no_perms);818 fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::all_exe);819 820 ASSERT_NO_ERROR(fs::real_path(Twine(TestDirectory) + "/noreadperm", Expanded,821 false));822 823 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noreadperm"));824}825TEST_F(FileSystemTest, RemoveDirectoriesNoExePerm) {826 ASSERT_NO_ERROR(827 fs::create_directories(Twine(TestDirectory) + "/noexeperm/foo"));828 ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/noexeperm/foo"));829 830 fs::setPermissions(Twine(TestDirectory) + "/noexeperm",831 fs::all_read | fs::all_write);832 833 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noexeperm",834 /*IgnoreErrors=*/true));835 836 // It's expected that the directory exists, but some environments appear to837 // allow the removal despite missing the 'x' permission, so be flexible.838 if (fs::exists(Twine(TestDirectory) + "/noexeperm")) {839 fs::setPermissions(Twine(TestDirectory) + "/noexeperm", fs::all_perms);840 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noexeperm",841 /*IgnoreErrors=*/false));842 }843}844#endif845 846 847TEST_F(FileSystemTest, TempFileKeepDiscard) {848 // We can keep then discard.849 auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");850 ASSERT_TRUE((bool)TempFileOrError);851 fs::TempFile File = std::move(*TempFileOrError);852 ASSERT_EQ(-1, TempFileOrError->FD);853 ASSERT_FALSE((bool)File.keep(TestDirectory + "/keep"));854 ASSERT_FALSE((bool)File.discard());855 ASSERT_TRUE(fs::exists(TestDirectory + "/keep"));856 ASSERT_NO_ERROR(fs::remove(TestDirectory + "/keep"));857}858 859TEST_F(FileSystemTest, TempFileDiscardDiscard) {860 // We can discard twice.861 auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");862 ASSERT_TRUE((bool)TempFileOrError);863 fs::TempFile File = std::move(*TempFileOrError);864 ASSERT_EQ(-1, TempFileOrError->FD);865 ASSERT_FALSE((bool)File.discard());866 ASSERT_FALSE((bool)File.discard());867 ASSERT_FALSE(fs::exists(TestDirectory + "/keep"));868}869 870TEST_F(FileSystemTest, TempFiles) {871 // Create a temp file.872 int FileDescriptor;873 SmallString<64> TempPath;874 ASSERT_NO_ERROR(875 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));876 877 // Make sure it exists.878 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));879 880 // Create another temp tile.881 int FD2;882 SmallString<64> TempPath2;883 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD2, TempPath2));884 ASSERT_TRUE(TempPath2.ends_with(".temp"));885 ASSERT_NE(TempPath.str(), TempPath2.str());886 887 fs::file_status A, B;888 ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));889 ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));890 EXPECT_FALSE(fs::equivalent(A, B));891 892 ::close(FD2);893 894 // Remove Temp2.895 ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));896 ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));897 ASSERT_EQ(fs::remove(Twine(TempPath2), false),898 errc::no_such_file_or_directory);899 900 std::error_code EC = fs::status(TempPath2.c_str(), B);901 EXPECT_EQ(EC, errc::no_such_file_or_directory);902 EXPECT_EQ(B.type(), fs::file_type::file_not_found);903 904 // Make sure Temp2 doesn't exist.905 ASSERT_EQ(fs::access(Twine(TempPath2), sys::fs::AccessMode::Exist),906 errc::no_such_file_or_directory);907 908 SmallString<64> TempPath3;909 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "", TempPath3));910 ASSERT_FALSE(TempPath3.ends_with("."));911 FileRemover Cleanup3(TempPath3);912 913 // Create a hard link to Temp1.914 ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));915#ifndef _WIN32916 // FIXME: Our implementation of equivalent() on Windows doesn't consider hard917 // links to be the same file.918 bool equal;919 ASSERT_NO_ERROR(fs::equivalent(Twine(TempPath), Twine(TempPath2), equal));920 EXPECT_TRUE(equal);921 ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));922 ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));923 EXPECT_TRUE(fs::equivalent(A, B));924#endif925 926 // Remove Temp1.927 ::close(FileDescriptor);928 ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));929 930 // Remove the hard link.931 ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));932 933 // Make sure Temp1 doesn't exist.934 ASSERT_EQ(fs::access(Twine(TempPath), sys::fs::AccessMode::Exist),935 errc::no_such_file_or_directory);936 937#ifdef _WIN32938 // Path name > 260 chars should get an error.939 const char *Path270 =940 "abcdefghijklmnopqrstuvwxyz9abcdefghijklmnopqrstuvwxyz8"941 "abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6"942 "abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"943 "abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"944 "abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";945 EXPECT_EQ(fs::createUniqueFile(Path270, FileDescriptor, TempPath),946 errc::invalid_argument);947 // Relative path < 247 chars, no problem.948 const char *Path216 =949 "abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6"950 "abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"951 "abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"952 "abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";953 ASSERT_NO_ERROR(fs::createTemporaryFile(Path216, "", TempPath));954 ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));955#endif956}957 958TEST_F(FileSystemTest, TempFileCollisions) {959 SmallString<128> TestDirectory;960 ASSERT_NO_ERROR(961 fs::createUniqueDirectory("CreateUniqueFileTest", TestDirectory));962 FileRemover Cleanup(TestDirectory);963 SmallString<128> Model = TestDirectory;964 path::append(Model, "%.tmp");965 std::vector<fs::TempFile> TempFiles;966 967 auto TryCreateTempFile = [&]() {968 Expected<fs::TempFile> T = fs::TempFile::create(Model);969 if (T) {970 TempFiles.push_back(std::move(*T));971 return true;972 } else {973 logAllUnhandledErrors(T.takeError(), errs(),974 "Failed to create temporary file: ");975 return false;976 }977 };978 979 // Our single-character template allows for 16 unique names. Check that980 // calling TryCreateTempFile repeatedly results in 16 successes.981 // Because the test depends on random numbers, it could theoretically fail.982 // However, the probability of this happening is tiny: with 32 calls, each983 // of which will retry up to 128 times, to not get a given digit we would984 // have to fail at least 15 + 17 * 128 = 2191 attempts. The probability of985 // 2191 attempts not producing a given hexadecimal digit is986 // (1 - 1/16) ** 2191 or 3.88e-62.987 int Successes = 0;988 for (int i = 0; i < 32; ++i)989 if (TryCreateTempFile()) ++Successes;990 EXPECT_EQ(Successes, 16);991 992 for (fs::TempFile &T : TempFiles)993 cantFail(T.discard());994}995 996TEST_F(FileSystemTest, CreateDir) {997 ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));998 ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));999 ASSERT_EQ(fs::create_directory(Twine(TestDirectory) + "foo", false),1000 errc::file_exists);1001 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo"));1002 1003#ifdef LLVM_ON_UNIX1004 // Set a 0000 umask so that we can test our directory permissions.1005 mode_t OldUmask = ::umask(0000);1006 1007 fs::file_status Status;1008 ASSERT_NO_ERROR(1009 fs::create_directory(Twine(TestDirectory) + "baz500", false,1010 fs::perms::owner_read | fs::perms::owner_exe));1011 ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz500", Status));1012 ASSERT_EQ(Status.permissions() & fs::perms::all_all,1013 fs::perms::owner_read | fs::perms::owner_exe);1014 ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "baz777", false,1015 fs::perms::all_all));1016 ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz777", Status));1017 ASSERT_EQ(Status.permissions() & fs::perms::all_all, fs::perms::all_all);1018 1019 // Restore umask to be safe.1020 ::umask(OldUmask);1021#endif1022 1023#ifdef _WIN321024 // Prove that create_directories() can handle a pathname > 248 characters,1025 // which is the documented limit for CreateDirectory().1026 // (248 is MAX_PATH subtracting room for an 8.3 filename.)1027 // Generate a directory path guaranteed to fall into that range.1028 size_t TmpLen = TestDirectory.size();1029 const char *OneDir = "\\123456789";1030 size_t OneDirLen = strlen(OneDir);1031 ASSERT_LT(OneDirLen, 12U);1032 size_t NLevels = ((248 - TmpLen) / OneDirLen) + 1;1033 SmallString<260> LongDir(TestDirectory);1034 for (size_t I = 0; I < NLevels; ++I)1035 LongDir.append(OneDir);1036 ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));1037 ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));1038 ASSERT_EQ(fs::create_directories(Twine(LongDir), false),1039 errc::file_exists);1040 // Tidy up, "recursively" removing the directories.1041 StringRef ThisDir(LongDir);1042 for (size_t J = 0; J < NLevels; ++J) {1043 ASSERT_NO_ERROR(fs::remove(ThisDir));1044 ThisDir = path::parent_path(ThisDir);1045 }1046 1047 // Also verify that paths with Unix separators are handled correctly.1048 std::string LongPathWithUnixSeparators(TestDirectory.str());1049 // Add at least one subdirectory to TestDirectory, and replace slashes with1050 // backslashes1051 do {1052 LongPathWithUnixSeparators.append("/DirNameWith19Charss");1053 } while (LongPathWithUnixSeparators.size() < 260);1054 llvm::replace(LongPathWithUnixSeparators, '\\', '/');1055 ASSERT_NO_ERROR(fs::create_directories(Twine(LongPathWithUnixSeparators)));1056 // cleanup1057 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) +1058 "/DirNameWith19Charss"));1059 1060 // Similarly for a relative pathname. Need to set the current directory to1061 // TestDirectory so that the one we create ends up in the right place.1062 char PreviousDir[260];1063 size_t PreviousDirLen = ::GetCurrentDirectoryA(260, PreviousDir);1064 ASSERT_GT(PreviousDirLen, 0U);1065 ASSERT_LT(PreviousDirLen, 260U);1066 ASSERT_NE(::SetCurrentDirectoryA(TestDirectory.c_str()), 0);1067 LongDir.clear();1068 // Generate a relative directory name with absolute length > 248.1069 size_t LongDirLen = 249 - TestDirectory.size();1070 LongDir.assign(LongDirLen, 'a');1071 ASSERT_NO_ERROR(fs::create_directory(Twine(LongDir)));1072 // While we're here, prove that .. and . handling works in these long paths.1073 const char *DotDotDirs = "\\..\\.\\b";1074 LongDir.append(DotDotDirs);1075 ASSERT_NO_ERROR(fs::create_directory("b"));1076 ASSERT_EQ(fs::create_directory(Twine(LongDir), false), errc::file_exists);1077 // And clean up.1078 ASSERT_NO_ERROR(fs::remove("b"));1079 ASSERT_NO_ERROR(fs::remove(1080 Twine(LongDir.substr(0, LongDir.size() - strlen(DotDotDirs)))));1081 ASSERT_NE(::SetCurrentDirectoryA(PreviousDir), 0);1082#endif1083}1084 1085TEST_F(FileSystemTest, DirectoryIteration) {1086 std::error_code ec;1087 for (fs::directory_iterator i(".", ec), e; i != e; i.increment(ec))1088 ASSERT_NO_ERROR(ec);1089 1090 // Create a known hierarchy to recurse over.1091 ASSERT_NO_ERROR(1092 fs::create_directories(Twine(TestDirectory) + "/recursive/a0/aa1"));1093 ASSERT_NO_ERROR(1094 fs::create_directories(Twine(TestDirectory) + "/recursive/a0/ab1"));1095 ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) +1096 "/recursive/dontlookhere/da1"));1097 ASSERT_NO_ERROR(1098 fs::create_directories(Twine(TestDirectory) + "/recursive/z0/za1"));1099 ASSERT_NO_ERROR(1100 fs::create_directories(Twine(TestDirectory) + "/recursive/pop/p1"));1101 typedef std::vector<std::string> v_t;1102 v_t visited;1103 for (fs::recursive_directory_iterator i(Twine(TestDirectory)1104 + "/recursive", ec), e; i != e; i.increment(ec)){1105 ASSERT_NO_ERROR(ec);1106 if (path::filename(i->path()) == "p1") {1107 i.pop();1108 // FIXME: recursive_directory_iterator should be more robust.1109 if (i == e) break;1110 }1111 if (path::filename(i->path()) == "dontlookhere")1112 i.no_push();1113 visited.push_back(std::string(path::filename(i->path())));1114 }1115 v_t::const_iterator a0 = find(visited, "a0");1116 v_t::const_iterator aa1 = find(visited, "aa1");1117 v_t::const_iterator ab1 = find(visited, "ab1");1118 v_t::const_iterator dontlookhere = find(visited, "dontlookhere");1119 v_t::const_iterator da1 = find(visited, "da1");1120 v_t::const_iterator z0 = find(visited, "z0");1121 v_t::const_iterator za1 = find(visited, "za1");1122 v_t::const_iterator pop = find(visited, "pop");1123 v_t::const_iterator p1 = find(visited, "p1");1124 1125 // Make sure that each path was visited correctly.1126 ASSERT_NE(a0, visited.end());1127 ASSERT_NE(aa1, visited.end());1128 ASSERT_NE(ab1, visited.end());1129 ASSERT_NE(dontlookhere, visited.end());1130 ASSERT_EQ(da1, visited.end()); // Not visited.1131 ASSERT_NE(z0, visited.end());1132 ASSERT_NE(za1, visited.end());1133 ASSERT_NE(pop, visited.end());1134 ASSERT_EQ(p1, visited.end()); // Not visited.1135 1136 // Make sure that parents were visited before children. No other ordering1137 // guarantees can be made across siblings.1138 ASSERT_LT(a0, aa1);1139 ASSERT_LT(a0, ab1);1140 ASSERT_LT(z0, za1);1141 1142 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/aa1"));1143 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/ab1"));1144 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0"));1145 ASSERT_NO_ERROR(1146 fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere/da1"));1147 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere"));1148 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop/p1"));1149 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop"));1150 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0/za1"));1151 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0"));1152 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive"));1153 1154 // Test recursive_directory_iterator level()1155 ASSERT_NO_ERROR(1156 fs::create_directories(Twine(TestDirectory) + "/reclevel/a/b/c"));1157 fs::recursive_directory_iterator I(Twine(TestDirectory) + "/reclevel", ec), E;1158 for (int l = 0; I != E; I.increment(ec), ++l) {1159 ASSERT_NO_ERROR(ec);1160 EXPECT_EQ(I.level(), l);1161 }1162 EXPECT_EQ(I, E);1163 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b/c"));1164 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b"));1165 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a"));1166 ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel"));1167}1168 1169TEST_F(FileSystemTest, DirectoryNotExecutable) {1170 ASSERT_EQ(fs::access(TestDirectory, sys::fs::AccessMode::Execute),1171 errc::permission_denied);1172}1173 1174#ifdef LLVM_ON_UNIX1175TEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) {1176 // Create a known hierarchy to recurse over.1177 ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink"));1178 ASSERT_NO_ERROR(1179 fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/a"));1180 ASSERT_NO_ERROR(1181 fs::create_directories(Twine(TestDirectory) + "/symlink/b/bb"));1182 ASSERT_NO_ERROR(1183 fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/ba"));1184 ASSERT_NO_ERROR(1185 fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/bc"));1186 ASSERT_NO_ERROR(1187 fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/c"));1188 ASSERT_NO_ERROR(1189 fs::create_directories(Twine(TestDirectory) + "/symlink/d/dd/ddd"));1190 ASSERT_NO_ERROR(fs::create_link(Twine(TestDirectory) + "/symlink/d/dd",1191 Twine(TestDirectory) + "/symlink/d/da"));1192 ASSERT_NO_ERROR(1193 fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/e"));1194 1195 typedef std::vector<std::string> v_t;1196 v_t VisitedNonBrokenSymlinks;1197 v_t VisitedBrokenSymlinks;1198 std::error_code ec;1199 using testing::UnorderedElementsAre;1200 using testing::UnorderedElementsAreArray;1201 1202 // Broken symbol links are expected to throw an error.1203 for (fs::directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e;1204 i != e; i.increment(ec)) {1205 ASSERT_NO_ERROR(ec);1206 if (i->status().getError() ==1207 std::make_error_code(std::errc::no_such_file_or_directory)) {1208 VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));1209 continue;1210 }1211 VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));1212 }1213 EXPECT_THAT(VisitedNonBrokenSymlinks, UnorderedElementsAre("b", "d"));1214 VisitedNonBrokenSymlinks.clear();1215 1216 EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre("a", "c", "e"));1217 VisitedBrokenSymlinks.clear();1218 1219 // Broken symbol links are expected to throw an error.1220 for (fs::recursive_directory_iterator i(1221 Twine(TestDirectory) + "/symlink", ec), e; i != e; i.increment(ec)) {1222 ASSERT_NO_ERROR(ec);1223 if (i->status().getError() ==1224 std::make_error_code(std::errc::no_such_file_or_directory)) {1225 VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));1226 continue;1227 }1228 VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));1229 }1230 EXPECT_THAT(VisitedNonBrokenSymlinks,1231 UnorderedElementsAre("b", "bb", "d", "da", "dd", "ddd", "ddd"));1232 VisitedNonBrokenSymlinks.clear();1233 1234 EXPECT_THAT(VisitedBrokenSymlinks,1235 UnorderedElementsAre("a", "ba", "bc", "c", "e"));1236 VisitedBrokenSymlinks.clear();1237 1238 for (fs::recursive_directory_iterator i(1239 Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false), e;1240 i != e; i.increment(ec)) {1241 ASSERT_NO_ERROR(ec);1242 if (i->status().getError() ==1243 std::make_error_code(std::errc::no_such_file_or_directory)) {1244 VisitedBrokenSymlinks.push_back(std::string(path::filename(i->path())));1245 continue;1246 }1247 VisitedNonBrokenSymlinks.push_back(std::string(path::filename(i->path())));1248 }1249 EXPECT_THAT(VisitedNonBrokenSymlinks,1250 UnorderedElementsAreArray({"a", "b", "ba", "bb", "bc", "c", "d",1251 "da", "dd", "ddd", "e"}));1252 VisitedNonBrokenSymlinks.clear();1253 1254 EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre());1255 VisitedBrokenSymlinks.clear();1256 1257 ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/symlink"));1258}1259#endif1260 1261#ifdef _WIN321262TEST_F(FileSystemTest, UTF8ToUTF16DirectoryIteration) {1263 // The Windows filesystem support uses UTF-16 and converts paths from the1264 // input UTF-8. The UTF-16 equivalent of the input path can be shorter in1265 // length.1266 1267 // This test relies on TestDirectory not being so long such that MAX_PATH1268 // would be exceeded (see widenPath). If that were the case, the UTF-161269 // path is likely to be longer than the input.1270 const char *Pi = "\xcf\x80"; // UTF-8 lower case pi.1271 std::string RootDir = (TestDirectory + "/" + Pi).str();1272 1273 // Create test directories.1274 ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/a"));1275 ASSERT_NO_ERROR(fs::create_directories(Twine(RootDir) + "/b"));1276 1277 std::error_code EC;1278 unsigned Count = 0;1279 for (fs::directory_iterator I(Twine(RootDir), EC), E; I != E;1280 I.increment(EC)) {1281 ASSERT_NO_ERROR(EC);1282 StringRef DirName = path::filename(I->path());1283 EXPECT_TRUE(DirName == "a" || DirName == "b");1284 ++Count;1285 }1286 EXPECT_EQ(Count, 2U);1287 1288 ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/a"));1289 ASSERT_NO_ERROR(fs::remove(Twine(RootDir) + "/b"));1290 ASSERT_NO_ERROR(fs::remove(Twine(RootDir)));1291}1292#endif1293 1294TEST_F(FileSystemTest, Remove) {1295 SmallString<64> BaseDir;1296 SmallString<64> Paths[4];1297 int fds[4];1298 ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir));1299 1300 ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz"));1301 ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz"));1302 ASSERT_NO_ERROR(fs::createUniqueFile(1303 Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0]));1304 ASSERT_NO_ERROR(fs::createUniqueFile(1305 Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1]));1306 ASSERT_NO_ERROR(fs::createUniqueFile(1307 Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2]));1308 ASSERT_NO_ERROR(fs::createUniqueFile(1309 Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3]));1310 1311 for (int fd : fds)1312 ::close(fd);1313 1314 EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz"));1315 EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz"));1316 EXPECT_TRUE(fs::exists(Paths[0]));1317 EXPECT_TRUE(fs::exists(Paths[1]));1318 EXPECT_TRUE(fs::exists(Paths[2]));1319 EXPECT_TRUE(fs::exists(Paths[3]));1320 1321 ASSERT_NO_ERROR(fs::remove_directories("D:/footest"));1322 1323 ASSERT_NO_ERROR(fs::remove_directories(Twine(BaseDir) + "/foo/bar/baz"));1324 ASSERT_FALSE(fs::exists(Twine(BaseDir) + "/foo/bar/baz"));1325 1326 ASSERT_NO_ERROR(fs::remove_directories(BaseDir));1327 ASSERT_FALSE(fs::exists(BaseDir));1328}1329 1330#ifdef _WIN321331TEST_F(FileSystemTest, CarriageReturn) {1332 SmallString<128> FilePathname(TestDirectory);1333 std::error_code EC;1334 path::append(FilePathname, "test");1335 1336 {1337 raw_fd_ostream File(FilePathname, EC, sys::fs::OF_TextWithCRLF);1338 ASSERT_NO_ERROR(EC);1339 File << '\n';1340 }1341 {1342 auto Buf = MemoryBuffer::getFile(FilePathname.str());1343 EXPECT_TRUE((bool)Buf);1344 EXPECT_EQ(Buf.get()->getBuffer(), "\r\n");1345 }1346 1347 {1348 raw_fd_ostream File(FilePathname, EC, sys::fs::OF_None);1349 ASSERT_NO_ERROR(EC);1350 File << '\n';1351 }1352 {1353 auto Buf = MemoryBuffer::getFile(FilePathname.str());1354 EXPECT_TRUE((bool)Buf);1355 EXPECT_EQ(Buf.get()->getBuffer(), "\n");1356 }1357 ASSERT_NO_ERROR(fs::remove(Twine(FilePathname)));1358}1359#endif1360 1361TEST_F(FileSystemTest, Resize) {1362 int FD;1363 SmallString<64> TempPath;1364 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));1365 ASSERT_NO_ERROR(fs::resize_file(FD, 123));1366 fs::file_status Status;1367 ASSERT_NO_ERROR(fs::status(FD, Status));1368 ASSERT_EQ(Status.getSize(), 123U);1369 ::close(FD);1370 ASSERT_NO_ERROR(fs::remove(TempPath));1371}1372 1373TEST_F(FileSystemTest, ResizeBeforeMapping) {1374 // Create a temp file.1375 int FD;1376 SmallString<64> TempPath;1377 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));1378 ASSERT_NO_ERROR(fs::resize_file_before_mapping_readwrite(FD, 123));1379 1380 // Map in temp file. On Windows, fs::resize_file_before_mapping_readwrite is1381 // a no-op and the mapping itself will resize the file.1382 std::error_code EC;1383 {1384 fs::mapped_file_region mfr(fs::convertFDToNativeFile(FD),1385 fs::mapped_file_region::readwrite, 123, 0, EC);1386 ASSERT_NO_ERROR(EC);1387 // Unmap temp file1388 }1389 1390 // Check the size.1391 fs::file_status Status;1392 ASSERT_NO_ERROR(fs::status(FD, Status));1393 ASSERT_EQ(Status.getSize(), 123U);1394 ::close(FD);1395 ASSERT_NO_ERROR(fs::remove(TempPath));1396}1397 1398TEST_F(FileSystemTest, MD5) {1399 int FD;1400 SmallString<64> TempPath;1401 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));1402 StringRef Data("abcdefghijklmnopqrstuvwxyz");1403 ASSERT_EQ(write(FD, Data.data(), Data.size()), static_cast<ssize_t>(Data.size()));1404 lseek(FD, 0, SEEK_SET);1405 auto Hash = fs::md5_contents(FD);1406 ::close(FD);1407 ASSERT_NO_ERROR(Hash.getError());1408 1409 EXPECT_STREQ("c3fcd3d76192e4007dfb496cca67e13b", Hash->digest().c_str());1410}1411 1412TEST_F(FileSystemTest, FileMapping) {1413 // Create a temp file.1414 int FileDescriptor;1415 SmallString<64> TempPath;1416 ASSERT_NO_ERROR(1417 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));1418 unsigned Size = 4096;1419 ASSERT_NO_ERROR(1420 fs::resize_file_before_mapping_readwrite(FileDescriptor, Size));1421 1422 // Map in temp file and add some content1423 std::error_code EC;1424 StringRef Val("hello there");1425 fs::mapped_file_region MaybeMFR;1426 EXPECT_FALSE(MaybeMFR);1427 {1428 fs::mapped_file_region mfr(fs::convertFDToNativeFile(FileDescriptor),1429 fs::mapped_file_region::readwrite, Size, 0, EC);1430 ASSERT_NO_ERROR(EC);1431 llvm::copy(Val, mfr.data());1432 // Explicitly add a 0.1433 mfr.data()[Val.size()] = 0;1434 1435 // Move it out of the scope and confirm mfr is reset.1436 MaybeMFR = std::move(mfr);1437 EXPECT_FALSE(mfr);1438#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST1439 EXPECT_DEATH(mfr.data(), "Mapping failed but used anyway!");1440 EXPECT_DEATH(mfr.size(), "Mapping failed but used anyway!");1441#endif1442 }1443 1444 // Check that the moved-to region is still valid.1445 EXPECT_EQ(Val, StringRef(MaybeMFR.data()));1446 EXPECT_EQ(Size, MaybeMFR.size());1447 1448 // Unmap temp file.1449 MaybeMFR.unmap();1450 1451 ASSERT_EQ(close(FileDescriptor), 0);1452 1453 // Map it back in read-only1454 {1455 int FD;1456 EC = fs::openFileForRead(Twine(TempPath), FD);1457 ASSERT_NO_ERROR(EC);1458 fs::mapped_file_region mfr(fs::convertFDToNativeFile(FD),1459 fs::mapped_file_region::readonly, Size, 0, EC);1460 ASSERT_NO_ERROR(EC);1461 1462 // Verify content1463 EXPECT_EQ(StringRef(mfr.const_data()), Val);1464 1465 // Unmap temp file1466 fs::mapped_file_region m(fs::convertFDToNativeFile(FD),1467 fs::mapped_file_region::readonly, Size, 0, EC);1468 ASSERT_NO_ERROR(EC);1469 ASSERT_EQ(close(FD), 0);1470 }1471 ASSERT_NO_ERROR(fs::remove(TempPath));1472}1473 1474TEST_F(FileSystemTest, FileMappingSync) {1475 // Create a temp file.1476 SmallString<0> TempPath(TestDirectory);1477 sys::path::append(TempPath, "test-%%%%");1478 auto TempFileOrError = fs::TempFile::create(TempPath);1479 ASSERT_TRUE((bool)TempFileOrError);1480 fs::TempFile File = std::move(*TempFileOrError);1481 StringRef Content("hello there");1482 std::string FileName = File.TmpName;1483 ASSERT_NO_ERROR(1484 fs::resize_file_before_mapping_readwrite(File.FD, Content.size()));1485 {1486 // Map in the file and write some content.1487 std::error_code EC;1488 fs::mapped_file_region MFR(fs::convertFDToNativeFile(File.FD),1489 fs::mapped_file_region::readwrite,1490 Content.size(), 0, EC);1491 1492 // Keep the file so it can be read.1493 ASSERT_FALSE((bool)File.keep());1494 1495 // Write content through mapped memory.1496 ASSERT_NO_ERROR(EC);1497 llvm::copy(Content, MFR.data());1498 1499 // Synchronize to file system.1500 ASSERT_FALSE((bool)MFR.sync());1501 1502 // Check the file content using file IO APIs.1503 auto Buffer = MemoryBuffer::getFile(FileName);1504 ASSERT_TRUE((bool)Buffer);1505 ASSERT_EQ(Content, Buffer->get()->getBuffer());1506 }1507 // Manually remove the test file.1508 ASSERT_FALSE((bool)fs::remove(FileName));1509}1510 1511TEST(Support, NormalizePath) {1512 // Input, Expected Win, Expected Posix1513 using TestTuple = std::tuple<const char *, const char *, const char *>;1514 std::vector<TestTuple> Tests;1515 Tests.emplace_back("a", "a", "a");1516 Tests.emplace_back("a/b", "a\\b", "a/b");1517 Tests.emplace_back("a\\b", "a\\b", "a/b");1518 Tests.emplace_back("a\\\\b", "a\\\\b", "a//b");1519 Tests.emplace_back("\\a", "\\a", "/a");1520 Tests.emplace_back("a\\", "a\\", "a/");1521 Tests.emplace_back("a\\t", "a\\t", "a/t");1522 1523 for (auto &T : Tests) {1524 SmallString<64> Win(std::get<0>(T));1525 SmallString<64> Posix(Win);1526 SmallString<64> WinSlash(Win);1527 path::native(Win, path::Style::windows);1528 path::native(Posix, path::Style::posix);1529 path::native(WinSlash, path::Style::windows_slash);1530 EXPECT_EQ(std::get<1>(T), Win);1531 EXPECT_EQ(std::get<2>(T), Posix);1532 EXPECT_EQ(std::get<2>(T), WinSlash);1533 }1534 1535 for (auto &T : Tests) {1536 SmallString<64> WinBackslash(std::get<0>(T));1537 SmallString<64> Posix(WinBackslash);1538 SmallString<64> WinSlash(WinBackslash);1539 path::make_preferred(WinBackslash, path::Style::windows_backslash);1540 path::make_preferred(Posix, path::Style::posix);1541 path::make_preferred(WinSlash, path::Style::windows_slash);1542 EXPECT_EQ(std::get<1>(T), WinBackslash);1543 EXPECT_EQ(std::get<0>(T), Posix); // Posix remains unchanged here1544 EXPECT_EQ(std::get<2>(T), WinSlash);1545 }1546 1547#if defined(_WIN32)1548 SmallString<64> PathHome;1549 path::home_directory(PathHome);1550 1551 const char *Path7a = "~/aaa";1552 SmallString<64> Path7(Path7a);1553 path::native(Path7, path::Style::windows_backslash);1554 EXPECT_TRUE(Path7.ends_with("\\aaa"));1555 EXPECT_TRUE(Path7.starts_with(PathHome));1556 EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));1557 Path7 = Path7a;1558 path::native(Path7, path::Style::windows_slash);1559 EXPECT_TRUE(Path7.ends_with("/aaa"));1560 EXPECT_TRUE(Path7.starts_with(PathHome));1561 EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));1562 1563 const char *Path8a = "~";1564 SmallString<64> Path8(Path8a);1565 path::native(Path8);1566 EXPECT_EQ(Path8, PathHome);1567 1568 const char *Path9a = "~aaa";1569 SmallString<64> Path9(Path9a);1570 path::native(Path9);1571 EXPECT_EQ(Path9, "~aaa");1572 1573 const char *Path10a = "aaa/~/b";1574 SmallString<64> Path10(Path10a);1575 path::native(Path10, path::Style::windows_backslash);1576 EXPECT_EQ(Path10, "aaa\\~\\b");1577#endif1578}1579 1580TEST(Support, RemoveLeadingDotSlash) {1581 StringRef Path1("././/foolz/wat");1582 StringRef Path2("./////");1583 1584 Path1 = path::remove_leading_dotslash(Path1);1585 EXPECT_EQ(Path1, "foolz/wat");1586 Path2 = path::remove_leading_dotslash(Path2);1587 EXPECT_EQ(Path2, "");1588}1589 1590static std::string remove_dots(StringRef path, bool remove_dot_dot,1591 path::Style style) {1592 SmallString<256> buffer(path);1593 path::remove_dots(buffer, remove_dot_dot, style);1594 return std::string(buffer.str());1595}1596 1597TEST(Support, RemoveDots) {1598 EXPECT_EQ("foolz\\wat",1599 remove_dots(".\\.\\\\foolz\\wat", false, path::Style::windows));1600 EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false, path::Style::windows));1601 1602 EXPECT_EQ("a\\..\\b\\c",1603 remove_dots(".\\a\\..\\b\\c", false, path::Style::windows));1604 EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true, path::Style::windows));1605 EXPECT_EQ("c", remove_dots(".\\.\\c", true, path::Style::windows));1606 EXPECT_EQ("..\\a\\c",1607 remove_dots("..\\a\\b\\..\\c", true, path::Style::windows));1608 EXPECT_EQ("..\\..\\a\\c",1609 remove_dots("..\\..\\a\\b\\..\\c", true, path::Style::windows));1610 EXPECT_EQ("C:\\a\\c", remove_dots("C:\\foo\\bar//..\\..\\a\\c", true,1611 path::Style::windows));1612 1613 EXPECT_EQ("C:\\bar",1614 remove_dots("C:/foo/../bar", true, path::Style::windows));1615 EXPECT_EQ("C:\\foo\\bar",1616 remove_dots("C:/foo/bar", true, path::Style::windows));1617 EXPECT_EQ("C:\\foo\\bar",1618 remove_dots("C:/foo\\bar", true, path::Style::windows));1619 EXPECT_EQ("\\", remove_dots("/", true, path::Style::windows));1620 EXPECT_EQ("C:\\", remove_dots("C:/", true, path::Style::windows));1621 1622 // Some clients of remove_dots expect it to remove trailing slashes. Again,1623 // this is emergent behavior that VFS relies on, and not inherently part of1624 // the specification.1625 EXPECT_EQ("C:\\foo\\bar",1626 remove_dots("C:\\foo\\bar\\", true, path::Style::windows));1627 EXPECT_EQ("/foo/bar",1628 remove_dots("/foo/bar/", true, path::Style::posix));1629 1630 // A double separator is rewritten.1631 EXPECT_EQ("C:\\foo\\bar",1632 remove_dots("C:/foo//bar", true, path::Style::windows));1633 1634 SmallString<64> Path1(".\\.\\c");1635 EXPECT_TRUE(path::remove_dots(Path1, true, path::Style::windows));1636 EXPECT_EQ("c", Path1);1637 1638 EXPECT_EQ("foolz/wat",1639 remove_dots("././/foolz/wat", false, path::Style::posix));1640 EXPECT_EQ("", remove_dots("./////", false, path::Style::posix));1641 1642 EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false, path::Style::posix));1643 EXPECT_EQ("b/c", remove_dots("./a/../b/c", true, path::Style::posix));1644 EXPECT_EQ("c", remove_dots("././c", true, path::Style::posix));1645 EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true, path::Style::posix));1646 EXPECT_EQ("../../a/c",1647 remove_dots("../../a/b/../c", true, path::Style::posix));1648 EXPECT_EQ("/a/c", remove_dots("/../../a/c", true, path::Style::posix));1649 EXPECT_EQ("/a/c",1650 remove_dots("/../a/b//../././/c", true, path::Style::posix));1651 EXPECT_EQ("/", remove_dots("/", true, path::Style::posix));1652 1653 // FIXME: Leaving behind this double leading slash seems like a bug.1654 EXPECT_EQ("//foo/bar",1655 remove_dots("//foo/bar/", true, path::Style::posix));1656 1657 SmallString<64> Path2("././c");1658 EXPECT_TRUE(path::remove_dots(Path2, true, path::Style::posix));1659 EXPECT_EQ("c", Path2);1660}1661 1662TEST(Support, ReplacePathPrefix) {1663 SmallString<64> Path1("/foo");1664 SmallString<64> Path2("/old/foo");1665 SmallString<64> Path3("/oldnew/foo");1666 SmallString<64> Path4("C:\\old/foo\\bar");1667 SmallString<64> OldPrefix("/old");1668 SmallString<64> OldPrefixSep("/old/");1669 SmallString<64> OldPrefixWin("c:/oLD/F");1670 SmallString<64> NewPrefix("/new");1671 SmallString<64> NewPrefix2("/longernew");1672 SmallString<64> EmptyPrefix("");1673 bool Found;1674 1675 SmallString<64> Path = Path1;1676 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);1677 EXPECT_FALSE(Found);1678 EXPECT_EQ(Path, "/foo");1679 Path = Path2;1680 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);1681 EXPECT_TRUE(Found);1682 EXPECT_EQ(Path, "/new/foo");1683 Path = Path2;1684 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2);1685 EXPECT_TRUE(Found);1686 EXPECT_EQ(Path, "/longernew/foo");1687 Path = Path1;1688 Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);1689 EXPECT_TRUE(Found);1690 EXPECT_EQ(Path, "/new/foo");1691 Path = Path2;1692 Found = path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);1693 EXPECT_TRUE(Found);1694 EXPECT_EQ(Path, "/foo");1695 Path = Path2;1696 Found = path::replace_path_prefix(Path, OldPrefixSep, EmptyPrefix);1697 EXPECT_TRUE(Found);1698 EXPECT_EQ(Path, "foo");1699 Path = Path3;1700 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);1701 EXPECT_TRUE(Found);1702 EXPECT_EQ(Path, "/newnew/foo");1703 Path = Path3;1704 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix2);1705 EXPECT_TRUE(Found);1706 EXPECT_EQ(Path, "/longernewnew/foo");1707 Path = Path1;1708 Found = path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);1709 EXPECT_TRUE(Found);1710 EXPECT_EQ(Path, "/new/foo");1711 Path = OldPrefix;1712 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);1713 EXPECT_TRUE(Found);1714 EXPECT_EQ(Path, "/new");1715 Path = OldPrefixSep;1716 Found = path::replace_path_prefix(Path, OldPrefix, NewPrefix);1717 EXPECT_TRUE(Found);1718 EXPECT_EQ(Path, "/new/");1719 Path = OldPrefix;1720 Found = path::replace_path_prefix(Path, OldPrefixSep, NewPrefix);1721 EXPECT_FALSE(Found);1722 EXPECT_EQ(Path, "/old");1723 Path = Path4;1724 Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix,1725 path::Style::windows);1726 EXPECT_TRUE(Found);1727 EXPECT_EQ(Path, "/newoo\\bar");1728 Path = Path4;1729 Found = path::replace_path_prefix(Path, OldPrefixWin, NewPrefix,1730 path::Style::posix);1731 EXPECT_FALSE(Found);1732 EXPECT_EQ(Path, "C:\\old/foo\\bar");1733}1734 1735TEST_F(FileSystemTest, OpenFileForRead) {1736 // Create a temp file.1737 int FileDescriptor;1738 SmallString<64> TempPath;1739 ASSERT_NO_ERROR(1740 fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));1741 FileRemover Cleanup(TempPath);1742 1743 // Make sure it exists.1744 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));1745 1746 // Open the file for read1747 int FileDescriptor2;1748 SmallString<64> ResultPath;1749 ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor2,1750 fs::OF_None, &ResultPath))1751 1752 // If we succeeded, check that the paths are the same (modulo case):1753 if (!ResultPath.empty()) {1754 // The paths returned by createTemporaryFile and getPathFromOpenFD1755 // should reference the same file on disk.1756 fs::UniqueID D1, D2;1757 ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));1758 ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));1759 ASSERT_EQ(D1, D2);1760 }1761 ::close(FileDescriptor);1762 ::close(FileDescriptor2);1763 1764#ifdef _WIN321765 // Since Windows Vista, file access time is not updated by default.1766 // This is instead updated manually by openFileForRead.1767 // https://blogs.technet.microsoft.com/filecab/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance/1768 // This part of the unit test is Windows specific as the updating of1769 // access times can be disabled on Linux using /etc/fstab.1770 1771 // Set access time to UNIX epoch.1772 ASSERT_NO_ERROR(sys::fs::openFileForWrite(Twine(TempPath), FileDescriptor,1773 fs::CD_OpenExisting));1774 TimePoint<> Epoch(std::chrono::milliseconds(0));1775 ASSERT_NO_ERROR(fs::setLastAccessAndModificationTime(FileDescriptor, Epoch));1776 ::close(FileDescriptor);1777 1778 // Open the file and ensure access time is updated, when forced.1779 ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor,1780 fs::OF_UpdateAtime, &ResultPath));1781 1782 sys::fs::file_status Status;1783 ASSERT_NO_ERROR(sys::fs::status(FileDescriptor, Status));1784 auto FileAccessTime = Status.getLastAccessedTime();1785 1786 ASSERT_NE(Epoch, FileAccessTime);1787 ::close(FileDescriptor);1788 1789 // Ideally this test would include a case when ATime is not forced to update,1790 // however the expected behaviour will differ depending on the configuration1791 // of the Windows file system.1792#endif1793}1794 1795TEST_F(FileSystemTest, OpenDirectoryAsFileForRead) {1796 std::string Buf(5, '?');1797 Expected<fs::file_t> FD = fs::openNativeFileForRead(TestDirectory);1798#ifdef _WIN321799 EXPECT_EQ(errorToErrorCode(FD.takeError()), errc::is_a_directory);1800#else1801 ASSERT_THAT_EXPECTED(FD, Succeeded());1802 auto Close = make_scope_exit([&] { fs::closeFile(*FD); });1803 Expected<size_t> BytesRead =1804 fs::readNativeFile(*FD, MutableArrayRef(&*Buf.begin(), Buf.size()));1805 EXPECT_EQ(errorToErrorCode(BytesRead.takeError()), errc::is_a_directory);1806#endif1807}1808 1809TEST_F(FileSystemTest, OpenDirectoryAsFileForWrite) {1810 int FD;1811 std::error_code EC = fs::openFileForWrite(Twine(TestDirectory), FD);1812 if (!EC)1813 ::close(FD);1814 EXPECT_EQ(EC, errc::is_a_directory);1815}1816 1817static void createFileWithData(const Twine &Path, bool ShouldExistBefore,1818 fs::CreationDisposition Disp, StringRef Data) {1819 int FD;1820 ASSERT_EQ(ShouldExistBefore, fs::exists(Path));1821 ASSERT_NO_ERROR(fs::openFileForWrite(Path, FD, Disp));1822 FileDescriptorCloser Closer(FD);1823 ASSERT_TRUE(fs::exists(Path));1824 1825 ASSERT_EQ(Data.size(), (size_t)write(FD, Data.data(), Data.size()));1826}1827 1828static void verifyFileContents(const Twine &Path, StringRef Contents) {1829 auto Buffer = MemoryBuffer::getFile(Path);1830 ASSERT_TRUE((bool)Buffer);1831 StringRef Data = Buffer.get()->getBuffer();1832 ASSERT_EQ(Data, Contents);1833}1834 1835TEST_F(FileSystemTest, CreateNew) {1836 int FD;1837 std::optional<FileDescriptorCloser> Closer;1838 1839 // Succeeds if the file does not exist.1840 ASSERT_FALSE(fs::exists(NonExistantFile));1841 ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));1842 ASSERT_TRUE(fs::exists(NonExistantFile));1843 1844 FileRemover Cleanup(NonExistantFile);1845 Closer.emplace(FD);1846 1847 // And creates a file of size 0.1848 sys::fs::file_status Status;1849 ASSERT_NO_ERROR(sys::fs::status(FD, Status));1850 EXPECT_EQ(0ULL, Status.getSize());1851 1852 // Close this first, before trying to re-open the file.1853 Closer.reset();1854 1855 // But fails if the file does exist.1856 ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));1857}1858 1859TEST_F(FileSystemTest, CreateAlways) {1860 int FD;1861 std::optional<FileDescriptorCloser> Closer;1862 1863 // Succeeds if the file does not exist.1864 ASSERT_FALSE(fs::exists(NonExistantFile));1865 ASSERT_NO_ERROR(1866 fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));1867 1868 Closer.emplace(FD);1869 1870 ASSERT_TRUE(fs::exists(NonExistantFile));1871 1872 FileRemover Cleanup(NonExistantFile);1873 1874 // And creates a file of size 0.1875 uint64_t FileSize;1876 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1877 ASSERT_EQ(0ULL, FileSize);1878 1879 // If we write some data to it re-create it with CreateAlways, it succeeds and1880 // truncates to 0 bytes.1881 ASSERT_EQ(4, write(FD, "Test", 4));1882 1883 Closer.reset();1884 1885 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1886 ASSERT_EQ(4ULL, FileSize);1887 1888 ASSERT_NO_ERROR(1889 fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));1890 Closer.emplace(FD);1891 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1892 ASSERT_EQ(0ULL, FileSize);1893}1894 1895TEST_F(FileSystemTest, OpenExisting) {1896 int FD;1897 1898 // Fails if the file does not exist.1899 ASSERT_FALSE(fs::exists(NonExistantFile));1900 ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));1901 ASSERT_FALSE(fs::exists(NonExistantFile));1902 1903 // Make a dummy file now so that we can try again when the file does exist.1904 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");1905 FileRemover Cleanup(NonExistantFile);1906 uint64_t FileSize;1907 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1908 ASSERT_EQ(4ULL, FileSize);1909 1910 // If we re-create it with different data, it overwrites rather than1911 // appending.1912 createFileWithData(NonExistantFile, true, fs::CD_OpenExisting, "Buzz");1913 verifyFileContents(NonExistantFile, "Buzz");1914}1915 1916TEST_F(FileSystemTest, OpenAlways) {1917 // Succeeds if the file does not exist.1918 createFileWithData(NonExistantFile, false, fs::CD_OpenAlways, "Fizz");1919 FileRemover Cleanup(NonExistantFile);1920 uint64_t FileSize;1921 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1922 ASSERT_EQ(4ULL, FileSize);1923 1924 // Now re-open it and write again, verifying the contents get over-written.1925 createFileWithData(NonExistantFile, true, fs::CD_OpenAlways, "Bu");1926 verifyFileContents(NonExistantFile, "Buzz");1927}1928 1929TEST_F(FileSystemTest, AppendSetsCorrectFileOffset) {1930 fs::CreationDisposition Disps[] = {fs::CD_CreateAlways, fs::CD_OpenAlways,1931 fs::CD_OpenExisting};1932 1933 // Write some data and re-open it with every possible disposition (this is a1934 // hack that shouldn't work, but is left for compatibility. OF_Append1935 // overrides1936 // the specified disposition.1937 for (fs::CreationDisposition Disp : Disps) {1938 int FD;1939 std::optional<FileDescriptorCloser> Closer;1940 1941 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");1942 1943 FileRemover Cleanup(NonExistantFile);1944 1945 uint64_t FileSize;1946 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1947 ASSERT_EQ(4ULL, FileSize);1948 ASSERT_NO_ERROR(1949 fs::openFileForWrite(NonExistantFile, FD, Disp, fs::OF_Append));1950 Closer.emplace(FD);1951 ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));1952 ASSERT_EQ(4ULL, FileSize);1953 1954 ASSERT_EQ(4, write(FD, "Buzz", 4));1955 Closer.reset();1956 1957 verifyFileContents(NonExistantFile, "FizzBuzz");1958 }1959}1960 1961static void verifyRead(int FD, StringRef Data, bool ShouldSucceed) {1962 std::vector<char> Buffer;1963 Buffer.resize(Data.size());1964 int Result = ::read(FD, Buffer.data(), Buffer.size());1965 if (ShouldSucceed) {1966 ASSERT_EQ((size_t)Result, Data.size());1967 ASSERT_EQ(Data, StringRef(Buffer.data(), Buffer.size()));1968 } else {1969 ASSERT_EQ(-1, Result);1970 ASSERT_EQ(EBADF, errno);1971 }1972}1973 1974static void verifyWrite(int FD, StringRef Data, bool ShouldSucceed) {1975 int Result = ::write(FD, Data.data(), Data.size());1976 if (ShouldSucceed)1977 ASSERT_EQ((size_t)Result, Data.size());1978 else {1979 ASSERT_EQ(-1, Result);1980 ASSERT_EQ(EBADF, errno);1981 }1982}1983 1984TEST_F(FileSystemTest, ReadOnlyFileCantWrite) {1985 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");1986 FileRemover Cleanup(NonExistantFile);1987 1988 int FD;1989 ASSERT_NO_ERROR(fs::openFileForRead(NonExistantFile, FD));1990 FileDescriptorCloser Closer(FD);1991 1992 verifyWrite(FD, "Buzz", false);1993 verifyRead(FD, "Fizz", true);1994}1995 1996TEST_F(FileSystemTest, WriteOnlyFileCantRead) {1997 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");1998 FileRemover Cleanup(NonExistantFile);1999 2000 int FD;2001 ASSERT_NO_ERROR(2002 fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));2003 FileDescriptorCloser Closer(FD);2004 verifyRead(FD, "Fizz", false);2005 verifyWrite(FD, "Buzz", true);2006}2007 2008TEST_F(FileSystemTest, ReadWriteFileCanReadOrWrite) {2009 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");2010 FileRemover Cleanup(NonExistantFile);2011 2012 int FD;2013 ASSERT_NO_ERROR(fs::openFileForReadWrite(NonExistantFile, FD,2014 fs::CD_OpenExisting, fs::OF_None));2015 FileDescriptorCloser Closer(FD);2016 verifyRead(FD, "Fizz", true);2017 verifyWrite(FD, "Buzz", true);2018}2019 2020TEST_F(FileSystemTest, readNativeFile) {2021 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "01234");2022 FileRemover Cleanup(NonExistantFile);2023 const auto &Read = [&](size_t ToRead) -> Expected<std::string> {2024 std::string Buf(ToRead, '?');2025 Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);2026 if (!FD)2027 return FD.takeError();2028 auto Close = make_scope_exit([&] { fs::closeFile(*FD); });2029 if (Expected<size_t> BytesRead = fs::readNativeFile(2030 *FD, MutableArrayRef(&*Buf.begin(), Buf.size())))2031 return Buf.substr(0, *BytesRead);2032 else2033 return BytesRead.takeError();2034 };2035 EXPECT_THAT_EXPECTED(Read(5), HasValue("01234"));2036 EXPECT_THAT_EXPECTED(Read(3), HasValue("012"));2037 EXPECT_THAT_EXPECTED(Read(6), HasValue("01234"));2038}2039 2040TEST_F(FileSystemTest, readNativeFileToEOF) {2041 constexpr StringLiteral Content = "0123456789";2042 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, Content);2043 FileRemover Cleanup(NonExistantFile);2044 const auto &Read = [&](SmallVectorImpl<char> &V,2045 std::optional<ssize_t> ChunkSize) {2046 Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);2047 if (!FD)2048 return FD.takeError();2049 auto Close = make_scope_exit([&] { fs::closeFile(*FD); });2050 if (ChunkSize)2051 return fs::readNativeFileToEOF(*FD, V, *ChunkSize);2052 return fs::readNativeFileToEOF(*FD, V);2053 };2054 2055 // Check basic operation.2056 {2057 SmallString<0> NoSmall;2058 SmallString<fs::DefaultReadChunkSize + Content.size()> StaysSmall;2059 SmallVectorImpl<char> *Vectors[] = {2060 static_cast<SmallVectorImpl<char> *>(&NoSmall),2061 static_cast<SmallVectorImpl<char> *>(&StaysSmall),2062 };2063 for (SmallVectorImpl<char> *V : Vectors) {2064 ASSERT_THAT_ERROR(Read(*V, std::nullopt), Succeeded());2065 ASSERT_EQ(Content, StringRef(V->begin(), V->size()));2066 }2067 ASSERT_EQ(fs::DefaultReadChunkSize + Content.size(), StaysSmall.capacity());2068 2069 // Check appending.2070 {2071 constexpr StringLiteral Prefix = "prefix-";2072 for (SmallVectorImpl<char> *V : Vectors) {2073 V->assign(Prefix.begin(), Prefix.end());2074 ASSERT_THAT_ERROR(Read(*V, std::nullopt), Succeeded());2075 ASSERT_EQ((Prefix + Content).str(), StringRef(V->begin(), V->size()));2076 }2077 }2078 }2079 2080 // Check that the chunk size (if specified) is respected.2081 SmallString<Content.size() + 5> SmallChunks;2082 ASSERT_THAT_ERROR(Read(SmallChunks, 5), Succeeded());2083 ASSERT_EQ(SmallChunks, Content);2084 ASSERT_EQ(Content.size() + 5, SmallChunks.capacity());2085}2086 2087TEST_F(FileSystemTest, readNativeFileSlice) {2088 createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "01234");2089 FileRemover Cleanup(NonExistantFile);2090 Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);2091 ASSERT_THAT_EXPECTED(FD, Succeeded());2092 auto Close = make_scope_exit([&] { fs::closeFile(*FD); });2093 const auto &Read = [&](size_t Offset,2094 size_t ToRead) -> Expected<std::string> {2095 std::string Buf(ToRead, '?');2096 if (Expected<size_t> BytesRead = fs::readNativeFileSlice(2097 *FD, MutableArrayRef(&*Buf.begin(), Buf.size()), Offset))2098 return Buf.substr(0, *BytesRead);2099 else2100 return BytesRead.takeError();2101 };2102 EXPECT_THAT_EXPECTED(Read(0, 5), HasValue("01234"));2103 EXPECT_THAT_EXPECTED(Read(0, 3), HasValue("012"));2104 EXPECT_THAT_EXPECTED(Read(2, 3), HasValue("234"));2105 EXPECT_THAT_EXPECTED(Read(0, 6), HasValue("01234"));2106 EXPECT_THAT_EXPECTED(Read(2, 6), HasValue("234"));2107 EXPECT_THAT_EXPECTED(Read(5, 5), HasValue(""));2108}2109 2110TEST_F(FileSystemTest, is_local) {2111 bool TestDirectoryIsLocal;2112 ASSERT_NO_ERROR(fs::is_local(TestDirectory, TestDirectoryIsLocal));2113 EXPECT_EQ(TestDirectoryIsLocal, fs::is_local(TestDirectory));2114 2115 int FD;2116 SmallString<128> TempPath;2117 ASSERT_NO_ERROR(2118 fs::createUniqueFile(Twine(TestDirectory) + "/temp", FD, TempPath));2119 FileRemover Cleanup(TempPath);2120 2121 // Make sure it exists.2122 ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));2123 2124 bool TempFileIsLocal;2125 ASSERT_NO_ERROR(fs::is_local(FD, TempFileIsLocal));2126 EXPECT_EQ(TempFileIsLocal, fs::is_local(FD));2127 ::close(FD);2128 2129 // Expect that the file and its parent directory are equally local or equally2130 // remote.2131 EXPECT_EQ(TestDirectoryIsLocal, TempFileIsLocal);2132}2133 2134TEST_F(FileSystemTest, getUmask) {2135#ifdef _WIN322136 EXPECT_EQ(fs::getUmask(), 0U) << "Should always be 0 on Windows.";2137#else2138 unsigned OldMask = ::umask(0022);2139 unsigned CurrentMask = fs::getUmask();2140 EXPECT_EQ(CurrentMask, 0022U)2141 << "getUmask() didn't return previously set umask()";2142 EXPECT_EQ(::umask(OldMask), mode_t(0022U))2143 << "getUmask() may have changed umask()";2144#endif2145}2146 2147TEST_F(FileSystemTest, RespectUmask) {2148#ifndef _WIN322149 unsigned OldMask = ::umask(0022);2150 2151 int FD;2152 SmallString<128> TempPath;2153 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));2154 2155 fs::perms AllRWE = static_cast<fs::perms>(0777);2156 2157 ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE));2158 2159 ErrorOr<fs::perms> Perms = fs::getPermissions(TempPath);2160 ASSERT_TRUE(!!Perms);2161 EXPECT_EQ(Perms.get(), AllRWE) << "Should have ignored umask by default";2162 2163 ASSERT_NO_ERROR(fs::setPermissions(TempPath, AllRWE));2164 2165 Perms = fs::getPermissions(TempPath);2166 ASSERT_TRUE(!!Perms);2167 EXPECT_EQ(Perms.get(), AllRWE) << "Should have ignored umask";2168 2169 ASSERT_NO_ERROR(2170 fs::setPermissions(FD, static_cast<fs::perms>(AllRWE & ~fs::getUmask())));2171 Perms = fs::getPermissions(TempPath);2172 ASSERT_TRUE(!!Perms);2173 EXPECT_EQ(Perms.get(), static_cast<fs::perms>(0755))2174 << "Did not respect umask";2175 2176 (void)::umask(0057);2177 2178 ASSERT_NO_ERROR(2179 fs::setPermissions(FD, static_cast<fs::perms>(AllRWE & ~fs::getUmask())));2180 Perms = fs::getPermissions(TempPath);2181 ASSERT_TRUE(!!Perms);2182 EXPECT_EQ(Perms.get(), static_cast<fs::perms>(0720))2183 << "Did not respect umask";2184 2185 (void)::umask(OldMask);2186 (void)::close(FD);2187#endif2188}2189 2190TEST_F(FileSystemTest, set_current_path) {2191 SmallString<128> path;2192 2193 ASSERT_NO_ERROR(fs::current_path(path));2194 ASSERT_NE(TestDirectory, path);2195 2196 struct RestorePath {2197 SmallString<128> path;2198 RestorePath(const SmallString<128> &path) : path(path) {}2199 ~RestorePath() { fs::set_current_path(path); }2200 } restore_path(path);2201 2202 ASSERT_NO_ERROR(fs::set_current_path(TestDirectory));2203 2204 ASSERT_NO_ERROR(fs::current_path(path));2205 2206 fs::UniqueID D1, D2;2207 ASSERT_NO_ERROR(fs::getUniqueID(TestDirectory, D1));2208 ASSERT_NO_ERROR(fs::getUniqueID(path, D2));2209 ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path;2210}2211 2212TEST_F(FileSystemTest, permissions) {2213 int FD;2214 SmallString<64> TempPath;2215 ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));2216 FileRemover Cleanup(TempPath);2217 2218 // Make sure it exists.2219 ASSERT_TRUE(fs::exists(Twine(TempPath)));2220 2221 auto CheckPermissions = [&](fs::perms Expected) {2222 ErrorOr<fs::perms> Actual = fs::getPermissions(TempPath);2223 return Actual && *Actual == Expected;2224 };2225 2226 std::error_code NoError;2227 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all), NoError);2228 EXPECT_TRUE(CheckPermissions(fs::all_all));2229 2230 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError);2231 EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe));2232 2233#if defined(_WIN32)2234 fs::perms ReadOnly = fs::all_read | fs::all_exe;2235 EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);2236 EXPECT_TRUE(CheckPermissions(ReadOnly));2237 2238 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);2239 EXPECT_TRUE(CheckPermissions(ReadOnly));2240 2241 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);2242 EXPECT_TRUE(CheckPermissions(fs::all_all));2243 2244 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);2245 EXPECT_TRUE(CheckPermissions(ReadOnly));2246 2247 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);2248 EXPECT_TRUE(CheckPermissions(fs::all_all));2249 2250 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);2251 EXPECT_TRUE(CheckPermissions(ReadOnly));2252 2253 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);2254 EXPECT_TRUE(CheckPermissions(fs::all_all));2255 2256 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);2257 EXPECT_TRUE(CheckPermissions(ReadOnly));2258 2259 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);2260 EXPECT_TRUE(CheckPermissions(fs::all_all));2261 2262 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);2263 EXPECT_TRUE(CheckPermissions(ReadOnly));2264 2265 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);2266 EXPECT_TRUE(CheckPermissions(fs::all_all));2267 2268 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);2269 EXPECT_TRUE(CheckPermissions(ReadOnly));2270 2271 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);2272 EXPECT_TRUE(CheckPermissions(fs::all_all));2273 2274 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);2275 EXPECT_TRUE(CheckPermissions(ReadOnly));2276 2277 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);2278 EXPECT_TRUE(CheckPermissions(fs::all_all));2279 2280 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);2281 EXPECT_TRUE(CheckPermissions(ReadOnly));2282 2283 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);2284 EXPECT_TRUE(CheckPermissions(ReadOnly));2285 2286 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);2287 EXPECT_TRUE(CheckPermissions(ReadOnly));2288 2289 EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);2290 EXPECT_TRUE(CheckPermissions(ReadOnly));2291 2292 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |2293 fs::set_gid_on_exe |2294 fs::sticky_bit),2295 NoError);2296 EXPECT_TRUE(CheckPermissions(ReadOnly));2297 2298 EXPECT_EQ(fs::setPermissions(TempPath, ReadOnly | fs::set_uid_on_exe |2299 fs::set_gid_on_exe |2300 fs::sticky_bit),2301 NoError);2302 EXPECT_TRUE(CheckPermissions(ReadOnly));2303 2304 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);2305 EXPECT_TRUE(CheckPermissions(fs::all_all));2306#else2307 EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);2308 EXPECT_TRUE(CheckPermissions(fs::no_perms));2309 2310 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);2311 EXPECT_TRUE(CheckPermissions(fs::owner_read));2312 2313 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);2314 EXPECT_TRUE(CheckPermissions(fs::owner_write));2315 2316 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);2317 EXPECT_TRUE(CheckPermissions(fs::owner_exe));2318 2319 EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);2320 EXPECT_TRUE(CheckPermissions(fs::owner_all));2321 2322 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);2323 EXPECT_TRUE(CheckPermissions(fs::group_read));2324 2325 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);2326 EXPECT_TRUE(CheckPermissions(fs::group_write));2327 2328 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);2329 EXPECT_TRUE(CheckPermissions(fs::group_exe));2330 2331 EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);2332 EXPECT_TRUE(CheckPermissions(fs::group_all));2333 2334 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);2335 EXPECT_TRUE(CheckPermissions(fs::others_read));2336 2337 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);2338 EXPECT_TRUE(CheckPermissions(fs::others_write));2339 2340 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);2341 EXPECT_TRUE(CheckPermissions(fs::others_exe));2342 2343 EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);2344 EXPECT_TRUE(CheckPermissions(fs::others_all));2345 2346 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);2347 EXPECT_TRUE(CheckPermissions(fs::all_read));2348 2349 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);2350 EXPECT_TRUE(CheckPermissions(fs::all_write));2351 2352 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);2353 EXPECT_TRUE(CheckPermissions(fs::all_exe));2354 2355 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);2356 EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe));2357 2358 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);2359 EXPECT_TRUE(CheckPermissions(fs::set_gid_on_exe));2360 2361 // Modern BSDs require root to set the sticky bit on files.2362 // AIX and Solaris without root will mask off (i.e., lose) the sticky bit2363 // on files.2364#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && \2365 !defined(_AIX) && !(defined(__sun__) && defined(__svr4__))2366 EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);2367 EXPECT_TRUE(CheckPermissions(fs::sticky_bit));2368 2369 EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |2370 fs::set_gid_on_exe |2371 fs::sticky_bit),2372 NoError);2373 EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe | fs::set_gid_on_exe |2374 fs::sticky_bit));2375 2376 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::set_uid_on_exe |2377 fs::set_gid_on_exe |2378 fs::sticky_bit),2379 NoError);2380 EXPECT_TRUE(CheckPermissions(fs::all_read | fs::set_uid_on_exe |2381 fs::set_gid_on_exe | fs::sticky_bit));2382 2383 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);2384 EXPECT_TRUE(CheckPermissions(fs::all_perms));2385#endif // !FreeBSD && !NetBSD && !OpenBSD && !AIX2386 2387 EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms & ~fs::sticky_bit),2388 NoError);2389 EXPECT_TRUE(CheckPermissions(fs::all_perms & ~fs::sticky_bit));2390#endif2391}2392 2393#ifdef _WIN322394TEST_F(FileSystemTest, widenPath) {2395 const std::wstring LongPathPrefix(L"\\\\?\\");2396 2397 // Test that the length limit is checked against the UTF-16 length and not the2398 // UTF-8 length.2399 std::string Input("C:\\foldername\\");2400 const std::string Pi("\xcf\x80"); // UTF-8 lower case pi.2401 // Add Pi up to the MAX_PATH limit.2402 const size_t NumChars = MAX_PATH - Input.size() - 1;2403 for (size_t i = 0; i < NumChars; ++i)2404 Input += Pi;2405 // Check that UTF-8 length already exceeds MAX_PATH.2406 EXPECT_GT(Input.size(), (size_t)MAX_PATH);2407 SmallVector<wchar_t, MAX_PATH + 16> Result;2408 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2409 // Result should not start with the long path prefix.2410 EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),2411 LongPathPrefix.size()) != 0);2412 EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);2413 2414 // Add another Pi to exceed the MAX_PATH limit.2415 Input += Pi;2416 // Construct the expected result.2417 SmallVector<wchar_t, MAX_PATH + 16> Expected;2418 ASSERT_NO_ERROR(windows::UTF8ToUTF16(Input, Expected));2419 Expected.insert(Expected.begin(), LongPathPrefix.begin(),2420 LongPathPrefix.end());2421 2422 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2423 EXPECT_EQ(Result, Expected);2424 // Pass a path with forward slashes, check that it ends up with2425 // backslashes when widened with the long path prefix.2426 SmallString<MAX_PATH + 16> InputForward(Input);2427 path::make_preferred(InputForward, path::Style::windows_slash);2428 ASSERT_NO_ERROR(windows::widenPath(InputForward, Result));2429 EXPECT_EQ(Result, Expected);2430 2431 // Pass a path which has the long path prefix prepended originally, but2432 // which is short enough to not require the long path prefix. If such a2433 // path is passed with forward slashes, make sure it gets normalized to2434 // backslashes.2435 SmallString<MAX_PATH + 16> PrefixedPath("\\\\?\\C:\\foldername");2436 ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));2437 // Mangle the input to forward slashes.2438 path::make_preferred(PrefixedPath, path::Style::windows_slash);2439 ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));2440 EXPECT_EQ(Result, Expected);2441 2442 // A short path with an inconsistent prefix is passed through as-is; this2443 // is a degenerate case that we currently don't care about handling.2444 PrefixedPath.assign("/\\?/C:/foldername");2445 ASSERT_NO_ERROR(windows::UTF8ToUTF16(PrefixedPath, Expected));2446 ASSERT_NO_ERROR(windows::widenPath(PrefixedPath, Result));2447 EXPECT_EQ(Result, Expected);2448 2449 // Test that UNC paths are handled correctly.2450 const std::string ShareName("\\\\sharename\\");2451 const std::string FileName("\\filename");2452 // Initialize directory name so that the input is within the MAX_PATH limit.2453 const char DirChar = 'x';2454 std::string DirName(MAX_PATH - ShareName.size() - FileName.size() - 1,2455 DirChar);2456 2457 Input = ShareName + DirName + FileName;2458 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2459 // Result should not start with the long path prefix.2460 EXPECT_TRUE(std::wmemcmp(Result.data(), LongPathPrefix.c_str(),2461 LongPathPrefix.size()) != 0);2462 EXPECT_EQ(Result.size(), (size_t)MAX_PATH - 1);2463 2464 // Extend the directory name so the input exceeds the MAX_PATH limit.2465 DirName += DirChar;2466 Input = ShareName + DirName + FileName;2467 // Construct the expected result.2468 ASSERT_NO_ERROR(windows::UTF8ToUTF16(StringRef(Input).substr(2), Expected));2469 const std::wstring UNCPrefix(LongPathPrefix + L"UNC\\");2470 Expected.insert(Expected.begin(), UNCPrefix.begin(), UNCPrefix.end());2471 2472 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2473 EXPECT_EQ(Result, Expected);2474 2475 // Check that Unix separators are handled correctly.2476 llvm::replace(Input, '\\', '/');2477 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2478 EXPECT_EQ(Result, Expected);2479 2480 // Check the removal of "dots".2481 Input = ShareName + DirName + "\\.\\foo\\.\\.." + FileName;2482 ASSERT_NO_ERROR(windows::widenPath(Input, Result));2483 EXPECT_EQ(Result, Expected);2484}2485#endif2486 2487#ifdef _WIN322488// Windows refuses lock request if file region is already locked by the same2489// process. POSIX system in this case updates the existing lock.2490TEST_F(FileSystemTest, FileLocker) {2491 using namespace std::chrono;2492 int FD;2493 std::error_code EC;2494 SmallString<64> TempPath;2495 EC = fs::createTemporaryFile("test", "temp", FD, TempPath);2496 ASSERT_NO_ERROR(EC);2497 FileRemover Cleanup(TempPath);2498 raw_fd_ostream Stream(TempPath, EC);2499 2500 EC = fs::tryLockFile(FD);2501 ASSERT_NO_ERROR(EC);2502 EC = fs::unlockFile(FD);2503 ASSERT_NO_ERROR(EC);2504 2505 if (auto L = Stream.lock()) {2506 ASSERT_ERROR(fs::tryLockFile(FD));2507 ASSERT_NO_ERROR(L->unlock());2508 ASSERT_NO_ERROR(fs::tryLockFile(FD));2509 ASSERT_NO_ERROR(fs::unlockFile(FD));2510 } else {2511 ADD_FAILURE();2512 handleAllErrors(L.takeError(), [&](ErrorInfoBase &EIB) {});2513 }2514 2515 ASSERT_NO_ERROR(fs::tryLockFile(FD));2516 ASSERT_NO_ERROR(fs::unlockFile(FD));2517 2518 {2519 Expected<fs::FileLocker> L1 = Stream.lock();2520 ASSERT_THAT_EXPECTED(L1, Succeeded());2521 raw_fd_ostream Stream2(FD, false);2522 Expected<fs::FileLocker> L2 = Stream2.tryLockFor(250ms);2523 ASSERT_THAT_EXPECTED(L2, Failed());2524 ASSERT_NO_ERROR(L1->unlock());2525 Expected<fs::FileLocker> L3 = Stream.tryLockFor(0ms);2526 ASSERT_THAT_EXPECTED(L3, Succeeded());2527 }2528 2529 ASSERT_NO_ERROR(fs::tryLockFile(FD));2530 ASSERT_NO_ERROR(fs::unlockFile(FD));2531}2532#endif2533 2534TEST_F(FileSystemTest, CopyFile) {2535 unittest::TempDir RootTestDirectory("CopyFileTest", /*Unique=*/true);2536 2537 SmallVector<std::string> Data;2538 SmallVector<SmallString<128>> Sources;2539 for (int I = 0, E = 3; I != E; ++I) {2540 Data.push_back(Twine(I).str());2541 Sources.emplace_back(RootTestDirectory.path());2542 path::append(Sources.back(), "source" + Data.back() + ".txt");2543 createFileWithData(Sources.back(), /*ShouldExistBefore=*/false,2544 fs::CD_CreateNew, Data.back());2545 }2546 2547 // Copy the first file to a non-existing file.2548 SmallString<128> Destination(RootTestDirectory.path());2549 path::append(Destination, "destination");2550 ASSERT_FALSE(fs::exists(Destination));2551 fs::copy_file(Sources[0], Destination);2552 verifyFileContents(Destination, Data[0]);2553 2554 // Copy the second file to an existing file.2555 fs::copy_file(Sources[1], Destination);2556 verifyFileContents(Destination, Data[1]);2557 2558 // Note: The remaining logic is targeted at a potential failure case related2559 // to file cloning and symlinks on Darwin. On Windows, fs::create_link() does2560 // not return success here so the test is skipped.2561#if !defined(_WIN32)2562 // Set up a symlink to the third file.2563 SmallString<128> Symlink(RootTestDirectory.path());2564 path::append(Symlink, "symlink");2565 ASSERT_NO_ERROR(fs::create_link(path::filename(Sources[2]), Symlink));2566 verifyFileContents(Symlink, Data[2]);2567 2568 // fs::getUniqueID() should follow symlinks. Otherwise, this isn't good test2569 // coverage.2570 fs::UniqueID SymlinkID;2571 fs::UniqueID Data2ID;2572 ASSERT_NO_ERROR(fs::getUniqueID(Symlink, SymlinkID));2573 ASSERT_NO_ERROR(fs::getUniqueID(Sources[2], Data2ID));2574 ASSERT_EQ(SymlinkID, Data2ID);2575 2576 // Copy the third file through the symlink.2577 fs::copy_file(Symlink, Destination);2578 verifyFileContents(Destination, Data[2]);2579 2580 // Confirm the destination is not a link to the original file, and not a2581 // symlink.2582 bool IsDestinationSymlink;2583 ASSERT_NO_ERROR(fs::is_symlink_file(Destination, IsDestinationSymlink));2584 ASSERT_FALSE(IsDestinationSymlink);2585 fs::UniqueID DestinationID;2586 ASSERT_NO_ERROR(fs::getUniqueID(Destination, DestinationID));2587 ASSERT_NE(SymlinkID, DestinationID);2588#endif2589}2590 2591} // anonymous namespace2592