VirtualFileSystemTest.cpp 38 KB

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