kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/doc/html_renderer_test.cc (about) 1 /* 2 * Copyright 2016 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "kythe/cxx/doc/html_renderer.h" 18 19 #include <map> 20 #include <string> 21 22 #include "absl/log/initialize.h" 23 #include "google/protobuf/stubs/common.h" 24 #include "google/protobuf/text_format.h" 25 #include "gtest/gtest.h" 26 #include "kythe/cxx/doc/html_markup_handler.h" 27 #include "kythe/cxx/doc/javadoxygen_markup_handler.h" 28 #include "kythe/cxx/doc/markup_handler.h" 29 #include "kythe/proto/common.pb.h" 30 #include "kythe/proto/xref.pb.h" 31 32 namespace kythe { 33 namespace { 34 using google::protobuf::TextFormat; 35 class TestHtmlRendererOptions : public HtmlRendererOptions { 36 public: 37 TestHtmlRendererOptions() { 38 node_info_["kythe://foo"].set_definition("kythe://food"); 39 node_info_["kythe://bar"].set_definition("kythe://bard"); 40 definition_locations_["kythe://food"].set_parent("kythe://foop"); 41 definition_locations_["kythe://bard"].set_parent("kythe://barp&q=1<"); 42 } 43 const proto::common::NodeInfo* node_info( 44 const std::string& ticket) const override { 45 auto record = node_info_.find(ticket); 46 return record != node_info_.end() ? &record->second : nullptr; 47 } 48 const proto::Anchor* anchor_for_ticket( 49 const std::string& ticket) const override { 50 auto record = definition_locations_.find(ticket); 51 return record != definition_locations_.end() ? &record->second : nullptr; 52 } 53 54 private: 55 std::map<std::string, proto::common::NodeInfo> node_info_; 56 std::map<std::string, proto::Anchor> definition_locations_; 57 }; 58 class HtmlRendererTest : public ::testing::Test { 59 public: 60 HtmlRendererTest() { 61 options_.make_link_uri = [](const proto::Anchor& anchor) { 62 return anchor.parent(); 63 }; 64 } 65 66 protected: 67 std::string RenderAsciiProtoDocument(const char* document_pb) { 68 proto::DocumentationReply::Document document; 69 if (!TextFormat::ParseFromString(document_pb, &document)) { 70 return "(invalid ascii protobuf)"; 71 } 72 Printable printable(document.text()); 73 return kythe::RenderHtml(options_, printable); 74 } 75 std::string RenderJavadoc(const char* raw_text) { 76 proto::Printable reply_; 77 reply_.set_raw_text(raw_text); 78 Printable input(reply_); 79 auto output = HandleMarkup({ParseJavadoxygen}, input); 80 return kythe::RenderHtml(options_, output); 81 } 82 std::string RenderHtml(const char* raw_text) { 83 proto::Printable reply_; 84 reply_.set_raw_text(raw_text); 85 Printable input(reply_); 86 auto output = HandleMarkup({ParseHtml}, input); 87 return kythe::RenderHtml(options_, output); 88 } 89 kythe::TestHtmlRendererOptions options_; 90 }; 91 TEST_F(HtmlRendererTest, RenderEmptyDoc) { 92 EXPECT_EQ("", RenderAsciiProtoDocument("")); 93 } 94 TEST_F(HtmlRendererTest, RenderSimpleDoc) { 95 EXPECT_EQ("Hello, world!", RenderAsciiProtoDocument(R"( 96 text { raw_text: "Hello, world!" } 97 )")); 98 } 99 TEST_F(HtmlRendererTest, RenderLink) { 100 EXPECT_EQ(R"(Hello, <a href="kythe://foop">world</a>!)", 101 RenderAsciiProtoDocument(R"( 102 text { 103 raw_text: "Hello, [world]!" 104 link: { definition: "kythe://foo" } 105 } 106 )")); 107 } 108 TEST_F(HtmlRendererTest, DropMissingLink) { 109 EXPECT_EQ(R"(Hello, world!)", RenderAsciiProtoDocument(R"( 110 text { 111 raw_text: "Hello, [world]!" 112 link: { definition: "kythe://baz" } 113 } 114 )")); 115 } 116 TEST_F(HtmlRendererTest, EscapeLink) { 117 EXPECT_EQ(R"(Hello, <a href="kythe://barp&q=1<">world</a>!)", 118 RenderAsciiProtoDocument(R"( 119 text { 120 raw_text: "Hello, [world]!" 121 link: { definition: "kythe://bar" } 122 } 123 )")); 124 } 125 TEST_F(HtmlRendererTest, RenderLinks) { 126 EXPECT_EQ( 127 R"(<a href="kythe://foop">Hello</a>, <a href="kythe://barp&q=1<">world</a>!)", 128 RenderAsciiProtoDocument(R"( 129 text { 130 raw_text: "[Hello], [world][!]" 131 link: { definition: "kythe://foo" } 132 link: { definition: "kythe://bar" } 133 } 134 )")); 135 } 136 TEST_F(HtmlRendererTest, SkipMissingLinks) { 137 EXPECT_EQ( 138 R"(<a href="kythe://foop">Hello</a>, world<a href="kythe://barp&q=1<">!</a>)", 139 RenderAsciiProtoDocument(R"( 140 text { 141 raw_text: "[Hello], [world][!]" 142 link: { definition: "kythe://foo" } 143 link: { } 144 link: { definition: "kythe://bar" } 145 } 146 )")); 147 } 148 TEST_F(HtmlRendererTest, SkipNestedMissingLinks) { 149 EXPECT_EQ( 150 R"(<a href="kythe://foop">Hello</a>, world<a href="kythe://barp&q=1<">!</a>)", 151 RenderAsciiProtoDocument(R"( 152 text { 153 raw_text: "[Hello], [world[!]]" 154 link: { definition: "kythe://foo" } 155 link: { } 156 link: { definition: "kythe://bar" } 157 } 158 )")); 159 } 160 TEST_F(HtmlRendererTest, EscapeHtml) { 161 EXPECT_EQ("<>&<>&[]\\", RenderAsciiProtoDocument(R"( 162 text { raw_text: "<>&\\<\\>\\&\\[\\]\\\\" } 163 )")); 164 } 165 TEST_F(HtmlRendererTest, JavadocTagBlocks) { 166 EXPECT_EQ( 167 "text\n<div class=\"kythe-doc-tag-section-title\">Author</div>" 168 "<div class=\"kythe-doc-tag-section-content\"><ul><li> a\n</li>" 169 "<li> b</li></ul></div>", 170 RenderJavadoc(R"(text 171 @author a 172 @author b)")); 173 } 174 TEST_F(HtmlRendererTest, JavadocTagBlockEmbedsCodeRef) { 175 EXPECT_EQ( 176 "text\n<div class=\"kythe-doc-tag-section-title\">Author</div>" 177 "<div class=\"kythe-doc-tag-section-content\"><ul><li> a <tt> robot</tt>" 178 "\n</li><li> b</li></ul></div>", 179 RenderJavadoc(R"(text 180 @author a {@code robot} 181 @author b)")); 182 } 183 TEST_F(HtmlRendererTest, EmptyTags) { 184 EXPECT_EQ("", RenderHtml("<I></I>")); 185 EXPECT_EQ("<i></i>", RenderHtml("<I><B></B></I>")); 186 } 187 TEST_F(HtmlRendererTest, PassThroughStyles) { 188 EXPECT_EQ( 189 "<b><i><h1><h2><h3><h4><h5><h6>x</h6></h5></h4></h3></h2></h1></i></b>", 190 RenderHtml("<B><I><H1><H2><H3><H4><H5><H6>x</H6></H5></H4></H3></H2></" 191 "H1></I></B>")); 192 } 193 TEST_F(HtmlRendererTest, PassThroughMoreStyles) { 194 EXPECT_EQ( 195 "<blockquote><small><tt><tt><big><ul><sub><sup>x</sup></sub></ul></big></" 196 "tt></tt></small></blockquote>", 197 RenderHtml("<BLOCKQUOTE><SMALL><TT><CODE><BIG><UL><SUB><SUP>x</SUP></" 198 "SUB></UL></BIG></CODE></TT></SMALL></BLOCKQUOTE>")); 199 } 200 TEST_F(HtmlRendererTest, PassThroughEntities) { 201 EXPECT_EQ("&foo;", RenderHtml("&foo;")); 202 EXPECT_EQ("&<bar>;", RenderHtml("&<bar>;")); 203 } 204 TEST_F(HtmlRendererTest, RenderHtmlLinks) { 205 EXPECT_EQ("<a href=\"foo.html\">bar</a>", 206 RenderHtml("<A HREF = \"foo.html\" >bar</A>")); 207 EXPECT_EQ("<a href=\""foo.html"\">bar</a>", 208 RenderHtml("<A HREF = \""foo.html"\" >bar</A>")); 209 EXPECT_EQ("<A HREF = \"& q;foo.html& q;\" >bar", 210 RenderHtml("<A HREF = \"& q;foo.html& q;\" >bar</A>")); 211 EXPECT_EQ("<A HREF = \"href=\"foo.html\">\" BAD>bar", 212 RenderHtml("<A HREF = \"foo.html\" BAD>bar</A>")); 213 } 214 constexpr char kSampleMarkedSource[] = R""(child { 215 child { 216 kind: CONTEXT 217 child { 218 kind: IDENTIFIER 219 pre_text: "namespace" 220 } 221 child { 222 kind: IDENTIFIER 223 pre_text: "(anonymous namespace)" 224 } 225 child { 226 kind: IDENTIFIER 227 pre_text: "ClassContainer" 228 } 229 post_child_text: "::" 230 add_final_list_token: true 231 } 232 child { 233 kind: IDENTIFIER 234 pre_text: "FunctionName" 235 } 236 } 237 child { 238 kind: PARAMETER 239 pre_text: "(" 240 child { 241 post_child_text: " " 242 child { 243 kind: TYPE 244 pre_text: "TypeOne*" 245 } 246 child { 247 child { 248 kind: CONTEXT 249 child { 250 kind: IDENTIFIER 251 pre_text: "namespace" 252 } 253 child { 254 kind: IDENTIFIER 255 pre_text: "(anonymous namespace)" 256 } 257 child { 258 kind: IDENTIFIER 259 pre_text: "ClassContainer" 260 } 261 child { 262 kind: IDENTIFIER 263 pre_text: "FunctionName" 264 } 265 post_child_text: "::" 266 add_final_list_token: true 267 } 268 child { 269 kind: IDENTIFIER 270 pre_text: "param_name_one" 271 } 272 } 273 } 274 child { 275 post_child_text: " " 276 child { 277 kind: TYPE 278 pre_text: "TypeTwo*" 279 } 280 child { 281 child { 282 kind: CONTEXT 283 child { 284 kind: IDENTIFIER 285 pre_text: "namespace" 286 } 287 child { 288 kind: IDENTIFIER 289 pre_text: "(anonymous namespace)" 290 } 291 child { 292 kind: IDENTIFIER 293 pre_text: "ClassContainer" 294 } 295 child { 296 kind: IDENTIFIER 297 pre_text: "FunctionName" 298 } 299 post_child_text: "::" 300 add_final_list_token: true 301 } 302 child { 303 kind: IDENTIFIER 304 pre_text: "param_name_two" 305 } 306 } 307 } 308 post_child_text: ", " 309 post_text: ")" 310 } 311 )""; 312 313 TEST_F(HtmlRendererTest, RenderSimpleParams) { 314 proto::common::MarkedSource marked; 315 ASSERT_TRUE(TextFormat::ParseFromString(kSampleMarkedSource, &marked)) 316 << "(invalid ascii protobuf)"; 317 auto params = kythe::RenderSimpleParams(marked); 318 ASSERT_EQ(2, params.size()); 319 EXPECT_EQ("param_name_one", params[0]); 320 EXPECT_EQ("param_name_two", params[1]); 321 } 322 TEST_F(HtmlRendererTest, RenderSimpleIdentifier) { 323 proto::common::MarkedSource marked; 324 ASSERT_TRUE(TextFormat::ParseFromString(kSampleMarkedSource, &marked)) 325 << "(invalid ascii protobuf)"; 326 EXPECT_EQ("FunctionName", kythe::RenderSimpleIdentifier(marked)); 327 } 328 TEST_F(HtmlRendererTest, RenderSimpleQualifiedName) { 329 proto::common::MarkedSource marked; 330 ASSERT_TRUE(TextFormat::ParseFromString(kSampleMarkedSource, &marked)) 331 << "(invalid ascii protobuf)"; 332 EXPECT_EQ("namespace::(anonymous namespace)::ClassContainer", 333 kythe::RenderSimpleQualifiedName(marked, false)); 334 EXPECT_EQ("namespace::(anonymous namespace)::ClassContainer::FunctionName", 335 kythe::RenderSimpleQualifiedName(marked, true)); 336 } 337 338 constexpr char kGoMarkedSource[] = R""( 339 kind: PARAMETER 340 child { 341 kind: TYPE 342 pre_text: "*pkg.receiver" 343 } 344 child { 345 kind: BOX 346 post_child_text: "." 347 child { 348 kind: BOX 349 child { 350 kind: CONTEXT 351 pre_text: "pkg" 352 } 353 child { 354 kind: IDENTIFIER 355 pre_text: "param" 356 } 357 } 358 child { 359 kind: BOX 360 pre_text: " " 361 } 362 child { 363 kind: TYPE 364 pre_text: "string" 365 } 366 } 367 )""; 368 369 TEST_F(HtmlRendererTest, RenderSimpleParamsGo) { 370 proto::common::MarkedSource marked; 371 ASSERT_TRUE(TextFormat::ParseFromString(kGoMarkedSource, &marked)) 372 << "(invalid ascii protobuf)"; 373 auto params = kythe::RenderSimpleParams(marked); 374 ASSERT_EQ(2, params.size()); 375 EXPECT_EQ("param", params[1]); 376 } 377 } // anonymous namespace 378 } // namespace kythe 379 380 int main(int argc, char** argv) { 381 GOOGLE_PROTOBUF_VERIFY_VERSION; 382 absl::InitializeLog(); 383 ::testing::InitGoogleTest(&argc, argv); 384 int result = RUN_ALL_TESTS(); 385 return result; 386 }