From 502f14d6f2eee10d2993ed22d820f12cf52462d6 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 3 Feb 2022 12:53:22 -0800 Subject: [VFS] Add a "redirecting-with" field to overlays Extend "fallthrough" to allow a third option: "fallback". Fallthrough allows the original path to used if the redirected (or mapped) path fails. Fallback is the reverse of this, ie. use the original path and fallback to the mapped path otherwise. While this result *can* be achieved today using multiple overlays, this adds a much more intuitive option. As an example, take two directories "A" and "B". We would like files from "A" to be used, unless they don't exist, in which case the VFS should fallback to those in "B". With the current fallthrough option this is possible by adding two overlays: one mapping from A -> B and another mapping from B -> A. Since the frontend *nests* the two RedirectingFileSystems, the result will be that "A" is mapped to "B" and back to "A", unless it isn't in "A" in which case it fallsthrough to "B" (or fails if it exists in neither). Using "fallback" semantics allows a single overlay instead: one mapping from "A" to "B" but only using that mapping if the operation in "A" fails first. "redirect-only" is used to represent the current "fallthrough: false" case. Differential Revision: https://reviews.llvm.org/D117937 --- llvm/unittests/Support/VirtualFileSystemTest.cpp | 135 ++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) (limited to 'llvm/unittests/Support/VirtualFileSystemTest.cpp') diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp index 6b191c6..657fc0a 100644 --- a/llvm/unittests/Support/VirtualFileSystemTest.cpp +++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp @@ -1910,7 +1910,25 @@ TEST_F(VFSFromYAMLTest, IllegalVFSFile) { Lower); EXPECT_EQ(nullptr, FS.get()); - EXPECT_EQ(26, NumDiagnostics); + // invalid redirect kind + FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n" + " 'type': 'directory-remap',\n" + " 'name': '//root/A',\n" + " 'external-contents': '//root/B' }]}", + Lower); + EXPECT_EQ(nullptr, FS.get()); + + // redirect and fallthrough passed + FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n" + " 'fallthrough': true,\n" + " 'roots': [{\n" + " 'type': 'directory-remap',\n" + " 'name': '//root/A',\n" + " 'external-contents': '//root/B' }]}", + Lower); + EXPECT_EQ(nullptr, FS.get()); + + EXPECT_EQ(28, NumDiagnostics); } TEST_F(VFSFromYAMLTest, UseExternalName) { @@ -2710,6 +2728,121 @@ TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) { EXPECT_FALSE(FS->exists(_c.path("c"))); } +TEST_F(VFSFromYAMLTest, RedirectingWith) { + IntrusiveRefCntPtr Both(new DummyFileSystem()); + Both->addDirectory("//root/a"); + Both->addRegularFile("//root/a/f"); + Both->addDirectory("//root/b"); + Both->addRegularFile("//root/b/f"); + + IntrusiveRefCntPtr AOnly(new DummyFileSystem()); + AOnly->addDirectory("//root/a"); + AOnly->addRegularFile("//root/a/f"); + + IntrusiveRefCntPtr BOnly(new DummyFileSystem()); + BOnly->addDirectory("//root/b"); + BOnly->addRegularFile("//root/b/f"); + + auto BaseStr = std::string(" 'roots': [\n" + " {\n" + " 'type': 'directory-remap',\n" + " 'name': '//root/a',\n" + " 'external-contents': '//root/b'\n" + " }\n" + " ]\n" + "}"); + auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr; + auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr; + auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr; + + auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected, + StringRef Message) { + auto AF = FS.openFileForRead("//root/a/f"); + ASSERT_FALSE(AF.getError()) << Message; + auto AFName = (*AF)->getName(); + ASSERT_FALSE(AFName.getError()) << Message; + EXPECT_EQ(Expected.str(), AFName.get()) << Message; + + auto AS = FS.status("//root/a/f"); + ASSERT_FALSE(AS.getError()) << Message; + EXPECT_EQ(Expected.str(), AS->getName()) << Message; + }; + + auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) { + EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message; + EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message; + }; + + { + // `f` in both `a` and `b` + + // `fallthrough` tries `external-name` first, so should be `b` + IntrusiveRefCntPtr Fallthrough = + getFromYAMLString(FallthroughStr, Both); + ASSERT_TRUE(Fallthrough.get() != nullptr); + ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist"); + + // `fallback` tries the original name first, so should be `a` + IntrusiveRefCntPtr Fallback = + getFromYAMLString(FallbackStr, Both); + ASSERT_TRUE(Fallback.get() != nullptr); + ExpectPath(*Fallback, "//root/a/f", "fallback, both exist"); + + // `redirect-only` is the same as `fallthrough` but doesn't try the + // original on failure, so no change here (ie. `b`) + IntrusiveRefCntPtr Redirect = + getFromYAMLString(RedirectOnlyStr, Both); + ASSERT_TRUE(Redirect.get() != nullptr); + ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist"); + } + + { + // `f` in `a` only + + // Fallthrough to the original path, `a` + IntrusiveRefCntPtr Fallthrough = + getFromYAMLString(FallthroughStr, AOnly); + ASSERT_TRUE(Fallthrough.get() != nullptr); + ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only"); + + // Original first, so still `a` + IntrusiveRefCntPtr Fallback = + getFromYAMLString(FallbackStr, AOnly); + ASSERT_TRUE(Fallback.get() != nullptr); + ExpectPath(*Fallback, "//root/a/f", "fallback, a only"); + + // Fails since no fallthrough + IntrusiveRefCntPtr Redirect = + getFromYAMLString(RedirectOnlyStr, AOnly); + ASSERT_TRUE(Redirect.get() != nullptr); + ExpectFailure(*Redirect, "redirect-only, a only"); + } + + { + // `f` in `b` only + + // Tries `b` first (no fallthrough) + IntrusiveRefCntPtr Fallthrough = + getFromYAMLString(FallthroughStr, BOnly); + ASSERT_TRUE(Fallthrough.get() != nullptr); + ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only"); + + // Tries original first but then fallsback to `b` + IntrusiveRefCntPtr Fallback = + getFromYAMLString(FallbackStr, BOnly); + ASSERT_TRUE(Fallback.get() != nullptr); + ExpectPath(*Fallback, "//root/b/f", "fallback, b only"); + + // Redirect exists, so uses it (`b`) + IntrusiveRefCntPtr Redirect = + getFromYAMLString(RedirectOnlyStr, BOnly); + ASSERT_TRUE(Redirect.get() != nullptr); + ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only"); + } + + EXPECT_EQ(0, NumDiagnostics); +} + TEST(VFSFromRemappedFilesTest, Basic) { IntrusiveRefCntPtr BaseFS = new vfs::InMemoryFileSystem; -- cgit v1.1