VirtualFileSystemTest.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. //===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "clang/Basic/VirtualFileSystem.h"
  10. #include "llvm/Support/Errc.h"
  11. #include "llvm/Support/MemoryBuffer.h"
  12. #include "llvm/Support/Path.h"
  13. #include "llvm/Support/SourceMgr.h"
  14. #include "gtest/gtest.h"
  15. #include <map>
  16. using namespace clang;
  17. using namespace llvm;
  18. using llvm::sys::fs::UniqueID;
  19. namespace {
  20. class DummyFileSystem : public vfs::FileSystem {
  21. int FSID; // used to produce UniqueIDs
  22. int FileID; // used to produce UniqueIDs
  23. std::map<std::string, vfs::Status> FilesAndDirs;
  24. static int getNextFSID() {
  25. static int Count = 0;
  26. return Count++;
  27. }
  28. public:
  29. DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
  30. ErrorOr<vfs::Status> status(const Twine &Path) override {
  31. std::map<std::string, vfs::Status>::iterator I =
  32. FilesAndDirs.find(Path.str());
  33. if (I == FilesAndDirs.end())
  34. return make_error_code(llvm::errc::no_such_file_or_directory);
  35. return I->second;
  36. }
  37. ErrorOr<std::unique_ptr<vfs::File>>
  38. openFileForRead(const Twine &Path) override {
  39. llvm_unreachable("unimplemented");
  40. }
  41. struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
  42. std::map<std::string, vfs::Status> &FilesAndDirs;
  43. std::map<std::string, vfs::Status>::iterator I;
  44. std::string Path;
  45. bool isInPath(StringRef S) {
  46. if (Path.size() < S.size() && S.find(Path) == 0) {
  47. auto LastSep = S.find_last_of('/');
  48. if (LastSep == Path.size() || LastSep == Path.size()-1)
  49. return true;
  50. }
  51. return false;
  52. }
  53. DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
  54. const Twine &_Path)
  55. : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
  56. Path(_Path.str()) {
  57. for ( ; I != FilesAndDirs.end(); ++I) {
  58. if (isInPath(I->first)) {
  59. CurrentEntry = I->second;
  60. break;
  61. }
  62. }
  63. }
  64. std::error_code increment() override {
  65. ++I;
  66. for ( ; I != FilesAndDirs.end(); ++I) {
  67. if (isInPath(I->first)) {
  68. CurrentEntry = I->second;
  69. break;
  70. }
  71. }
  72. if (I == FilesAndDirs.end())
  73. CurrentEntry = vfs::Status();
  74. return std::error_code();
  75. }
  76. };
  77. vfs::directory_iterator dir_begin(const Twine &Dir,
  78. std::error_code &EC) override {
  79. return vfs::directory_iterator(
  80. std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
  81. }
  82. void addEntry(StringRef Path, const vfs::Status &Status) {
  83. FilesAndDirs[Path] = Status;
  84. }
  85. void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
  86. vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
  87. 0, 0, 1024, sys::fs::file_type::regular_file, Perms);
  88. addEntry(Path, S);
  89. }
  90. void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
  91. vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
  92. 0, 0, 0, sys::fs::file_type::directory_file, Perms);
  93. addEntry(Path, S);
  94. }
  95. void addSymlink(StringRef Path) {
  96. vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
  97. 0, 0, 0, sys::fs::file_type::symlink_file, sys::fs::all_all);
  98. addEntry(Path, S);
  99. }
  100. };
  101. } // end anonymous namespace
  102. TEST(VirtualFileSystemTest, StatusQueries) {
  103. IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
  104. ErrorOr<vfs::Status> Status((std::error_code()));
  105. D->addRegularFile("/foo");
  106. Status = D->status("/foo");
  107. ASSERT_FALSE(Status.getError());
  108. EXPECT_TRUE(Status->isStatusKnown());
  109. EXPECT_FALSE(Status->isDirectory());
  110. EXPECT_TRUE(Status->isRegularFile());
  111. EXPECT_FALSE(Status->isSymlink());
  112. EXPECT_FALSE(Status->isOther());
  113. EXPECT_TRUE(Status->exists());
  114. D->addDirectory("/bar");
  115. Status = D->status("/bar");
  116. ASSERT_FALSE(Status.getError());
  117. EXPECT_TRUE(Status->isStatusKnown());
  118. EXPECT_TRUE(Status->isDirectory());
  119. EXPECT_FALSE(Status->isRegularFile());
  120. EXPECT_FALSE(Status->isSymlink());
  121. EXPECT_FALSE(Status->isOther());
  122. EXPECT_TRUE(Status->exists());
  123. D->addSymlink("/baz");
  124. Status = D->status("/baz");
  125. ASSERT_FALSE(Status.getError());
  126. EXPECT_TRUE(Status->isStatusKnown());
  127. EXPECT_FALSE(Status->isDirectory());
  128. EXPECT_FALSE(Status->isRegularFile());
  129. EXPECT_TRUE(Status->isSymlink());
  130. EXPECT_FALSE(Status->isOther());
  131. EXPECT_TRUE(Status->exists());
  132. EXPECT_TRUE(Status->equivalent(*Status));
  133. ErrorOr<vfs::Status> Status2 = D->status("/foo");
  134. ASSERT_FALSE(Status2.getError());
  135. EXPECT_FALSE(Status->equivalent(*Status2));
  136. }
  137. TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
  138. IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
  139. ErrorOr<vfs::Status> Status((std::error_code()));
  140. EXPECT_FALSE(Status = D->status("/foo"));
  141. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
  142. EXPECT_FALSE(Status = O->status("/foo"));
  143. D->addRegularFile("/foo");
  144. Status = D->status("/foo");
  145. EXPECT_FALSE(Status.getError());
  146. ErrorOr<vfs::Status> Status2((std::error_code()));
  147. Status2 = O->status("/foo");
  148. EXPECT_FALSE(Status2.getError());
  149. EXPECT_TRUE(Status->equivalent(*Status2));
  150. }
  151. TEST(VirtualFileSystemTest, OverlayFiles) {
  152. IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
  153. IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
  154. IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
  155. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  156. new vfs::OverlayFileSystem(Base));
  157. O->pushOverlay(Middle);
  158. O->pushOverlay(Top);
  159. ErrorOr<vfs::Status> Status1((std::error_code())),
  160. Status2((std::error_code())), Status3((std::error_code())),
  161. StatusB((std::error_code())), StatusM((std::error_code())),
  162. StatusT((std::error_code()));
  163. Base->addRegularFile("/foo");
  164. StatusB = Base->status("/foo");
  165. ASSERT_FALSE(StatusB.getError());
  166. Status1 = O->status("/foo");
  167. ASSERT_FALSE(Status1.getError());
  168. Middle->addRegularFile("/foo");
  169. StatusM = Middle->status("/foo");
  170. ASSERT_FALSE(StatusM.getError());
  171. Status2 = O->status("/foo");
  172. ASSERT_FALSE(Status2.getError());
  173. Top->addRegularFile("/foo");
  174. StatusT = Top->status("/foo");
  175. ASSERT_FALSE(StatusT.getError());
  176. Status3 = O->status("/foo");
  177. ASSERT_FALSE(Status3.getError());
  178. EXPECT_TRUE(Status1->equivalent(*StatusB));
  179. EXPECT_TRUE(Status2->equivalent(*StatusM));
  180. EXPECT_TRUE(Status3->equivalent(*StatusT));
  181. EXPECT_FALSE(Status1->equivalent(*Status2));
  182. EXPECT_FALSE(Status2->equivalent(*Status3));
  183. EXPECT_FALSE(Status1->equivalent(*Status3));
  184. }
  185. TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
  186. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  187. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  188. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  189. new vfs::OverlayFileSystem(Lower));
  190. O->pushOverlay(Upper);
  191. Lower->addDirectory("/lower-only");
  192. Upper->addDirectory("/upper-only");
  193. // non-merged paths should be the same
  194. ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
  195. ASSERT_FALSE(Status1.getError());
  196. ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
  197. ASSERT_FALSE(Status2.getError());
  198. EXPECT_TRUE(Status1->equivalent(*Status2));
  199. Status1 = Upper->status("/upper-only");
  200. ASSERT_FALSE(Status1.getError());
  201. Status2 = O->status("/upper-only");
  202. ASSERT_FALSE(Status2.getError());
  203. EXPECT_TRUE(Status1->equivalent(*Status2));
  204. }
  205. TEST(VirtualFileSystemTest, MergedDirPermissions) {
  206. // merged directories get the permissions of the upper dir
  207. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  208. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  209. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  210. new vfs::OverlayFileSystem(Lower));
  211. O->pushOverlay(Upper);
  212. ErrorOr<vfs::Status> Status((std::error_code()));
  213. Lower->addDirectory("/both", sys::fs::owner_read);
  214. Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
  215. Status = O->status("/both");
  216. ASSERT_FALSE(Status.getError());
  217. EXPECT_EQ(0740, Status->getPermissions());
  218. // permissions (as usual) are not recursively applied
  219. Lower->addRegularFile("/both/foo", sys::fs::owner_read);
  220. Upper->addRegularFile("/both/bar", sys::fs::owner_write);
  221. Status = O->status("/both/foo");
  222. ASSERT_FALSE( Status.getError());
  223. EXPECT_EQ(0400, Status->getPermissions());
  224. Status = O->status("/both/bar");
  225. ASSERT_FALSE(Status.getError());
  226. EXPECT_EQ(0200, Status->getPermissions());
  227. }
  228. namespace {
  229. struct ScopedDir {
  230. SmallString<128> Path;
  231. ScopedDir(const Twine &Name, bool Unique=false) {
  232. std::error_code EC;
  233. if (Unique) {
  234. EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
  235. } else {
  236. Path = Name.str();
  237. EC = llvm::sys::fs::create_directory(Twine(Path));
  238. }
  239. if (EC)
  240. Path = "";
  241. EXPECT_FALSE(EC);
  242. }
  243. ~ScopedDir() {
  244. if (Path != "")
  245. EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
  246. }
  247. operator StringRef() { return Path.str(); }
  248. };
  249. }
  250. TEST(VirtualFileSystemTest, BasicRealFSIteration) {
  251. ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
  252. IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
  253. std::error_code EC;
  254. vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
  255. ASSERT_FALSE(EC);
  256. EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
  257. ScopedDir _a(TestDirectory+"/a");
  258. ScopedDir _ab(TestDirectory+"/a/b");
  259. ScopedDir _c(TestDirectory+"/c");
  260. ScopedDir _cd(TestDirectory+"/c/d");
  261. I = FS->dir_begin(Twine(TestDirectory), EC);
  262. ASSERT_FALSE(EC);
  263. ASSERT_NE(vfs::directory_iterator(), I);
  264. // Check either a or c, since we can't rely on the iteration order.
  265. EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
  266. I.increment(EC);
  267. ASSERT_FALSE(EC);
  268. ASSERT_NE(vfs::directory_iterator(), I);
  269. EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
  270. I.increment(EC);
  271. EXPECT_EQ(vfs::directory_iterator(), I);
  272. }
  273. TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
  274. ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
  275. IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
  276. std::error_code EC;
  277. auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
  278. ASSERT_FALSE(EC);
  279. EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
  280. ScopedDir _a(TestDirectory+"/a");
  281. ScopedDir _ab(TestDirectory+"/a/b");
  282. ScopedDir _c(TestDirectory+"/c");
  283. ScopedDir _cd(TestDirectory+"/c/d");
  284. I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
  285. ASSERT_FALSE(EC);
  286. ASSERT_NE(vfs::recursive_directory_iterator(), I);
  287. std::vector<std::string> Contents;
  288. for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
  289. I.increment(EC)) {
  290. Contents.push_back(I->getName());
  291. }
  292. // Check contents, which may be in any order
  293. EXPECT_EQ(4U, Contents.size());
  294. int Counts[4] = { 0, 0, 0, 0 };
  295. for (const std::string &Name : Contents) {
  296. ASSERT_FALSE(Name.empty());
  297. int Index = Name[Name.size()-1] - 'a';
  298. ASSERT_TRUE(Index >= 0 && Index < 4);
  299. Counts[Index]++;
  300. }
  301. EXPECT_EQ(1, Counts[0]); // a
  302. EXPECT_EQ(1, Counts[1]); // b
  303. EXPECT_EQ(1, Counts[2]); // c
  304. EXPECT_EQ(1, Counts[3]); // d
  305. }
  306. template <typename T, size_t N>
  307. std::vector<StringRef> makeStringRefVector(const T (&Arr)[N]) {
  308. std::vector<StringRef> Vec;
  309. for (size_t i = 0; i != N; ++i)
  310. Vec.push_back(Arr[i]);
  311. return Vec;
  312. }
  313. template <typename DirIter>
  314. static void checkContents(DirIter I, ArrayRef<StringRef> Expected) {
  315. std::error_code EC;
  316. auto ExpectedIter = Expected.begin(), ExpectedEnd = Expected.end();
  317. for (DirIter E;
  318. !EC && I != E && ExpectedIter != ExpectedEnd;
  319. I.increment(EC), ++ExpectedIter)
  320. EXPECT_EQ(*ExpectedIter, I->getName());
  321. EXPECT_EQ(ExpectedEnd, ExpectedIter);
  322. EXPECT_EQ(DirIter(), I);
  323. }
  324. TEST(VirtualFileSystemTest, OverlayIteration) {
  325. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  326. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  327. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  328. new vfs::OverlayFileSystem(Lower));
  329. O->pushOverlay(Upper);
  330. std::error_code EC;
  331. checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
  332. Lower->addRegularFile("/file1");
  333. checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
  334. Upper->addRegularFile("/file2");
  335. {
  336. const char *Contents[] = {"/file2", "/file1"};
  337. checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
  338. }
  339. Lower->addDirectory("/dir1");
  340. Lower->addRegularFile("/dir1/foo");
  341. Upper->addDirectory("/dir2");
  342. Upper->addRegularFile("/dir2/foo");
  343. checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
  344. {
  345. const char *Contents[] = {"/dir2", "/file2", "/dir1", "/file1"};
  346. checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
  347. }
  348. }
  349. TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
  350. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  351. IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
  352. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  353. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  354. new vfs::OverlayFileSystem(Lower));
  355. O->pushOverlay(Middle);
  356. O->pushOverlay(Upper);
  357. std::error_code EC;
  358. checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
  359. ArrayRef<StringRef>());
  360. Lower->addRegularFile("/file1");
  361. checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
  362. ArrayRef<StringRef>("/file1"));
  363. Upper->addDirectory("/dir");
  364. Upper->addRegularFile("/dir/file2");
  365. {
  366. const char *Contents[] = {"/dir", "/dir/file2", "/file1"};
  367. checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
  368. makeStringRefVector(Contents));
  369. }
  370. Lower->addDirectory("/dir1");
  371. Lower->addRegularFile("/dir1/foo");
  372. Lower->addDirectory("/dir1/a");
  373. Lower->addRegularFile("/dir1/a/b");
  374. Middle->addDirectory("/a");
  375. Middle->addDirectory("/a/b");
  376. Middle->addDirectory("/a/b/c");
  377. Middle->addRegularFile("/a/b/c/d");
  378. Middle->addRegularFile("/hiddenByUp");
  379. Upper->addDirectory("/dir2");
  380. Upper->addRegularFile("/dir2/foo");
  381. Upper->addRegularFile("/hiddenByUp");
  382. checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
  383. ArrayRef<StringRef>("/dir2/foo"));
  384. {
  385. const char *Contents[] = { "/dir", "/dir/file2", "/dir2", "/dir2/foo",
  386. "/hiddenByUp", "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
  387. "/dir1/a/b", "/dir1/foo", "/file1" };
  388. checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
  389. makeStringRefVector(Contents));
  390. }
  391. }
  392. TEST(VirtualFileSystemTest, ThreeLevelIteration) {
  393. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  394. IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
  395. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  396. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  397. new vfs::OverlayFileSystem(Lower));
  398. O->pushOverlay(Middle);
  399. O->pushOverlay(Upper);
  400. std::error_code EC;
  401. checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
  402. Middle->addRegularFile("/file2");
  403. checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
  404. Lower->addRegularFile("/file1");
  405. Upper->addRegularFile("/file3");
  406. {
  407. const char *Contents[] = {"/file3", "/file2", "/file1"};
  408. checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
  409. }
  410. }
  411. TEST(VirtualFileSystemTest, HiddenInIteration) {
  412. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  413. IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
  414. IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
  415. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  416. new vfs::OverlayFileSystem(Lower));
  417. O->pushOverlay(Middle);
  418. O->pushOverlay(Upper);
  419. std::error_code EC;
  420. Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
  421. Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
  422. Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
  423. Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
  424. Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
  425. Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
  426. Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
  427. Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
  428. {
  429. const char *Contents[] = {"/hiddenByUp", "/onlyInUp", "/hiddenByMid",
  430. "/onlyInMid", "/onlyInLow"};
  431. checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
  432. }
  433. // Make sure we get the top-most entry
  434. {
  435. std::error_code EC;
  436. vfs::directory_iterator I = O->dir_begin("/", EC), E;
  437. for ( ; !EC && I != E; I.increment(EC))
  438. if (I->getName() == "/hiddenByUp")
  439. break;
  440. ASSERT_NE(E, I);
  441. EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
  442. }
  443. {
  444. std::error_code EC;
  445. vfs::directory_iterator I = O->dir_begin("/", EC), E;
  446. for ( ; !EC && I != E; I.increment(EC))
  447. if (I->getName() == "/hiddenByMid")
  448. break;
  449. ASSERT_NE(E, I);
  450. EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
  451. }
  452. }
  453. // NOTE: in the tests below, we use '//root/' as our root directory, since it is
  454. // a legal *absolute* path on Windows as well as *nix.
  455. class VFSFromYAMLTest : public ::testing::Test {
  456. public:
  457. int NumDiagnostics;
  458. void SetUp() override { NumDiagnostics = 0; }
  459. static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
  460. VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
  461. ++Test->NumDiagnostics;
  462. }
  463. IntrusiveRefCntPtr<vfs::FileSystem>
  464. getFromYAMLRawString(StringRef Content,
  465. IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
  466. std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
  467. return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, this,
  468. ExternalFS);
  469. }
  470. IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
  471. StringRef Content,
  472. IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
  473. std::string VersionPlusContent("{\n 'version':0,\n");
  474. VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
  475. return getFromYAMLRawString(VersionPlusContent, ExternalFS);
  476. }
  477. };
  478. TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
  479. IntrusiveRefCntPtr<vfs::FileSystem> FS;
  480. FS = getFromYAMLString("");
  481. EXPECT_EQ(nullptr, FS.get());
  482. FS = getFromYAMLString("[]");
  483. EXPECT_EQ(nullptr, FS.get());
  484. FS = getFromYAMLString("'string'");
  485. EXPECT_EQ(nullptr, FS.get());
  486. EXPECT_EQ(3, NumDiagnostics);
  487. }
  488. TEST_F(VFSFromYAMLTest, MappedFiles) {
  489. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  490. Lower->addRegularFile("//root/foo/bar/a");
  491. IntrusiveRefCntPtr<vfs::FileSystem> FS =
  492. getFromYAMLString("{ 'roots': [\n"
  493. "{\n"
  494. " 'type': 'directory',\n"
  495. " 'name': '//root/',\n"
  496. " 'contents': [ {\n"
  497. " 'type': 'file',\n"
  498. " 'name': 'file1',\n"
  499. " 'external-contents': '//root/foo/bar/a'\n"
  500. " },\n"
  501. " {\n"
  502. " 'type': 'file',\n"
  503. " 'name': 'file2',\n"
  504. " 'external-contents': '//root/foo/b'\n"
  505. " }\n"
  506. " ]\n"
  507. "}\n"
  508. "]\n"
  509. "}",
  510. Lower);
  511. ASSERT_TRUE(FS.get() != nullptr);
  512. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  513. new vfs::OverlayFileSystem(Lower));
  514. O->pushOverlay(FS);
  515. // file
  516. ErrorOr<vfs::Status> S = O->status("//root/file1");
  517. ASSERT_FALSE(S.getError());
  518. EXPECT_EQ("//root/foo/bar/a", S->getName());
  519. ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
  520. EXPECT_EQ("//root/foo/bar/a", SLower->getName());
  521. EXPECT_TRUE(S->equivalent(*SLower));
  522. // directory
  523. S = O->status("//root/");
  524. ASSERT_FALSE(S.getError());
  525. EXPECT_TRUE(S->isDirectory());
  526. EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
  527. // broken mapping
  528. EXPECT_EQ(O->status("//root/file2").getError(),
  529. llvm::errc::no_such_file_or_directory);
  530. EXPECT_EQ(0, NumDiagnostics);
  531. }
  532. TEST_F(VFSFromYAMLTest, CaseInsensitive) {
  533. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  534. Lower->addRegularFile("//root/foo/bar/a");
  535. IntrusiveRefCntPtr<vfs::FileSystem> FS =
  536. getFromYAMLString("{ 'case-sensitive': 'false',\n"
  537. " 'roots': [\n"
  538. "{\n"
  539. " 'type': 'directory',\n"
  540. " 'name': '//root/',\n"
  541. " 'contents': [ {\n"
  542. " 'type': 'file',\n"
  543. " 'name': 'XX',\n"
  544. " 'external-contents': '//root/foo/bar/a'\n"
  545. " }\n"
  546. " ]\n"
  547. "}]}",
  548. Lower);
  549. ASSERT_TRUE(FS.get() != nullptr);
  550. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  551. new vfs::OverlayFileSystem(Lower));
  552. O->pushOverlay(FS);
  553. ErrorOr<vfs::Status> S = O->status("//root/XX");
  554. ASSERT_FALSE(S.getError());
  555. ErrorOr<vfs::Status> SS = O->status("//root/xx");
  556. ASSERT_FALSE(SS.getError());
  557. EXPECT_TRUE(S->equivalent(*SS));
  558. SS = O->status("//root/xX");
  559. EXPECT_TRUE(S->equivalent(*SS));
  560. SS = O->status("//root/Xx");
  561. EXPECT_TRUE(S->equivalent(*SS));
  562. EXPECT_EQ(0, NumDiagnostics);
  563. }
  564. TEST_F(VFSFromYAMLTest, CaseSensitive) {
  565. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  566. Lower->addRegularFile("//root/foo/bar/a");
  567. IntrusiveRefCntPtr<vfs::FileSystem> FS =
  568. getFromYAMLString("{ 'case-sensitive': 'true',\n"
  569. " 'roots': [\n"
  570. "{\n"
  571. " 'type': 'directory',\n"
  572. " 'name': '//root/',\n"
  573. " 'contents': [ {\n"
  574. " 'type': 'file',\n"
  575. " 'name': 'XX',\n"
  576. " 'external-contents': '//root/foo/bar/a'\n"
  577. " }\n"
  578. " ]\n"
  579. "}]}",
  580. Lower);
  581. ASSERT_TRUE(FS.get() != nullptr);
  582. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  583. new vfs::OverlayFileSystem(Lower));
  584. O->pushOverlay(FS);
  585. ErrorOr<vfs::Status> SS = O->status("//root/xx");
  586. EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
  587. SS = O->status("//root/xX");
  588. EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
  589. SS = O->status("//root/Xx");
  590. EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
  591. EXPECT_EQ(0, NumDiagnostics);
  592. }
  593. TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
  594. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  595. // invalid YAML at top-level
  596. IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
  597. EXPECT_EQ(nullptr, FS.get());
  598. // invalid YAML in roots
  599. FS = getFromYAMLString("{ 'roots':[}", Lower);
  600. // invalid YAML in directory
  601. FS = getFromYAMLString(
  602. "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
  603. Lower);
  604. EXPECT_EQ(nullptr, FS.get());
  605. // invalid configuration
  606. FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
  607. EXPECT_EQ(nullptr, FS.get());
  608. FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
  609. EXPECT_EQ(nullptr, FS.get());
  610. // invalid roots
  611. FS = getFromYAMLString("{ 'roots':'' }", Lower);
  612. EXPECT_EQ(nullptr, FS.get());
  613. FS = getFromYAMLString("{ 'roots':{} }", Lower);
  614. EXPECT_EQ(nullptr, FS.get());
  615. // invalid entries
  616. FS = getFromYAMLString(
  617. "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
  618. EXPECT_EQ(nullptr, FS.get());
  619. FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
  620. "'external-contents': 'other' }",
  621. Lower);
  622. EXPECT_EQ(nullptr, FS.get());
  623. FS = getFromYAMLString(
  624. "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
  625. Lower);
  626. EXPECT_EQ(nullptr, FS.get());
  627. FS = getFromYAMLString(
  628. "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
  629. Lower);
  630. EXPECT_EQ(nullptr, FS.get());
  631. FS = getFromYAMLString(
  632. "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
  633. Lower);
  634. EXPECT_EQ(nullptr, FS.get());
  635. FS = getFromYAMLString(
  636. "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
  637. Lower);
  638. EXPECT_EQ(nullptr, FS.get());
  639. FS = getFromYAMLString(
  640. "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
  641. Lower);
  642. EXPECT_EQ(nullptr, FS.get());
  643. // missing mandatory fields
  644. FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
  645. EXPECT_EQ(nullptr, FS.get());
  646. FS = getFromYAMLString(
  647. "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
  648. EXPECT_EQ(nullptr, FS.get());
  649. FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
  650. EXPECT_EQ(nullptr, FS.get());
  651. // duplicate keys
  652. FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
  653. EXPECT_EQ(nullptr, FS.get());
  654. FS = getFromYAMLString(
  655. "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
  656. Lower);
  657. EXPECT_EQ(nullptr, FS.get());
  658. FS =
  659. getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
  660. "'external-contents':'blah' } ] }",
  661. Lower);
  662. EXPECT_EQ(nullptr, FS.get());
  663. // missing version
  664. FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
  665. EXPECT_EQ(nullptr, FS.get());
  666. // bad version number
  667. FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
  668. EXPECT_EQ(nullptr, FS.get());
  669. FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
  670. EXPECT_EQ(nullptr, FS.get());
  671. FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
  672. EXPECT_EQ(nullptr, FS.get());
  673. EXPECT_EQ(24, NumDiagnostics);
  674. }
  675. TEST_F(VFSFromYAMLTest, UseExternalName) {
  676. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  677. Lower->addRegularFile("//root/external/file");
  678. IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
  679. "{ 'roots': [\n"
  680. " { 'type': 'file', 'name': '//root/A',\n"
  681. " 'external-contents': '//root/external/file'\n"
  682. " },\n"
  683. " { 'type': 'file', 'name': '//root/B',\n"
  684. " 'use-external-name': true,\n"
  685. " 'external-contents': '//root/external/file'\n"
  686. " },\n"
  687. " { 'type': 'file', 'name': '//root/C',\n"
  688. " 'use-external-name': false,\n"
  689. " 'external-contents': '//root/external/file'\n"
  690. " }\n"
  691. "] }", Lower);
  692. ASSERT_TRUE(nullptr != FS.get());
  693. // default true
  694. EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
  695. // explicit
  696. EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
  697. EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
  698. // global configuration
  699. FS = getFromYAMLString(
  700. "{ 'use-external-names': false,\n"
  701. " 'roots': [\n"
  702. " { 'type': 'file', 'name': '//root/A',\n"
  703. " 'external-contents': '//root/external/file'\n"
  704. " },\n"
  705. " { 'type': 'file', 'name': '//root/B',\n"
  706. " 'use-external-name': true,\n"
  707. " 'external-contents': '//root/external/file'\n"
  708. " },\n"
  709. " { 'type': 'file', 'name': '//root/C',\n"
  710. " 'use-external-name': false,\n"
  711. " 'external-contents': '//root/external/file'\n"
  712. " }\n"
  713. "] }", Lower);
  714. ASSERT_TRUE(nullptr != FS.get());
  715. // default
  716. EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
  717. // explicit
  718. EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
  719. EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
  720. }
  721. TEST_F(VFSFromYAMLTest, MultiComponentPath) {
  722. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  723. Lower->addRegularFile("//root/other");
  724. // file in roots
  725. IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
  726. "{ 'roots': [\n"
  727. " { 'type': 'file', 'name': '//root/path/to/file',\n"
  728. " 'external-contents': '//root/other' }]\n"
  729. "}", Lower);
  730. ASSERT_TRUE(nullptr != FS.get());
  731. EXPECT_FALSE(FS->status("//root/path/to/file").getError());
  732. EXPECT_FALSE(FS->status("//root/path/to").getError());
  733. EXPECT_FALSE(FS->status("//root/path").getError());
  734. EXPECT_FALSE(FS->status("//root/").getError());
  735. // at the start
  736. FS = getFromYAMLString(
  737. "{ 'roots': [\n"
  738. " { 'type': 'directory', 'name': '//root/path/to',\n"
  739. " 'contents': [ { 'type': 'file', 'name': 'file',\n"
  740. " 'external-contents': '//root/other' }]}]\n"
  741. "}", Lower);
  742. ASSERT_TRUE(nullptr != FS.get());
  743. EXPECT_FALSE(FS->status("//root/path/to/file").getError());
  744. EXPECT_FALSE(FS->status("//root/path/to").getError());
  745. EXPECT_FALSE(FS->status("//root/path").getError());
  746. EXPECT_FALSE(FS->status("//root/").getError());
  747. // at the end
  748. FS = getFromYAMLString(
  749. "{ 'roots': [\n"
  750. " { 'type': 'directory', 'name': '//root/',\n"
  751. " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
  752. " 'external-contents': '//root/other' }]}]\n"
  753. "}", Lower);
  754. ASSERT_TRUE(nullptr != FS.get());
  755. EXPECT_FALSE(FS->status("//root/path/to/file").getError());
  756. EXPECT_FALSE(FS->status("//root/path/to").getError());
  757. EXPECT_FALSE(FS->status("//root/path").getError());
  758. EXPECT_FALSE(FS->status("//root/").getError());
  759. }
  760. TEST_F(VFSFromYAMLTest, TrailingSlashes) {
  761. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  762. Lower->addRegularFile("//root/other");
  763. // file in roots
  764. IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
  765. "{ 'roots': [\n"
  766. " { 'type': 'directory', 'name': '//root/path/to////',\n"
  767. " 'contents': [ { 'type': 'file', 'name': 'file',\n"
  768. " 'external-contents': '//root/other' }]}]\n"
  769. "}", Lower);
  770. ASSERT_TRUE(nullptr != FS.get());
  771. EXPECT_FALSE(FS->status("//root/path/to/file").getError());
  772. EXPECT_FALSE(FS->status("//root/path/to").getError());
  773. EXPECT_FALSE(FS->status("//root/path").getError());
  774. EXPECT_FALSE(FS->status("//root/").getError());
  775. }
  776. TEST_F(VFSFromYAMLTest, DirectoryIteration) {
  777. IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
  778. Lower->addDirectory("//root/");
  779. Lower->addDirectory("//root/foo");
  780. Lower->addDirectory("//root/foo/bar");
  781. Lower->addRegularFile("//root/foo/bar/a");
  782. Lower->addRegularFile("//root/foo/bar/b");
  783. Lower->addRegularFile("//root/file3");
  784. IntrusiveRefCntPtr<vfs::FileSystem> FS =
  785. getFromYAMLString("{ 'use-external-names': false,\n"
  786. " 'roots': [\n"
  787. "{\n"
  788. " 'type': 'directory',\n"
  789. " 'name': '//root/',\n"
  790. " 'contents': [ {\n"
  791. " 'type': 'file',\n"
  792. " 'name': 'file1',\n"
  793. " 'external-contents': '//root/foo/bar/a'\n"
  794. " },\n"
  795. " {\n"
  796. " 'type': 'file',\n"
  797. " 'name': 'file2',\n"
  798. " 'external-contents': '//root/foo/bar/b'\n"
  799. " }\n"
  800. " ]\n"
  801. "}\n"
  802. "]\n"
  803. "}",
  804. Lower);
  805. ASSERT_TRUE(FS.get() != NULL);
  806. IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
  807. new vfs::OverlayFileSystem(Lower));
  808. O->pushOverlay(FS);
  809. std::error_code EC;
  810. {
  811. const char *Contents[] = {"//root/file1", "//root/file2", "//root/file3",
  812. "//root/foo"};
  813. checkContents(O->dir_begin("//root/", EC), makeStringRefVector(Contents));
  814. }
  815. {
  816. const char *Contents[] = {"//root/foo/bar/a", "//root/foo/bar/b"};
  817. checkContents(O->dir_begin("//root/foo/bar", EC),
  818. makeStringRefVector(Contents));
  819. }
  820. }