| 1 | //===-- clang-doc/HTMLMustacheGeneratorTest.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 "config.h" |
| 13 | #include "support/Utils.h" |
| 14 | #include "clang/Basic/Version.h" |
| 15 | #include "llvm/Support/Path.h" |
| 16 | #include "llvm/Testing/Support/Error.h" |
| 17 | #include "llvm/Testing/Support/SupportHelpers.h" |
| 18 | #include "gmock/gmock.h" |
| 19 | #include "gtest/gtest.h" |
| 20 | |
| 21 | using namespace llvm; |
| 22 | using namespace testing; |
| 23 | using namespace clang; |
| 24 | using namespace clang::doc; |
| 25 | |
| 26 | // FIXME: Don't enable unit tests that can read files. Remove once we can use |
| 27 | // lit to test these properties. |
| 28 | #define ENABLE_LOCAL_TEST 0 |
| 29 | |
| 30 | static const std::string ClangDocVersion = getClangToolFullVersion(ToolName: "clang-doc" ); |
| 31 | |
| 32 | static std::unique_ptr<Generator> getHTMLMustacheGenerator() { |
| 33 | auto G = findGeneratorByName(Format: "mustache" ); |
| 34 | if (!G) |
| 35 | return nullptr; |
| 36 | return std::move(G.get()); |
| 37 | } |
| 38 | |
| 39 | static ClangDocContext |
| 40 | getClangDocContext(std::vector<std::string> UserStylesheets = {}, |
| 41 | StringRef RepositoryUrl = "" , |
| 42 | StringRef RepositoryLinePrefix = "" , StringRef Base = "" ) { |
| 43 | ClangDocContext CDCtx{ |
| 44 | {}, "test-project" , {}, {}, {}, RepositoryUrl, RepositoryLinePrefix, |
| 45 | Base, UserStylesheets}; |
| 46 | CDCtx.UserStylesheets.insert(position: CDCtx.UserStylesheets.begin(), x: "" ); |
| 47 | CDCtx.JsScripts.emplace_back(args: "" ); |
| 48 | return CDCtx; |
| 49 | } |
| 50 | |
| 51 | static void verifyFileContents(const Twine &Path, StringRef Contents) { |
| 52 | auto Buffer = MemoryBuffer::getFile(Filename: Path); |
| 53 | ASSERT_TRUE((bool)Buffer); |
| 54 | StringRef Data = Buffer.get()->getBuffer(); |
| 55 | ASSERT_EQ(Data, Contents); |
| 56 | } |
| 57 | |
| 58 | TEST(HTMLMustacheGeneratorTest, createResources) { |
| 59 | auto G = getHTMLMustacheGenerator(); |
| 60 | ASSERT_THAT(G, NotNull()) << "Could not find HTMLMustacheGenerator" ; |
| 61 | ClangDocContext CDCtx = getClangDocContext(); |
| 62 | EXPECT_THAT_ERROR(G->createResources(CDCtx), Failed()) |
| 63 | << "Empty UserStylesheets or JsScripts should fail!" ; |
| 64 | |
| 65 | unittest::TempDir RootTestDirectory("createResourcesTest" , /*Unique=*/true); |
| 66 | CDCtx.OutDirectory = RootTestDirectory.path(); |
| 67 | |
| 68 | unittest::TempFile CSS("clang-doc-mustache" , "css" , "CSS" ); |
| 69 | unittest::TempFile JS("mustache" , "js" , "JavaScript" ); |
| 70 | |
| 71 | CDCtx.UserStylesheets[0] = CSS.path(); |
| 72 | CDCtx.JsScripts[0] = JS.path(); |
| 73 | |
| 74 | EXPECT_THAT_ERROR(G->createResources(CDCtx), Succeeded()) |
| 75 | << "Failed to create resources with valid UserStylesheets and JsScripts" ; |
| 76 | { |
| 77 | SmallString<256> PathBuf; |
| 78 | llvm::sys::path::append(path&: PathBuf, a: RootTestDirectory.path(), |
| 79 | b: "clang-doc-mustache.css" ); |
| 80 | verifyFileContents(Path: PathBuf, Contents: "CSS" ); |
| 81 | } |
| 82 | |
| 83 | { |
| 84 | SmallString<256> PathBuf; |
| 85 | llvm::sys::path::append(path&: PathBuf, a: RootTestDirectory.path(), b: "mustache.js" ); |
| 86 | verifyFileContents(Path: PathBuf, Contents: "JavaScript" ); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | TEST(HTMLGeneratorTest, emitFunctionHTML) { |
| 91 | #if ENABLE_LOCAL_TEST |
| 92 | auto G = getHTMLMustacheGenerator(); |
| 93 | assert(G && "Could not find HTMLMustacheGenerator" ); |
| 94 | ClangDocContext CDCtx = getClangDocContext(); |
| 95 | std::string Buffer; |
| 96 | llvm::raw_string_ostream Actual(Buffer); |
| 97 | |
| 98 | unittest::TempDir RootTestDirectory("emitRecordHTML" , |
| 99 | /*Unique=*/true); |
| 100 | CDCtx.OutDirectory = RootTestDirectory.path(); |
| 101 | |
| 102 | getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx); |
| 103 | |
| 104 | // FIXME: This is a terrible hack, since we can't initialize the templates |
| 105 | // directly. We'll need to update the interfaces so that we can call |
| 106 | // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp |
| 107 | EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx), |
| 108 | Succeeded()) |
| 109 | << "Failed to generate docs." ; |
| 110 | |
| 111 | CDCtx.RepositoryUrl = "http://www.repository.com" ; |
| 112 | |
| 113 | FunctionInfo I; |
| 114 | I.Name = "f" ; |
| 115 | I.Namespace.emplace_back(EmptySID, "A" , InfoType::IT_namespace); |
| 116 | |
| 117 | I.DefLoc = Location(10, 10, "dir/test.cpp" , true); |
| 118 | I.Loc.emplace_back(12, 12, "test.cpp" ); |
| 119 | |
| 120 | I.Access = AccessSpecifier::AS_none; |
| 121 | |
| 122 | SmallString<16> PathTo; |
| 123 | llvm::sys::path::native("path/to" , PathTo); |
| 124 | I.ReturnType = doc::TypeInfo( |
| 125 | Reference(EmptySID, "float" , InfoType::IT_default, "float" , PathTo)); |
| 126 | I.Params.emplace_back(doc::TypeInfo("int" , PathTo), "P" ); |
| 127 | I.IsMethod = true; |
| 128 | I.Parent = Reference(EmptySID, "Parent" , InfoType::IT_record); |
| 129 | |
| 130 | auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| 131 | assert(!Err); |
| 132 | std::string Expected = R"raw(IT_Function |
| 133 | )raw" ; |
| 134 | |
| 135 | // FIXME: Functions are not handled yet. |
| 136 | EXPECT_EQ(Expected, Actual.str()); |
| 137 | #endif |
| 138 | } |
| 139 | |
| 140 | TEST(HTMLMustacheGeneratorTest, emitCommentHTML) { |
| 141 | #if ENABLE_LOCAL_TEST |
| 142 | auto G = getHTMLMustacheGenerator(); |
| 143 | assert(G && "Could not find HTMLMustacheGenerator" ); |
| 144 | ClangDocContext CDCtx = getClangDocContext(); |
| 145 | std::string Buffer; |
| 146 | llvm::raw_string_ostream Actual(Buffer); |
| 147 | |
| 148 | unittest::TempDir RootTestDirectory("emitCommentHTML" , |
| 149 | /*Unique=*/true); |
| 150 | CDCtx.OutDirectory = RootTestDirectory.path(); |
| 151 | |
| 152 | getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx); |
| 153 | |
| 154 | // FIXME: This is a terrible hack, since we can't initialize the templates |
| 155 | // directly. We'll need to update the interfaces so that we can call |
| 156 | // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp |
| 157 | EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx), |
| 158 | Succeeded()) |
| 159 | << "Failed to generate docs." ; |
| 160 | |
| 161 | CDCtx.RepositoryUrl = "http://www.repository.com" ; |
| 162 | |
| 163 | FunctionInfo I; |
| 164 | I.Name = "f" ; |
| 165 | I.DefLoc = Location(10, 10, "test.cpp" , true); |
| 166 | I.ReturnType = doc::TypeInfo("void" ); |
| 167 | I.Params.emplace_back(doc::TypeInfo("int" ), "I" ); |
| 168 | I.Params.emplace_back(doc::TypeInfo("int" ), "J" ); |
| 169 | I.Access = AccessSpecifier::AS_none; |
| 170 | |
| 171 | CommentInfo Top; |
| 172 | Top.Kind = "FullComment" ; |
| 173 | |
| 174 | Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| 175 | CommentInfo *BlankLine = Top.Children.back().get(); |
| 176 | BlankLine->Kind = "ParagraphComment" ; |
| 177 | BlankLine->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 178 | BlankLine->Children.back()->Kind = "TextComment" ; |
| 179 | |
| 180 | Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| 181 | CommentInfo *Brief = Top.Children.back().get(); |
| 182 | Brief->Kind = "ParagraphComment" ; |
| 183 | Brief->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 184 | Brief->Children.back()->Kind = "TextComment" ; |
| 185 | Brief->Children.back()->Name = "ParagraphComment" ; |
| 186 | Brief->Children.back()->Text = " Brief description." ; |
| 187 | |
| 188 | Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| 189 | CommentInfo *Extended = Top.Children.back().get(); |
| 190 | Extended->Kind = "ParagraphComment" ; |
| 191 | Extended->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 192 | Extended->Children.back()->Kind = "TextComment" ; |
| 193 | Extended->Children.back()->Text = " Extended description that" ; |
| 194 | Extended->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 195 | Extended->Children.back()->Kind = "TextComment" ; |
| 196 | Extended->Children.back()->Text = " continues onto the next line." ; |
| 197 | |
| 198 | Top.Children.emplace_back(std::make_unique<CommentInfo>()); |
| 199 | CommentInfo *Entities = Top.Children.back().get(); |
| 200 | Entities->Kind = "ParagraphComment" ; |
| 201 | Entities->Children.emplace_back(std::make_unique<CommentInfo>()); |
| 202 | Entities->Children.back()->Kind = "TextComment" ; |
| 203 | Entities->Children.back()->Name = "ParagraphComment" ; |
| 204 | Entities->Children.back()->Text = |
| 205 | " Comment with html entities: &, <, >, \", \'." ; |
| 206 | |
| 207 | I.Description.emplace_back(std::move(Top)); |
| 208 | |
| 209 | auto Err = G->generateDocForInfo(&I, Actual, CDCtx); |
| 210 | assert(!Err); |
| 211 | std::string Expected = R"raw(IT_Function |
| 212 | )raw" ; |
| 213 | |
| 214 | // FIXME: Functions are not handled yet. |
| 215 | EXPECT_EQ(Expected, Actual.str()); |
| 216 | #endif |
| 217 | } |
| 218 | |