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"
149379d19fSRaphael Isemann 
159379d19fSRaphael Isemann #include "gmock/gmock.h"
169379d19fSRaphael Isemann #include "gtest/gtest.h"
179379d19fSRaphael Isemann 
189379d19fSRaphael Isemann using namespace lldb_private;
199379d19fSRaphael Isemann 
209379d19fSRaphael Isemann namespace {
219379d19fSRaphael Isemann struct CppModuleConfigurationTest : public testing::Test {
225dca0596SRaphael Isemann   SubsystemRAII<FileSystem, HostInfo> subsystems;
239379d19fSRaphael Isemann };
249379d19fSRaphael Isemann } // namespace
259379d19fSRaphael Isemann 
269379d19fSRaphael Isemann /// Returns the Clang resource include directory.
279379d19fSRaphael Isemann static std::string ResourceInc() {
289379d19fSRaphael Isemann   llvm::SmallString<256> resource_dir;
299379d19fSRaphael Isemann   llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
309379d19fSRaphael Isemann                           "include");
31*e28d8f90SJonas Devlieghere   return std::string(resource_dir);
329379d19fSRaphael Isemann }
339379d19fSRaphael Isemann 
349379d19fSRaphael Isemann /// Utility function turningn a list of paths into a FileSpecList.
359379d19fSRaphael Isemann static FileSpecList makeFiles(llvm::ArrayRef<std::string> paths) {
369379d19fSRaphael Isemann   FileSpecList result;
379379d19fSRaphael Isemann   for (const std::string &path : paths)
381a2805b8SRaphael Isemann     result.Append(FileSpec(path, FileSpec::Style::posix));
399379d19fSRaphael Isemann   return result;
409379d19fSRaphael Isemann }
419379d19fSRaphael Isemann 
429379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Linux) {
439379d19fSRaphael Isemann   // Test the average Linux configuration.
449379d19fSRaphael Isemann   std::string libcpp = "/usr/include/c++/v1";
459379d19fSRaphael Isemann   std::string usr = "/usr/include";
469379d19fSRaphael Isemann   CppModuleConfiguration config(
479379d19fSRaphael Isemann       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
489379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
499379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
509379d19fSRaphael Isemann               testing::ElementsAre(libcpp, ResourceInc(), usr));
519379d19fSRaphael Isemann }
529379d19fSRaphael Isemann 
539379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Sysroot) {
549379d19fSRaphael Isemann   // Test that having a sysroot for the whole system works fine.
559379d19fSRaphael Isemann   std::string libcpp = "/home/user/sysroot/usr/include/c++/v1";
569379d19fSRaphael Isemann   std::string usr = "/home/user/sysroot/usr/include";
579379d19fSRaphael Isemann   CppModuleConfiguration config(
589379d19fSRaphael Isemann       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
599379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
609379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
619379d19fSRaphael Isemann               testing::ElementsAre(libcpp, ResourceInc(), usr));
629379d19fSRaphael Isemann }
639379d19fSRaphael Isemann 
649379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, LinuxLocalLibCpp) {
659379d19fSRaphael Isemann   // Test that a locally build libc++ is detected.
669379d19fSRaphael Isemann   std::string libcpp = "/home/user/llvm-build/include/c++/v1";
679379d19fSRaphael Isemann   std::string usr = "/usr/include";
689379d19fSRaphael Isemann   CppModuleConfiguration config(
699379d19fSRaphael Isemann       makeFiles({usr + "/bits/types.h", libcpp + "/vector"}));
709379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
719379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
729379d19fSRaphael Isemann               testing::ElementsAre(libcpp, ResourceInc(), usr));
739379d19fSRaphael Isemann }
749379d19fSRaphael Isemann 
759379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, UnrelatedLibrary) {
769379d19fSRaphael Isemann   // Test that having an unrelated library in /usr/include doesn't break.
779379d19fSRaphael Isemann   std::string libcpp = "/home/user/llvm-build/include/c++/v1";
789379d19fSRaphael Isemann   std::string usr = "/usr/include";
799379d19fSRaphael Isemann   CppModuleConfiguration config(makeFiles(
809379d19fSRaphael Isemann       {usr + "/bits/types.h", libcpp + "/vector", usr + "/boost/vector"}));
819379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
829379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
839379d19fSRaphael Isemann               testing::ElementsAre(libcpp, ResourceInc(), usr));
849379d19fSRaphael Isemann }
859379d19fSRaphael Isemann 
869379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, Xcode) {
879379d19fSRaphael Isemann   // Test detection of libc++ coming from Xcode with generic platform names.
889379d19fSRaphael Isemann   std::string p = "/Applications/Xcode.app/Contents/Developer/";
899379d19fSRaphael Isemann   std::string libcpp = p + "Toolchains/B.xctoolchain/usr/include/c++/v1";
909379d19fSRaphael Isemann   std::string usr =
919379d19fSRaphael Isemann       p + "Platforms/A.platform/Developer/SDKs/OSVers.sdk/usr/include";
929379d19fSRaphael Isemann   CppModuleConfiguration config(
939379d19fSRaphael Isemann       makeFiles({libcpp + "/unordered_map", usr + "/stdio.h"}));
949379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
959379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
969379d19fSRaphael Isemann               testing::ElementsAre(libcpp, ResourceInc(), usr));
979379d19fSRaphael Isemann }
989379d19fSRaphael Isemann 
999379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, LibCppV2) {
1009379d19fSRaphael Isemann   // Test that a "v2" of libc++ is still correctly detected.
1019379d19fSRaphael Isemann   CppModuleConfiguration config(
1029379d19fSRaphael Isemann       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v2/vector"}));
1039379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
1049379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
1059379d19fSRaphael Isemann               testing::ElementsAre("/usr/include/c++/v2", ResourceInc(),
1069379d19fSRaphael Isemann                                    "/usr/include"));
1079379d19fSRaphael Isemann }
1089379d19fSRaphael Isemann 
1099379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, UnknownLibCppFile) {
1109379d19fSRaphael Isemann   // Test that having some unknown file in the libc++ path doesn't break
1119379d19fSRaphael Isemann   // anything.
1129379d19fSRaphael Isemann   CppModuleConfiguration config(makeFiles(
1139379d19fSRaphael Isemann       {"/usr/include/bits/types.h", "/usr/include/c++/v1/non_existing_file"}));
1149379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre("std"));
1159379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(),
1169379d19fSRaphael Isemann               testing::ElementsAre("/usr/include/c++/v1", ResourceInc(),
1179379d19fSRaphael Isemann                                    "/usr/include"));
1189379d19fSRaphael Isemann }
1199379d19fSRaphael Isemann 
1209379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, MissingUsrInclude) {
1219379d19fSRaphael Isemann   // Test that we don't load 'std' if we can't find the C standard library.
1229379d19fSRaphael Isemann   CppModuleConfiguration config(makeFiles({"/usr/include/c++/v1/vector"}));
1239379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
1249379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
1259379d19fSRaphael Isemann }
1269379d19fSRaphael Isemann 
1279379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, MissingLibCpp) {
1289379d19fSRaphael Isemann   // Test that we don't load 'std' if we don't have a libc++.
1299379d19fSRaphael Isemann   CppModuleConfiguration config(makeFiles({"/usr/include/bits/types.h"}));
1309379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
1319379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
1329379d19fSRaphael Isemann }
1339379d19fSRaphael Isemann 
1349379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, IgnoreLibStdCpp) {
1359379d19fSRaphael Isemann   // Test that we don't do anything bad when we encounter libstdc++ paths.
1369379d19fSRaphael Isemann   CppModuleConfiguration config(makeFiles(
1379379d19fSRaphael Isemann       {"/usr/include/bits/types.h", "/usr/include/c++/8.0.1/vector"}));
1389379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
1399379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
1409379d19fSRaphael Isemann }
1419379d19fSRaphael Isemann 
1429379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, AmbiguousCLib) {
1439379d19fSRaphael Isemann   // Test that we don't do anything when we are not sure where the
1449379d19fSRaphael Isemann   // right C standard library is.
1459379d19fSRaphael Isemann   CppModuleConfiguration config(
1469379d19fSRaphael Isemann       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
1479379d19fSRaphael Isemann                  "/sysroot/usr/include/bits/types.h"}));
1489379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
1499379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
1509379d19fSRaphael Isemann }
1519379d19fSRaphael Isemann 
1529379d19fSRaphael Isemann TEST_F(CppModuleConfigurationTest, AmbiguousLibCpp) {
1539379d19fSRaphael Isemann   // Test that we don't do anything when we are not sure where the
1549379d19fSRaphael Isemann   // right libc++ is.
1559379d19fSRaphael Isemann   CppModuleConfiguration config(
1569379d19fSRaphael Isemann       makeFiles({"/usr/include/bits/types.h", "/usr/include/c++/v1/vector",
1579379d19fSRaphael Isemann                  "/usr/include/c++/v2/vector"}));
1589379d19fSRaphael Isemann   EXPECT_THAT(config.GetImportedModules(), testing::ElementsAre());
1599379d19fSRaphael Isemann   EXPECT_THAT(config.GetIncludeDirs(), testing::ElementsAre());
1609379d19fSRaphael Isemann }
161