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