//===-- FileCollectorTest.cpp -----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "gmock/gmock.h" #include "gtest/gtest.h" #include "lldb/Utility/FileCollector.h" #include "lldb/Utility/FileSpec.h" #include "llvm/Support/FileSystem.h" using namespace llvm; using namespace lldb_private; namespace llvm { namespace vfs { inline bool operator==(const llvm::vfs::YAMLVFSEntry &LHS, const llvm::vfs::YAMLVFSEntry &RHS) { return LHS.VPath == RHS.VPath && LHS.RPath == RHS.RPath; } } // namespace vfs } // namespace llvm namespace { class TestingFileCollector : public FileCollector { public: using FileCollector::FileCollector; using FileCollector::m_root; using FileCollector::m_seen; using FileCollector::m_symlink_map; using FileCollector::m_vfs_writer; bool HasSeen(FileSpec fs) { return m_seen.find(fs.GetPath()) != m_seen.end(); } }; struct ScopedDir { SmallString<128> Path; ScopedDir(const Twine &Name, bool Unique = false) { std::error_code EC; if (Unique) { EC = llvm::sys::fs::createUniqueDirectory(Name, Path); } else { Path = Name.str(); EC = llvm::sys::fs::create_directory(Twine(Path)); } if (EC) Path = ""; EXPECT_FALSE(EC); // Ensure the path is the real path so tests can use it to compare against // realpath output. SmallString<128> RealPath; if (!llvm::sys::fs::real_path(Path, RealPath)) Path.swap(RealPath); } ~ScopedDir() { if (Path != "") { EXPECT_FALSE(llvm::sys::fs::remove_directories(Path.str())); } } operator StringRef() { return Path.str(); } }; 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() { if (Path != "") { EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); } } operator StringRef() { return Path.str(); } }; struct ScopedFile { SmallString<128> Path; ScopedFile(const Twine &Name) { std::error_code EC; EC = llvm::sys::fs::createUniqueFile(Name, Path); if (EC) Path = ""; EXPECT_FALSE(EC); } ~ScopedFile() { if (Path != "") { EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); } } operator StringRef() { return Path.str(); } }; } // end anonymous namespace TEST(FileCollectorTest, AddFile) { ScopedDir root("add_file_root", true); FileSpec root_fs(root.Path); TestingFileCollector file_collector(root_fs, root_fs); file_collector.AddFile(FileSpec("/path/to/a")); file_collector.AddFile(FileSpec("/path/to/b")); file_collector.AddFile(FileSpec("/path/to/c")); // Make sure the root is correct. EXPECT_EQ(file_collector.m_root, root_fs); // Make sure we've seen all the added files. EXPECT_TRUE(file_collector.HasSeen(FileSpec("/path/to/a"))); EXPECT_TRUE(file_collector.HasSeen(FileSpec("/path/to/b"))); EXPECT_TRUE(file_collector.HasSeen(FileSpec("/path/to/c"))); // Make sure we've only seen the added files. EXPECT_FALSE(file_collector.HasSeen(FileSpec("/path/to/d"))); } TEST(FileCollectorTest, CopyFiles) { ScopedDir file_root("file_root", true); ScopedFile a(file_root + "/aaa"); ScopedFile b(file_root + "/bbb"); ScopedFile c(file_root + "/ccc"); // Create file collector and add files. ScopedDir root("copy_files_root", true); FileSpec root_fs(root.Path); TestingFileCollector file_collector(root_fs, root_fs); file_collector.AddFile(a.Path); file_collector.AddFile(b.Path); file_collector.AddFile(c.Path); // Make sure we can copy the files. std::error_code ec = file_collector.CopyFiles(true); EXPECT_FALSE(ec); // Now add a bogus file and make sure we error out. file_collector.AddFile("/some/bogus/file"); ec = file_collector.CopyFiles(true); EXPECT_TRUE(ec); // However, if stop_on_error is true the copy should still succeed. ec = file_collector.CopyFiles(false); EXPECT_FALSE(ec); } #ifndef _WIN32 TEST(FileCollectorTest, Symlinks) { // Root where the original files live. ScopedDir file_root("file_root", true); // Create some files in the file root. ScopedFile a(file_root + "/aaa"); ScopedFile b(file_root + "/bbb"); ScopedFile c(file_root + "/ccc"); // Create a directory foo with file ddd. ScopedDir foo(file_root + "/foo"); ScopedFile d(foo + "/ddd"); // Create a file eee in the foo's parent directory. ScopedFile e(foo + "/../eee"); // Create a symlink bar pointing to foo. ScopedLink symlink(file_root + "/foo", file_root + "/bar"); // Root where files are copied to. ScopedDir reproducer_root("reproducer_root", true); FileSpec root_fs(reproducer_root.Path); TestingFileCollector file_collector(root_fs, root_fs); // Add all the files to the collector. file_collector.AddFile(a.Path); file_collector.AddFile(b.Path); file_collector.AddFile(c.Path); file_collector.AddFile(d.Path); file_collector.AddFile(e.Path); file_collector.AddFile(file_root + "/bar/ddd"); auto mapping = file_collector.m_vfs_writer.getMappings(); { // Make sure the common case works. std::string vpath = (file_root + "/aaa").str(); std::string rpath = (reproducer_root.Path + file_root.Path + "/aaa").str(); printf("%s -> %s\n", vpath.c_str(), rpath.c_str()); EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath))); } { // Make sure the virtual path points to the real source path. std::string vpath = (file_root + "/bar/ddd").str(); std::string rpath = (reproducer_root.Path + file_root.Path + "/foo/ddd").str(); printf("%s -> %s\n", vpath.c_str(), rpath.c_str()); EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath))); } { // Make sure that .. is removed from the source path. std::string vpath = (file_root + "/eee").str(); std::string rpath = (reproducer_root.Path + file_root.Path + "/eee").str(); printf("%s -> %s\n", vpath.c_str(), rpath.c_str()); EXPECT_THAT(mapping, testing::Contains(vfs::YAMLVFSEntry(vpath, rpath))); } } #endif