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