xref: /llvm-project/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp (revision 0f6dbb5f164662c3e6a167a89e7a89f07c60e32b)
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 
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
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 
41 TEST(HTMLGeneratorTest, emitNamespaceHTML) {
42   NamespaceInfo I;
43   I.Name = "Namespace";
44   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
45 
46   I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
47                                      InfoType::IT_namespace,
48                                      "Namespace::ChildNamespace", "Namespace");
49   I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
50                                   "Namespace::ChildStruct", "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({"user-provided-stylesheet.css"});
62   auto Err = G->generateDocForInfo(&I, 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 
145 TEST(HTMLGeneratorTest, emitRecordHTML) {
146   RecordInfo I;
147   I.Name = "r";
148   I.Path = "X/Y/Z";
149   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
150 
151   I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true);
152   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
153 
154   SmallString<16> PathTo;
155   llvm::sys::path::native("path/to", PathTo);
156   I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
157   I.TagType = TagTypeKind::TTK_Class;
158   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "F", PathTo);
159   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
160 
161   I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
162                                   "X::Y::Z::r::ChildStruct", "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({}, "http://www.repository.com");
173   auto Err = G->generateDocForInfo(&I, 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 
264 TEST(HTMLGeneratorTest, emitFunctionHTML) {
265   FunctionInfo I;
266   I.Name = "f";
267   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
268 
269   I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false);
270   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
271 
272   I.Access = AccessSpecifier::AS_none;
273 
274   SmallString<16> PathTo;
275   llvm::sys::path::native("path/to", PathTo);
276   I.ReturnType = TypeInfo(
277       Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
278   I.Params.emplace_back(TypeInfo("int", PathTo), "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({}, "https://www.repository.com");
287   auto Err = G->generateDocForInfo(&I, 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 
318 TEST(HTMLGeneratorTest, emitEnumHTML) {
319   EnumInfo I;
320   I.Name = "e";
321   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
322 
323   I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true);
324   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
325 
326   I.Members.emplace_back("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({}, "www.repository.com");
334   auto Err = G->generateDocForInfo(&I, 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 
367 TEST(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(TypeInfo("int"), "I");
373   I.Params.emplace_back(TypeInfo("int"), "J");
374   I.Access = AccessSpecifier::AS_none;
375 
376   CommentInfo Top;
377   Top.Kind = "FullComment";
378 
379   Top.Children.emplace_back(std::make_unique<CommentInfo>());
380   CommentInfo *BlankLine = Top.Children.back().get();
381   BlankLine->Kind = "ParagraphComment";
382   BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
383   BlankLine->Children.back()->Kind = "TextComment";
384 
385   Top.Children.emplace_back(std::make_unique<CommentInfo>());
386   CommentInfo *Brief = Top.Children.back().get();
387   Brief->Kind = "ParagraphComment";
388   Brief->Children.emplace_back(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(std::make_unique<CommentInfo>());
394   CommentInfo *Extended = Top.Children.back().get();
395   Extended->Kind = "ParagraphComment";
396   Extended->Children.emplace_back(std::make_unique<CommentInfo>());
397   Extended->Children.back()->Kind = "TextComment";
398   Extended->Children.back()->Text = " Extended description that";
399   Extended->Children.emplace_back(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(std::make_unique<CommentInfo>());
404   CommentInfo *Entities = Top.Children.back().get();
405   Entities->Kind = "ParagraphComment";
406   Entities->Children.emplace_back(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(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, 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