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