kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/common/kythe_uri_test.cc (about)

     1  /*
     2   * Copyright 2015 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/common/kythe_uri.h"
    18  
    19  #include <string>
    20  
    21  #include "absl/log/initialize.h"
    22  #include "gmock/gmock.h"
    23  #include "google/protobuf/stubs/common.h"
    24  #include "gtest/gtest.h"
    25  #include "kythe/proto/storage.pb.h"
    26  #include "protobuf-matchers/protocol-buffer-matchers.h"
    27  
    28  namespace kythe {
    29  namespace {
    30  using ::protobuf_matchers::EqualsProto;
    31  
    32  struct MakeURI {
    33    const char* signature = "";
    34    const char* corpus = "";
    35    const char* root = "";
    36    const char* path = "";
    37    const char* language = "";
    38  
    39    MakeURI& Language(const char* v) {
    40      language = v;
    41      return *this;
    42    }
    43    MakeURI& Corpus(const char* v) {
    44      corpus = v;
    45      return *this;
    46    }
    47    MakeURI& Root(const char* v) {
    48      root = v;
    49      return *this;
    50    }
    51    MakeURI& Signature(const char* v) {
    52      signature = v;
    53      return *this;
    54    }
    55    MakeURI& Path(const char* v) {
    56      path = v;
    57      return *this;
    58    }
    59  
    60    kythe::proto::VName v_name() const {
    61      kythe::proto::VName v_name;
    62      v_name.set_signature(signature);
    63      v_name.set_corpus(corpus);
    64      v_name.set_root(root);
    65      v_name.set_path(path);
    66      v_name.set_language(language);
    67      return v_name;
    68    }
    69  
    70    URI uri() const { return URI(v_name()); }
    71  
    72    operator URI() const { return URI(v_name()); }  // NOLINT
    73  };
    74  
    75  TEST(KytheUri, Parse) {
    76    struct {
    77      std::string input;
    78      URI expect;
    79    } tests[] = {
    80        {"", MakeURI()},
    81        {"kythe:", MakeURI()},
    82        {"kythe://", MakeURI()},
    83  
    84        // Individual components.
    85        {"#sig", MakeURI().Signature("sig")},
    86        {"kythe:#sig", MakeURI().Signature("sig")},
    87        {"kythe://corpus", MakeURI().Corpus("corpus")},
    88        {"kythe://corpus/", MakeURI().Corpus("corpus/")},
    89        {"kythe://corpus/with/path", MakeURI().Corpus("corpus/with/path")},
    90        {"//corpus/with/path", MakeURI().Corpus("corpus/with/path")},
    91        {"kythe:?root=R", MakeURI().Root("R")},
    92        {"kythe:?path=P", MakeURI().Path("P")},
    93        {"kythe:?lang=L", MakeURI().Language("L")},
    94  
    95        // Special characters.
    96        {"kythe:#-%2B_%2F", MakeURI().Signature("-+_/")},
    97  
    98        // Corner cases about relative paths. NB: MakeURI() goes through VNames.
    99        {"kythe://..", MakeURI().Corpus("..")},
   100        {"kythe://../", MakeURI().Corpus("../")},
   101        {"kythe://../..", MakeURI().Corpus("../..")},
   102        {"kythe://a/../b//c", MakeURI().Corpus("a/../b//c")},
   103  
   104        // Multiple attributes, with permutation of order.
   105        {"kythe:?lang=L?root=R", MakeURI().Root("R").Language("L")},
   106        {"kythe:?lang=L?path=P?root=R",
   107         MakeURI().Root("R").Language("L").Path("P")},
   108        {"kythe:?root=R?path=P?lang=L",
   109         MakeURI().Root("R").Language("L").Path("P")},
   110  
   111        // Corpora with slashes.
   112        {"kythe:///Users/foo", MakeURI().Corpus("/Users/foo")},
   113        {"kythe:///", MakeURI().Corpus("/")},
   114        {"kythe://kythe//branch", MakeURI().Corpus("kythe//branch")},
   115  
   116        // Corpus labels are not cleaned.
   117        {"//a//?lang=foo?path=b/c/..",
   118         MakeURI().Corpus("a//").Path("b").Language("foo")},
   119        {"kythe://a/./b/..//c/#sig",
   120         MakeURI().Signature("sig").Corpus("a/./b/..//c/")},
   121  
   122        // Everything.
   123        {"kythe://bitbucket.org/creachadair/"
   124         "stringset?path=stringset.go?lang=go?root=blah#sig",
   125         MakeURI()
   126             .Signature("sig")
   127             .Corpus("bitbucket.org/creachadair/stringset")
   128             .Root("blah")
   129             .Path("stringset.go")
   130             .Language("go")},
   131  
   132        // Regression: Escape sequences in the corpus specification.
   133        {"kythe://libstdc%2B%2B?lang=c%2B%2B?path=bits/basic_string.h?root=/usr/"
   134         "include/c%2B%2B/4.8",
   135         MakeURI()
   136             .Corpus("libstdc++")
   137             .Path("bits/basic_string.h")
   138             .Root("/usr/include/c++/4.8")
   139             .Language("c++")}};
   140    for (auto& test : tests) {
   141      auto parsed = URI::FromString(test.input);
   142      EXPECT_TRUE(parsed.first) << test.input;
   143      EXPECT_TRUE(parsed.second == test.expect)
   144          << parsed.second.ToString() << " got, expected "
   145          << test.expect.ToString();
   146    }
   147  }
   148  
   149  TEST(KytheUri, ParseErrors) {
   150    auto tests = {
   151        "invalid corpus",
   152        "http://unsupported-scheme",
   153        "?huh=bogus+attribute+key",
   154        "?path=",   // empty query value
   155        "?root=?",  // empty query value
   156        "//a/%x/bad-escaping",
   157        "kythe:///invalid-corpus?blah",
   158        "/another-invalid-corpus",
   159        "random/opaque/failure",
   160    };
   161    for (auto& test : tests) {
   162      auto parsed = URI::FromString(test);
   163      EXPECT_FALSE(parsed.first) << test;
   164    }
   165  }
   166  
   167  TEST(KytheUri, Equality) {
   168    struct {
   169      const char *a, *b;
   170    } equal[] = {
   171        // Various empty equivalencies.
   172        {"", ""},
   173        {"", "kythe:"},
   174        {"kythe://", ""},
   175        {"kythe://", "kythe:"},
   176  
   177        // Order of attributes is normalized.
   178        {"kythe:?root=R?path=P", "kythe://?path=P?root=R"},
   179        {"kythe:?root=R?path=P?lang=L", "kythe://?path=P?lang=L?root=R"},
   180  
   181        // Escaping is respected.
   182        {"kythe:?path=%50", "kythe://?path=P"},
   183        {"kythe:?lang=%4c?path=%50", "kythe://?lang=L?path=P"},
   184  
   185        // Paths are cleaned.
   186        {"kythe://a?path=b/../c#sig", "kythe://a?path=c#sig"},
   187        {"kythe://a?path=b/../d/./e/../../c#sig", "kythe://a?path=c#sig"},
   188        {"//a?path=b/c/../d?lang=%67%6F", "kythe://a?path=b/d?lang=go"}};
   189    for (auto& test : equal) {
   190      auto a_parse = URI::FromString(test.a);
   191      auto b_parse = URI::FromString(test.b);
   192      EXPECT_TRUE(a_parse.first) << test.a;
   193      EXPECT_TRUE(b_parse.first) << test.b;
   194      EXPECT_TRUE(a_parse.second == b_parse.second) << test.a << " != " << test.b;
   195    }
   196    struct {
   197      const char *a, *b;
   198    } nonequal[] = {{"kythe://a", "kythe://a?path=P"},
   199                    {"bogus", "bogus"},
   200                    {"bogus", "kythe://good"},
   201                    {"kythe://good", "bogus"}};
   202    for (auto& test : nonequal) {
   203      auto a_parse = URI::FromString(test.a);
   204      auto b_parse = URI::FromString(test.b);
   205      if (!a_parse.first || !b_parse.first) {
   206        continue;
   207      }
   208      EXPECT_TRUE(a_parse.second != b_parse.second) << test.a << " == " << test.b;
   209    }
   210    EXPECT_TRUE(URI() == URI());
   211    auto empty_parse = URI::FromString("kythe://");
   212    EXPECT_TRUE(empty_parse.first);
   213    EXPECT_TRUE(empty_parse.second == URI());
   214  }
   215  
   216  TEST(KytheUri, RoundTrip) {
   217    auto uri = MakeURI()
   218                   .Signature("magic carpet ride")
   219                   .Corpus("code.google.com/p/go.tools")
   220                   .Path("cmd/godoc/doc.go")
   221                   .Language("go");
   222    auto uri_roundtrip = URI::FromString(uri.uri().ToString());
   223    EXPECT_TRUE(uri_roundtrip.first);
   224    const auto& other_vname = uri_roundtrip.second.v_name();
   225    EXPECT_THAT(uri.v_name(), EqualsProto(other_vname));
   226  }
   227  
   228  TEST(KytheUri, OneSlashRoundTrip) {
   229    auto uri = MakeURI()
   230                   .Signature("/")
   231                   .Corpus("/Users/foo")
   232                   .Path("/Users/foo/bar")
   233                   .Language("go");
   234    auto uri_roundtrip = URI::FromString(uri.uri().ToString());
   235    EXPECT_TRUE(uri_roundtrip.first);
   236    const auto& other_vname = uri_roundtrip.second.v_name();
   237    EXPECT_THAT(uri.v_name(), EqualsProto(other_vname));
   238  }
   239  
   240  TEST(KytheUri, TwoSlashRoundTrip) {
   241    auto uri = MakeURI()
   242                   .Signature("/")
   243                   .Corpus("//Users/foo")
   244                   .Path("/Users/foo/bar")
   245                   .Language("go");
   246    auto uri_roundtrip = URI::FromString(uri.uri().ToString());
   247    EXPECT_TRUE(uri_roundtrip.first);
   248    const auto& other_vname = uri_roundtrip.second.v_name();
   249    EXPECT_THAT(uri.v_name(), EqualsProto(other_vname));
   250  }
   251  
   252  // TODO(zarko): Check the "////////" corpus once we settle whether corpus
   253  // names should be path-cleaned.
   254  
   255  TEST(KytheUri, Strings) {
   256    constexpr char empty[] = "kythe:";
   257    constexpr char canonical[] = "kythe:?lang=L?path=P?root=R";
   258    constexpr char cleaned[] = "kythe://a?path=c#sig";
   259    struct {
   260      const char *input, *want;
   261    } tests[] = {// Empty forms
   262                 {"", empty},
   263                 {"kythe:", empty},
   264                 {"kythe://", empty},
   265                 {"kythe:#", empty},
   266                 {"kythe://#", empty},
   267  
   268                 // Check ordering
   269                 {"kythe:?root=R?path=P?lang=L", canonical},
   270                 {"kythe:?root=R?lang=L?path=P", canonical},
   271                 {"kythe:?lang=L?path=P?root=R", canonical},
   272                 {"kythe://?lang=L?path=P?root=R#", canonical},
   273  
   274                 // Check escaping
   275                 {"kythe://?path=%50", "kythe:?path=P"},
   276                 {"kythe://?path=%2B", "kythe:?path=%2B"},
   277                 {"kythe://?path=a+b", "kythe:?path=a%2Bb"},
   278                 {"kythe://?path=%20", "kythe:?path=%20"},
   279                 {"kythe://?path=a/b", "kythe:?path=a/b"},
   280  
   281                 // Path cleaning
   282                 {"kythe://a?path=b/../c#sig", cleaned},
   283                 {"kythe://a?path=./d/.././c#sig", cleaned},
   284  
   285                 // Regression: Escape sequences in the corpus specification.
   286                 {"kythe://libstdc%2B%2B?path=bits/"
   287                  "basic_string.h?lang=c%2B%2B?root=/usr/include/c%2B%2B/4.8",
   288                  "kythe://libstdc%2B%2B?lang=c%2B%2B?path=bits/"
   289                  "basic_string.h?root=/usr/include/c%2B%2B/4.8"}};
   290    for (const auto& test : tests) {
   291      auto parsed = URI::FromString(test.input);
   292      EXPECT_TRUE(parsed.first);
   293      EXPECT_EQ(test.want, parsed.second.ToString());
   294    }
   295  }
   296  
   297  }  // anonymous namespace
   298  }  // namespace kythe
   299  
   300  int main(int argc, char** argv) {
   301    GOOGLE_PROTOBUF_VERIFY_VERSION;
   302    absl::InitializeLog();
   303    ::testing::InitGoogleTest(&argc, argv);
   304    int result = RUN_ALL_TESTS();
   305    return result;
   306  }