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