| //===-- clang-doc/HTMLGeneratorTest.cpp -----------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ClangDocTest.h" |
| #include "Generators.h" |
| #include "Representation.h" |
| #include "Serialize.h" |
| #include "clang/Basic/Version.h" |
| #include "gtest/gtest.h" |
| |
| namespace clang { |
| namespace doc { |
| |
| static const std::string ClangDocVersion = |
| clang::getClangToolFullVersion("clang-doc"); |
| |
| std::unique_ptr<Generator> getHTMLGenerator() { |
| auto G = doc::findGeneratorByName("html"); |
| if (!G) |
| return nullptr; |
| return std::move(G.get()); |
| } |
| |
| ClangDocContext |
| getClangDocContext(std::vector<std::string> UserStylesheets = {}, |
| StringRef RepositoryUrl = "") { |
| ClangDocContext CDCtx{ |
| {}, "test-project", {}, {}, {}, RepositoryUrl, UserStylesheets, {}}; |
| CDCtx.UserStylesheets.insert( |
| CDCtx.UserStylesheets.begin(), |
| "../share/clang/clang-doc-default-stylesheet.css"); |
| CDCtx.JsScripts.emplace_back("index.js"); |
| return CDCtx; |
| } |
| |
| TEST(HTMLGeneratorTest, emitNamespaceHTML) { |
| NamespaceInfo I; |
| I.Name = "Namespace"; |
| I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); |
| |
| I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", |
| InfoType::IT_namespace, "Namespace"); |
| I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, |
| "Namespace"); |
| I.ChildFunctions.emplace_back(); |
| I.ChildFunctions.back().Access = AccessSpecifier::AS_none; |
| I.ChildFunctions.back().Name = "OneFunction"; |
| I.ChildEnums.emplace_back(); |
| I.ChildEnums.back().Name = "OneEnum"; |
| |
| auto G = getHTMLGenerator(); |
| assert(G); |
| std::string Buffer; |
| llvm::raw_string_ostream Actual(Buffer); |
| ClangDocContext CDCtx = getClangDocContext({"user-provided-stylesheet.css"}); |
| auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| assert(!Err); |
| std::string Expected = R"raw(<!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title>namespace Namespace</title> |
| <link rel="stylesheet" href="../clang-doc-default-stylesheet.css"/> |
| <link rel="stylesheet" href="../user-provided-stylesheet.css"/> |
| <script src="../index.js"></script> |
| <header id="project-title">test-project</header> |
| <main> |
| <div id="sidebar-left" path="Namespace" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div> |
| <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content"> |
| <h1>namespace Namespace</h1> |
| <h2 id="Namespaces">Namespaces</h2> |
| <ul> |
| <li> |
| <a href="ChildNamespace/index.html">ChildNamespace</a> |
| </li> |
| </ul> |
| <h2 id="Records">Records</h2> |
| <ul> |
| <li> |
| <a href="ChildStruct.html">ChildStruct</a> |
| </li> |
| </ul> |
| <h2 id="Functions">Functions</h2> |
| <div> |
| <h3 id="0000000000000000000000000000000000000000">OneFunction</h3> |
| <p>OneFunction()</p> |
| </div> |
| <h2 id="Enums">Enums</h2> |
| <div> |
| <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3> |
| </div> |
| </div> |
| <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"> |
| <ol> |
| <li> |
| <span> |
| <a href="#Namespaces">Namespaces</a> |
| </span> |
| </li> |
| <li> |
| <span> |
| <a href="#Records">Records</a> |
| </span> |
| </li> |
| <li> |
| <span> |
| <a href="#Functions">Functions</a> |
| </span> |
| <ul> |
| <li> |
| <span> |
| <a href="#0000000000000000000000000000000000000000">OneFunction</a> |
| </span> |
| </li> |
| </ul> |
| </li> |
| <li> |
| <span> |
| <a href="#Enums">Enums</a> |
| </span> |
| <ul> |
| <li> |
| <span> |
| <a href="#0000000000000000000000000000000000000000">OneEnum</a> |
| </span> |
| </li> |
| </ul> |
| </li> |
| </ol> |
| </div> |
| </main> |
| <footer> |
| <span class="no-break">)raw" + |
| ClangDocVersion + R"raw(</span> |
| </footer> |
| )raw"; |
| |
| EXPECT_EQ(Expected, Actual.str()); |
| } |
| |
| TEST(HTMLGeneratorTest, emitRecordHTML) { |
| RecordInfo I; |
| I.Name = "r"; |
| I.Path = "X/Y/Z"; |
| I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); |
| |
| I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true); |
| I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); |
| |
| SmallString<16> PathTo; |
| llvm::sys::path::native("path/to", PathTo); |
| I.Members.emplace_back("int", "X/Y", "X", AccessSpecifier::AS_private); |
| I.TagType = TagTypeKind::TTK_Class; |
| I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo); |
| I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); |
| |
| I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, |
| "X/Y/Z/r"); |
| I.ChildFunctions.emplace_back(); |
| I.ChildFunctions.back().Name = "OneFunction"; |
| I.ChildEnums.emplace_back(); |
| I.ChildEnums.back().Name = "OneEnum"; |
| |
| auto G = getHTMLGenerator(); |
| assert(G); |
| std::string Buffer; |
| llvm::raw_string_ostream Actual(Buffer); |
| ClangDocContext CDCtx = getClangDocContext({}, "http://www.repository.com"); |
| auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| assert(!Err); |
| std::string Expected = R"raw(<!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title>class r</title> |
| <link rel="stylesheet" href="../../../clang-doc-default-stylesheet.css"/> |
| <script src="../../../index.js"></script> |
| <header id="project-title">test-project</header> |
| <main> |
| <div id="sidebar-left" path="X/Y/Z" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div> |
| <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content"> |
| <h1>class r</h1> |
| <p> |
| Defined at line |
| <a href="http://www.repository.com/dir/test.cpp#10">10</a> |
| of file |
| <a href="http://www.repository.com/dir/test.cpp">test.cpp</a> |
| </p> |
| <p> |
| Inherits from |
| <a href="../../../path/to/F.html">F</a> |
| , G |
| </p> |
| <h2 id="Members">Members</h2> |
| <ul> |
| <li> |
| private |
| <a href="../../../X/Y/int.html">int</a> |
| X |
| </li> |
| </ul> |
| <h2 id="Records">Records</h2> |
| <ul> |
| <li> |
| <a href="../../../X/Y/Z/r/ChildStruct.html">ChildStruct</a> |
| </li> |
| </ul> |
| <h2 id="Functions">Functions</h2> |
| <div> |
| <h3 id="0000000000000000000000000000000000000000">OneFunction</h3> |
| <p>public OneFunction()</p> |
| </div> |
| <h2 id="Enums">Enums</h2> |
| <div> |
| <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3> |
| </div> |
| </div> |
| <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"> |
| <ol> |
| <li> |
| <span> |
| <a href="#Members">Members</a> |
| </span> |
| </li> |
| <li> |
| <span> |
| <a href="#Records">Records</a> |
| </span> |
| </li> |
| <li> |
| <span> |
| <a href="#Functions">Functions</a> |
| </span> |
| <ul> |
| <li> |
| <span> |
| <a href="#0000000000000000000000000000000000000000">OneFunction</a> |
| </span> |
| </li> |
| </ul> |
| </li> |
| <li> |
| <span> |
| <a href="#Enums">Enums</a> |
| </span> |
| <ul> |
| <li> |
| <span> |
| <a href="#0000000000000000000000000000000000000000">OneEnum</a> |
| </span> |
| </li> |
| </ul> |
| </li> |
| </ol> |
| </div> |
| </main> |
| <footer> |
| <span class="no-break">)raw" + |
| ClangDocVersion + R"raw(</span> |
| </footer> |
| )raw"; |
| |
| EXPECT_EQ(Expected, Actual.str()); |
| } |
| |
| TEST(HTMLGeneratorTest, emitFunctionHTML) { |
| FunctionInfo I; |
| I.Name = "f"; |
| I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); |
| |
| I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false); |
| I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); |
| |
| I.Access = AccessSpecifier::AS_none; |
| |
| SmallString<16> PathTo; |
| llvm::sys::path::native("path/to", PathTo); |
| I.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default, PathTo); |
| I.Params.emplace_back("int", PathTo, "P"); |
| I.IsMethod = true; |
| I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record); |
| |
| auto G = getHTMLGenerator(); |
| assert(G); |
| std::string Buffer; |
| llvm::raw_string_ostream Actual(Buffer); |
| ClangDocContext CDCtx = getClangDocContext({}, "https://www.repository.com"); |
| auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| assert(!Err); |
| std::string Expected = R"raw(<!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title></title> |
| <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> |
| <script src="index.js"></script> |
| <header id="project-title">test-project</header> |
| <main> |
| <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div> |
| <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content"> |
| <h3 id="0000000000000000000000000000000000000000">f</h3> |
| <p> |
| <a href="path/to/float.html">float</a> |
| f( |
| <a href="path/to/int.html">int</a> |
| P) |
| </p> |
| <p>Defined at line 10 of file dir/test.cpp</p> |
| </div> |
| <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div> |
| </main> |
| <footer> |
| <span class="no-break">)raw" + |
| ClangDocVersion + R"raw(</span> |
| </footer> |
| )raw"; |
| |
| EXPECT_EQ(Expected, Actual.str()); |
| } |
| |
| TEST(HTMLGeneratorTest, emitEnumHTML) { |
| EnumInfo I; |
| I.Name = "e"; |
| I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); |
| |
| I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true); |
| I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); |
| |
| I.Members.emplace_back("X"); |
| I.Scoped = true; |
| |
| auto G = getHTMLGenerator(); |
| assert(G); |
| std::string Buffer; |
| llvm::raw_string_ostream Actual(Buffer); |
| ClangDocContext CDCtx = getClangDocContext({}, "www.repository.com"); |
| auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| assert(!Err); |
| std::string Expected = R"raw(<!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title></title> |
| <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> |
| <script src="index.js"></script> |
| <header id="project-title">test-project</header> |
| <main> |
| <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div> |
| <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content"> |
| <h3 id="0000000000000000000000000000000000000000">enum class e</h3> |
| <ul> |
| <li>X</li> |
| </ul> |
| <p> |
| Defined at line |
| <a href="https://www.repository.com/test.cpp#10">10</a> |
| of file |
| <a href="https://www.repository.com/test.cpp">test.cpp</a> |
| </p> |
| </div> |
| <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div> |
| </main> |
| <footer> |
| <span class="no-break">)raw" + |
| ClangDocVersion + R"raw(</span> |
| </footer> |
| )raw"; |
| |
| EXPECT_EQ(Expected, Actual.str()); |
| } |
| |
| TEST(HTMLGeneratorTest, emitCommentHTML) { |
| FunctionInfo I; |
| I.Name = "f"; |
| I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); |
| I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); |
| I.Params.emplace_back("int", "I"); |
| I.Params.emplace_back("int", "J"); |
| I.Access = AccessSpecifier::AS_none; |
| |
| CommentInfo Top; |
| Top.Kind = "FullComment"; |
| |
| Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| CommentInfo *BlankLine = Top.Children.back().get(); |
| BlankLine->Kind = "ParagraphComment"; |
| BlankLine->Children.emplace_back(std::make_unique<CommentInfo>()); |
| BlankLine->Children.back()->Kind = "TextComment"; |
| |
| Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| CommentInfo *Brief = Top.Children.back().get(); |
| Brief->Kind = "ParagraphComment"; |
| Brief->Children.emplace_back(std::make_unique<CommentInfo>()); |
| Brief->Children.back()->Kind = "TextComment"; |
| Brief->Children.back()->Name = "ParagraphComment"; |
| Brief->Children.back()->Text = " Brief description."; |
| |
| Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| CommentInfo *Extended = Top.Children.back().get(); |
| Extended->Kind = "ParagraphComment"; |
| Extended->Children.emplace_back(std::make_unique<CommentInfo>()); |
| Extended->Children.back()->Kind = "TextComment"; |
| Extended->Children.back()->Text = " Extended description that"; |
| Extended->Children.emplace_back(std::make_unique<CommentInfo>()); |
| Extended->Children.back()->Kind = "TextComment"; |
| Extended->Children.back()->Text = " continues onto the next line."; |
| |
| Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| CommentInfo *Entities = Top.Children.back().get(); |
| Entities->Kind = "ParagraphComment"; |
| Entities->Children.emplace_back(std::make_unique<CommentInfo>()); |
| Entities->Children.back()->Kind = "TextComment"; |
| Entities->Children.back()->Name = "ParagraphComment"; |
| Entities->Children.back()->Text = |
| " Comment with html entities: &, <, >, \", \'."; |
| |
| I.Description.emplace_back(std::move(Top)); |
| |
| auto G = getHTMLGenerator(); |
| assert(G); |
| std::string Buffer; |
| llvm::raw_string_ostream Actual(Buffer); |
| ClangDocContext CDCtx = getClangDocContext(); |
| auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| assert(!Err); |
| std::string Expected = R"raw(<!DOCTYPE html> |
| <meta charset="utf-8"/> |
| <title></title> |
| <link rel="stylesheet" href="clang-doc-default-stylesheet.css"/> |
| <script src="index.js"></script> |
| <header id="project-title">test-project</header> |
| <main> |
| <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div> |
| <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content"> |
| <h3 id="0000000000000000000000000000000000000000">f</h3> |
| <p>void f(int I, int J)</p> |
| <p>Defined at line 10 of file test.cpp</p> |
| <div> |
| <div> |
| <p> Brief description.</p> |
| <p> Extended description that continues onto the next line.</p> |
| <p> Comment with html entities: &, <, >, ", '.</p> |
| </div> |
| </div> |
| </div> |
| <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right"></div> |
| </main> |
| <footer> |
| <span class="no-break">)raw" + |
| ClangDocVersion + R"raw(</span> |
| </footer> |
| )raw"; |
| |
| EXPECT_EQ(Expected, Actual.str()); |
| } |
| |
| } // namespace doc |
| } // namespace clang |