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