1 //===-- ConfigCompileTests.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 "Config.h"
10 #include "ConfigFragment.h"
11 #include "ConfigTesting.h"
12 #include "Diagnostics.h"
13 #include "Feature.h"
14 #include "TestFS.h"
15 #include "clang/Basic/DiagnosticSema.h"
16 #include "llvm/ADT/None.h"
17 #include "llvm/ADT/Optional.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/SourceMgr.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <string>
24 
25 namespace clang {
26 namespace clangd {
27 namespace config {
28 namespace {
29 using ::testing::AllOf;
30 using ::testing::Contains;
31 using ::testing::ElementsAre;
32 using ::testing::IsEmpty;
33 using ::testing::SizeIs;
34 using ::testing::StartsWith;
35 using ::testing::UnorderedElementsAre;
36 
37 class ConfigCompileTests : public ::testing::Test {
38 protected:
39   CapturedDiags Diags;
40   Config Conf;
41   Fragment Frag;
42   Params Parm;
43 
compileAndApply()44   bool compileAndApply() {
45     Conf = Config();
46     Diags.Diagnostics.clear();
47     auto Compiled = std::move(Frag).compile(Diags.callback());
48     return Compiled(Parm, Conf);
49   }
50 };
51 
TEST_F(ConfigCompileTests,Condition)52 TEST_F(ConfigCompileTests, Condition) {
53   // No condition.
54   Frag = {};
55   Frag.CompileFlags.Add.emplace_back("X");
56   EXPECT_TRUE(compileAndApply()) << "Empty config";
57   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
58   EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(1));
59 
60   // Regex with no file.
61   Frag = {};
62   Frag.If.PathMatch.emplace_back("fo*");
63   EXPECT_FALSE(compileAndApply());
64   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
65   EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(0));
66 
67   // Following tests have a file path set.
68   Parm.Path = "bar";
69 
70   // Non-matching regex.
71   Frag = {};
72   Frag.If.PathMatch.emplace_back("fo*");
73   EXPECT_FALSE(compileAndApply());
74   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
75 
76   // Matching regex.
77   Frag = {};
78   Frag.If.PathMatch.emplace_back("fo*");
79   Frag.If.PathMatch.emplace_back("ba*r");
80   EXPECT_TRUE(compileAndApply());
81   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
82 
83   // Excluded regex.
84   Frag = {};
85   Frag.If.PathMatch.emplace_back("b.*");
86   Frag.If.PathExclude.emplace_back(".*r");
87   EXPECT_FALSE(compileAndApply()) << "Included but also excluded";
88   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
89 
90   // Invalid regex.
91   Frag = {};
92   Frag.If.PathMatch.emplace_back("**]@theu");
93   EXPECT_TRUE(compileAndApply());
94   EXPECT_THAT(Diags.Diagnostics, SizeIs(1));
95   EXPECT_THAT(Diags.Diagnostics.front().Message, StartsWith("Invalid regex"));
96 
97   // Valid regex and unknown key.
98   Frag = {};
99   Frag.If.HasUnrecognizedCondition = true;
100   Frag.If.PathMatch.emplace_back("ba*r");
101   EXPECT_FALSE(compileAndApply());
102   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
103 
104   // Only matches case-insensitively.
105   Frag = {};
106   Frag.If.PathMatch.emplace_back("B.*R");
107   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
108 #ifdef CLANGD_PATH_CASE_INSENSITIVE
109   EXPECT_TRUE(compileAndApply());
110 #else
111   EXPECT_FALSE(compileAndApply());
112 #endif
113 
114   Frag = {};
115   Frag.If.PathExclude.emplace_back("B.*R");
116   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
117 #ifdef CLANGD_PATH_CASE_INSENSITIVE
118   EXPECT_FALSE(compileAndApply());
119 #else
120   EXPECT_TRUE(compileAndApply());
121 #endif
122 }
123 
TEST_F(ConfigCompileTests,CompileCommands)124 TEST_F(ConfigCompileTests, CompileCommands) {
125   Frag.CompileFlags.Compiler.emplace("tpc.exe");
126   Frag.CompileFlags.Add.emplace_back("-foo");
127   Frag.CompileFlags.Remove.emplace_back("--include-directory=");
128   std::vector<std::string> Argv = {"clang", "-I", "bar/", "--", "a.cc"};
129   EXPECT_TRUE(compileAndApply());
130   EXPECT_THAT(Conf.CompileFlags.Edits, SizeIs(3));
131   for (auto &Edit : Conf.CompileFlags.Edits)
132     Edit(Argv);
133   EXPECT_THAT(Argv, ElementsAre("tpc.exe", "-foo", "--", "a.cc"));
134 }
135 
TEST_F(ConfigCompileTests,CompilationDatabase)136 TEST_F(ConfigCompileTests, CompilationDatabase) {
137   Frag.CompileFlags.CompilationDatabase.emplace("None");
138   EXPECT_TRUE(compileAndApply());
139   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
140             Config::CDBSearchSpec::NoCDBSearch);
141 
142   Frag.CompileFlags.CompilationDatabase.emplace("Ancestors");
143   EXPECT_TRUE(compileAndApply());
144   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
145             Config::CDBSearchSpec::Ancestors);
146 
147   // Relative path not allowed without directory set.
148   Frag.CompileFlags.CompilationDatabase.emplace("Something");
149   EXPECT_TRUE(compileAndApply());
150   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
151             Config::CDBSearchSpec::Ancestors)
152       << "default value";
153   EXPECT_THAT(Diags.Diagnostics,
154               ElementsAre(diagMessage(
155                   "CompilationDatabase must be an absolute path, because this "
156                   "fragment is not associated with any directory.")));
157 
158   // Relative path allowed if directory is set.
159   Frag.Source.Directory = testRoot();
160   EXPECT_TRUE(compileAndApply());
161   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
162             Config::CDBSearchSpec::FixedDir);
163   EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something"));
164   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
165 
166   // Absolute path allowed.
167   Frag.Source.Directory.clear();
168   Frag.CompileFlags.CompilationDatabase.emplace(testPath("Something2"));
169   EXPECT_TRUE(compileAndApply());
170   EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
171             Config::CDBSearchSpec::FixedDir);
172   EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something2"));
173   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
174 }
175 
TEST_F(ConfigCompileTests,Index)176 TEST_F(ConfigCompileTests, Index) {
177   Frag.Index.Background.emplace("Skip");
178   EXPECT_TRUE(compileAndApply());
179   EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
180 
181   Frag = {};
182   Frag.Index.Background.emplace("Foo");
183   EXPECT_TRUE(compileAndApply());
184   EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Build)
185       << "by default";
186   EXPECT_THAT(
187       Diags.Diagnostics,
188       ElementsAre(diagMessage(
189           "Invalid Background value 'Foo'. Valid values are Build, Skip.")));
190 }
191 
TEST_F(ConfigCompileTests,PathSpecMatch)192 TEST_F(ConfigCompileTests, PathSpecMatch) {
193   auto BarPath = llvm::sys::path::convert_to_slash(testPath("foo/bar.h"));
194   Parm.Path = BarPath;
195 
196   struct {
197     std::string Directory;
198     std::string PathSpec;
199     bool ShouldMatch;
200   } Cases[] = {
201       {
202           // Absolute path matches.
203           "",
204           llvm::sys::path::convert_to_slash(testPath("foo/bar.h")),
205           true,
206       },
207       {
208           // Absolute path fails.
209           "",
210           llvm::sys::path::convert_to_slash(testPath("bar/bar.h")),
211           false,
212       },
213       {
214           // Relative should fail to match as /foo/bar.h doesn't reside under
215           // /baz/.
216           testPath("baz"),
217           "bar\\.h",
218           false,
219       },
220       {
221           // Relative should pass with /foo as directory.
222           testPath("foo"),
223           "bar\\.h",
224           true,
225       },
226   };
227 
228   // PathMatch
229   for (const auto &Case : Cases) {
230     Frag = {};
231     Frag.If.PathMatch.emplace_back(Case.PathSpec);
232     Frag.Source.Directory = Case.Directory;
233     EXPECT_EQ(compileAndApply(), Case.ShouldMatch);
234     ASSERT_THAT(Diags.Diagnostics, IsEmpty());
235   }
236 
237   // PathEclude
238   for (const auto &Case : Cases) {
239     SCOPED_TRACE(Case.Directory);
240     SCOPED_TRACE(Case.PathSpec);
241     Frag = {};
242     Frag.If.PathExclude.emplace_back(Case.PathSpec);
243     Frag.Source.Directory = Case.Directory;
244     EXPECT_NE(compileAndApply(), Case.ShouldMatch);
245     ASSERT_THAT(Diags.Diagnostics, IsEmpty());
246   }
247 }
248 
TEST_F(ConfigCompileTests,DiagnosticsIncludeCleaner)249 TEST_F(ConfigCompileTests, DiagnosticsIncludeCleaner) {
250   // Defaults to None.
251   EXPECT_TRUE(compileAndApply());
252   EXPECT_EQ(Conf.Diagnostics.UnusedIncludes,
253             Config::UnusedIncludesPolicy::None);
254 
255   Frag = {};
256   Frag.Diagnostics.UnusedIncludes.emplace("None");
257   EXPECT_TRUE(compileAndApply());
258   EXPECT_EQ(Conf.Diagnostics.UnusedIncludes,
259             Config::UnusedIncludesPolicy::None);
260 
261   Frag = {};
262   Frag.Diagnostics.UnusedIncludes.emplace("Strict");
263   EXPECT_TRUE(compileAndApply());
264   EXPECT_EQ(Conf.Diagnostics.UnusedIncludes,
265             Config::UnusedIncludesPolicy::Strict);
266 
267   Frag = {};
268   EXPECT_TRUE(Conf.Diagnostics.Includes.IgnoreHeader.empty())
269       << Conf.Diagnostics.Includes.IgnoreHeader.size();
270   Frag.Diagnostics.Includes.IgnoreHeader.push_back(
271       Located<std::string>("foo.h"));
272   Frag.Diagnostics.Includes.IgnoreHeader.push_back(
273       Located<std::string>(".*inc"));
274   EXPECT_TRUE(compileAndApply());
275   auto HeaderFilter = [this](llvm::StringRef Path) {
276     for (auto &Filter : Conf.Diagnostics.Includes.IgnoreHeader) {
277       if (Filter(Path))
278         return true;
279     }
280     return false;
281   };
282   EXPECT_TRUE(HeaderFilter("foo.h"));
283   EXPECT_FALSE(HeaderFilter("bar.h"));
284 }
285 
TEST_F(ConfigCompileTests,DiagnosticSuppression)286 TEST_F(ConfigCompileTests, DiagnosticSuppression) {
287   Frag.Diagnostics.Suppress.emplace_back("bugprone-use-after-move");
288   Frag.Diagnostics.Suppress.emplace_back("unreachable-code");
289   Frag.Diagnostics.Suppress.emplace_back("-Wunused-variable");
290   Frag.Diagnostics.Suppress.emplace_back("typecheck_bool_condition");
291   Frag.Diagnostics.Suppress.emplace_back("err_unexpected_friend");
292   Frag.Diagnostics.Suppress.emplace_back("warn_alloca");
293   EXPECT_TRUE(compileAndApply());
294   EXPECT_THAT(Conf.Diagnostics.Suppress.keys(),
295               UnorderedElementsAre("bugprone-use-after-move",
296                                    "unreachable-code", "unused-variable",
297                                    "typecheck_bool_condition",
298                                    "unexpected_friend", "warn_alloca"));
299   EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
300       diag::warn_unreachable, Conf.Diagnostics.Suppress, LangOptions()));
301   // Subcategory not respected/suppressed.
302   EXPECT_FALSE(isBuiltinDiagnosticSuppressed(
303       diag::warn_unreachable_break, Conf.Diagnostics.Suppress, LangOptions()));
304   EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
305       diag::warn_unused_variable, Conf.Diagnostics.Suppress, LangOptions()));
306   EXPECT_TRUE(isBuiltinDiagnosticSuppressed(diag::err_typecheck_bool_condition,
307                                             Conf.Diagnostics.Suppress,
308                                             LangOptions()));
309   EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
310       diag::err_unexpected_friend, Conf.Diagnostics.Suppress, LangOptions()));
311   EXPECT_TRUE(isBuiltinDiagnosticSuppressed(
312       diag::warn_alloca, Conf.Diagnostics.Suppress, LangOptions()));
313 
314   Frag.Diagnostics.Suppress.emplace_back("*");
315   EXPECT_TRUE(compileAndApply());
316   EXPECT_TRUE(Conf.Diagnostics.SuppressAll);
317   EXPECT_THAT(Conf.Diagnostics.Suppress, IsEmpty());
318 }
319 
TEST_F(ConfigCompileTests,Tidy)320 TEST_F(ConfigCompileTests, Tidy) {
321   auto &Tidy = Frag.Diagnostics.ClangTidy;
322   Tidy.Add.emplace_back("bugprone-use-after-move");
323   Tidy.Add.emplace_back("llvm-*");
324   Tidy.Remove.emplace_back("llvm-include-order");
325   Tidy.Remove.emplace_back("readability-*");
326   Tidy.CheckOptions.emplace_back(
327       std::make_pair(std::string("StrictMode"), std::string("true")));
328   Tidy.CheckOptions.emplace_back(std::make_pair(
329       std::string("example-check.ExampleOption"), std::string("0")));
330   EXPECT_TRUE(compileAndApply());
331   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.size(), 2U);
332   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup("StrictMode"),
333             "true");
334   EXPECT_EQ(Conf.Diagnostics.ClangTidy.CheckOptions.lookup(
335                 "example-check.ExampleOption"),
336             "0");
337 #if CLANGD_TIDY_CHECKS
338   EXPECT_EQ(
339       Conf.Diagnostics.ClangTidy.Checks,
340       "bugprone-use-after-move,llvm-*,-llvm-include-order,-readability-*");
341   EXPECT_THAT(Diags.Diagnostics, IsEmpty());
342 #else // !CLANGD_TIDY_CHECKS
343   EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "llvm-*,-readability-*");
344   EXPECT_THAT(
345       Diags.Diagnostics,
346       ElementsAre(
347           diagMessage(
348               "clang-tidy check 'bugprone-use-after-move' was not found"),
349           diagMessage("clang-tidy check 'llvm-include-order' was not found")));
350 #endif
351 }
352 
TEST_F(ConfigCompileTests,TidyBadChecks)353 TEST_F(ConfigCompileTests, TidyBadChecks) {
354   auto &Tidy = Frag.Diagnostics.ClangTidy;
355   Tidy.Add.emplace_back("unknown-check");
356   Tidy.Remove.emplace_back("*");
357   Tidy.Remove.emplace_back("llvm-includeorder");
358   EXPECT_TRUE(compileAndApply());
359   // Ensure bad checks are stripped from the glob.
360   EXPECT_EQ(Conf.Diagnostics.ClangTidy.Checks, "-*");
361   EXPECT_THAT(
362       Diags.Diagnostics,
363       ElementsAre(
364           AllOf(diagMessage("clang-tidy check 'unknown-check' was not found"),
365                 diagKind(llvm::SourceMgr::DK_Warning)),
366           AllOf(
367               diagMessage("clang-tidy check 'llvm-includeorder' was not found"),
368               diagKind(llvm::SourceMgr::DK_Warning))));
369 }
370 
TEST_F(ConfigCompileTests,ExternalServerNeedsTrusted)371 TEST_F(ConfigCompileTests, ExternalServerNeedsTrusted) {
372   Fragment::IndexBlock::ExternalBlock External;
373   External.Server.emplace("xxx");
374   Frag.Index.External = std::move(External);
375   compileAndApply();
376   EXPECT_THAT(
377       Diags.Diagnostics,
378       ElementsAre(diagMessage(
379           "Remote index may not be specified by untrusted configuration. "
380           "Copy this into user config to use it.")));
381   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
382 }
383 
TEST_F(ConfigCompileTests,ExternalBlockWarnOnMultipleSource)384 TEST_F(ConfigCompileTests, ExternalBlockWarnOnMultipleSource) {
385   Frag.Source.Trusted = true;
386   Fragment::IndexBlock::ExternalBlock External;
387   External.File.emplace("");
388   External.Server.emplace("");
389   Frag.Index.External = std::move(External);
390   compileAndApply();
391 #ifdef CLANGD_ENABLE_REMOTE
392   EXPECT_THAT(
393       Diags.Diagnostics,
394       Contains(
395           AllOf(diagMessage("Exactly one of File, Server or None must be set."),
396                 diagKind(llvm::SourceMgr::DK_Error))));
397 #else
398   ASSERT_TRUE(Conf.Index.External.hasValue());
399   EXPECT_EQ(Conf.Index.External->Kind, Config::ExternalIndexSpec::File);
400 #endif
401 }
402 
TEST_F(ConfigCompileTests,ExternalBlockDisableWithNone)403 TEST_F(ConfigCompileTests, ExternalBlockDisableWithNone) {
404   compileAndApply();
405   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
406 
407   Fragment::IndexBlock::ExternalBlock External;
408   External.IsNone = true;
409   Frag.Index.External = std::move(External);
410   compileAndApply();
411   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
412 }
413 
TEST_F(ConfigCompileTests,ExternalBlockErrOnNoSource)414 TEST_F(ConfigCompileTests, ExternalBlockErrOnNoSource) {
415   Frag.Index.External.emplace(Fragment::IndexBlock::ExternalBlock{});
416   compileAndApply();
417   EXPECT_THAT(
418       Diags.Diagnostics,
419       Contains(
420           AllOf(diagMessage("Exactly one of File, Server or None must be set."),
421                 diagKind(llvm::SourceMgr::DK_Error))));
422 }
423 
TEST_F(ConfigCompileTests,ExternalBlockDisablesBackgroundIndex)424 TEST_F(ConfigCompileTests, ExternalBlockDisablesBackgroundIndex) {
425   auto BazPath = testPath("foo/bar/baz.h", llvm::sys::path::Style::posix);
426   Parm.Path = BazPath;
427   Frag.Index.Background.emplace("Build");
428   Fragment::IndexBlock::ExternalBlock External;
429   External.File.emplace(testPath("foo"));
430   External.MountPoint.emplace(
431       testPath("foo/bar", llvm::sys::path::Style::posix));
432   Frag.Index.External = std::move(External);
433   compileAndApply();
434   EXPECT_EQ(Conf.Index.Background, Config::BackgroundPolicy::Skip);
435 }
436 
TEST_F(ConfigCompileTests,ExternalBlockMountPoint)437 TEST_F(ConfigCompileTests, ExternalBlockMountPoint) {
438   auto GetFrag = [](llvm::StringRef Directory,
439                     llvm::Optional<const char *> MountPoint) {
440     Fragment Frag;
441     Frag.Source.Directory = Directory.str();
442     Fragment::IndexBlock::ExternalBlock External;
443     External.File.emplace(testPath("foo"));
444     if (MountPoint)
445       External.MountPoint.emplace(*MountPoint);
446     Frag.Index.External = std::move(External);
447     return Frag;
448   };
449 
450   auto BarPath = testPath("foo/bar.h", llvm::sys::path::Style::posix);
451   BarPath = llvm::sys::path::convert_to_slash(BarPath);
452   Parm.Path = BarPath;
453   // Non-absolute MountPoint without a directory raises an error.
454   Frag = GetFrag("", "foo");
455   compileAndApply();
456   ASSERT_THAT(
457       Diags.Diagnostics,
458       ElementsAre(
459           AllOf(diagMessage("MountPoint must be an absolute path, because this "
460                             "fragment is not associated with any directory."),
461                 diagKind(llvm::SourceMgr::DK_Error))));
462   EXPECT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
463 
464   auto FooPath = testPath("foo/", llvm::sys::path::Style::posix);
465   FooPath = llvm::sys::path::convert_to_slash(FooPath);
466   // Ok when relative.
467   Frag = GetFrag(testRoot(), "foo/");
468   compileAndApply();
469   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
470   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
471   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
472 
473   // None defaults to ".".
474   Frag = GetFrag(FooPath, llvm::None);
475   compileAndApply();
476   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
477   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
478   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
479 
480   // Without a file, external index is empty.
481   Parm.Path = "";
482   Frag = GetFrag("", FooPath.c_str());
483   compileAndApply();
484   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
485   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
486 
487   // File outside MountPoint, no index.
488   auto BazPath = testPath("bar/baz.h", llvm::sys::path::Style::posix);
489   BazPath = llvm::sys::path::convert_to_slash(BazPath);
490   Parm.Path = BazPath;
491   Frag = GetFrag("", FooPath.c_str());
492   compileAndApply();
493   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
494   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
495 
496   // File under MountPoint, index should be set.
497   BazPath = testPath("foo/baz.h", llvm::sys::path::Style::posix);
498   BazPath = llvm::sys::path::convert_to_slash(BazPath);
499   Parm.Path = BazPath;
500   Frag = GetFrag("", FooPath.c_str());
501   compileAndApply();
502   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
503   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
504   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
505 
506   // Only matches case-insensitively.
507   BazPath = testPath("fOo/baz.h", llvm::sys::path::Style::posix);
508   BazPath = llvm::sys::path::convert_to_slash(BazPath);
509   Parm.Path = BazPath;
510 
511   FooPath = testPath("FOO/", llvm::sys::path::Style::posix);
512   FooPath = llvm::sys::path::convert_to_slash(FooPath);
513   Frag = GetFrag("", FooPath.c_str());
514   compileAndApply();
515   ASSERT_THAT(Diags.Diagnostics, IsEmpty());
516 #ifdef CLANGD_PATH_CASE_INSENSITIVE
517   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::File);
518   EXPECT_THAT(Conf.Index.External.MountPoint, FooPath);
519 #else
520   ASSERT_EQ(Conf.Index.External.Kind, Config::ExternalIndexSpec::None);
521 #endif
522 }
523 
TEST_F(ConfigCompileTests,AllScopes)524 TEST_F(ConfigCompileTests, AllScopes) {
525   // Defaults to true.
526   EXPECT_TRUE(compileAndApply());
527   EXPECT_TRUE(Conf.Completion.AllScopes);
528 
529   Frag = {};
530   Frag.Completion.AllScopes = false;
531   EXPECT_TRUE(compileAndApply());
532   EXPECT_FALSE(Conf.Completion.AllScopes);
533 
534   Frag = {};
535   Frag.Completion.AllScopes = true;
536   EXPECT_TRUE(compileAndApply());
537   EXPECT_TRUE(Conf.Completion.AllScopes);
538 }
539 
TEST_F(ConfigCompileTests,Style)540 TEST_F(ConfigCompileTests, Style) {
541   Frag = {};
542   Frag.Style.FullyQualifiedNamespaces.push_back(std::string("foo"));
543   Frag.Style.FullyQualifiedNamespaces.push_back(std::string("bar"));
544   EXPECT_TRUE(compileAndApply());
545   EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
546 }
547 } // namespace
548 } // namespace config
549 } // namespace clangd
550 } // namespace clang
551