180814287SRaphael Isemann //===-- CppModuleConfigurationTest.cpp ------------------------------------===// 29379d19fSRaphael Isemann // 39379d19fSRaphael Isemann // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 49379d19fSRaphael Isemann // See https://llvm.org/LICENSE.txt for license information. 59379d19fSRaphael Isemann // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 69379d19fSRaphael Isemann // 79379d19fSRaphael Isemann //===----------------------------------------------------------------------===// 89379d19fSRaphael Isemann 99379d19fSRaphael Isemann #include "Plugins/ExpressionParser/Clang/CppModuleConfiguration.h" 109379d19fSRaphael Isemann #include "Plugins/ExpressionParser/Clang/ClangHost.h" 115dca0596SRaphael Isemann #include "TestingSupport/SubsystemRAII.h" 129379d19fSRaphael Isemann #include "lldb/Host/FileSystem.h" 139379d19fSRaphael Isemann #include "lldb/Host/HostInfo.h" 14*99b7b41eSRaphael Isemann #include "llvm/Support/SmallVectorMemoryBuffer.h" 159379d19fSRaphael Isemann 169379d19fSRaphael Isemann #include "gmock/gmock.h" 179379d19fSRaphael Isemann #include "gtest/gtest.h" 189379d19fSRaphael Isemann 199379d19fSRaphael Isemann using namespace lldb_private; 209379d19fSRaphael Isemann 219379d19fSRaphael Isemann namespace { 229379d19fSRaphael Isemann struct CppModuleConfigurationTest : public testing::Test { 23*99b7b41eSRaphael Isemann llvm::MemoryBufferRef m_empty_buffer; 24*99b7b41eSRaphael Isemann llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> m_fs; 25*99b7b41eSRaphael Isemann 26*99b7b41eSRaphael Isemann CppModuleConfigurationTest() 27*99b7b41eSRaphael Isemann : m_empty_buffer("", "<empty buffer>"), 28*99b7b41eSRaphael Isemann m_fs(new llvm::vfs::InMemoryFileSystem()) {} 29*99b7b41eSRaphael Isemann 30*99b7b41eSRaphael Isemann void SetUp() override { 31*99b7b41eSRaphael Isemann FileSystem::Initialize(m_fs); 32*99b7b41eSRaphael Isemann HostInfo::Initialize(); 33*99b7b41eSRaphael Isemann } 34*99b7b41eSRaphael Isemann 35*99b7b41eSRaphael Isemann void TearDown() override { 36*99b7b41eSRaphael Isemann HostInfo::Terminate(); 37*99b7b41eSRaphael Isemann FileSystem::Terminate(); 38*99b7b41eSRaphael Isemann } 39*99b7b41eSRaphael Isemann 40*99b7b41eSRaphael Isemann /// Utility function turning a list of paths into a FileSpecList. 41*99b7b41eSRaphael Isemann FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) { 42*99b7b41eSRaphael Isemann FileSpecList result; 43*99b7b41eSRaphael Isemann for (const std::string &path : paths) { 44*99b7b41eSRaphael Isemann result.Append(FileSpec(path, FileSpec::Style::posix)); 45*99b7b41eSRaphael Isemann if (!m_fs->addFileNoOwn(path, static_cast<time_t>(0), m_empty_buffer)) 46*99b7b41eSRaphael Isemann llvm_unreachable("Invalid test configuration?"); 47*99b7b41eSRaphael Isemann } 48*99b7b41eSRaphael Isemann return result; 49*99b7b41eSRaphael Isemann } 509379d19fSRaphael Isemann }; 519379d19fSRaphael Isemann } // namespace 529379d19fSRaphael Isemann 539379d19fSRaphael Isemann /// Returns the Clang resource include directory. 549379d19fSRaphael Isemann static std::string ResourceInc() { 559379d19fSRaphael Isemann llvm::SmallString<256> resource_dir; 569379d19fSRaphael Isemann llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(), 579379d19fSRaphael Isemann "include"); 58e28d8f90SJonas Devlieghere return std::string(resource_dir); 599379d19fSRaphael Isemann } 609379d19fSRaphael Isemann 619379d19fSRaphael Isemann 629379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Linux) { 639379d19fSRaphael Isemann // Test the average Linux configuration. 64*99b7b41eSRaphael Isemann 659379d19fSRaphael Isemann std::string usr = "/usr/include"; 66*99b7b41eSRaphael Isemann std::string libcpp = "/usr/include/c++/v1"; 67*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 68*99b7b41eSRaphael Isemann usr + "/stdio.h", 69*99b7b41eSRaphael Isemann // C++ library 70*99b7b41eSRaphael Isemann libcpp + "/vector", 71*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 72*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 739379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 749379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 759379d19fSRaphael Isemann testing::ElementsAre(libcpp, ResourceInc(), usr)); 769379d19fSRaphael Isemann } 779379d19fSRaphael Isemann 789379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Sysroot) { 799379d19fSRaphael Isemann // Test that having a sysroot for the whole system works fine. 80*99b7b41eSRaphael Isemann 819379d19fSRaphael Isemann std::string libcpp = "/home/user/sysroot/usr/include/c++/v1"; 829379d19fSRaphael Isemann std::string usr = "/home/user/sysroot/usr/include"; 83*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 84*99b7b41eSRaphael Isemann usr + "/stdio.h", 85*99b7b41eSRaphael Isemann // C++ library 86*99b7b41eSRaphael Isemann libcpp + "/vector", 87*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 88*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 899379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 909379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 919379d19fSRaphael Isemann testing::ElementsAre(libcpp, ResourceInc(), usr)); 929379d19fSRaphael Isemann } 939379d19fSRaphael Isemann 949379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) { 959379d19fSRaphael Isemann // Test that a locally build libc++ is detected. 96*99b7b41eSRaphael Isemann 979379d19fSRaphael Isemann std::string usr = "/usr/include"; 98*99b7b41eSRaphael Isemann std::string libcpp = "/home/user/llvm-build/include/c++/v1"; 99*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 100*99b7b41eSRaphael Isemann usr + "/stdio.h", 101*99b7b41eSRaphael Isemann // C++ library 102*99b7b41eSRaphael Isemann libcpp + "/vector", 103*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 104*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1059379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 1069379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 1079379d19fSRaphael Isemann testing::ElementsAre(libcpp, ResourceInc(), usr)); 1089379d19fSRaphael Isemann } 1099379d19fSRaphael Isemann 1109379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) { 1119379d19fSRaphael Isemann // Test that having an unrelated library in /usr/include doesn't break. 112*99b7b41eSRaphael Isemann 1139379d19fSRaphael Isemann std::string usr = "/usr/include"; 114*99b7b41eSRaphael Isemann std::string libcpp = "/home/user/llvm-build/include/c++/v1"; 115*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 116*99b7b41eSRaphael Isemann usr + "/stdio.h", 117*99b7b41eSRaphael Isemann // unrelated library 118*99b7b41eSRaphael Isemann usr + "/boost/vector", 119*99b7b41eSRaphael Isemann // C++ library 120*99b7b41eSRaphael Isemann libcpp + "/vector", 121*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 122*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1239379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 1249379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 1259379d19fSRaphael Isemann testing::ElementsAre(libcpp, ResourceInc(), usr)); 1269379d19fSRaphael Isemann } 1279379d19fSRaphael Isemann 1289379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Xcode) { 1299379d19fSRaphael Isemann // Test detection of libc++ coming from Xcode with generic platform names. 130*99b7b41eSRaphael Isemann 1319379d19fSRaphael Isemann std::string p = "/Applications/Xcode.app/Contents/Developer/"; 1329379d19fSRaphael Isemann std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1"; 1339379d19fSRaphael Isemann std::string usr = 1349379d19fSRaphael Isemann p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include"; 135*99b7b41eSRaphael Isemann std::vector<std::string> files = { 136*99b7b41eSRaphael Isemann // C library 137*99b7b41eSRaphael Isemann usr + "/stdio.h", 138*99b7b41eSRaphael Isemann // C++ library 139*99b7b41eSRaphael Isemann libcpp + "/vector", 140*99b7b41eSRaphael Isemann libcpp + "/module.modulemap", 141*99b7b41eSRaphael Isemann }; 142*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1439379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 1449379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 1459379d19fSRaphael Isemann testing::ElementsAre(libcpp, ResourceInc(), usr)); 1469379d19fSRaphael Isemann } 1479379d19fSRaphael Isemann 1489379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, LibCppV2) { 1499379d19fSRaphael Isemann // Test that a "v2" of libc++ is still correctly detected. 150*99b7b41eSRaphael Isemann 151*99b7b41eSRaphael Isemann std::string libcpp = "/usr/include/c++/v2"; 152*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 153*99b7b41eSRaphael Isemann "/usr/include/stdio.h", 154*99b7b41eSRaphael Isemann // C++ library 155*99b7b41eSRaphael Isemann libcpp + "/vector", 156*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 157*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1589379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 1599379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 1609379d19fSRaphael Isemann testing::ElementsAre("/usr/include/c++/v2", ResourceInc(), 1619379d19fSRaphael Isemann "/usr/include")); 1629379d19fSRaphael Isemann } 1639379d19fSRaphael Isemann 1649379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) { 1659379d19fSRaphael Isemann // Test that having some unknown file in the libc++ path doesn't break 1669379d19fSRaphael Isemann // anything. 167*99b7b41eSRaphael Isemann 168*99b7b41eSRaphael Isemann std::string libcpp = "/usr/include/c++/v1"; 169*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C library 170*99b7b41eSRaphael Isemann "/usr/include/stdio.h", 171*99b7b41eSRaphael Isemann // C++ library 172*99b7b41eSRaphael Isemann libcpp + "/non_existing_file", 173*99b7b41eSRaphael Isemann libcpp + "/module.modulemap", 174*99b7b41eSRaphael Isemann libcpp + "/vector"}; 175*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1769379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std")); 1779379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), 1789379d19fSRaphael Isemann testing::ElementsAre("/usr/include/c++/v1", ResourceInc(), 1799379d19fSRaphael Isemann "/usr/include")); 1809379d19fSRaphael Isemann } 1819379d19fSRaphael Isemann 1829379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, MissingUsrInclude) { 1839379d19fSRaphael Isemann // Test that we don't load 'std' if we can't find the C standard library. 184*99b7b41eSRaphael Isemann 185*99b7b41eSRaphael Isemann std::string libcpp = "/usr/include/c++/v1"; 186*99b7b41eSRaphael Isemann std::vector<std::string> files = {// C++ library 187*99b7b41eSRaphael Isemann libcpp + "/vector", 188*99b7b41eSRaphael Isemann libcpp + "/module.modulemap"}; 189*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 1909379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 1919379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 1929379d19fSRaphael Isemann } 1939379d19fSRaphael Isemann 1949379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, MissingLibCpp) { 1959379d19fSRaphael Isemann // Test that we don't load 'std' if we don't have a libc++. 196*99b7b41eSRaphael Isemann 197*99b7b41eSRaphael Isemann std::string usr = "/usr/include"; 198*99b7b41eSRaphael Isemann std::vector<std::string> files = { 199*99b7b41eSRaphael Isemann // C library 200*99b7b41eSRaphael Isemann usr + "/stdio.h", 201*99b7b41eSRaphael Isemann }; 202*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 2039379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 2049379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 2059379d19fSRaphael Isemann } 2069379d19fSRaphael Isemann 2079379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) { 2089379d19fSRaphael Isemann // Test that we don't do anything bad when we encounter libstdc++ paths. 209*99b7b41eSRaphael Isemann 210*99b7b41eSRaphael Isemann std::string usr = "/usr/include"; 211*99b7b41eSRaphael Isemann std::vector<std::string> files = { 212*99b7b41eSRaphael Isemann // C library 213*99b7b41eSRaphael Isemann usr + "/stdio.h", 214*99b7b41eSRaphael Isemann // C++ library 215*99b7b41eSRaphael Isemann usr + "/c++/8.0.1/vector", 216*99b7b41eSRaphael Isemann }; 217*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 2189379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 2199379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 2209379d19fSRaphael Isemann } 2219379d19fSRaphael Isemann 2229379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, AmbiguousCLib) { 2239379d19fSRaphael Isemann // Test that we don't do anything when we are not sure where the 2249379d19fSRaphael Isemann // right C standard library is. 225*99b7b41eSRaphael Isemann 226*99b7b41eSRaphael Isemann std::string usr1 = "/usr/include"; 227*99b7b41eSRaphael Isemann std::string usr2 = "/usr/include/other/path"; 228*99b7b41eSRaphael Isemann std::string libcpp = usr1 + "c++/v1"; 229*99b7b41eSRaphael Isemann std::vector<std::string> files = { 230*99b7b41eSRaphael Isemann // First C library 231*99b7b41eSRaphael Isemann usr1 + "/stdio.h", 232*99b7b41eSRaphael Isemann // Second C library 233*99b7b41eSRaphael Isemann usr2 + "/stdio.h", 234*99b7b41eSRaphael Isemann // C++ library 235*99b7b41eSRaphael Isemann libcpp + "/vector", 236*99b7b41eSRaphael Isemann libcpp + "/module.modulemap", 237*99b7b41eSRaphael Isemann }; 238*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 2399379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 2409379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 2419379d19fSRaphael Isemann } 2429379d19fSRaphael Isemann 2439379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) { 2449379d19fSRaphael Isemann // Test that we don't do anything when we are not sure where the 2459379d19fSRaphael Isemann // right libc++ is. 246*99b7b41eSRaphael Isemann 247*99b7b41eSRaphael Isemann std::string usr = "/usr/include"; 248*99b7b41eSRaphael Isemann std::string libcpp1 = usr + "c++/v1"; 249*99b7b41eSRaphael Isemann std::string libcpp2 = usr + "c++/v2"; 250*99b7b41eSRaphael Isemann std::vector<std::string> files = { 251*99b7b41eSRaphael Isemann // C library 252*99b7b41eSRaphael Isemann usr + "/stdio.h", 253*99b7b41eSRaphael Isemann // First C++ library 254*99b7b41eSRaphael Isemann libcpp1 + "/vector", 255*99b7b41eSRaphael Isemann libcpp1 + "/module.modulemap", 256*99b7b41eSRaphael Isemann // Second C++ library 257*99b7b41eSRaphael Isemann libcpp2 + "/vector", 258*99b7b41eSRaphael Isemann libcpp2 + "/module.modulemap", 259*99b7b41eSRaphael Isemann }; 260*99b7b41eSRaphael Isemann CppModuleConfiguration config(makeFiles(files)); 2619379d19fSRaphael Isemann EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre()); 2629379d19fSRaphael Isemann EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre()); 2639379d19fSRaphael Isemann } 264