mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 00:26:43 +00:00

This PR adds a new command-line option that allows users to specify the prefix used for line-based anchors in repository URLs. Different repository interfaces use different formats for line anchors (GitHub uses `#L123`, googlesource uses `#123`, etc.). This option enables users to customize the line prefix to match their repository platform without requiring hard-coded values for each service. Fixes #59814
489 lines
15 KiB
C++
489 lines
15 KiB
C++
//===-- 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 = "",
|
|
StringRef RepositoryLinePrefix = "", StringRef Base = "") {
|
|
ClangDocContext CDCtx{
|
|
{}, "test-project", {}, {}, {}, RepositoryUrl, RepositoryLinePrefix,
|
|
Base, 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.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
|
|
InfoType::IT_namespace,
|
|
"Namespace::ChildNamespace", "Namespace");
|
|
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
|
|
"Namespace::ChildStruct", "Namespace");
|
|
I.Children.Functions.emplace_back();
|
|
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
|
|
I.Children.Functions.back().Name = "OneFunction";
|
|
I.Children.Enums.emplace_back();
|
|
I.Children.Enums.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_json.js"></script>
|
|
<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>
|
|
<table id="0000000000000000000000000000000000000000">
|
|
<thead>
|
|
<tr>
|
|
<th colspan="2">enum OneEnum</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</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(TypeInfo("int"), "X", AccessSpecifier::AS_private);
|
|
I.TagType = TagTypeKind::Class;
|
|
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "F", PathTo);
|
|
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
|
|
|
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
|
|
"X::Y::Z::r::ChildStruct", "X/Y/Z/r");
|
|
I.Children.Functions.emplace_back();
|
|
I.Children.Functions.back().Name = "OneFunction";
|
|
I.Children.Enums.emplace_back();
|
|
I.Children.Enums.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_json.js"></script>
|
|
<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>
|
|
<div>private int X</div>
|
|
</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>
|
|
<table id="0000000000000000000000000000000000000000">
|
|
<thead>
|
|
<tr>
|
|
<th colspan="2">enum OneEnum</th>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</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(
|
|
Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
|
|
I.Params.emplace_back(TypeInfo("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_json.js"></script>
|
|
<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
|
|
<a href="https://www.repository.com/dir/test.cpp#10">10</a>
|
|
of file
|
|
<a href="https://www.repository.com/dir/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, 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_json.js"></script>
|
|
<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">
|
|
<table id="0000000000000000000000000000000000000000">
|
|
<thead>
|
|
<tr>
|
|
<th colspan="2">enum class e</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>X</td>
|
|
<td>0</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<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("void");
|
|
I.Params.emplace_back(TypeInfo("int"), "I");
|
|
I.Params.emplace_back(TypeInfo("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_json.js"></script>
|
|
<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
|