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