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&amp;q=1&lt;">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&amp;q=1&lt;">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&amp;q=1&lt;">!</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&amp;q=1&lt;">!</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("&lt;&gt;&amp;&lt;&gt;&amp;[]\\", 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("&amp;&lt;bar&gt;;", 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=\"&quot;foo.html&quot;\">bar</a>",
   208              RenderHtml("<A HREF = \"&quot;foo.html&quot;\" >bar</A>"));
   209    EXPECT_EQ("&lt;A HREF = \"&amp; q;foo.html&amp; q;\" &gt;bar",
   210              RenderHtml("<A HREF = \"& q;foo.html& q;\" >bar</A>"));
   211    EXPECT_EQ("&lt;A HREF = \"href=\"foo.html\">\" BAD&gt;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  }