clang-tools  11.0.0
MarkupTests.cpp
Go to the documentation of this file.
1 //===-- MarkupTests.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 #include "support/Markup.h"
9 #include "clang/Basic/LLVM.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/Support/raw_ostream.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
14 
15 namespace clang {
16 namespace clangd {
17 namespace markup {
18 namespace {
19 
20 std::string escape(llvm::StringRef Text) {
21  return Paragraph().appendText(Text.str()).asMarkdown();
22 }
23 
24 MATCHER_P(escaped, C, "") {
25  return testing::ExplainMatchResult(::testing::HasSubstr(std::string{'\\', C}),
26  arg, result_listener);
27 }
28 
29 MATCHER(escapedNone, "") {
30  return testing::ExplainMatchResult(::testing::Not(::testing::HasSubstr("\\")),
31  arg, result_listener);
32 }
33 
34 TEST(Render, Escaping) {
35  // Check all ASCII punctuation.
36  std::string Punctuation = R"txt(!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~)txt";
37  std::string EscapedPunc = R"txt(!"#$%&'()\*+,-./:;<=>?@[\\]^\_\`{|}~)txt";
38  EXPECT_EQ(escape(Punctuation), EscapedPunc);
39 
40  // Inline code
41  EXPECT_EQ(escape("`foo`"), R"(\`foo\`)");
42  EXPECT_EQ(escape("`foo"), R"(\`foo)");
43  EXPECT_EQ(escape("foo`"), R"(foo\`)");
44  EXPECT_EQ(escape("``foo``"), R"(\`\`foo\`\`)");
45  // Code blocks
46  EXPECT_EQ(escape("```"), R"(\`\`\`)"); // This could also be inline code!
47  EXPECT_EQ(escape("~~~"), R"(\~~~)");
48 
49  // Rulers and headings
50  EXPECT_THAT(escape("## Heading"), escaped('#'));
51  EXPECT_THAT(escape("Foo # bar"), escapedNone());
52  EXPECT_EQ(escape("---"), R"(\---)");
53  EXPECT_EQ(escape("-"), R"(\-)");
54  EXPECT_EQ(escape("==="), R"(\===)");
55  EXPECT_EQ(escape("="), R"(\=)");
56  EXPECT_EQ(escape("***"), R"(\*\*\*)"); // \** could start emphasis!
57 
58  // HTML tags.
59  EXPECT_THAT(escape("<pre"), escaped('<'));
60  EXPECT_THAT(escape("< pre"), escapedNone());
61  EXPECT_THAT(escape("if a<b then"), escaped('<'));
62  EXPECT_THAT(escape("if a<b then c."), escapedNone());
63  EXPECT_THAT(escape("if a<b then c='foo'."), escaped('<'));
64  EXPECT_THAT(escape("std::vector<T>"), escaped('<'));
65  EXPECT_THAT(escape("std::vector<std::string>"), escaped('<'));
66  EXPECT_THAT(escape("std::map<int, int>"), escapedNone());
67  // Autolinks
68  EXPECT_THAT(escape("Email <foo@bar.com>"), escapedNone());
69  EXPECT_THAT(escape("Website <http://foo.bar>"), escapedNone());
70 
71  // Bullet lists.
72  EXPECT_THAT(escape("- foo"), escaped('-'));
73  EXPECT_THAT(escape("* foo"), escaped('*'));
74  EXPECT_THAT(escape("+ foo"), escaped('+'));
75  EXPECT_THAT(escape("+"), escaped('+'));
76  EXPECT_THAT(escape("a + foo"), escapedNone());
77  EXPECT_THAT(escape("a+ foo"), escapedNone());
78  EXPECT_THAT(escape("1. foo"), escaped('.'));
79  EXPECT_THAT(escape("a. foo"), escapedNone());
80 
81  // Emphasis.
82  EXPECT_EQ(escape("*foo*"), R"(\*foo\*)");
83  EXPECT_EQ(escape("**foo**"), R"(\*\*foo\*\*)");
84  EXPECT_THAT(escape("*foo"), escaped('*'));
85  EXPECT_THAT(escape("foo *"), escapedNone());
86  EXPECT_THAT(escape("foo * bar"), escapedNone());
87  EXPECT_THAT(escape("foo_bar"), escapedNone());
88  EXPECT_THAT(escape("foo _bar"), escaped('_'));
89  EXPECT_THAT(escape("foo_ bar"), escaped('_'));
90  EXPECT_THAT(escape("foo _ bar"), escapedNone());
91 
92  // HTML entities.
93  EXPECT_THAT(escape("fish &chips;"), escaped('&'));
94  EXPECT_THAT(escape("fish & chips;"), escapedNone());
95  EXPECT_THAT(escape("fish &chips"), escapedNone());
96  EXPECT_THAT(escape("foo &#42; bar"), escaped('&'));
97  EXPECT_THAT(escape("foo &#xaf; bar"), escaped('&'));
98  EXPECT_THAT(escape("foo &?; bar"), escapedNone());
99 
100  // Links.
101  EXPECT_THAT(escape("[foo](bar)"), escaped(']'));
102  EXPECT_THAT(escape("[foo]: bar"), escaped(']'));
103  // No need to escape these, as the target never exists.
104  EXPECT_THAT(escape("[foo][]"), escapedNone());
105  EXPECT_THAT(escape("[foo][bar]"), escapedNone());
106  EXPECT_THAT(escape("[foo]"), escapedNone());
107 
108  // In code blocks we don't need to escape ASCII punctuation.
109  Paragraph P = Paragraph();
110  P.appendCode("* foo !+ bar * baz");
111  EXPECT_EQ(P.asMarkdown(), "`* foo !+ bar * baz`");
112 
113  // But we have to escape the backticks.
114  P = Paragraph();
115  P.appendCode("foo`bar`baz", /*Preserve=*/true);
116  EXPECT_EQ(P.asMarkdown(), "`foo``bar``baz`");
117  // In plain-text, we fall back to different quotes.
118  EXPECT_EQ(P.asPlainText(), "'foo`bar`baz'");
119 
120  // Inline code blocks starting or ending with backticks should add spaces.
121  P = Paragraph();
122  P.appendCode("`foo");
123  EXPECT_EQ(P.asMarkdown(), "` ``foo `");
124  P = Paragraph();
125  P.appendCode("foo`");
126  EXPECT_EQ(P.asMarkdown(), "` foo`` `");
127  P = Paragraph();
128  P.appendCode("`foo`");
129  EXPECT_EQ(P.asMarkdown(), "` ``foo`` `");
130 
131  // Code blocks might need more than 3 backticks.
132  Document D;
133  D.addCodeBlock("foobarbaz `\nqux");
134  EXPECT_EQ(D.asMarkdown(), "```cpp\n"
135  "foobarbaz `\nqux\n"
136  "```");
137  D = Document();
138  D.addCodeBlock("foobarbaz ``\nqux");
139  EXPECT_THAT(D.asMarkdown(), "```cpp\n"
140  "foobarbaz ``\nqux\n"
141  "```");
142  D = Document();
143  D.addCodeBlock("foobarbaz ```\nqux");
144  EXPECT_EQ(D.asMarkdown(), "````cpp\n"
145  "foobarbaz ```\nqux\n"
146  "````");
147  D = Document();
148  D.addCodeBlock("foobarbaz ` `` ``` ```` `\nqux");
149  EXPECT_EQ(D.asMarkdown(), "`````cpp\n"
150  "foobarbaz ` `` ``` ```` `\nqux\n"
151  "`````");
152 }
153 
154 TEST(Paragraph, Chunks) {
155  Paragraph P = Paragraph();
156  P.appendText("One ");
157  P.appendCode("fish");
158  P.appendText(", two ");
159  P.appendCode("fish", /*Preserve=*/true);
160 
161  EXPECT_EQ(P.asMarkdown(), "One `fish`, two `fish`");
162  EXPECT_EQ(P.asPlainText(), "One fish, two `fish`");
163 }
164 
165 TEST(Paragraph, SeparationOfChunks) {
166  // This test keeps appending contents to a single Paragraph and checks
167  // expected accumulated contents after each one.
168  // Purpose is to check for separation between different chunks.
169  Paragraph P;
170 
171  P.appendText("after ");
172  EXPECT_EQ(P.asMarkdown(), "after");
173  EXPECT_EQ(P.asPlainText(), "after");
174 
175  P.appendCode("foobar").appendSpace();
176  EXPECT_EQ(P.asMarkdown(), "after `foobar`");
177  EXPECT_EQ(P.asPlainText(), "after foobar");
178 
179  P.appendText("bat");
180  EXPECT_EQ(P.asMarkdown(), "after `foobar` bat");
181  EXPECT_EQ(P.asPlainText(), "after foobar bat");
182 
183  P.appendCode("no").appendCode("space");
184  EXPECT_EQ(P.asMarkdown(), "after `foobar` bat`no` `space`");
185  EXPECT_EQ(P.asPlainText(), "after foobar batno space");
186 }
187 
188 TEST(Paragraph, ExtraSpaces) {
189  // Make sure spaces inside chunks are dropped.
190  Paragraph P;
191  P.appendText("foo\n \t baz");
192  P.appendCode(" bar\n");
193  EXPECT_EQ(P.asMarkdown(), "foo baz`bar`");
194  EXPECT_EQ(P.asPlainText(), "foo bazbar");
195 }
196 
197 TEST(Paragraph, SpacesCollapsed) {
198  Paragraph P;
199  P.appendText(" foo bar ");
200  P.appendText(" baz ");
201  EXPECT_EQ(P.asMarkdown(), "foo bar baz");
202  EXPECT_EQ(P.asPlainText(), "foo bar baz");
203 }
204 
205 TEST(Paragraph, NewLines) {
206  // New lines before and after chunks are dropped.
207  Paragraph P;
208  P.appendText(" \n foo\nbar\n ");
209  P.appendCode(" \n foo\nbar \n ");
210  EXPECT_EQ(P.asMarkdown(), "foo bar `foo bar`");
211  EXPECT_EQ(P.asPlainText(), "foo bar foo bar");
212 }
213 
214 TEST(Document, Separators) {
215  Document D;
216  D.addParagraph().appendText("foo");
217  D.addCodeBlock("test");
218  D.addParagraph().appendText("bar");
219 
220  const char ExpectedMarkdown[] = R"md(foo
221 ```cpp
222 test
223 ```
224 bar)md";
225  EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);
226 
227  const char ExpectedText[] = R"pt(foo
228 
229 test
230 
231 bar)pt";
232  EXPECT_EQ(D.asPlainText(), ExpectedText);
233 }
234 
235 TEST(Document, Ruler) {
236  Document D;
237  D.addParagraph().appendText("foo");
238  D.addRuler();
239 
240  // Ruler followed by paragraph.
241  D.addParagraph().appendText("bar");
242  EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nbar");
243  EXPECT_EQ(D.asPlainText(), "foo\n\nbar");
244 
245  D = Document();
246  D.addParagraph().appendText("foo");
247  D.addRuler();
248  D.addCodeBlock("bar");
249  // Ruler followed by a codeblock.
250  EXPECT_EQ(D.asMarkdown(), "foo \n\n---\n```cpp\nbar\n```");
251  EXPECT_EQ(D.asPlainText(), "foo\n\nbar");
252 
253  // Ruler followed by another ruler
254  D = Document();
255  D.addParagraph().appendText("foo");
256  D.addRuler();
257  D.addRuler();
258  EXPECT_EQ(D.asMarkdown(), "foo");
259  EXPECT_EQ(D.asPlainText(), "foo");
260 
261  // Multiple rulers between blocks
262  D.addRuler();
263  D.addParagraph().appendText("foo");
264  EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nfoo");
265  EXPECT_EQ(D.asPlainText(), "foo\n\nfoo");
266 }
267 
268 TEST(Document, Append) {
269  Document D;
270  D.addParagraph().appendText("foo");
271  D.addRuler();
272  Document E;
273  E.addRuler();
274  E.addParagraph().appendText("bar");
275  D.append(std::move(E));
276  EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nbar");
277 }
278 
279 TEST(Document, Heading) {
280  Document D;
281  D.addHeading(1).appendText("foo");
282  D.addHeading(2).appendText("bar");
283  D.addParagraph().appendText("baz");
284  EXPECT_EQ(D.asMarkdown(), "# foo \n## bar \nbaz");
285  EXPECT_EQ(D.asPlainText(), "foo\nbar\nbaz");
286 }
287 
288 TEST(CodeBlock, Render) {
289  Document D;
290  // Code blocks preserves any extra spaces.
291  D.addCodeBlock("foo\n bar\n baz");
292 
293  llvm::StringRef ExpectedMarkdown =
294  R"md(```cpp
295 foo
296  bar
297  baz
298 ```)md";
299  llvm::StringRef ExpectedPlainText =
300  R"pt(foo
301  bar
302  baz)pt";
303  EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);
304  EXPECT_EQ(D.asPlainText(), ExpectedPlainText);
305  D.addCodeBlock("foo");
306  ExpectedMarkdown =
307  R"md(```cpp
308 foo
309  bar
310  baz
311 ```
312 ```cpp
313 foo
314 ```)md";
315  EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);
316  ExpectedPlainText =
317  R"pt(foo
318  bar
319  baz
320 
321 foo)pt";
322  EXPECT_EQ(D.asPlainText(), ExpectedPlainText);
323 }
324 
325 TEST(BulletList, Render) {
326  BulletList L;
327  // Flat list
328  L.addItem().addParagraph().appendText("foo");
329  EXPECT_EQ(L.asMarkdown(), "- foo");
330  EXPECT_EQ(L.asPlainText(), "- foo");
331 
332  L.addItem().addParagraph().appendText("bar");
333  llvm::StringRef Expected = R"md(- foo
334 - bar)md";
335  EXPECT_EQ(L.asMarkdown(), Expected);
336  EXPECT_EQ(L.asPlainText(), Expected);
337 
338  // Nested list, with a single item.
339  Document &D = L.addItem();
340  // First item with foo\nbaz
341  D.addParagraph().appendText("foo");
342  D.addParagraph().appendText("baz");
343 
344  // Nest one level.
345  Document &Inner = D.addBulletList().addItem();
346  Inner.addParagraph().appendText("foo");
347 
348  // Nest one more level.
349  BulletList &InnerList = Inner.addBulletList();
350  // Single item, baz\nbaz
351  Document &DeepDoc = InnerList.addItem();
352  DeepDoc.addParagraph().appendText("baz");
353  DeepDoc.addParagraph().appendText("baz");
354  StringRef ExpectedMarkdown = R"md(- foo
355 - bar
356 - foo
357  baz
358  - foo
359  - baz
360  baz)md";
361  EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown);
362  StringRef ExpectedPlainText = R"pt(- foo
363 - bar
364 - foo
365  baz
366  - foo
367  - baz
368  baz)pt";
369  EXPECT_EQ(L.asPlainText(), ExpectedPlainText);
370 
371  // Termination
372  Inner.addParagraph().appendText("after");
373  ExpectedMarkdown = R"md(- foo
374 - bar
375 - foo
376  baz
377  - foo
378  - baz
379  baz
380 
381  after)md";
382  EXPECT_EQ(L.asMarkdown(), ExpectedMarkdown);
383  ExpectedPlainText = R"pt(- foo
384 - bar
385 - foo
386  baz
387  - foo
388  - baz
389  baz
390  after)pt";
391  EXPECT_EQ(L.asPlainText(), ExpectedPlainText);
392 }
393 
394 } // namespace
395 } // namespace markup
396 } // namespace clangd
397 } // namespace clang
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:704
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:27
clang::clangd::Punctuation
Definition: FuzzyMatch.h:45
Text
std::string Text
Definition: HTMLGenerator.cpp:80
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:49
Markup.h
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:29
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::MATCHER
MATCHER(Declared, "")
Definition: BackgroundIndexTests.cpp:31