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

source code of clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp