Diego Astiazaran e27f778a19 [clang-doc] Generate HTML links for children namespaces/records
Path is now stored in the references to the child while serializing,
then this path is used to generate the relative path in the HTML
generator.
Now some references have paths and some don't so in the reducing phase,
references are now properly merged checking for empty attributes.
Tests added for HTML and YAML generators, merging and serializing.
computeRelativePath function had a bug when the filepath is part of the
given directory; it returned a path that starts with a separator. This
has been fixed.

Differential Revision: https://reviews.llvm.org/D65987

llvm-svn: 368602
2019-08-12 18:42:46 +00:00

405 lines
11 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 "gtest/gtest.h"
namespace clang {
namespace 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{{}, {}, {}, {}, 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().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>
<div id="index" path=""></div>
<ul>
<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>
</ul>
<div>
<h1>namespace Namespace</h1>
<h2 id="Namespaces">Namespaces</h2>
<ul>
<li>
<a href="Namespace/ChildNamespace.html">ChildNamespace</a>
</li>
</ul>
<h2 id="Records">Records</h2>
<ul>
<li>
<a href="Namespace/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>
)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>
<div id="index" path="X/Y/Z"></div>
<ul>
<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>
</ul>
<div>
<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="../int.html">int</a>
X
</li>
</ul>
<h2 id="Records">Records</h2>
<ul>
<li>
<a href="r/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>
)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"});
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>
<div id="index" path=""></div>
<div>
<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>
)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>
<div id="index" path=""></div>
<div>
<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>
)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");
CommentInfo Top;
Top.Kind = "FullComment";
Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
CommentInfo *BlankLine = Top.Children.back().get();
BlankLine->Kind = "ParagraphComment";
BlankLine->Children.emplace_back(llvm::make_unique<CommentInfo>());
BlankLine->Children.back()->Kind = "TextComment";
Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
CommentInfo *Brief = Top.Children.back().get();
Brief->Kind = "ParagraphComment";
Brief->Children.emplace_back(llvm::make_unique<CommentInfo>());
Brief->Children.back()->Kind = "TextComment";
Brief->Children.back()->Name = "ParagraphComment";
Brief->Children.back()->Text = " Brief description.";
Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
CommentInfo *Extended = Top.Children.back().get();
Extended->Kind = "ParagraphComment";
Extended->Children.emplace_back(llvm::make_unique<CommentInfo>());
Extended->Children.back()->Kind = "TextComment";
Extended->Children.back()->Text = " Extended description that";
Extended->Children.emplace_back(llvm::make_unique<CommentInfo>());
Extended->Children.back()->Kind = "TextComment";
Extended->Children.back()->Text = " continues onto the next line.";
Top.Children.emplace_back(llvm::make_unique<CommentInfo>());
CommentInfo *Entities = Top.Children.back().get();
Entities->Kind = "ParagraphComment";
Entities->Children.emplace_back(llvm::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>
<div id="index" path=""></div>
<div>
<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: &amp;, &lt;, &gt;, &quot;, &apos;.</p>
</div>
</div>
</div>
)raw";
EXPECT_EQ(Expected, Actual.str());
}
} // namespace doc
} // namespace clang