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