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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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