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