xref: /llvm-project/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp (revision 21fb70c6ab3b25fe8f5f8384714a9a359b4b5a54)
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, "Namespace");
48   I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
49                                   "Namespace");
50   I.Children.Functions.emplace_back();
51   I.Children.Functions.back().Access = AccessSpecifier::AS_none;
52   I.Children.Functions.back().Name = "OneFunction";
53   I.Children.Enums.emplace_back();
54   I.Children.Enums.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 
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(TypeInfo("int", "X/Y"), "X",
156                          AccessSpecifier::AS_private);
157   I.TagType = TagTypeKind::TTK_Class;
158   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, 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");
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>
199         private
200         <a href="../../../X/Y/int.html">int</a>
201          X
202       </li>
203     </ul>
204     <h2 id="Records">Records</h2>
205     <ul>
206       <li>
207         <a href="../../../X/Y/Z/r/ChildStruct.html">ChildStruct</a>
208       </li>
209     </ul>
210     <h2 id="Functions">Functions</h2>
211     <div>
212       <h3 id="0000000000000000000000000000000000000000">OneFunction</h3>
213       <p>public OneFunction()</p>
214     </div>
215     <h2 id="Enums">Enums</h2>
216     <div>
217       <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
218     </div>
219   </div>
220   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
221     <ol>
222       <li>
223         <span>
224           <a href="#Members">Members</a>
225         </span>
226       </li>
227       <li>
228         <span>
229           <a href="#Records">Records</a>
230         </span>
231       </li>
232       <li>
233         <span>
234           <a href="#Functions">Functions</a>
235         </span>
236         <ul>
237           <li>
238             <span>
239               <a href="#0000000000000000000000000000000000000000">OneFunction</a>
240             </span>
241           </li>
242         </ul>
243       </li>
244       <li>
245         <span>
246           <a href="#Enums">Enums</a>
247         </span>
248         <ul>
249           <li>
250             <span>
251               <a href="#0000000000000000000000000000000000000000">OneEnum</a>
252             </span>
253           </li>
254         </ul>
255       </li>
256     </ol>
257   </div>
258 </main>
259 <footer>
260   <span class="no-break">)raw" +
261                          ClangDocVersion + R"raw(</span>
262 </footer>
263 )raw";
264 
265   EXPECT_EQ(Expected, Actual.str());
266 }
267 
268 TEST(HTMLGeneratorTest, emitFunctionHTML) {
269   FunctionInfo I;
270   I.Name = "f";
271   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
272 
273   I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false);
274   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
275 
276   I.Access = AccessSpecifier::AS_none;
277 
278   SmallString<16> PathTo;
279   llvm::sys::path::native("path/to", PathTo);
280   I.ReturnType =
281       TypeInfo(Reference(EmptySID, "float", InfoType::IT_default, PathTo));
282   I.Params.emplace_back(TypeInfo("int", PathTo), "P");
283   I.IsMethod = true;
284   I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
285 
286   auto G = getHTMLGenerator();
287   assert(G);
288   std::string Buffer;
289   llvm::raw_string_ostream Actual(Buffer);
290   ClangDocContext CDCtx = getClangDocContext({}, "https://www.repository.com");
291   auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
292   assert(!Err);
293   std::string Expected = R"raw(<!DOCTYPE html>
294 <meta charset="utf-8"/>
295 <title></title>
296 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
297 <script src="index.js"></script>
298 <header id="project-title">test-project</header>
299 <main>
300   <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
301   <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
302     <h3 id="0000000000000000000000000000000000000000">f</h3>
303     <p>
304       <a href="path/to/float.html">float</a>
305        f(
306       <a href="path/to/int.html">int</a>
307        P)
308     </p>
309     <p>Defined at line 10 of file dir/test.cpp</p>
310   </div>
311   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
312 </main>
313 <footer>
314   <span class="no-break">)raw" +
315                          ClangDocVersion + R"raw(</span>
316 </footer>
317 )raw";
318 
319   EXPECT_EQ(Expected, Actual.str());
320 }
321 
322 TEST(HTMLGeneratorTest, emitEnumHTML) {
323   EnumInfo I;
324   I.Name = "e";
325   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
326 
327   I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true);
328   I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
329 
330   I.Members.emplace_back("X");
331   I.Scoped = true;
332 
333   auto G = getHTMLGenerator();
334   assert(G);
335   std::string Buffer;
336   llvm::raw_string_ostream Actual(Buffer);
337   ClangDocContext CDCtx = getClangDocContext({}, "www.repository.com");
338   auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
339   assert(!Err);
340   std::string Expected = R"raw(<!DOCTYPE html>
341 <meta charset="utf-8"/>
342 <title></title>
343 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
344 <script src="index.js"></script>
345 <header id="project-title">test-project</header>
346 <main>
347   <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
348   <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
349     <h3 id="0000000000000000000000000000000000000000">enum class e</h3>
350     <ul>
351       <li>X</li>
352     </ul>
353     <p>
354       Defined at line
355       <a href="https://www.repository.com/test.cpp#10">10</a>
356        of file
357       <a href="https://www.repository.com/test.cpp">test.cpp</a>
358     </p>
359   </div>
360   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
361 </main>
362 <footer>
363   <span class="no-break">)raw" +
364                          ClangDocVersion + R"raw(</span>
365 </footer>
366 )raw";
367 
368   EXPECT_EQ(Expected, Actual.str());
369 }
370 
371 TEST(HTMLGeneratorTest, emitCommentHTML) {
372   FunctionInfo I;
373   I.Name = "f";
374   I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
375   I.ReturnType = TypeInfo("void");
376   I.Params.emplace_back(TypeInfo("int"), "I");
377   I.Params.emplace_back(TypeInfo("int"), "J");
378   I.Access = AccessSpecifier::AS_none;
379 
380   CommentInfo Top;
381   Top.Kind = "FullComment";
382 
383   Top.Children.emplace_back(std::make_unique<CommentInfo>());
384   CommentInfo *BlankLine = Top.Children.back().get();
385   BlankLine->Kind = "ParagraphComment";
386   BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
387   BlankLine->Children.back()->Kind = "TextComment";
388 
389   Top.Children.emplace_back(std::make_unique<CommentInfo>());
390   CommentInfo *Brief = Top.Children.back().get();
391   Brief->Kind = "ParagraphComment";
392   Brief->Children.emplace_back(std::make_unique<CommentInfo>());
393   Brief->Children.back()->Kind = "TextComment";
394   Brief->Children.back()->Name = "ParagraphComment";
395   Brief->Children.back()->Text = " Brief description.";
396 
397   Top.Children.emplace_back(std::make_unique<CommentInfo>());
398   CommentInfo *Extended = Top.Children.back().get();
399   Extended->Kind = "ParagraphComment";
400   Extended->Children.emplace_back(std::make_unique<CommentInfo>());
401   Extended->Children.back()->Kind = "TextComment";
402   Extended->Children.back()->Text = " Extended description that";
403   Extended->Children.emplace_back(std::make_unique<CommentInfo>());
404   Extended->Children.back()->Kind = "TextComment";
405   Extended->Children.back()->Text = " continues onto the next line.";
406 
407   Top.Children.emplace_back(std::make_unique<CommentInfo>());
408   CommentInfo *Entities = Top.Children.back().get();
409   Entities->Kind = "ParagraphComment";
410   Entities->Children.emplace_back(std::make_unique<CommentInfo>());
411   Entities->Children.back()->Kind = "TextComment";
412   Entities->Children.back()->Name = "ParagraphComment";
413   Entities->Children.back()->Text =
414       " Comment with html entities: &, <, >, \", \'.";
415 
416   I.Description.emplace_back(std::move(Top));
417 
418   auto G = getHTMLGenerator();
419   assert(G);
420   std::string Buffer;
421   llvm::raw_string_ostream Actual(Buffer);
422   ClangDocContext CDCtx = getClangDocContext();
423   auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
424   assert(!Err);
425   std::string Expected = R"raw(<!DOCTYPE html>
426 <meta charset="utf-8"/>
427 <title></title>
428 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/>
429 <script src="index.js"></script>
430 <header id="project-title">test-project</header>
431 <main>
432   <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
433   <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
434     <h3 id="0000000000000000000000000000000000000000">f</h3>
435     <p>void f(int I, int J)</p>
436     <p>Defined at line 10 of file test.cpp</p>
437     <div>
438       <div>
439         <p> Brief description.</p>
440         <p> Extended description that continues onto the next line.</p>
441         <p> Comment with html entities: &amp;, &lt;, &gt;, &quot;, &apos;.</p>
442       </div>
443     </div>
444   </div>
445   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div>
446 </main>
447 <footer>
448   <span class="no-break">)raw" +
449                          ClangDocVersion + R"raw(</span>
450 </footer>
451 )raw";
452 
453   EXPECT_EQ(Expected, Actual.str());
454 }
455 
456 } // namespace doc
457 } // namespace clang
458