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