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.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 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 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: &, <, >, ", '.</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