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