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 }