303 lines · cpp
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-exception6//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 18using namespace lldb_private;19 20namespace {21struct CppModuleConfigurationTest : public testing::Test {22 llvm::MemoryBufferRef m_empty_buffer;23 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> m_fs;24 25 CppModuleConfigurationTest()26 : m_empty_buffer("", "<empty buffer>"),27 m_fs(new llvm::vfs::InMemoryFileSystem()) {}28 29 void SetUp() override {30 FileSystem::Initialize(m_fs);31 HostInfo::Initialize();32 }33 34 void TearDown() override {35 HostInfo::Terminate();36 FileSystem::Terminate();37 }38 39 /// Utility function turning a list of paths into a FileSpecList.40 FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {41 FileSpecList result;42 for (const std::string &path : paths) {43 result.Append(FileSpec(path, FileSpec::Style::posix));44 if (!m_fs->addFileNoOwn(path, static_cast<time_t>(0), m_empty_buffer))45 llvm_unreachable("Invalid test configuration?");46 }47 return result;48 }49};50} // namespace51 52/// Returns the Clang resource include directory.53static std::string ResourceInc() {54 llvm::SmallString<256> resource_dir;55 llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),56 "include");57 return std::string(resource_dir);58}59 60TEST_F(CppModuleConfigurationTest, Linux) {61 // Test the average Linux configuration.62 63 std::string usr = "/usr/include";64 std::string libcpp = "/usr/include/c++/v1";65 std::vector<std::string> files = {// C library66 usr + "/stdio.h",67 // C++ library68 libcpp + "/vector",69 libcpp + "/module.modulemap"};70 CppModuleConfiguration config(makeFiles(files), llvm::Triple());71 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));72 EXPECT_THAT(config.GetIncludeDirs(),73 testing::ElementsAre(libcpp, ResourceInc(), usr));74}75 76TEST_F(CppModuleConfigurationTest, LinuxTargetSpecificInclude) {77 // Test the average Linux configuration.78 79 std::string usr = "/usr/include";80 std::string usr_target = "/usr/include/x86_64-linux-gnu";81 std::string libcpp = "/usr/include/c++/v1";82 std::string libcpp_target = "/usr/include/x86_64-unknown-linux-gnu/c++/v1";83 std::vector<std::string> files = {84 // C library85 usr + "/stdio.h", usr_target + "/sys/cdefs.h",86 // C++ library87 libcpp + "/vector", libcpp + "/module.modulemap"};88 CppModuleConfiguration config(makeFiles(files),89 llvm::Triple("x86_64-unknown-linux-gnu"));90 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));91 EXPECT_THAT(config.GetIncludeDirs(),92 testing::ElementsAre(libcpp, ResourceInc(), usr, usr_target,93 libcpp_target));94}95 96TEST_F(CppModuleConfigurationTest, Sysroot) {97 // Test that having a sysroot for the whole system works fine.98 99 std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";100 std::string usr = "/home/user/sysroot/usr/include";101 std::vector<std::string> files = {// C library102 usr + "/stdio.h",103 // C++ library104 libcpp + "/vector",105 libcpp + "/module.modulemap"};106 CppModuleConfiguration config(makeFiles(files), llvm::Triple());107 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));108 EXPECT_THAT(config.GetIncludeDirs(),109 testing::ElementsAre(libcpp, ResourceInc(), usr));110}111 112TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {113 // Test that a locally build libc++ is detected.114 115 std::string usr = "/usr/include";116 std::string libcpp = "/home/user/llvm-build/include/c++/v1";117 std::vector<std::string> files = {// C library118 usr + "/stdio.h",119 // C++ library120 libcpp + "/vector",121 libcpp + "/module.modulemap"};122 CppModuleConfiguration config(makeFiles(files), llvm::Triple());123 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));124 EXPECT_THAT(config.GetIncludeDirs(),125 testing::ElementsAre(libcpp, ResourceInc(), usr));126}127 128TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {129 // Test that having an unrelated library in /usr/include doesn't break.130 131 std::string usr = "/usr/include";132 std::string libcpp = "/home/user/llvm-build/include/c++/v1";133 std::vector<std::string> files = {// C library134 usr + "/stdio.h",135 // unrelated library136 usr + "/boost/vector",137 // C++ library138 libcpp + "/vector",139 libcpp + "/module.modulemap"};140 CppModuleConfiguration config(makeFiles(files), llvm::Triple());141 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));142 EXPECT_THAT(config.GetIncludeDirs(),143 testing::ElementsAre(libcpp, ResourceInc(), usr));144}145 146TEST_F(CppModuleConfigurationTest, UnrelatedLibraryWithTargetSpecificInclude) {147 // Test that having an unrelated library in /usr/include doesn't break.148 149 std::string usr = "/usr/include";150 std::string libcpp = "/home/user/llvm-build/include/c++/v1";151 std::string libcpp_target =152 "/home/user/llvm-build/include/x86_64-unknown-linux-gnu/c++/v1";153 std::vector<std::string> files = {// C library154 usr + "/stdio.h",155 // unrelated library156 usr + "/boost/vector",157 // C++ library158 libcpp + "/vector",159 libcpp + "/module.modulemap"};160 CppModuleConfiguration config(makeFiles(files),161 llvm::Triple("x86_64-unknown-linux-gnu"));162 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));163 EXPECT_THAT(config.GetIncludeDirs(),164 testing::ElementsAre(libcpp, ResourceInc(), usr, libcpp_target));165}166 167TEST_F(CppModuleConfigurationTest, Xcode) {168 // Test detection of libc++ coming from Xcode with generic platform names.169 170 std::string p = "/Applications/Xcode.app/Contents/Developer/";171 std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";172 std::string usr =173 p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";174 std::vector<std::string> files = {175 // C library176 usr + "/stdio.h",177 // C++ library178 libcpp + "/vector",179 libcpp + "/module.modulemap",180 };181 CppModuleConfiguration config(makeFiles(files), llvm::Triple());182 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));183 EXPECT_THAT(config.GetIncludeDirs(),184 testing::ElementsAre(libcpp, ResourceInc(), usr));185}186 187TEST_F(CppModuleConfigurationTest, LibCppV2) {188 // Test that a "v2" of libc++ is still correctly detected.189 190 std::string libcpp = "/usr/include/c++/v2";191 std::vector<std::string> files = {// C library192 "/usr/include/stdio.h",193 // C++ library194 libcpp + "/vector",195 libcpp + "/module.modulemap"};196 CppModuleConfiguration config(makeFiles(files), llvm::Triple());197 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));198 EXPECT_THAT(config.GetIncludeDirs(),199 testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),200 "/usr/include"));201}202 203TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {204 // Test that having some unknown file in the libc++ path doesn't break205 // anything.206 207 std::string libcpp = "/usr/include/c++/v1";208 std::vector<std::string> files = {// C library209 "/usr/include/stdio.h",210 // C++ library211 libcpp + "/non_existing_file",212 libcpp + "/module.modulemap",213 libcpp + "/vector"};214 CppModuleConfiguration config(makeFiles(files), llvm::Triple());215 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));216 EXPECT_THAT(config.GetIncludeDirs(),217 testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),218 "/usr/include"));219}220 221TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {222 // Test that we don't load 'std' if we can't find the C standard library.223 224 std::string libcpp = "/usr/include/c++/v1";225 std::vector<std::string> files = {// C++ library226 libcpp + "/vector",227 libcpp + "/module.modulemap"};228 CppModuleConfiguration config(makeFiles(files), llvm::Triple());229 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());230 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());231}232 233TEST_F(CppModuleConfigurationTest, MissingLibCpp) {234 // Test that we don't load 'std' if we don't have a libc++.235 236 std::string usr = "/usr/include";237 std::vector<std::string> files = {238 // C library239 usr + "/stdio.h",240 };241 CppModuleConfiguration config(makeFiles(files), llvm::Triple());242 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());243 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());244}245 246TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {247 // Test that we don't do anything bad when we encounter libstdc++ paths.248 249 std::string usr = "/usr/include";250 std::vector<std::string> files = {251 // C library252 usr + "/stdio.h",253 // C++ library254 usr + "/c++/8.0.1/vector",255 };256 CppModuleConfiguration config(makeFiles(files), llvm::Triple());257 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());258 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());259}260 261TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {262 // Test that we don't do anything when we are not sure where the263 // right C standard library is.264 265 std::string usr1 = "/usr/include";266 std::string usr2 = "/usr/include/other/path";267 std::string libcpp = usr1 + "c++/v1";268 std::vector<std::string> files = {269 // First C library270 usr1 + "/stdio.h",271 // Second C library272 usr2 + "/stdio.h",273 // C++ library274 libcpp + "/vector",275 libcpp + "/module.modulemap",276 };277 CppModuleConfiguration config(makeFiles(files), llvm::Triple());278 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());279 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());280}281 282TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {283 // Test that we don't do anything when we are not sure where the284 // right libc++ is.285 286 std::string usr = "/usr/include";287 std::string libcpp1 = usr + "c++/v1";288 std::string libcpp2 = usr + "c++/v2";289 std::vector<std::string> files = {290 // C library291 usr + "/stdio.h",292 // First C++ library293 libcpp1 + "/vector",294 libcpp1 + "/module.modulemap",295 // Second C++ library296 libcpp2 + "/vector",297 libcpp2 + "/module.modulemap",298 };299 CppModuleConfiguration config(makeFiles(files), llvm::Triple());300 EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());301 EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());302}303