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 "gtest/gtest.h" 14 15 namespace clang { 16 namespace doc { 17 18 std::unique_ptr<Generator> getHTMLGenerator() { 19 auto G = doc::findGeneratorByName("html"); 20 if (!G) 21 return nullptr; 22 return std::move(G.get()); 23 } 24 25 ClangDocContext 26 getClangDocContext(std::vector<std::string> UserStylesheets = {}, 27 StringRef RepositoryUrl = "") { 28 ClangDocContext CDCtx{{}, {}, {}, {}, RepositoryUrl, UserStylesheets, {}}; 29 CDCtx.UserStylesheets.insert( 30 CDCtx.UserStylesheets.begin(), 31 "../share/clang/clang-doc-default-stylesheet.css"); 32 CDCtx.JsScripts.emplace_back("index.js"); 33 return CDCtx; 34 } 35 36 TEST(HTMLGeneratorTest, emitNamespaceHTML) { 37 NamespaceInfo I; 38 I.Name = "Namespace"; 39 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 40 41 I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", 42 InfoType::IT_namespace, "Namespace"); 43 I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, 44 "Namespace"); 45 I.ChildFunctions.emplace_back(); 46 I.ChildFunctions.back().Access = AccessSpecifier::AS_none; 47 I.ChildFunctions.back().Name = "OneFunction"; 48 I.ChildEnums.emplace_back(); 49 I.ChildEnums.back().Name = "OneEnum"; 50 51 auto G = getHTMLGenerator(); 52 assert(G); 53 std::string Buffer; 54 llvm::raw_string_ostream Actual(Buffer); 55 ClangDocContext CDCtx = getClangDocContext({"user-provided-stylesheet.css"}); 56 auto Err = G->generateDocForInfo(&I, Actual, CDCtx); 57 assert(!Err); 58 std::string Expected = R"raw(<!DOCTYPE html> 59 <meta charset="utf-8"/> 60 <title>namespace Namespace</title> 61 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> 62 <link rel="stylesheet" href="user-provided-stylesheet.css"/> 63 <script src="index.js"></script> 64 <div id="index" path=""></div> 65 <ul> 66 <li> 67 <span> 68 <a href="#Namespaces">Namespaces</a> 69 </span> 70 </li> 71 <li> 72 <span> 73 <a href="#Records">Records</a> 74 </span> 75 </li> 76 <li> 77 <span> 78 <a href="#Functions">Functions</a> 79 </span> 80 <ul> 81 <li> 82 <span> 83 <a href="#0000000000000000000000000000000000000000">OneFunction</a> 84 </span> 85 </li> 86 </ul> 87 </li> 88 <li> 89 <span> 90 <a href="#Enums">Enums</a> 91 </span> 92 <ul> 93 <li> 94 <span> 95 <a href="#0000000000000000000000000000000000000000">OneEnum</a> 96 </span> 97 </li> 98 </ul> 99 </li> 100 </ul> 101 <div> 102 <h1>namespace Namespace</h1> 103 <h2 id="Namespaces">Namespaces</h2> 104 <ul> 105 <li> 106 <a href="Namespace/ChildNamespace.html">ChildNamespace</a> 107 </li> 108 </ul> 109 <h2 id="Records">Records</h2> 110 <ul> 111 <li> 112 <a href="Namespace/ChildStruct.html">ChildStruct</a> 113 </li> 114 </ul> 115 <h2 id="Functions">Functions</h2> 116 <div> 117 <h3 id="0000000000000000000000000000000000000000">OneFunction</h3> 118 <p>OneFunction()</p> 119 </div> 120 <h2 id="Enums">Enums</h2> 121 <div> 122 <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3> 123 </div> 124 </div> 125 )raw"; 126 127 EXPECT_EQ(Expected, Actual.str()); 128 } 129 130 TEST(HTMLGeneratorTest, emitRecordHTML) { 131 RecordInfo I; 132 I.Name = "r"; 133 I.Path = "X/Y/Z"; 134 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 135 136 I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true); 137 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); 138 139 SmallString<16> PathTo; 140 llvm::sys::path::native("path/to", PathTo); 141 I.Members.emplace_back("int", "X/Y", "X", AccessSpecifier::AS_private); 142 I.TagType = TagTypeKind::TTK_Class; 143 I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo); 144 I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); 145 146 I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, 147 "X/Y/Z/r"); 148 I.ChildFunctions.emplace_back(); 149 I.ChildFunctions.back().Name = "OneFunction"; 150 I.ChildEnums.emplace_back(); 151 I.ChildEnums.back().Name = "OneEnum"; 152 153 auto G = getHTMLGenerator(); 154 assert(G); 155 std::string Buffer; 156 llvm::raw_string_ostream Actual(Buffer); 157 ClangDocContext CDCtx = getClangDocContext({}, "http://www.repository.com"); 158 auto Err = G->generateDocForInfo(&I, Actual, CDCtx); 159 assert(!Err); 160 std::string Expected = R"raw(<!DOCTYPE html> 161 <meta charset="utf-8"/> 162 <title>class r</title> 163 <link rel="stylesheet" href="../../../clang-doc-default-stylesheet.css"/> 164 <script src="../../../index.js"></script> 165 <div id="index" path="X/Y/Z"></div> 166 <ul> 167 <li> 168 <span> 169 <a href="#Members">Members</a> 170 </span> 171 </li> 172 <li> 173 <span> 174 <a href="#Records">Records</a> 175 </span> 176 </li> 177 <li> 178 <span> 179 <a href="#Functions">Functions</a> 180 </span> 181 <ul> 182 <li> 183 <span> 184 <a href="#0000000000000000000000000000000000000000">OneFunction</a> 185 </span> 186 </li> 187 </ul> 188 </li> 189 <li> 190 <span> 191 <a href="#Enums">Enums</a> 192 </span> 193 <ul> 194 <li> 195 <span> 196 <a href="#0000000000000000000000000000000000000000">OneEnum</a> 197 </span> 198 </li> 199 </ul> 200 </li> 201 </ul> 202 <div> 203 <h1>class r</h1> 204 <p> 205 Defined at line 206 <a href="http://www.repository.com/dir/test.cpp#10">10</a> 207 of file 208 <a href="http://www.repository.com/dir/test.cpp">test.cpp</a> 209 </p> 210 <p> 211 Inherits from 212 <a href="../../../path/to/F.html">F</a> 213 , G 214 </p> 215 <h2 id="Members">Members</h2> 216 <ul> 217 <li> 218 private 219 <a href="../int.html">int</a> 220 X 221 </li> 222 </ul> 223 <h2 id="Records">Records</h2> 224 <ul> 225 <li> 226 <a href="r/ChildStruct.html">ChildStruct</a> 227 </li> 228 </ul> 229 <h2 id="Functions">Functions</h2> 230 <div> 231 <h3 id="0000000000000000000000000000000000000000">OneFunction</h3> 232 <p>public OneFunction()</p> 233 </div> 234 <h2 id="Enums">Enums</h2> 235 <div> 236 <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3> 237 </div> 238 </div> 239 )raw"; 240 241 EXPECT_EQ(Expected, Actual.str()); 242 } 243 244 TEST(HTMLGeneratorTest, emitFunctionHTML) { 245 FunctionInfo I; 246 I.Name = "f"; 247 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 248 249 I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false); 250 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); 251 252 I.Access = AccessSpecifier::AS_none; 253 254 SmallString<16> PathTo; 255 llvm::sys::path::native("path/to", PathTo); 256 I.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default, PathTo); 257 I.Params.emplace_back("int", PathTo, "P"); 258 I.IsMethod = true; 259 I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record); 260 261 auto G = getHTMLGenerator(); 262 assert(G); 263 std::string Buffer; 264 llvm::raw_string_ostream Actual(Buffer); 265 ClangDocContext CDCtx = getClangDocContext({}, "https://www.repository.com"); 266 auto Err = G->generateDocForInfo(&I, Actual, CDCtx); 267 assert(!Err); 268 std::string Expected = R"raw(<!DOCTYPE html> 269 <meta charset="utf-8"/> 270 <title></title> 271 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> 272 <script src="index.js"></script> 273 <div id="index" path=""></div> 274 <div> 275 <h3 id="0000000000000000000000000000000000000000">f</h3> 276 <p> 277 <a href="path/to/float.html">float</a> 278 f( 279 <a href="path/to/int.html">int</a> 280 P) 281 </p> 282 <p>Defined at line 10 of file dir/test.cpp</p> 283 </div> 284 )raw"; 285 286 EXPECT_EQ(Expected, Actual.str()); 287 } 288 289 TEST(HTMLGeneratorTest, emitEnumHTML) { 290 EnumInfo I; 291 I.Name = "e"; 292 I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 293 294 I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true); 295 I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); 296 297 I.Members.emplace_back("X"); 298 I.Scoped = true; 299 300 auto G = getHTMLGenerator(); 301 assert(G); 302 std::string Buffer; 303 llvm::raw_string_ostream Actual(Buffer); 304 ClangDocContext CDCtx = getClangDocContext({}, "www.repository.com"); 305 auto Err = G->generateDocForInfo(&I, Actual, CDCtx); 306 assert(!Err); 307 std::string Expected = R"raw(<!DOCTYPE html> 308 <meta charset="utf-8"/> 309 <title></title> 310 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> 311 <script src="index.js"></script> 312 <div id="index" path=""></div> 313 <div> 314 <h3 id="0000000000000000000000000000000000000000">enum class e</h3> 315 <ul> 316 <li>X</li> 317 </ul> 318 <p> 319 Defined at line 320 <a href="https://www.repository.com/test.cpp#10">10</a> 321 of file 322 <a href="https://www.repository.com/test.cpp">test.cpp</a> 323 </p> 324 </div> 325 )raw"; 326 327 EXPECT_EQ(Expected, Actual.str()); 328 } 329 330 TEST(HTMLGeneratorTest, emitCommentHTML) { 331 FunctionInfo I; 332 I.Name = "f"; 333 I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); 334 I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); 335 I.Params.emplace_back("int", "I"); 336 I.Params.emplace_back("int", "J"); 337 I.Access = AccessSpecifier::AS_none; 338 339 CommentInfo Top; 340 Top.Kind = "FullComment"; 341 342 Top.Children.emplace_back(std::make_unique<CommentInfo>()); 343 CommentInfo *BlankLine = Top.Children.back().get(); 344 BlankLine->Kind = "ParagraphComment"; 345 BlankLine->Children.emplace_back(std::make_unique<CommentInfo>()); 346 BlankLine->Children.back()->Kind = "TextComment"; 347 348 Top.Children.emplace_back(std::make_unique<CommentInfo>()); 349 CommentInfo *Brief = Top.Children.back().get(); 350 Brief->Kind = "ParagraphComment"; 351 Brief->Children.emplace_back(std::make_unique<CommentInfo>()); 352 Brief->Children.back()->Kind = "TextComment"; 353 Brief->Children.back()->Name = "ParagraphComment"; 354 Brief->Children.back()->Text = " Brief description."; 355 356 Top.Children.emplace_back(std::make_unique<CommentInfo>()); 357 CommentInfo *Extended = Top.Children.back().get(); 358 Extended->Kind = "ParagraphComment"; 359 Extended->Children.emplace_back(std::make_unique<CommentInfo>()); 360 Extended->Children.back()->Kind = "TextComment"; 361 Extended->Children.back()->Text = " Extended description that"; 362 Extended->Children.emplace_back(std::make_unique<CommentInfo>()); 363 Extended->Children.back()->Kind = "TextComment"; 364 Extended->Children.back()->Text = " continues onto the next line."; 365 366 Top.Children.emplace_back(std::make_unique<CommentInfo>()); 367 CommentInfo *Entities = Top.Children.back().get(); 368 Entities->Kind = "ParagraphComment"; 369 Entities->Children.emplace_back(std::make_unique<CommentInfo>()); 370 Entities->Children.back()->Kind = "TextComment"; 371 Entities->Children.back()->Name = "ParagraphComment"; 372 Entities->Children.back()->Text = 373 " Comment with html entities: &, <, >, \", \'."; 374 375 I.Description.emplace_back(std::move(Top)); 376 377 auto G = getHTMLGenerator(); 378 assert(G); 379 std::string Buffer; 380 llvm::raw_string_ostream Actual(Buffer); 381 ClangDocContext CDCtx = getClangDocContext(); 382 auto Err = G->generateDocForInfo(&I, Actual, CDCtx); 383 assert(!Err); 384 std::string Expected = R"raw(<!DOCTYPE html> 385 <meta charset="utf-8"/> 386 <title></title> 387 <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> 388 <script src="index.js"></script> 389 <div id="index" path=""></div> 390 <div> 391 <h3 id="0000000000000000000000000000000000000000">f</h3> 392 <p>void f(int I, int J)</p> 393 <p>Defined at line 10 of file test.cpp</p> 394 <div> 395 <div> 396 <p> Brief description.</p> 397 <p> Extended description that continues onto the next line.</p> 398 <p> Comment with html entities: &, <, >, ", '.</p> 399 </div> 400 </div> 401 </div> 402 )raw"; 403 404 EXPECT_EQ(Expected, Actual.str()); 405 } 406 407 } // namespace doc 408 } // namespace clang 409