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