From 0b9981b180ef2f08d2a97cfda2fb6ca35ad5e93c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 15 Oct 2019 17:14:24 +0000 Subject: [VirtualFileSystem] Support virtual working directory in the RedirectingFS Before this patch, changing the working directory of the RedirectingFS would just forward to its external file system. This prevented us from having a working directory that only existed in the VFS mapping. This patch adds support for a virtual working directory in the RedirectingFileSystem. It now keeps track of its own WD in addition to updating the WD of the external file system. This ensures that we can still fall through for relative paths. This change was originally motivated by the reproducer infrastructure in LLDB where we want to deal transparently with relative paths. Differential revision: https://reviews.llvm.org/D65677 llvm-svn: 374917 --- llvm/unittests/Support/VirtualFileSystemTest.cpp | 175 ++++++++++++++++++++++- 1 file changed, 171 insertions(+), 4 deletions(-) (limited to 'llvm/unittests/Support/VirtualFileSystemTest.cpp') diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp index a867bc57e94..b81cb86f0b6 100644 --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -41,7 +41,9 @@ struct DummyFile : public vfs::File { class DummyFileSystem : public vfs::FileSystem { int FSID; // used to produce UniqueIDs int FileID; // used to produce UniqueIDs + std::string WorkingDirectory; std::map FilesAndDirs; + typedef std::map::const_iterator const_iterator; static int getNextFSID() { static int Count = 0; @@ -52,8 +54,7 @@ public: DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} ErrorOr status(const Twine &Path) override { - std::map::iterator I = - FilesAndDirs.find(Path.str()); + auto I = findEntry(Path); if (I == FilesAndDirs.end()) return make_error_code(llvm::errc::no_such_file_or_directory); return I->second; @@ -66,15 +67,16 @@ public: return S.getError(); } llvm::ErrorOr getCurrentWorkingDirectory() const override { - return std::string(); + return WorkingDirectory; } std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + WorkingDirectory = Path.str(); return std::error_code(); } // Map any symlink to "/symlink". std::error_code getRealPath(const Twine &Path, SmallVectorImpl &Output) const override { - auto I = FilesAndDirs.find(Path.str()); + auto I = findEntry(Path); if (I == FilesAndDirs.end()) return make_error_code(llvm::errc::no_such_file_or_directory); if (I->second.isSymlink()) { @@ -136,6 +138,14 @@ public: FilesAndDirs[Path] = Status; } + const_iterator findEntry(const Twine &Path) const { + SmallString<128> P; + Path.toVector(P); + std::error_code EC = makeAbsolute(P); + assert(!EC); + return FilesAndDirs.find(P.str()); + } + void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { vfs::Status S(Path, UniqueID(FSID, FileID++), std::chrono::system_clock::now(), 0, 0, 1024, @@ -158,6 +168,12 @@ public: } }; +class ErrorDummyFileSystem : public DummyFileSystem { + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return llvm::errc::no_such_file_or_directory; + } +}; + /// Replace back-slashes by front-slashes. std::string getPosixPath(std::string S) { SmallString<128> Result; @@ -1994,3 +2010,154 @@ TEST_F(VFSFromYAMLTest, GetRealPath) { EXPECT_EQ(FS->getRealPath("/non_existing", RealPath), errc::no_such_file_or_directory); } + +TEST_F(VFSFromYAMLTest, WorkingDirectory) { + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addDirectory("//root/"); + Lower->addDirectory("//root/foo"); + Lower->addRegularFile("//root/foo/a"); + Lower->addRegularFile("//root/foo/b"); + IntrusiveRefCntPtr FS = getFromYAMLString( + "{ 'use-external-names': false,\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'bar/a',\n" + " 'external-contents': '//root/foo/a'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar/"); + ASSERT_FALSE(EC); + + llvm::ErrorOr WorkingDir = FS->getCurrentWorkingDirectory(); + ASSERT_TRUE(WorkingDir); + EXPECT_EQ(*WorkingDir, "//root/bar/"); + + llvm::ErrorOr Status = FS->status("./a"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->isStatusKnown()); + EXPECT_FALSE(Status->isDirectory()); + EXPECT_TRUE(Status->isRegularFile()); + EXPECT_FALSE(Status->isSymlink()); + EXPECT_FALSE(Status->isOther()); + EXPECT_TRUE(Status->exists()); + + EC = FS->setCurrentWorkingDirectory("bogus"); + ASSERT_TRUE(EC); + WorkingDir = FS->getCurrentWorkingDirectory(); + ASSERT_TRUE(WorkingDir); + EXPECT_EQ(*WorkingDir, "//root/bar/"); + + EC = FS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + WorkingDir = FS->getCurrentWorkingDirectory(); + ASSERT_TRUE(WorkingDir); + EXPECT_EQ(*WorkingDir, "//root/"); + + EC = FS->setCurrentWorkingDirectory("bar/"); + ASSERT_FALSE(EC); + WorkingDir = FS->getCurrentWorkingDirectory(); + ASSERT_TRUE(WorkingDir); + EXPECT_EQ(*WorkingDir, "//root/bar/"); +} + +TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) { + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addDirectory("//root/"); + Lower->addDirectory("//root/foo"); + Lower->addRegularFile("//root/foo/a"); + Lower->addRegularFile("//root/foo/b"); + Lower->addRegularFile("//root/c"); + IntrusiveRefCntPtr FS = getFromYAMLString( + "{ 'use-external-names': false,\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'bar/a',\n" + " 'external-contents': '//root/foo/a'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + ASSERT_TRUE(FS.get() != nullptr); + + llvm::ErrorOr Status = FS->status("bar/a"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->exists()); + + Status = FS->status("foo/a"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->exists()); + + EC = FS->setCurrentWorkingDirectory("//root/bar/"); + ASSERT_FALSE(EC); + + Status = FS->status("./a"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->exists()); + + Status = FS->status("./b"); + ASSERT_TRUE(Status.getError()); + + Status = FS->status("./c"); + ASSERT_TRUE(Status.getError()); + + EC = FS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + + Status = FS->status("c"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->exists()); +} + +TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) { + IntrusiveRefCntPtr Lower(new ErrorDummyFileSystem()); + Lower->addDirectory("//root/"); + Lower->addDirectory("//root/foo"); + Lower->addRegularFile("//root/foo/a"); + Lower->addRegularFile("//root/foo/b"); + Lower->addRegularFile("//root/c"); + IntrusiveRefCntPtr FS = getFromYAMLString( + "{ 'use-external-names': false,\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'bar/a',\n" + " 'external-contents': '//root/foo/a'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + std::error_code EC = FS->setCurrentWorkingDirectory("//root/"); + ASSERT_FALSE(EC); + ASSERT_TRUE(FS.get() != nullptr); + + llvm::ErrorOr Status = FS->status("bar/a"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->exists()); + + Status = FS->status("foo/a"); + ASSERT_TRUE(Status.getError()); +} -- cgit v1.2.3