1 //===-- CppModuleConfigurationTest.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Plugins/ExpressionParser/Clang/CppModuleConfiguration.h"
10 #include "Plugins/ExpressionParser/Clang/ClangHost.h"
11 #include "TestingSupport/SubsystemRAII.h"
12 #include "lldb/Host/FileSystem.h"
13 #include "lldb/Host/HostInfo.h"
14 
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 using namespace lldb_private;
19 
20 namespace {
21 struct CppModuleConfigurationTest : public testing::Test {
22   SubsystemRAII<FileSystem, HostInfo> subsystems;
23 };
24 } // namespace
25 
26 /// Returns the Clang resource include directory.
ResourceInc()27 static std::string ResourceInc() {
28   llvm::SmallString<256> resource_dir;
29   llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
30                           "include");
31   return std::string(resource_dir);
32 }
33 
34 /// Utility function turningn a list of paths into a FileSpecList.
makeFiles(llvm::ArrayRef<std::string> paths)35 static FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
36   FileSpecList result;
37   for (const std::string &path : paths)
38     result.Append(FileSpec(path, FileSpec::Style::posix));
39   return result;
40 }
41 
TEST_F(CppModuleConfigurationTest,Linux)42 TEST_F(CppModuleConfigurationTest, Linux) {
43   // Test the average Linux configuration.
44   std::string libcpp = "/usr/include/c++/v1";
45   std::string usr = "/usr/include";
46   CppModuleConfiguration config(
47       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
48   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
49   EXPECT_THAT(config.GetIncludeDirs(),
50               testing::ElementsAre(libcpp, ResourceInc(), usr));
51 }
52 
TEST_F(CppModuleConfigurationTest,Sysroot)53 TEST_F(CppModuleConfigurationTest, Sysroot) {
54   // Test that having a sysroot for the whole system works fine.
55   std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";
56   std::string usr = "/home/user/sysroot/usr/include";
57   CppModuleConfiguration config(
58       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
59   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
60   EXPECT_THAT(config.GetIncludeDirs(),
61               testing::ElementsAre(libcpp, ResourceInc(), usr));
62 }
63 
TEST_F(CppModuleConfigurationTest,LinuxLocalLibCpp)64 TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
65   // Test that a locally build libc++ is detected.
66   std::string libcpp = "/home/user/llvm-build/include/c++/v1";
67   std::string usr = "/usr/include";
68   CppModuleConfiguration config(
69       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
70   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
71   EXPECT_THAT(config.GetIncludeDirs(),
72               testing::ElementsAre(libcpp, ResourceInc(), usr));
73 }
74 
TEST_F(CppModuleConfigurationTest,UnrelatedLibrary)75 TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
76   // Test that having an unrelated library in /usr/include doesn't break.
77   std::string libcpp = "/home/user/llvm-build/include/c++/v1";
78   std::string usr = "/usr/include";
79   CppModuleConfiguration config(makeFiles(
80       {usr + "/bits/types.h", libcpp + "/vector", usr + "/boost/vector"}));
81   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
82   EXPECT_THAT(config.GetIncludeDirs(),
83               testing::ElementsAre(libcpp, ResourceInc(), usr));
84 }
85 
TEST_F(CppModuleConfigurationTest,Xcode)86 TEST_F(CppModuleConfigurationTest, Xcode) {
87   // Test detection of libc++ coming from Xcode with generic platform names.
88   std::string p = "/Applications/Xcode.app/Contents/Developer/";
89   std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";
90   std::string usr =
91       p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
92   CppModuleConfiguration config(
93       makeFiles({libcpp + "/unordered_map", usr + "/stdio.h"}));
94   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
95   EXPECT_THAT(config.GetIncludeDirs(),
96               testing::ElementsAre(libcpp, ResourceInc(), usr));
97 }
98 
TEST_F(CppModuleConfigurationTest,LibCppV2)99 TEST_F(CppModuleConfigurationTest, LibCppV2) {
100   // Test that a "v2" of libc++ is still correctly detected.
101   CppModuleConfiguration config(
102       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v2/vector"}));
103   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
104   EXPECT_THAT(config.GetIncludeDirs(),
105               testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
106                                    "/usr/include"));
107 }
108 
TEST_F(CppModuleConfigurationTest,UnknownLibCppFile)109 TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
110   // Test that having some unknown file in the libc++ path doesn't break
111   // anything.
112   CppModuleConfiguration config(makeFiles(
113       {"/usr/include/bits/types.h", "/usr/include/c++/v1/non_existing_file"}));
114   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
115   EXPECT_THAT(config.GetIncludeDirs(),
116               testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
117                                    "/usr/include"));
118 }
119 
TEST_F(CppModuleConfigurationTest,MissingUsrInclude)120 TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
121   // Test that we don't load 'std' if we can't find the C standard library.
122   CppModuleConfiguration config(makeFiles({"/usr/include/c++/v1/vector"}));
123   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
124   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
125 }
126 
TEST_F(CppModuleConfigurationTest,MissingLibCpp)127 TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
128   // Test that we don't load 'std' if we don't have a libc++.
129   CppModuleConfiguration config(makeFiles({"/usr/include/bits/types.h"}));
130   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
131   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
132 }
133 
TEST_F(CppModuleConfigurationTest,IgnoreLibStdCpp)134 TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
135   // Test that we don't do anything bad when we encounter libstdc++ paths.
136   CppModuleConfiguration config(makeFiles(
137       {"/usr/include/bits/types.h", "/usr/include/c++/8.0.1/vector"}));
138   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
139   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
140 }
141 
TEST_F(CppModuleConfigurationTest,AmbiguousCLib)142 TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
143   // Test that we don't do anything when we are not sure where the
144   // right C standard library is.
145   CppModuleConfiguration config(
146       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
147                  "/sysroot/usr/include/bits/types.h"}));
148   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
149   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
150 }
151 
TEST_F(CppModuleConfigurationTest,AmbiguousLibCpp)152 TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
153   // Test that we don't do anything when we are not sure where the
154   // right libc++ is.
155   CppModuleConfiguration config(
156       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
157                  "/usr/include/c++/v2/vector"}));
158   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
159   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
160 }
161