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