2018-10-10 21:27:25 +08:00
|
|
|
//===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
|
2014-02-21 05:59:23 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-10 21:27:25 +08:00
|
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
#include "llvm/ADT/Triple.h"
|
2018-04-30 21:52:15 +08:00
|
|
|
#include "llvm/Config/llvm-config.h"
|
2014-06-14 01:20:50 +08:00
|
|
|
#include "llvm/Support/Errc.h"
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
#include "llvm/Support/Host.h"
|
2014-02-22 07:39:37 +08:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
2014-02-22 07:39:37 +08:00
|
|
|
#include "llvm/Support/SourceMgr.h"
|
2018-09-04 22:15:53 +08:00
|
|
|
#include "gmock/gmock.h"
|
2014-02-21 05:59:23 +08:00
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <map>
|
2018-09-04 22:15:53 +08:00
|
|
|
#include <string>
|
2015-10-07 07:40:43 +08:00
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
using namespace llvm;
|
|
|
|
using llvm::sys::fs::UniqueID;
|
2018-10-02 00:07:03 +08:00
|
|
|
using testing::ElementsAre;
|
|
|
|
using testing::Pair;
|
|
|
|
using testing::UnorderedElementsAre;
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
namespace {
|
2015-12-11 07:41:39 +08:00
|
|
|
struct DummyFile : public vfs::File {
|
|
|
|
vfs::Status S;
|
|
|
|
explicit DummyFile(vfs::Status S) : S(S) {}
|
|
|
|
llvm::ErrorOr<vfs::Status> status() override { return S; }
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
|
|
|
|
getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
|
|
|
|
bool IsVolatile) override {
|
|
|
|
llvm_unreachable("unimplemented");
|
|
|
|
}
|
2016-01-27 03:01:06 +08:00
|
|
|
std::error_code close() override { return std::error_code(); }
|
2015-12-11 07:41:39 +08:00
|
|
|
};
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
class DummyFileSystem : public vfs::FileSystem {
|
|
|
|
int FSID; // used to produce UniqueIDs
|
|
|
|
int FileID; // used to produce UniqueIDs
|
|
|
|
std::map<std::string, vfs::Status> FilesAndDirs;
|
|
|
|
|
|
|
|
static int getNextFSID() {
|
|
|
|
static int Count = 0;
|
|
|
|
return Count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
|
|
|
|
|
2014-10-02 00:56:40 +08:00
|
|
|
ErrorOr<vfs::Status> status(const Twine &Path) override {
|
2014-02-21 05:59:23 +08:00
|
|
|
std::map<std::string, vfs::Status>::iterator I =
|
2014-02-22 07:39:37 +08:00
|
|
|
FilesAndDirs.find(Path.str());
|
2014-02-21 05:59:23 +08:00
|
|
|
if (I == FilesAndDirs.end())
|
2014-06-14 01:20:50 +08:00
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
2014-02-21 05:59:23 +08:00
|
|
|
return I->second;
|
|
|
|
}
|
2014-10-27 06:44:13 +08:00
|
|
|
ErrorOr<std::unique_ptr<vfs::File>>
|
|
|
|
openFileForRead(const Twine &Path) override {
|
2015-12-11 07:41:39 +08:00
|
|
|
auto S = status(Path);
|
|
|
|
if (S)
|
|
|
|
return std::unique_ptr<vfs::File>(new DummyFile{*S});
|
|
|
|
return S.getError();
|
2014-02-21 05:59:23 +08:00
|
|
|
}
|
2015-10-05 21:55:20 +08:00
|
|
|
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
|
|
|
|
return std::error_code();
|
|
|
|
}
|
2018-05-18 21:22:49 +08:00
|
|
|
// Map any symlink to "/symlink".
|
|
|
|
std::error_code getRealPath(const Twine &Path,
|
|
|
|
SmallVectorImpl<char> &Output) const override {
|
|
|
|
auto I = FilesAndDirs.find(Path.str());
|
|
|
|
if (I == FilesAndDirs.end())
|
|
|
|
return make_error_code(llvm::errc::no_such_file_or_directory);
|
|
|
|
if (I->second.isSymlink()) {
|
|
|
|
Output.clear();
|
|
|
|
Twine("/symlink").toVector(Output);
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
Output.clear();
|
|
|
|
Path.toVector(Output);
|
|
|
|
return std::error_code();
|
|
|
|
}
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2018-10-10 21:27:25 +08:00
|
|
|
struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
|
2014-06-25 03:37:16 +08:00
|
|
|
std::map<std::string, vfs::Status> &FilesAndDirs;
|
|
|
|
std::map<std::string, vfs::Status>::iterator I;
|
|
|
|
std::string Path;
|
2014-06-26 04:25:40 +08:00
|
|
|
bool isInPath(StringRef S) {
|
|
|
|
if (Path.size() < S.size() && S.find(Path) == 0) {
|
|
|
|
auto LastSep = S.find_last_of('/');
|
2018-10-10 21:27:25 +08:00
|
|
|
if (LastSep == Path.size() || LastSep == Path.size() - 1)
|
2014-06-26 04:25:40 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
|
|
|
|
const Twine &_Path)
|
|
|
|
: FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
|
|
|
|
Path(_Path.str()) {
|
2018-10-10 21:27:25 +08:00
|
|
|
for (; I != FilesAndDirs.end(); ++I) {
|
2014-06-26 04:25:40 +08:00
|
|
|
if (isInPath(I->first)) {
|
2018-09-14 20:47:38 +08:00
|
|
|
CurrentEntry =
|
|
|
|
vfs::directory_entry(I->second.getName(), I->second.getType());
|
2014-06-25 03:37:16 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::error_code increment() override {
|
|
|
|
++I;
|
2018-10-10 21:27:25 +08:00
|
|
|
for (; I != FilesAndDirs.end(); ++I) {
|
2014-06-26 04:25:40 +08:00
|
|
|
if (isInPath(I->first)) {
|
2018-09-14 20:47:38 +08:00
|
|
|
CurrentEntry =
|
|
|
|
vfs::directory_entry(I->second.getName(), I->second.getType());
|
2014-06-25 03:37:16 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (I == FilesAndDirs.end())
|
2018-09-14 20:47:38 +08:00
|
|
|
CurrentEntry = vfs::directory_entry();
|
2014-06-25 03:37:16 +08:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
vfs::directory_iterator dir_begin(const Twine &Dir,
|
|
|
|
std::error_code &EC) override {
|
|
|
|
return vfs::directory_iterator(
|
|
|
|
std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
|
|
|
|
}
|
|
|
|
|
2014-02-21 05:59:23 +08:00
|
|
|
void addEntry(StringRef Path, const vfs::Status &Status) {
|
|
|
|
FilesAndDirs[Path] = Status;
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
|
2016-11-09 18:52:22 +08:00
|
|
|
vfs::Status S(Path, UniqueID(FSID, FileID++),
|
|
|
|
std::chrono::system_clock::now(), 0, 0, 1024,
|
|
|
|
sys::fs::file_type::regular_file, Perms);
|
2014-02-21 05:59:23 +08:00
|
|
|
addEntry(Path, S);
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
|
2016-11-09 18:52:22 +08:00
|
|
|
vfs::Status S(Path, UniqueID(FSID, FileID++),
|
|
|
|
std::chrono::system_clock::now(), 0, 0, 0,
|
|
|
|
sys::fs::file_type::directory_file, Perms);
|
2014-02-21 05:59:23 +08:00
|
|
|
addEntry(Path, S);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addSymlink(StringRef Path) {
|
2016-11-09 18:52:22 +08:00
|
|
|
vfs::Status S(Path, UniqueID(FSID, FileID++),
|
|
|
|
std::chrono::system_clock::now(), 0, 0, 0,
|
|
|
|
sys::fs::file_type::symlink_file, sys::fs::all_all);
|
2014-02-21 05:59:23 +08:00
|
|
|
addEntry(Path, S);
|
|
|
|
}
|
|
|
|
};
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
|
|
|
|
/// Replace back-slashes by front-slashes.
|
|
|
|
std::string getPosixPath(std::string S) {
|
|
|
|
SmallString<128> Result;
|
|
|
|
llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);
|
|
|
|
return Result.str();
|
2018-08-09 06:31:14 +08:00
|
|
|
}
|
2014-02-21 05:59:23 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
TEST(VirtualFileSystemTest, StatusQueries) {
|
2014-02-21 05:59:23 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
|
2014-06-13 04:37:59 +08:00
|
|
|
ErrorOr<vfs::Status> Status((std::error_code()));
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
D->addRegularFile("/foo");
|
|
|
|
Status = D->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status->isStatusKnown());
|
|
|
|
EXPECT_FALSE(Status->isDirectory());
|
|
|
|
EXPECT_TRUE(Status->isRegularFile());
|
|
|
|
EXPECT_FALSE(Status->isSymlink());
|
|
|
|
EXPECT_FALSE(Status->isOther());
|
|
|
|
EXPECT_TRUE(Status->exists());
|
|
|
|
|
|
|
|
D->addDirectory("/bar");
|
|
|
|
Status = D->status("/bar");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status->isStatusKnown());
|
|
|
|
EXPECT_TRUE(Status->isDirectory());
|
|
|
|
EXPECT_FALSE(Status->isRegularFile());
|
|
|
|
EXPECT_FALSE(Status->isSymlink());
|
|
|
|
EXPECT_FALSE(Status->isOther());
|
|
|
|
EXPECT_TRUE(Status->exists());
|
|
|
|
|
|
|
|
D->addSymlink("/baz");
|
|
|
|
Status = D->status("/baz");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status->isStatusKnown());
|
|
|
|
EXPECT_FALSE(Status->isDirectory());
|
|
|
|
EXPECT_FALSE(Status->isRegularFile());
|
|
|
|
EXPECT_TRUE(Status->isSymlink());
|
|
|
|
EXPECT_FALSE(Status->isOther());
|
|
|
|
EXPECT_TRUE(Status->exists());
|
|
|
|
|
|
|
|
EXPECT_TRUE(Status->equivalent(*Status));
|
|
|
|
ErrorOr<vfs::Status> Status2 = D->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status2.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_FALSE(Status->equivalent(*Status2));
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
|
2014-02-21 05:59:23 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
|
2014-06-13 04:37:59 +08:00
|
|
|
ErrorOr<vfs::Status> Status((std::error_code()));
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_FALSE(Status = D->status("/foo"));
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
|
|
|
|
EXPECT_FALSE(Status = O->status("/foo"));
|
|
|
|
|
|
|
|
D->addRegularFile("/foo");
|
|
|
|
Status = D->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
|
2014-06-13 04:37:59 +08:00
|
|
|
ErrorOr<vfs::Status> Status2((std::error_code()));
|
2014-02-21 05:59:23 +08:00
|
|
|
Status2 = O->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(Status2.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status->equivalent(*Status2));
|
|
|
|
}
|
|
|
|
|
2018-05-18 21:22:49 +08:00
|
|
|
TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addRegularFile("/foo");
|
|
|
|
Lower->addSymlink("/lower_link");
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
// Regular file.
|
|
|
|
SmallString<16> RealPath;
|
|
|
|
EXPECT_FALSE(O->getRealPath("/foo", RealPath));
|
|
|
|
EXPECT_EQ(RealPath.str(), "/foo");
|
|
|
|
|
|
|
|
// Expect no error getting real path for symlink in lower overlay.
|
|
|
|
EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
|
|
|
|
EXPECT_EQ(RealPath.str(), "/symlink");
|
|
|
|
|
|
|
|
// Try a non-existing link.
|
|
|
|
EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
|
|
|
|
errc::no_such_file_or_directory);
|
|
|
|
|
|
|
|
// Add a new symlink in upper.
|
|
|
|
Upper->addSymlink("/upper_link");
|
|
|
|
EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
|
|
|
|
EXPECT_EQ(RealPath.str(), "/symlink");
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
TEST(VirtualFileSystemTest, OverlayFiles) {
|
2014-02-21 05:59:23 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Base));
|
2014-02-21 05:59:23 +08:00
|
|
|
O->pushOverlay(Middle);
|
|
|
|
O->pushOverlay(Top);
|
|
|
|
|
2014-06-13 04:37:59 +08:00
|
|
|
ErrorOr<vfs::Status> Status1((std::error_code())),
|
|
|
|
Status2((std::error_code())), Status3((std::error_code())),
|
|
|
|
StatusB((std::error_code())), StatusM((std::error_code())),
|
|
|
|
StatusT((std::error_code()));
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
Base->addRegularFile("/foo");
|
|
|
|
StatusB = Base->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(StatusB.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Status1 = O->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status1.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Middle->addRegularFile("/foo");
|
|
|
|
StatusM = Middle->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(StatusM.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Status2 = O->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status2.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Top->addRegularFile("/foo");
|
|
|
|
StatusT = Top->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(StatusT.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Status3 = O->status("/foo");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status3.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
|
|
|
|
EXPECT_TRUE(Status1->equivalent(*StatusB));
|
|
|
|
EXPECT_TRUE(Status2->equivalent(*StatusM));
|
|
|
|
EXPECT_TRUE(Status3->equivalent(*StatusT));
|
|
|
|
|
|
|
|
EXPECT_FALSE(Status1->equivalent(*Status2));
|
|
|
|
EXPECT_FALSE(Status2->equivalent(*Status3));
|
|
|
|
EXPECT_FALSE(Status1->equivalent(*Status3));
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
|
2014-02-21 05:59:23 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
2014-02-21 05:59:23 +08:00
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
Lower->addDirectory("/lower-only");
|
|
|
|
Upper->addDirectory("/upper-only");
|
|
|
|
|
|
|
|
// non-merged paths should be the same
|
|
|
|
ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status1.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status2.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status1->equivalent(*Status2));
|
|
|
|
|
|
|
|
Status1 = Upper->status("/upper-only");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status1.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
Status2 = O->status("/upper-only");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status2.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_TRUE(Status1->equivalent(*Status2));
|
|
|
|
}
|
|
|
|
|
2014-02-22 07:39:37 +08:00
|
|
|
TEST(VirtualFileSystemTest, MergedDirPermissions) {
|
2014-02-21 05:59:23 +08:00
|
|
|
// merged directories get the permissions of the upper dir
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
2014-02-21 05:59:23 +08:00
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
2014-06-13 04:37:59 +08:00
|
|
|
ErrorOr<vfs::Status> Status((std::error_code()));
|
2014-02-21 05:59:23 +08:00
|
|
|
Lower->addDirectory("/both", sys::fs::owner_read);
|
|
|
|
Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
|
|
|
|
Status = O->status("/both");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_EQ(0740, Status->getPermissions());
|
|
|
|
|
|
|
|
// permissions (as usual) are not recursively applied
|
|
|
|
Lower->addRegularFile("/both/foo", sys::fs::owner_read);
|
|
|
|
Upper->addRegularFile("/both/bar", sys::fs::owner_write);
|
|
|
|
Status = O->status("/both/foo");
|
2018-10-10 21:27:25 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_EQ(0400, Status->getPermissions());
|
|
|
|
Status = O->status("/both/bar");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(Status.getError());
|
2014-02-21 05:59:23 +08:00
|
|
|
EXPECT_EQ(0200, Status->getPermissions());
|
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
namespace {
|
|
|
|
struct ScopedDir {
|
|
|
|
SmallString<128> Path;
|
2018-10-10 21:27:25 +08:00
|
|
|
ScopedDir(const Twine &Name, bool Unique = false) {
|
2014-06-25 03:37:16 +08:00
|
|
|
std::error_code EC;
|
|
|
|
if (Unique) {
|
2018-10-10 21:27:25 +08:00
|
|
|
EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
|
2014-06-25 03:37:16 +08:00
|
|
|
} else {
|
|
|
|
Path = Name.str();
|
|
|
|
EC = llvm::sys::fs::create_directory(Twine(Path));
|
|
|
|
}
|
|
|
|
if (EC)
|
|
|
|
Path = "";
|
|
|
|
EXPECT_FALSE(EC);
|
|
|
|
}
|
|
|
|
~ScopedDir() {
|
2017-06-16 05:01:24 +08:00
|
|
|
if (Path != "") {
|
2014-06-25 03:37:16 +08:00
|
|
|
EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
|
2017-06-16 05:01:24 +08:00
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
operator StringRef() { return Path.str(); }
|
|
|
|
};
|
2017-03-14 08:14:40 +08:00
|
|
|
|
|
|
|
struct ScopedLink {
|
|
|
|
SmallString<128> Path;
|
|
|
|
ScopedLink(const Twine &To, const Twine &From) {
|
|
|
|
Path = From.str();
|
|
|
|
std::error_code EC = sys::fs::create_link(To, From);
|
|
|
|
if (EC)
|
|
|
|
Path = "";
|
|
|
|
EXPECT_FALSE(EC);
|
|
|
|
}
|
|
|
|
~ScopedLink() {
|
2017-06-16 05:01:24 +08:00
|
|
|
if (Path != "") {
|
2017-03-14 08:14:40 +08:00
|
|
|
EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
|
2017-06-16 05:01:24 +08:00
|
|
|
}
|
2017-03-14 08:14:40 +08:00
|
|
|
}
|
|
|
|
operator StringRef() { return Path.str(); }
|
|
|
|
};
|
2015-10-07 07:40:43 +08:00
|
|
|
} // end anonymous namespace
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
TEST(VirtualFileSystemTest, BasicRealFSIteration) {
|
2018-10-10 21:27:25 +08:00
|
|
|
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
2014-06-25 03:37:16 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
|
|
|
|
|
2018-10-10 21:27:25 +08:00
|
|
|
ScopedDir _a(TestDirectory + "/a");
|
|
|
|
ScopedDir _ab(TestDirectory + "/a/b");
|
|
|
|
ScopedDir _c(TestDirectory + "/c");
|
|
|
|
ScopedDir _cd(TestDirectory + "/c/d");
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
I = FS->dir_begin(Twine(TestDirectory), EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
ASSERT_NE(vfs::directory_iterator(), I);
|
2014-06-25 05:08:13 +08:00
|
|
|
// Check either a or c, since we can't rely on the iteration order.
|
2018-09-14 20:47:38 +08:00
|
|
|
EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
|
2014-06-25 03:37:16 +08:00
|
|
|
I.increment(EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
ASSERT_NE(vfs::directory_iterator(), I);
|
2018-09-14 20:47:38 +08:00
|
|
|
EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
|
2014-06-25 03:37:16 +08:00
|
|
|
I.increment(EC);
|
|
|
|
EXPECT_EQ(vfs::directory_iterator(), I);
|
|
|
|
}
|
|
|
|
|
2017-03-14 08:14:40 +08:00
|
|
|
#ifdef LLVM_ON_UNIX
|
|
|
|
TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
|
|
|
|
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
|
|
|
|
|
|
|
|
ScopedLink _a("no_such_file", TestDirectory + "/a");
|
|
|
|
ScopedDir _b(TestDirectory + "/b");
|
|
|
|
ScopedLink _c("no_such_file", TestDirectory + "/c");
|
|
|
|
|
2018-10-02 00:07:03 +08:00
|
|
|
// Should get no iteration error, but a stat error for the broken symlinks.
|
|
|
|
std::map<std::string, std::error_code> StatResults;
|
2017-03-14 08:14:40 +08:00
|
|
|
std::error_code EC;
|
|
|
|
for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
|
|
|
|
I != E; I.increment(EC)) {
|
2018-10-02 00:07:03 +08:00
|
|
|
EXPECT_FALSE(EC);
|
|
|
|
StatResults[sys::path::filename(I->path())] =
|
|
|
|
FS->status(I->path()).getError();
|
2017-03-14 08:14:40 +08:00
|
|
|
}
|
2018-10-02 00:07:03 +08:00
|
|
|
EXPECT_THAT(
|
|
|
|
StatResults,
|
|
|
|
ElementsAre(
|
|
|
|
Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
|
|
|
|
Pair("b", std::error_code()),
|
|
|
|
Pair("c",
|
|
|
|
std::make_error_code(std::errc::no_such_file_or_directory))));
|
2017-03-14 08:14:40 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-06-26 04:25:40 +08:00
|
|
|
TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
|
2018-10-10 21:27:25 +08:00
|
|
|
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
2014-06-26 04:25:40 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
|
|
|
|
|
2018-10-10 21:27:25 +08:00
|
|
|
ScopedDir _a(TestDirectory + "/a");
|
|
|
|
ScopedDir _ab(TestDirectory + "/a/b");
|
|
|
|
ScopedDir _c(TestDirectory + "/c");
|
|
|
|
ScopedDir _cd(TestDirectory + "/c/d");
|
2014-06-26 04:25:40 +08:00
|
|
|
|
|
|
|
I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
ASSERT_NE(vfs::recursive_directory_iterator(), I);
|
|
|
|
|
|
|
|
std::vector<std::string> Contents;
|
|
|
|
for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
|
|
|
|
I.increment(EC)) {
|
2018-09-14 20:47:38 +08:00
|
|
|
Contents.push_back(I->path());
|
2014-06-26 04:25:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check contents, which may be in any order
|
|
|
|
EXPECT_EQ(4U, Contents.size());
|
2018-10-10 21:27:25 +08:00
|
|
|
int Counts[4] = {0, 0, 0, 0};
|
2014-06-26 04:25:40 +08:00
|
|
|
for (const std::string &Name : Contents) {
|
|
|
|
ASSERT_FALSE(Name.empty());
|
2018-10-10 21:27:25 +08:00
|
|
|
int Index = Name[Name.size() - 1] - 'a';
|
2014-06-26 04:25:40 +08:00
|
|
|
ASSERT_TRUE(Index >= 0 && Index < 4);
|
|
|
|
Counts[Index]++;
|
|
|
|
}
|
|
|
|
EXPECT_EQ(1, Counts[0]); // a
|
|
|
|
EXPECT_EQ(1, Counts[1]); // b
|
|
|
|
EXPECT_EQ(1, Counts[2]); // c
|
|
|
|
EXPECT_EQ(1, Counts[3]); // d
|
|
|
|
}
|
|
|
|
|
2017-03-14 08:14:40 +08:00
|
|
|
#ifdef LLVM_ON_UNIX
|
|
|
|
TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
|
|
|
|
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
|
|
|
|
|
|
|
|
ScopedLink _a("no_such_file", TestDirectory + "/a");
|
|
|
|
ScopedDir _b(TestDirectory + "/b");
|
|
|
|
ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
|
|
|
|
ScopedDir _bb(TestDirectory + "/b/b");
|
|
|
|
ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
|
|
|
|
ScopedLink _c("no_such_file", TestDirectory + "/c");
|
|
|
|
ScopedDir _d(TestDirectory + "/d");
|
|
|
|
ScopedDir _dd(TestDirectory + "/d/d");
|
|
|
|
ScopedDir _ddd(TestDirectory + "/d/d/d");
|
|
|
|
ScopedLink _e("no_such_file", TestDirectory + "/e");
|
|
|
|
|
2018-04-05 03:47:25 +08:00
|
|
|
std::vector<std::string> VisitedBrokenSymlinks;
|
|
|
|
std::vector<std::string> VisitedNonBrokenSymlinks;
|
2017-03-14 08:14:40 +08:00
|
|
|
std::error_code EC;
|
|
|
|
for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
|
|
|
|
I != E; I.increment(EC)) {
|
2018-10-02 00:07:03 +08:00
|
|
|
EXPECT_FALSE(EC);
|
|
|
|
(FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
|
|
|
|
.push_back(I->path());
|
2017-03-14 08:14:40 +08:00
|
|
|
}
|
|
|
|
|
2018-04-05 03:47:25 +08:00
|
|
|
// Check visited file names.
|
2018-10-02 00:07:03 +08:00
|
|
|
EXPECT_THAT(VisitedBrokenSymlinks,
|
|
|
|
UnorderedElementsAre(StringRef(_a), StringRef(_ba),
|
|
|
|
StringRef(_bc), StringRef(_c),
|
|
|
|
StringRef(_e)));
|
|
|
|
EXPECT_THAT(VisitedNonBrokenSymlinks,
|
|
|
|
UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d),
|
|
|
|
StringRef(_dd), StringRef(_ddd)));
|
2017-03-14 08:14:40 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-06-26 04:25:40 +08:00
|
|
|
template <typename DirIter>
|
2016-05-12 04:58:47 +08:00
|
|
|
static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
|
2014-06-25 03:37:16 +08:00
|
|
|
std::error_code EC;
|
2016-05-12 04:58:47 +08:00
|
|
|
SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
|
|
|
|
SmallVector<std::string, 4> InputToCheck;
|
|
|
|
|
|
|
|
// Do not rely on iteration order to check for contents, sort both
|
|
|
|
// content vectors before comparison.
|
|
|
|
for (DirIter E; !EC && I != E; I.increment(EC))
|
2018-09-14 20:47:38 +08:00
|
|
|
InputToCheck.push_back(I->path());
|
2016-05-12 04:58:47 +08:00
|
|
|
|
2018-09-27 06:16:28 +08:00
|
|
|
llvm::sort(InputToCheck);
|
|
|
|
llvm::sort(Expected);
|
2016-05-12 04:58:47 +08:00
|
|
|
EXPECT_EQ(InputToCheck.size(), Expected.size());
|
|
|
|
|
|
|
|
unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
|
|
|
|
for (unsigned Idx = 0; Idx != LastElt; ++Idx)
|
2016-05-13 03:13:04 +08:00
|
|
|
EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(VirtualFileSystemTest, OverlayIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
std::error_code EC;
|
2014-06-25 12:34:10 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
Lower->addRegularFile("/file1");
|
2014-06-25 12:34:10 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
Upper->addRegularFile("/file2");
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
Lower->addDirectory("/dir1");
|
|
|
|
Lower->addRegularFile("/dir1/foo");
|
|
|
|
Upper->addDirectory("/dir2");
|
|
|
|
Upper->addRegularFile("/dir2/foo");
|
2014-06-25 12:34:10 +08:00
|
|
|
checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
|
2014-06-26 04:25:40 +08:00
|
|
|
TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(Middle);
|
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
|
|
|
|
ArrayRef<StringRef>());
|
|
|
|
|
|
|
|
Lower->addRegularFile("/file1");
|
|
|
|
checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
|
|
|
|
ArrayRef<StringRef>("/file1"));
|
|
|
|
|
|
|
|
Upper->addDirectory("/dir");
|
|
|
|
Upper->addRegularFile("/dir/file2");
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
|
|
|
|
{"/dir", "/dir/file2", "/file1"});
|
2014-06-26 04:25:40 +08:00
|
|
|
|
|
|
|
Lower->addDirectory("/dir1");
|
|
|
|
Lower->addRegularFile("/dir1/foo");
|
|
|
|
Lower->addDirectory("/dir1/a");
|
|
|
|
Lower->addRegularFile("/dir1/a/b");
|
|
|
|
Middle->addDirectory("/a");
|
|
|
|
Middle->addDirectory("/a/b");
|
|
|
|
Middle->addDirectory("/a/b/c");
|
|
|
|
Middle->addRegularFile("/a/b/c/d");
|
|
|
|
Middle->addRegularFile("/hiddenByUp");
|
|
|
|
Upper->addDirectory("/dir2");
|
|
|
|
Upper->addRegularFile("/dir2/foo");
|
|
|
|
Upper->addRegularFile("/hiddenByUp");
|
|
|
|
checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
|
|
|
|
ArrayRef<StringRef>("/dir2/foo"));
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
|
|
|
|
{"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
|
|
|
|
"/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
|
|
|
|
"/dir1/a/b", "/dir1/foo", "/file1"});
|
2014-06-26 04:25:40 +08:00
|
|
|
}
|
|
|
|
|
2014-06-25 03:37:16 +08:00
|
|
|
TEST(VirtualFileSystemTest, ThreeLevelIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(Middle);
|
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
std::error_code EC;
|
2014-06-25 12:34:10 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
Middle->addRegularFile("/file2");
|
2014-06-25 12:34:10 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
Lower->addRegularFile("/file1");
|
|
|
|
Upper->addRegularFile("/file3");
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(VirtualFileSystemTest, HiddenInIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(Middle);
|
|
|
|
O->pushOverlay(Upper);
|
|
|
|
|
|
|
|
std::error_code EC;
|
2018-09-14 20:47:38 +08:00
|
|
|
Lower->addRegularFile("/onlyInLow");
|
|
|
|
Lower->addDirectory("/hiddenByMid");
|
|
|
|
Lower->addDirectory("/hiddenByUp");
|
|
|
|
Middle->addRegularFile("/onlyInMid");
|
|
|
|
Middle->addRegularFile("/hiddenByMid");
|
|
|
|
Middle->addDirectory("/hiddenByUp");
|
|
|
|
Upper->addRegularFile("/onlyInUp");
|
|
|
|
Upper->addRegularFile("/hiddenByUp");
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(
|
|
|
|
O->dir_begin("/", EC),
|
|
|
|
{"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
// Make sure we get the top-most entry
|
2014-06-25 05:08:13 +08:00
|
|
|
{
|
|
|
|
std::error_code EC;
|
|
|
|
vfs::directory_iterator I = O->dir_begin("/", EC), E;
|
2018-10-10 21:27:25 +08:00
|
|
|
for (; !EC && I != E; I.increment(EC))
|
2018-09-14 20:47:38 +08:00
|
|
|
if (I->path() == "/hiddenByUp")
|
2014-06-25 05:08:13 +08:00
|
|
|
break;
|
|
|
|
ASSERT_NE(E, I);
|
2018-09-14 20:47:38 +08:00
|
|
|
EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
|
2014-06-25 05:08:13 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
std::error_code EC;
|
|
|
|
vfs::directory_iterator I = O->dir_begin("/", EC), E;
|
2018-10-10 21:27:25 +08:00
|
|
|
for (; !EC && I != E; I.increment(EC))
|
2018-09-14 20:47:38 +08:00
|
|
|
if (I->path() == "/hiddenByMid")
|
2014-06-25 05:08:13 +08:00
|
|
|
break;
|
|
|
|
ASSERT_NE(E, I);
|
2018-09-14 20:47:38 +08:00
|
|
|
EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
|
2014-06-25 05:08:13 +08:00
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:14 +08:00
|
|
|
class InMemoryFileSystemTest : public ::testing::Test {
|
|
|
|
protected:
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::vfs::InMemoryFileSystem FS;
|
|
|
|
llvm::vfs::InMemoryFileSystem NormalizedFS;
|
2015-10-13 00:16:39 +08:00
|
|
|
|
|
|
|
InMemoryFileSystemTest()
|
|
|
|
: FS(/*UseNormalizedPaths=*/false),
|
|
|
|
NormalizedFS(/*UseNormalizedPaths=*/true) {}
|
2015-10-05 21:55:14 +08:00
|
|
|
};
|
|
|
|
|
2018-09-04 22:15:53 +08:00
|
|
|
MATCHER_P2(IsHardLinkTo, FS, Target, "") {
|
|
|
|
StringRef From = arg;
|
|
|
|
StringRef To = Target;
|
|
|
|
auto OpenedFrom = FS->openFileForRead(From);
|
|
|
|
auto OpenedTo = FS->openFileForRead(To);
|
|
|
|
return !OpenedFrom.getError() && !OpenedTo.getError() &&
|
|
|
|
(*OpenedFrom)->status()->getUniqueID() ==
|
|
|
|
(*OpenedTo)->status()->getUniqueID();
|
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:14 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, IsEmpty) {
|
|
|
|
auto Stat = FS.status("/a");
|
2018-10-10 21:27:25 +08:00
|
|
|
ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
|
2015-10-05 21:55:14 +08:00
|
|
|
Stat = FS.status("/");
|
2015-10-06 04:20:50 +08:00
|
|
|
ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
|
2015-10-05 21:55:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, WindowsPath) {
|
|
|
|
FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
|
|
|
|
auto Stat = FS.status("c:");
|
2015-10-06 20:16:27 +08:00
|
|
|
#if !defined(_WIN32)
|
2015-10-05 21:55:14 +08:00
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
|
2015-10-06 20:16:27 +08:00
|
|
|
#endif
|
2015-10-05 21:55:14 +08:00
|
|
|
Stat = FS.status("c:/windows/system128/foo.cpp");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
|
|
|
|
FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
|
|
|
|
Stat = FS.status("d:/windows/foo.cpp");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, OverlayFile) {
|
|
|
|
FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
|
2015-10-13 00:16:39 +08:00
|
|
|
NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
|
2015-10-05 21:55:14 +08:00
|
|
|
auto Stat = FS.status("/");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
|
2015-10-13 00:16:39 +08:00
|
|
|
Stat = FS.status("/.");
|
|
|
|
ASSERT_FALSE(Stat);
|
|
|
|
Stat = NormalizedFS.status("/.");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
|
2015-10-05 21:55:14 +08:00
|
|
|
Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_EQ("/a", Stat->getName());
|
|
|
|
}
|
|
|
|
|
2015-10-06 18:04:08 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
|
|
|
|
auto Buf = MemoryBuffer::getMemBuffer("a");
|
|
|
|
FS.addFileNoOwn("/a", 0, Buf.get());
|
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_EQ("/a", Stat->getName());
|
|
|
|
}
|
|
|
|
|
2015-10-05 21:55:14 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
|
|
|
|
FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
|
2015-10-13 00:16:39 +08:00
|
|
|
FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
|
|
|
|
FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
|
|
|
|
NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
|
|
|
|
NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
|
|
|
|
NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
|
2015-10-05 21:55:14 +08:00
|
|
|
auto File = FS.openFileForRead("/a");
|
|
|
|
ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
|
|
|
|
File = FS.openFileForRead("/a"); // Open again.
|
|
|
|
ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
|
2015-10-13 00:16:39 +08:00
|
|
|
File = NormalizedFS.openFileForRead("/././a"); // Open again.
|
|
|
|
ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
|
2015-10-05 21:55:14 +08:00
|
|
|
File = FS.openFileForRead("/");
|
2015-10-06 04:20:50 +08:00
|
|
|
ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
|
2015-10-05 21:55:14 +08:00
|
|
|
File = FS.openFileForRead("/b");
|
2015-10-06 04:20:50 +08:00
|
|
|
ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
|
2015-10-13 00:16:39 +08:00
|
|
|
File = FS.openFileForRead("./c");
|
|
|
|
ASSERT_FALSE(File);
|
|
|
|
File = FS.openFileForRead("e/../d");
|
|
|
|
ASSERT_FALSE(File);
|
|
|
|
File = NormalizedFS.openFileForRead("./c");
|
2015-10-07 16:32:50 +08:00
|
|
|
ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
|
2015-10-13 00:16:39 +08:00
|
|
|
File = NormalizedFS.openFileForRead("e/../d");
|
|
|
|
ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
|
|
|
|
ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
|
|
|
|
ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
|
|
|
|
ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
|
|
|
|
ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
|
2015-10-05 21:55:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
|
|
|
|
FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
|
|
|
|
FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
vfs::directory_iterator I = FS.dir_begin("/", EC);
|
|
|
|
ASSERT_FALSE(EC);
|
2018-09-14 20:47:38 +08:00
|
|
|
ASSERT_EQ("/a", I->path());
|
2015-10-05 21:55:14 +08:00
|
|
|
I.increment(EC);
|
|
|
|
ASSERT_FALSE(EC);
|
2018-09-14 20:47:38 +08:00
|
|
|
ASSERT_EQ("/b", I->path());
|
2015-10-05 21:55:14 +08:00
|
|
|
I.increment(EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
ASSERT_EQ(vfs::directory_iterator(), I);
|
|
|
|
|
|
|
|
I = FS.dir_begin("/b", EC);
|
|
|
|
ASSERT_FALSE(EC);
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
// When on Windows, we end up with "/b\\c" as the name. Convert to Posix
|
|
|
|
// path for the sake of the comparison.
|
2018-09-14 20:47:38 +08:00
|
|
|
ASSERT_EQ("/b/c", getPosixPath(I->path()));
|
2015-10-05 21:55:14 +08:00
|
|
|
I.increment(EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
ASSERT_EQ(vfs::directory_iterator(), I);
|
|
|
|
}
|
|
|
|
|
2015-10-06 22:45:16 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
|
|
|
|
FS.setCurrentWorkingDirectory("/b");
|
|
|
|
FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
|
|
|
|
|
|
|
|
auto Stat = FS.status("/b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
ASSERT_EQ("/b/c", Stat->getName());
|
2015-10-06 22:45:16 +08:00
|
|
|
ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
|
|
|
|
|
|
|
|
Stat = FS.status("c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
2016-01-10 00:33:16 +08:00
|
|
|
|
|
|
|
NormalizedFS.setCurrentWorkingDirectory("/b/c");
|
|
|
|
NormalizedFS.setCurrentWorkingDirectory(".");
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
ASSERT_EQ("/b/c",
|
|
|
|
getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
|
2016-01-10 00:33:16 +08:00
|
|
|
NormalizedFS.setCurrentWorkingDirectory("..");
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
ASSERT_EQ("/b",
|
|
|
|
getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
|
2015-10-06 22:45:16 +08:00
|
|
|
}
|
|
|
|
|
2018-05-24 21:52:48 +08:00
|
|
|
#if !defined(_WIN32)
|
2018-05-24 19:17:00 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, GetRealPath) {
|
|
|
|
SmallString<16> Path;
|
|
|
|
EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
|
|
|
|
|
|
|
|
auto GetRealPath = [this](StringRef P) {
|
|
|
|
SmallString<16> Output;
|
|
|
|
auto EC = FS.getRealPath(P, Output);
|
|
|
|
EXPECT_FALSE(EC);
|
|
|
|
return Output.str().str();
|
|
|
|
};
|
|
|
|
|
|
|
|
FS.setCurrentWorkingDirectory("a");
|
|
|
|
EXPECT_EQ(GetRealPath("b"), "a/b");
|
|
|
|
EXPECT_EQ(GetRealPath("../b"), "b");
|
|
|
|
EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
|
|
|
|
|
|
|
|
FS.setCurrentWorkingDirectory("/a");
|
|
|
|
EXPECT_EQ(GetRealPath("b"), "/a/b");
|
|
|
|
EXPECT_EQ(GetRealPath("../b"), "/b");
|
|
|
|
EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
|
|
|
|
}
|
2018-05-24 21:52:48 +08:00
|
|
|
#endif // _WIN32
|
2018-05-24 19:17:00 +08:00
|
|
|
|
2017-11-10 00:01:16 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
|
|
|
|
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
|
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
|
|
|
|
Stat = FS.status("/a/b");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
|
|
|
|
Stat = FS.status("/a/b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
|
|
|
|
ASSERT_EQ(0xFEEDFACE, Stat->getUser());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
|
|
|
|
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
|
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
|
|
|
|
Stat = FS.status("/a/b");
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
|
|
|
|
Stat = FS.status("/a/b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
|
|
|
|
ASSERT_EQ(0xDABBAD00, Stat->getGroup());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
|
|
|
|
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
|
|
|
|
sys::fs::file_type::socket_file);
|
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
Stat = FS.status("/a/b");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
Stat = FS.status("/a/b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
|
|
|
|
ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
|
2018-10-10 21:27:25 +08:00
|
|
|
FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
|
|
|
|
sys::fs::perms::owner_read | sys::fs::perms::owner_write);
|
2017-11-10 00:01:16 +08:00
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
|
2018-10-10 21:27:25 +08:00
|
|
|
sys::fs::perms::owner_exe,
|
|
|
|
Stat->getPermissions());
|
2017-11-10 00:01:16 +08:00
|
|
|
Stat = FS.status("/a/b");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
|
2018-10-10 21:27:25 +08:00
|
|
|
sys::fs::perms::owner_exe,
|
|
|
|
Stat->getPermissions());
|
2017-11-10 00:01:16 +08:00
|
|
|
Stat = FS.status("/a/b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
|
|
|
|
Stat->getPermissions());
|
|
|
|
}
|
|
|
|
|
2017-11-17 03:34:08 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
|
|
|
|
FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
|
|
|
|
/*Group=*/None, sys::fs::file_type::directory_file);
|
|
|
|
FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
|
|
|
|
/*Group=*/None, sys::fs::file_type::regular_file);
|
|
|
|
auto Stat = FS.status("/a");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isDirectory());
|
|
|
|
Stat = FS.status("/a/b");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
}
|
|
|
|
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
// Test that the name returned by status() is in the same form as the path that
|
|
|
|
// was requested (to match the behavior of RealFileSystem).
|
|
|
|
TEST_F(InMemoryFileSystemTest, StatusName) {
|
|
|
|
NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
|
|
|
|
/*User=*/None,
|
|
|
|
/*Group=*/None, sys::fs::file_type::regular_file);
|
|
|
|
NormalizedFS.setCurrentWorkingDirectory("/a/b");
|
|
|
|
|
|
|
|
// Access using InMemoryFileSystem::status.
|
|
|
|
auto Stat = NormalizedFS.status("../b/c");
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
|
|
|
|
<< NormalizedFS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
ASSERT_EQ("../b/c", Stat->getName());
|
|
|
|
|
|
|
|
// Access using InMemoryFileAdaptor::status.
|
|
|
|
auto File = NormalizedFS.openFileForRead("../b/c");
|
|
|
|
ASSERT_FALSE(File.getError()) << File.getError() << "\n"
|
|
|
|
<< NormalizedFS.toString();
|
|
|
|
Stat = (*File)->status();
|
|
|
|
ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
|
|
|
|
<< NormalizedFS.toString();
|
|
|
|
ASSERT_TRUE(Stat->isRegularFile());
|
|
|
|
ASSERT_EQ("../b/c", Stat->getName());
|
|
|
|
|
|
|
|
// Access using a directory iterator.
|
|
|
|
std::error_code EC;
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
// When on Windows, we end up with "../b\\c" as the name. Convert to Posix
|
|
|
|
// path for the sake of the comparison.
|
2018-09-14 20:47:38 +08:00
|
|
|
ASSERT_EQ("../b/c", getPosixPath(It->path()));
|
[VirtualFileSystem] InMemoryFileSystem::status: Return a Status with the requested name
Summary:
InMemoryFileSystem::status behaves differently than
RealFileSystem::status. The Name contained in the Status returned by
RealFileSystem::status will be the path as requested by the caller,
whereas InMemoryFileSystem::status returns the normalized path.
For example, when requested the status for "../src/first.h",
RealFileSystem returns a Status with "../src/first.h" as the Name.
InMemoryFileSystem returns "/absolute/path/to/src/first.h".
The reason for this change is that I want to make a unit test in the
clangd testsuite (where we use an InMemoryFileSystem) to reproduce a
bug I get with the clangd program (where a RealFileSystem is used).
This difference in behavior "hides" the bug in the unit test version.
An indirect impact of this change is that a -Wnonportable-include-path
warning is now emitted in test PCH/case-insensitive-include.c. This is
because the real path of the included file (with the wrong case) was not
available previously, whereas it is now.
Reviewers: malaperle, ilya-biryukov, bkramer
Reviewed By: ilya-biryukov
Subscribers: eric_niebler, malaperle, omtcyfz, hokein, bkramer, ilya-biryukov, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D48903
llvm-svn: 339063
2018-08-07 05:48:20 +08:00
|
|
|
}
|
|
|
|
|
2018-09-04 22:15:53 +08:00
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
|
|
|
|
StringRef FromLink = "/path/to/FROM/link";
|
|
|
|
StringRef Target = "/path/to/TO/file";
|
|
|
|
FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(FromLink, Target));
|
|
|
|
EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
|
|
|
|
EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize());
|
|
|
|
EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() ==
|
|
|
|
FS.getBufferForFile(Target)->get()->getBuffer());
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
|
|
|
|
StringRef Link0 = "/path/to/0/link";
|
|
|
|
StringRef Link1 = "/path/to/1/link";
|
|
|
|
StringRef Link2 = "/path/to/2/link";
|
|
|
|
StringRef Target = "/path/to/target";
|
|
|
|
FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link2, Target));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link1, Link2));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link0, Link1));
|
|
|
|
EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
|
|
|
|
EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
|
|
|
|
EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
|
|
|
|
EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
|
|
|
|
StringRef Link = "/path/to/link";
|
|
|
|
StringRef Target = "/path/to/target";
|
|
|
|
FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
|
|
|
|
FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
|
|
|
|
EXPECT_FALSE(FS.addHardLink(Link, Target));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
|
|
|
|
StringRef Link = "/path/to/link";
|
|
|
|
StringRef Target = "/path/to/target";
|
|
|
|
FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link, Target));
|
|
|
|
EXPECT_FALSE(FS.addHardLink(Link, Target));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
|
|
|
|
StringRef Link = "/path/to/link";
|
|
|
|
StringRef Target = "/path/to/target";
|
|
|
|
StringRef Content = "content of target";
|
|
|
|
EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link, Target));
|
|
|
|
EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
|
|
|
|
StringRef Link = "/path/to/link";
|
|
|
|
StringRef Target = "/path/to/target";
|
|
|
|
StringRef Content = "content of target";
|
|
|
|
StringRef LinkContent = "different content of link";
|
|
|
|
EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
|
|
|
|
EXPECT_TRUE(FS.addHardLink(Link, Target));
|
|
|
|
EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
|
|
|
|
StringRef Dir = "path/to/dummy/dir";
|
|
|
|
StringRef Link = "/path/to/link";
|
|
|
|
StringRef File = "path/to/dummy/dir/target";
|
|
|
|
StringRef Content = "content of target";
|
|
|
|
EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
|
|
|
|
EXPECT_FALSE(FS.addHardLink(Link, Dir));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
|
|
|
|
StringRef Dir = "path/to/dummy/dir";
|
|
|
|
StringRef Target = "path/to/dummy/dir/target";
|
|
|
|
StringRef Content = "content of target";
|
|
|
|
EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
|
|
|
|
EXPECT_FALSE(FS.addHardLink(Dir, Target));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
|
|
|
|
StringRef CommonContent = "content string";
|
|
|
|
FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
|
|
|
|
FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
|
|
|
|
EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
|
|
|
|
std::error_code EC;
|
|
|
|
FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
|
|
|
|
EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
|
|
|
|
auto I = vfs::recursive_directory_iterator(FS, "/", EC);
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
std::vector<std::string> Nodes;
|
|
|
|
for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
|
|
|
|
I.increment(EC)) {
|
2018-09-14 20:47:38 +08:00
|
|
|
Nodes.push_back(getPosixPath(I->path()));
|
2018-09-04 22:15:53 +08:00
|
|
|
}
|
|
|
|
EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
|
|
|
|
}
|
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
// NOTE: in the tests below, we use '//root/' as our root directory, since it is
|
|
|
|
// a legal *absolute* path on Windows as well as *nix.
|
2014-02-25 04:56:37 +08:00
|
|
|
class VFSFromYAMLTest : public ::testing::Test {
|
|
|
|
public:
|
|
|
|
int NumDiagnostics;
|
2014-03-06 05:32:20 +08:00
|
|
|
|
2015-04-11 10:00:23 +08:00
|
|
|
void SetUp() override { NumDiagnostics = 0; }
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
|
|
|
|
VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
|
|
|
|
++Test->NumDiagnostics;
|
|
|
|
}
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem>
|
|
|
|
getFromYAMLRawString(StringRef Content,
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
|
2014-08-28 04:03:29 +08:00
|
|
|
std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
|
Reapply [2] [VFS] Add 'overlay-relative' field to YAML files
This reapplies r261552 and r263748. Fixed testcase to reapply.
The VFS overlay mapping between virtual paths and real paths is done through
the 'external-contents' entries in YAML files, which contains hardcoded paths
to the real files.
When a module compilation crashes, headers are dumped into <name>.cache/vfs
directory and are mapped via the <name>.cache/vfs/vfs.yaml. The script
generated for reproduction uses -ivfsoverlay pointing to file to gather the
mapping between virtual paths and files inside <name>.cache/vfs. Currently, we
are only capable of reproducing such crashes in the same machine as they
happen, because of the hardcoded paths in 'external-contents'.
To be able to reproduce a crash in another machine, this patch introduces a new
option in the VFS yaml file called 'overlay-relative'. When it's equal to
'true' it means that the provided path to the YAML file through the
-ivfsoverlay option should also be used to prefix the final path for every
'external-contents'.
Example, given the invocation snippet "... -ivfsoverlay
<name>.cache/vfs/vfs.yaml" and the following entry in the yaml file:
"overlay-relative": "true",
"roots": [
...
"type": "directory",
"name": "/usr/include",
"contents": [
{
"type": "file",
"name": "stdio.h",
"external-contents": "/usr/include/stdio.h"
},
...
Here, a file manager request for virtual "/usr/include/stdio.h", that will map
into real path "/<absolute_path_to>/<name>.cache/vfs/usr/include/stdio.h.
This is a useful feature for debugging module crashes in machines other than
the one where the error happened.
Differential Revision: http://reviews.llvm.org/D17457
rdar://problem/24499339
llvm-svn: 263893
2016-03-20 10:08:48 +08:00
|
|
|
return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
|
2014-08-18 07:27:13 +08:00
|
|
|
ExternalFS);
|
2014-02-25 04:56:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
|
|
|
|
StringRef Content,
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
|
|
|
|
std::string VersionPlusContent("{\n 'version':0,\n");
|
|
|
|
VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
|
|
|
|
return getFromYAMLRawString(VersionPlusContent, ExternalFS);
|
|
|
|
}
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
|
|
|
|
// This is intended as a "XFAIL" for windows hosts.
|
|
|
|
bool supportsSameDirMultipleYAMLEntries() {
|
|
|
|
Triple Host(Triple::normalize(sys::getProcessTriple()));
|
|
|
|
return !Host.isOSWindows();
|
|
|
|
}
|
2014-02-25 04:56:37 +08:00
|
|
|
};
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS;
|
|
|
|
FS = getFromYAMLString("");
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("[]");
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("'string'");
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_EQ(3, NumDiagnostics);
|
|
|
|
}
|
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, MappedFiles) {
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/foo/bar/a");
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'file1',\n"
|
|
|
|
" 'external-contents': '//root/foo/bar/a'\n"
|
|
|
|
" },\n"
|
|
|
|
" {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'file2',\n"
|
|
|
|
" 'external-contents': '//root/foo/b'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
|
|
|
// file
|
2014-03-06 05:32:20 +08:00
|
|
|
ErrorOr<vfs::Status> S = O->status("//root/file1");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(S.getError());
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_EQ("//root/foo/bar/a", S->getName());
|
2015-12-11 07:41:39 +08:00
|
|
|
EXPECT_TRUE(S->IsVFSMapped);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
|
|
|
|
EXPECT_EQ("//root/foo/bar/a", SLower->getName());
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_TRUE(S->equivalent(*SLower));
|
2015-12-11 07:41:39 +08:00
|
|
|
EXPECT_FALSE(SLower->IsVFSMapped);
|
|
|
|
|
|
|
|
// file after opening
|
|
|
|
auto OpenedF = O->openFileForRead("//root/file1");
|
|
|
|
ASSERT_FALSE(OpenedF.getError());
|
|
|
|
auto OpenedS = (*OpenedF)->status();
|
|
|
|
ASSERT_FALSE(OpenedS.getError());
|
|
|
|
EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
|
|
|
|
EXPECT_TRUE(OpenedS->IsVFSMapped);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// directory
|
2014-03-06 05:32:20 +08:00
|
|
|
S = O->status("//root/");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(S.getError());
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_TRUE(S->isDirectory());
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// broken mapping
|
2014-06-14 01:20:50 +08:00
|
|
|
EXPECT_EQ(O->status("//root/file2").getError(),
|
|
|
|
llvm::errc::no_such_file_or_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_EQ(0, NumDiagnostics);
|
|
|
|
}
|
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, CaseInsensitive) {
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/foo/bar/a");
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'case-sensitive': 'false',\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'XX',\n"
|
|
|
|
" 'external-contents': '//root/foo/bar/a'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}]}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
ErrorOr<vfs::Status> S = O->status("//root/XX");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(S.getError());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
ErrorOr<vfs::Status> SS = O->status("//root/xx");
|
2014-05-31 11:20:52 +08:00
|
|
|
ASSERT_FALSE(SS.getError());
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_TRUE(S->equivalent(*SS));
|
2014-03-06 05:32:20 +08:00
|
|
|
SS = O->status("//root/xX");
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_TRUE(S->equivalent(*SS));
|
2014-03-06 05:32:20 +08:00
|
|
|
SS = O->status("//root/Xx");
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_TRUE(S->equivalent(*SS));
|
|
|
|
EXPECT_EQ(0, NumDiagnostics);
|
|
|
|
}
|
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, CaseSensitive) {
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/foo/bar/a");
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'case-sensitive': 'true',\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'XX',\n"
|
|
|
|
" 'external-contents': '//root/foo/bar/a'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}]}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
2014-03-06 05:32:20 +08:00
|
|
|
ErrorOr<vfs::Status> SS = O->status("//root/xx");
|
2014-06-14 01:20:50 +08:00
|
|
|
EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
|
2014-03-06 05:32:20 +08:00
|
|
|
SS = O->status("//root/xX");
|
2014-06-14 01:20:50 +08:00
|
|
|
EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
|
2014-03-06 05:32:20 +08:00
|
|
|
SS = O->status("//root/Xx");
|
2014-06-14 01:20:50 +08:00
|
|
|
EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_EQ(0, NumDiagnostics);
|
|
|
|
}
|
|
|
|
|
2014-02-25 04:56:37 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
|
2014-02-22 07:39:37 +08:00
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
|
|
|
|
// invalid YAML at top-level
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
// invalid YAML in roots
|
|
|
|
FS = getFromYAMLString("{ 'roots':[}", Lower);
|
|
|
|
// invalid YAML in directory
|
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// invalid configuration
|
|
|
|
FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// invalid roots
|
|
|
|
FS = getFromYAMLString("{ 'roots':'' }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("{ 'roots':{} }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// invalid entries
|
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
|
|
|
|
"'external-contents': 'other' }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// missing mandatory fields
|
|
|
|
FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// duplicate keys
|
|
|
|
FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS =
|
|
|
|
getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
|
|
|
|
"'external-contents':'blah' } ] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// missing version
|
|
|
|
FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
|
|
|
|
// bad version number
|
|
|
|
FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
2014-02-22 07:39:37 +08:00
|
|
|
EXPECT_EQ(24, NumDiagnostics);
|
|
|
|
}
|
2014-02-25 12:34:14 +08:00
|
|
|
|
2014-02-27 08:25:12 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, UseExternalName) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/external/file");
|
2014-02-27 08:25:12 +08:00
|
|
|
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS =
|
|
|
|
getFromYAMLString("{ 'roots': [\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/A',\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" },\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/B',\n"
|
|
|
|
" 'use-external-name': true,\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" },\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/C',\n"
|
|
|
|
" 'use-external-name': false,\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" }\n"
|
|
|
|
"] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-02-27 08:25:12 +08:00
|
|
|
|
|
|
|
// default true
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
|
2014-02-27 08:25:12 +08:00
|
|
|
// explicit
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
|
|
|
|
EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
|
2014-02-27 08:25:12 +08:00
|
|
|
|
|
|
|
// global configuration
|
2018-10-10 21:27:25 +08:00
|
|
|
FS = getFromYAMLString("{ 'use-external-names': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/A',\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" },\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/B',\n"
|
|
|
|
" 'use-external-name': true,\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" },\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/C',\n"
|
|
|
|
" 'use-external-name': false,\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" }\n"
|
|
|
|
"] }",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-02-27 08:25:12 +08:00
|
|
|
|
|
|
|
// default
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
|
2014-02-27 08:25:12 +08:00
|
|
|
// explicit
|
2014-03-06 05:32:20 +08:00
|
|
|
EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
|
|
|
|
EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
|
2014-02-27 08:25:12 +08:00
|
|
|
}
|
|
|
|
|
2014-02-25 12:34:14 +08:00
|
|
|
TEST_F(VFSFromYAMLTest, MultiComponentPath) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/other");
|
2014-02-25 12:34:14 +08:00
|
|
|
|
|
|
|
// file in roots
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS =
|
|
|
|
getFromYAMLString("{ 'roots': [\n"
|
|
|
|
" { 'type': 'file', 'name': '//root/path/to/file',\n"
|
|
|
|
" 'external-contents': '//root/other' }]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(FS->status("//root/path/to/file").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path/to").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/").getError());
|
2014-02-25 12:34:14 +08:00
|
|
|
|
|
|
|
// at the start
|
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots': [\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" { 'type': 'directory', 'name': '//root/path/to',\n"
|
2014-02-25 12:34:14 +08:00
|
|
|
" 'contents': [ { 'type': 'file', 'name': 'file',\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" 'external-contents': '//root/other' }]}]\n"
|
2018-10-10 21:27:25 +08:00
|
|
|
"}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(FS->status("//root/path/to/file").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path/to").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/").getError());
|
2014-02-25 12:34:14 +08:00
|
|
|
|
|
|
|
// at the end
|
|
|
|
FS = getFromYAMLString(
|
|
|
|
"{ 'roots': [\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" { 'type': 'directory', 'name': '//root/',\n"
|
2014-02-25 12:34:14 +08:00
|
|
|
" 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" 'external-contents': '//root/other' }]}]\n"
|
2018-10-10 21:27:25 +08:00
|
|
|
"}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(FS->status("//root/path/to/file").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path/to").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/").getError());
|
2014-02-25 12:34:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, TrailingSlashes) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
2014-03-06 05:32:20 +08:00
|
|
|
Lower->addRegularFile("//root/other");
|
2014-02-25 12:34:14 +08:00
|
|
|
|
|
|
|
// file in roots
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'roots': [\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" { 'type': 'directory', 'name': '//root/path/to////',\n"
|
2014-02-25 12:34:14 +08:00
|
|
|
" 'contents': [ { 'type': 'file', 'name': 'file',\n"
|
2014-03-06 05:32:20 +08:00
|
|
|
" 'external-contents': '//root/other' }]}]\n"
|
2018-10-10 21:27:25 +08:00
|
|
|
"}",
|
|
|
|
Lower);
|
2014-07-05 11:08:06 +08:00
|
|
|
ASSERT_TRUE(nullptr != FS.get());
|
2014-05-31 11:20:52 +08:00
|
|
|
EXPECT_FALSE(FS->status("//root/path/to/file").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path/to").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/path").getError());
|
|
|
|
EXPECT_FALSE(FS->status("//root/").getError());
|
2014-02-25 12:34:14 +08:00
|
|
|
}
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, DirectoryIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/");
|
|
|
|
Lower->addDirectory("//root/foo");
|
|
|
|
Lower->addDirectory("//root/foo/bar");
|
|
|
|
Lower->addRegularFile("//root/foo/bar/a");
|
|
|
|
Lower->addRegularFile("//root/foo/bar/b");
|
|
|
|
Lower->addRegularFile("//root/file3");
|
2018-10-10 21:27:25 +08:00
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'use-external-names': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'file1',\n"
|
|
|
|
" 'external-contents': '//root/foo/bar/a'\n"
|
|
|
|
" },\n"
|
|
|
|
" {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'file2',\n"
|
|
|
|
" 'external-contents': '//root/foo/bar/b'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
2015-10-07 07:40:43 +08:00
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
2014-06-25 03:37:16 +08:00
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
|
|
|
std::error_code EC;
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(O->dir_begin("//root/", EC),
|
2016-05-12 12:43:27 +08:00
|
|
|
{"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
|
2014-06-25 03:37:16 +08:00
|
|
|
|
2016-01-10 18:45:19 +08:00
|
|
|
checkContents(O->dir_begin("//root/foo/bar", EC),
|
|
|
|
{"//root/foo/bar/a", "//root/foo/bar/b"});
|
2014-06-25 03:37:16 +08:00
|
|
|
}
|
[VFS] Reapply #2: Reconstruct the VFS overlay tree for more accurate lookup
Reapply r269100 and r269270, reverted due to
https://llvm.org/bugs/show_bug.cgi?id=27725. Isolate the testcase that
corresponds to the new feature side of this commit and skip it on
windows hosts until we find why it does not work on these platforms.
Original commit message:
The way we currently build the internal VFS overlay representation leads
to inefficient path search and might yield wrong answers when asked for
recursive or regular directory iteration.
Currently, when reading an YAML file, each YAML root entry is placed
inside a new root in the filesystem overlay. In the crash reproducer, a
simple "@import Foundation" currently maps to 43 roots, and when looking
up paths, we traverse a directory tree for each of these different
roots, until we find a match (or don't). This has two consequences:
- It's slow.
- Directory iteration gives incomplete results since it only return
results within one root - since contents of the same directory can be
declared inside different roots, the result isn't accurate.
This is in part fault of the way we currently write out the YAML file
when emitting the crash reproducer - we could generate only one root and
that would make it fast and correct again. However, we should not rely
on how the client writes the YAML, but provide a good internal
representation regardless.
Build a proper virtual directory tree out of the YAML representation,
allowing faster search and proper iteration. Besides the crash
reproducer, this potentially benefits other VFS clients.
llvm-svn: 269327
2016-05-13 03:13:07 +08:00
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
|
|
|
|
// https://llvm.org/bugs/show_bug.cgi?id=27725
|
|
|
|
if (!supportsSameDirMultipleYAMLEntries())
|
|
|
|
return;
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/zab");
|
|
|
|
Lower->addDirectory("//root/baz");
|
|
|
|
Lower->addRegularFile("//root/zab/a");
|
|
|
|
Lower->addRegularFile("//root/zab/b");
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'use-external-names': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/baz/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'x',\n"
|
|
|
|
" 'external-contents': '//root/zab/a'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"},\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/baz/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'y',\n"
|
|
|
|
" 'external-contents': '//root/zab/b'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
|
|
|
|
checkContents(O->dir_begin("//root/baz/", EC),
|
|
|
|
{"//root/baz/x", "//root/baz/y"});
|
|
|
|
}
|
2016-05-14 08:00:18 +08:00
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/a");
|
|
|
|
Lower->addDirectory("//root/a/b");
|
|
|
|
Lower->addDirectory("//root/a/b/c");
|
|
|
|
Lower->addRegularFile("//root/a/b/c/file");
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'use-external-names': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/a/b/c/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'file',\n"
|
|
|
|
" 'external-contents': '//root/a/b/c/file'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"},\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
|
|
|
|
new vfs::OverlayFileSystem(Lower));
|
|
|
|
O->pushOverlay(FS);
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
|
|
|
|
// Test recursive_directory_iterator level()
|
|
|
|
vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
|
2018-10-10 21:27:25 +08:00
|
|
|
*O, "//root", EC),
|
|
|
|
E;
|
2016-05-14 08:00:18 +08:00
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
for (int l = 0; I != E; I.increment(EC), ++l) {
|
|
|
|
ASSERT_FALSE(EC);
|
|
|
|
EXPECT_EQ(I.level(), l);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(I, E);
|
|
|
|
}
|
2018-08-08 03:05:41 +08:00
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, RelativePaths) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
// Filename at root level without a parent directory.
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'roots': [\n"
|
|
|
|
" { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" }\n"
|
2018-10-10 21:27:25 +08:00
|
|
|
"] }",
|
|
|
|
Lower);
|
2018-08-08 03:05:41 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
|
|
|
|
|
|
|
// Relative file path.
|
2018-10-10 21:27:25 +08:00
|
|
|
FS = getFromYAMLString("{ 'roots': [\n"
|
|
|
|
" { 'type': 'file', 'name': 'relative/file/path.h',\n"
|
|
|
|
" 'external-contents': '//root/external/file'\n"
|
|
|
|
" }\n"
|
|
|
|
"] }",
|
|
|
|
Lower);
|
2018-08-08 03:05:41 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
|
|
|
|
|
|
|
// Relative directory path.
|
|
|
|
FS = getFromYAMLString(
|
2018-10-10 21:27:25 +08:00
|
|
|
"{ 'roots': [\n"
|
|
|
|
" { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
|
|
|
|
" 'contents': []\n"
|
|
|
|
" }\n"
|
|
|
|
"] }",
|
|
|
|
Lower);
|
2018-08-08 03:05:41 +08:00
|
|
|
EXPECT_EQ(nullptr, FS.get());
|
|
|
|
|
|
|
|
EXPECT_EQ(3, NumDiagnostics);
|
|
|
|
}
|
2018-10-27 06:14:33 +08:00
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/");
|
|
|
|
Lower->addRegularFile("//root/a");
|
|
|
|
Lower->addRegularFile("//root/b");
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'use-external-names': false,\n"
|
|
|
|
" 'fallthrough': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'c',\n"
|
|
|
|
" 'external-contents': '//root/a'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
checkContents(FS->dir_begin("//root/", EC),
|
|
|
|
{"//root/c"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/");
|
|
|
|
Lower->addRegularFile("//root/a");
|
|
|
|
Lower->addRegularFile("//root/b");
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
|
|
|
|
"{ 'use-external-names': false,\n"
|
|
|
|
" 'roots': [\n"
|
|
|
|
"{\n"
|
|
|
|
" 'type': 'directory',\n"
|
|
|
|
" 'name': '//root/',\n"
|
|
|
|
" 'contents': [ {\n"
|
|
|
|
" 'type': 'file',\n"
|
|
|
|
" 'name': 'a',\n"
|
|
|
|
" 'external-contents': '//root/a'\n"
|
|
|
|
" }\n"
|
|
|
|
" ]\n"
|
|
|
|
"}\n"
|
|
|
|
"]\n"
|
|
|
|
"}",
|
|
|
|
Lower);
|
|
|
|
ASSERT_TRUE(FS.get() != nullptr);
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
checkContents(FS->dir_begin("//root/", EC),
|
|
|
|
{"//root/a", "//root/b"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
|
|
|
|
IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
|
|
|
|
Lower->addDirectory("//root/");
|
|
|
|
Lower->addDirectory("//root/foo");
|
|
|
|
Lower->addRegularFile("//root/foo/a");
|
|
|
|
Lower->addRegularFile("//root/foo/b");
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> 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;
|
|
|
|
checkContents(FS->dir_begin("//root/foo", EC),
|
|
|
|
{"//root/foo/a", "//root/foo/b"});
|
|
|
|
}
|