1 //===-- clang-doc/HTMLGeneratorTest.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 "ClangDocTest.h"
10 #include "Generators.h"
11 #include "Representation.h"
12 #include "Serialize.h"
13 #include "clang/Basic/Version.h"
14 #include "gtest/gtest.h"
15
16 namespace clang {
17 namespace doc {
18
19 static const std::string ClangDocVersion =
20 clang::getClangToolFullVersion("clang-doc");
21
getHTMLGenerator()22 std::unique_ptr<Generator> getHTMLGenerator() {
23 auto G = doc::findGeneratorByName("html");
24 if (!G)
25 return nullptr;
26 return std::move(G.get());
27 }
28
29 ClangDocContext
getClangDocContext(std::vector<std::string> UserStylesheets={},StringRef RepositoryUrl="")30 getClangDocContext(std::vector<std::string> UserStylesheets = {},
31 StringRef RepositoryUrl = "") {
32 ClangDocContext CDCtx{
33 {}, "test-project", {}, {}, {}, RepositoryUrl, UserStylesheets, {}};
34 CDCtx.UserStylesheets.insert(
35 CDCtx.UserStylesheets.begin(),
36 "../share/clang/clang-doc-default-stylesheet.css");
37 CDCtx.JsScripts.emplace_back("index.js");
38 return CDCtx;
39 }
40
TEST(HTMLGeneratorTest,emitNamespaceHTML)41 TEST(HTMLGeneratorTest, emitNamespaceHTML) {
42 NamespaceInfo I;
43 I.Name = "Namespace";
44 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
45
46 I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
47 InfoType::IT_namespace, "Namespace");
48 I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
49 "Namespace");
50 I.ChildFunctions.emplace_back();
51 I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
52 I.ChildFunctions.back().Name = "OneFunction";
53 I.ChildEnums.emplace_back();
54 I.ChildEnums.back().Name = "OneEnum";
55
56 auto G = getHTMLGenerator();
57 assert(G);
58 std::string Buffer;
59 llvm::raw_string_ostream Actual(Buffer);
60 ClangDocContext CDCtx = getClangDocContext({"user-provided-stylesheet.css"});
61 auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
62 assert(!Err);
63 std::string Expected = R"raw(<!DOCTYPE html>
64 <meta charset="utf-8"/>
65 <title>namespace Namespace</title>
66 <link rel="stylesheet" href="../clang-doc-default-stylesheet.css"/>
67 <link rel="stylesheet" href="../user-provided-stylesheet.css"/>
68 <script src="../index.js"></script>
69 <header id="project-title">test-project</header>
70 <main>
71 <div id="sidebar-left" path="Namespace" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
72 <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
73 <h1>namespace Namespace</h1>
74 <h2 id="Namespaces">Namespaces</h2>
75 <ul>
76 <li>
77 <a href="ChildNamespace/index.html">ChildNamespace</a>
78 </li>
79 </ul>
80 <h2 id="Records">Records</h2>
81 <ul>
82 <li>
83 <a href="ChildStruct.html">ChildStruct</a>
84 </li>
85 </ul>
86 <h2 id="Functions">Functions</h2>
87 <div>
88 <h3 id="0000000000000000000000000000000000000000">OneFunction</h3>
89 <p>OneFunction()</p>
90 </div>
91 <h2 id="Enums">Enums</h2>
92 <div>
93 <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
94 </div>
95 </div>
96 <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
97 <ol>
98 <li>
99 <span>
100 <a href="#Namespaces">Namespaces</a>
101 </span>
102 </li>
103 <li>
104 <span>
105 <a href="#Records">Records</a>
106 </span>
107 </li>
108 <li>
109 <span>
110 <a href="#Functions">Functions</a>
111 </span>
112 <ul>
113 <li>
114 <span>
115 <a href="#0000000000000000000000000000000000000000">OneFunction</a>
116 </span>
117 </li>
118 </ul>
119 </li>
120 <li>
121 <span>
122 <a href="#Enums">Enums</a>
123 </span>
124 <ul>
125 <li>
126 <span>
127 <a href="#0000000000000000000000000000000000000000">OneEnum</a>
128 </span>
129 </li>
130 </ul>
131 </li>
132 </ol>
133 </div>
134 </main>
135 <footer>
136 <span class="no-break">)raw" +
137 ClangDocVersion + R"raw(</span>
138 </footer>
139 )raw";
140
141 EXPECT_EQ(Expected, Actual.str());
142 }
143
TEST(HTMLGeneratorTest,emitRecordHTML)144 TEST(HTMLGeneratorTest, emitRecordHTML) {
145 RecordInfo I;
146 I.Name = "r";
147 I.Path = "X/Y/Z";
148 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
149
150 I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true);
151 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
152
153 SmallString<16> PathTo;
154 llvm::sys::path::native("path/to", PathTo);
155 I.Members.emplace_back("int", "X/Y", "X", AccessSpecifier::AS_private);
156 I.TagType = TagTypeKind::TTK_Class;
157 I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo);
158 I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
159
160 I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
161 "X/Y/Z/r");
162 I.ChildFunctions.emplace_back();
163 I.ChildFunctions.back().Name = "OneFunction";
164 I.ChildEnums.emplace_back();
165 I.ChildEnums.back().Name = "OneEnum";
166
167 auto G = getHTMLGenerator();
168 assert(G);
169 std::string Buffer;
170 llvm::raw_string_ostream Actual(Buffer);
171 ClangDocContext CDCtx = getClangDocContext({}, "http://www.repository.com");
172 auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
173 assert(!Err);
174 std::string Expected = R"raw(<!DOCTYPE html>
175 <meta charset="utf-8"/>
176 <title>class r</title>
177 <link rel="stylesheet" href="../../../clang-doc-default-stylesheet.css"/>
178 <script src="../../../index.js"></script>
179 <header id="project-title">test-project</header>
180 <main>
181 <div id="sidebar-left" path="X/Y/Z" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
182 <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
183 <h1>class r</h1>
184 <p>
185 Defined at line
186 <a href="http://www.repository.com/dir/test.cpp#10">10</a>
187 of file
188 <a href="http://www.repository.com/dir/test.cpp">test.cpp</a>
189 </p>
190 <p>
191 Inherits from
192 <a href="../../../path/to/F.html">F</a>
193 , G
194 </p>
195 <h2 id="Members">Members</h2>
196 <ul>
197 <li>
198 private
199 <a href="../../../X/Y/int.html">int</a>
200 X
201 </li>
202 </ul>
203 <h2 id="Records">Records</h2>
204 <ul>
205 <li>
206 <a href="../../../X/Y/Z/r/ChildStruct.html">ChildStruct</a>
207 </li>
208 </ul>
209 <h2 id="Functions">Functions</h2>
210 <div>
211 <h3 id="0000000000000000000000000000000000000000">OneFunction</h3>
212 <p>public OneFunction()</p>
213 </div>
214 <h2 id="Enums">Enums</h2>
215 <div>
216 <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
217 </div>
218 </div>
219 <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
220 <ol>
221 <li>
222 <span>
223 <a href="#Members">Members</a>
224 </span>
225 </li>
226 <li>
227 <span>
228 <a href="#Records">Records</a>
229 </span>
230 </li>
231 <li>
232 <span>
233 <a href="#Functions">Functions</a>
234 </span>
235 <ul>
236 <li>
237 <span>
238 <a href="#0000000000000000000000000000000000000000">OneFunction</a>
239 </span>
240 </li>
241 </ul>
242 </li>
243 <li>
244 <span>
245 <a href="#Enums">Enums</a>
246 </span>
247 <ul>
248 <li>
249 <span>
250 <a href="#0000000000000000000000000000000000000000">OneEnum</a>
251 </span>
252 </li>
253 </ul>
254 </li>
255 </ol>
256 </div>
257 </main>
258 <footer>
259 <span class="no-break">)raw" +
260 ClangDocVersion + R"raw(</span>
261 </footer>
262 )raw";
263
264 EXPECT_EQ(Expected, Actual.str());
265 }
266
267 TEST(HTMLGeneratorTest, emitFunctionHTML) {
268 FunctionInfo I;
269 I.Name = "f";
270 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
271
272 I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false);
273 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
274
275 I.Access = AccessSpecifier::AS_none;
276
277 SmallString<16> PathTo;
278 llvm::sys::path::native("path/to", PathTo);
279 I.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default, PathTo);
280 I.Params.emplace_back("int", PathTo, "P");
281 I.IsMethod = true;
282 I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
283
284 auto G = getHTMLGenerator();
285 assert(G);
286 std::string Buffer;
287 llvm::raw_string_ostream Actual(Buffer);
288 ClangDocContext CDCtx = getClangDocContext({}, "https://www.repository.com");
289 auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
290 assert(!Err);
291 std::string Expected = R"raw(<!DOCTYPE html>
292 <meta charset="utf-8"/>
293 <title></title>
294 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
295 <script src="index.js"></script>
296 <header id="project-title">test-project</header>
297 <main>
298 <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
299 <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
300 <h3 id="0000000000000000000000000000000000000000">f</h3>
301 <p>
302 <a href="path/to/float.html">float</a>
303 f(
304 <a href="path/to/int.html">int</a>
305 P)
306 </p>
307 <p>Defined at line 10 of file dir/test.cpp</p>
308 </div>
309 <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
310 </main>
311 <footer>
312 <span class="no-break">)raw" +
313 ClangDocVersion + R"raw(</span>
314 </footer>
315 )raw";
316
317 EXPECT_EQ(Expected, Actual.str());
318 }
319
TEST(HTMLGeneratorTest,emitEnumHTML)320 TEST(HTMLGeneratorTest, emitEnumHTML) {
321 EnumInfo I;
322 I.Name = "e";
323 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
324
325 I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true);
326 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
327
328 I.Members.emplace_back("X");
329 I.Scoped = true;
330
331 auto G = getHTMLGenerator();
332 assert(G);
333 std::string Buffer;
334 llvm::raw_string_ostream Actual(Buffer);
335 ClangDocContext CDCtx = getClangDocContext({}, "www.repository.com");
336 auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
337 assert(!Err);
338 std::string Expected = R"raw(<!DOCTYPE html>
339 <meta charset="utf-8"/>
340 <title></title>
341 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
342 <script src="index.js"></script>
343 <header id="project-title">test-project</header>
344 <main>
345 <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
346 <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
347 <h3 id="0000000000000000000000000000000000000000">enum class e</h3>
348 <ul>
349 <li>X</li>
350 </ul>
351 <p>
352 Defined at line
353 <a href="https://www.repository.com/test.cpp#10">10</a>
354 of file
355 <a href="https://www.repository.com/test.cpp">test.cpp</a>
356 </p>
357 </div>
358 <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
359 </main>
360 <footer>
361 <span class="no-break">)raw" +
362 ClangDocVersion + R"raw(</span>
363 </footer>
364 )raw";
365
366 EXPECT_EQ(Expected, Actual.str());
367 }
368
369 TEST(HTMLGeneratorTest, emitCommentHTML) {
370 FunctionInfo I;
371 I.Name = "f";
372 I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
373 I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
374 I.Params.emplace_back("int", "I");
375 I.Params.emplace_back("int", "J");
376 I.Access = AccessSpecifier::AS_none;
377
378 CommentInfo Top;
379 Top.Kind = "FullComment";
380
381 Top.Children.emplace_back(std::make_unique<CommentInfo>());
382 CommentInfo *BlankLine = Top.Children.back().get();
383 BlankLine->Kind = "ParagraphComment";
384 BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
385 BlankLine->Children.back()->Kind = "TextComment";
386
387 Top.Children.emplace_back(std::make_unique<CommentInfo>());
388 CommentInfo *Brief = Top.Children.back().get();
389 Brief->Kind = "ParagraphComment";
390 Brief->Children.emplace_back(std::make_unique<CommentInfo>());
391 Brief->Children.back()->Kind = "TextComment";
392 Brief->Children.back()->Name = "ParagraphComment";
393 Brief->Children.back()->Text = " Brief description.";
394
395 Top.Children.emplace_back(std::make_unique<CommentInfo>());
396 CommentInfo *Extended = Top.Children.back().get();
397 Extended->Kind = "ParagraphComment";
398 Extended->Children.emplace_back(std::make_unique<CommentInfo>());
399 Extended->Children.back()->Kind = "TextComment";
400 Extended->Children.back()->Text = " Extended description that";
401 Extended->Children.emplace_back(std::make_unique<CommentInfo>());
402 Extended->Children.back()->Kind = "TextComment";
403 Extended->Children.back()->Text = " continues onto the next line.";
404
405 Top.Children.emplace_back(std::make_unique<CommentInfo>());
406 CommentInfo *Entities = Top.Children.back().get();
407 Entities->Kind = "ParagraphComment";
408 Entities->Children.emplace_back(std::make_unique<CommentInfo>());
409 Entities->Children.back()->Kind = "TextComment";
410 Entities->Children.back()->Name = "ParagraphComment";
411 Entities->Children.back()->Text =
412 " Comment with html entities: &, <, >, \", \'.";
413
414 I.Description.emplace_back(std::move(Top));
415
416 auto G = getHTMLGenerator();
417 assert(G);
418 std::string Buffer;
419 llvm::raw_string_ostream Actual(Buffer);
420 ClangDocContext CDCtx = getClangDocContext();
421 auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
422 assert(!Err);
423 std::string Expected = R"raw(<!DOCTYPE html>
424 <meta charset="utf-8"/>
425 <title></title>
426 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
427 <script src="index.js"></script>
428 <header id="project-title">test-project</header>
429 <main>
430 <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
431 <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
432 <h3 id="0000000000000000000000000000000000000000">f</h3>
433 <p>void f(int I, int J)</p>
434 <p>Defined at line 10 of file test.cpp</p>
435 <div>
436 <div>
437 <p> Brief description.</p>
438 <p> Extended description that continues onto the next line.</p>
439 <p> Comment with html entities: &, <, >, ", '.</p>
440 </div>
441 </div>
442 </div>
443 <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
444 </main>
445 <footer>
446 <span class="no-break">)raw" +
447 ClangDocVersion + R"raw(</span>
448 </footer>
449 )raw";
450
451 EXPECT_EQ(Expected, Actual.str());
452 }
453
454 } // namespace doc
455 } // namespace clang
456