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