1 /* 2 * Copyright 2010, Christophe Huriaux 3 * Copyright 2014, Haiku, inc. 4 * Distributed under the terms of the MIT licence 5 */ 6 7 8 #include "UrlTest.h" 9 10 11 #include <cstdlib> 12 #include <cstring> 13 #include <cstdio> 14 15 #include <NetworkKit.h> 16 17 #include <cppunit/TestCaller.h> 18 #include <cppunit/TestSuite.h> 19 20 21 UrlTest::UrlTest() 22 { 23 } 24 25 26 UrlTest::~UrlTest() 27 { 28 } 29 30 31 // Test that parsing a valid URL and converting back to string doesn't alter it 32 void UrlTest::ParseTest() 33 { 34 uint8 testIndex; 35 BUrl testUrl; 36 37 const char* kTestLength[] = 38 { 39 "http://user:pass@www.foo.com:80/path?query#fragment", 40 "http://user:pass@www.foo.com:80/path?query#", 41 "http://user:pass@www.foo.com:80/path?query", 42 "http://user:pass@www.foo.com:80/path?", 43 "http://user:pass@www.foo.com:80/path", 44 "http://user:pass@www.foo.com:80/", 45 "http://user:pass@www.foo.com", 46 "http://user:pass@", 47 "http://www.foo.com", 48 "http://", 49 "http:" 50 }; 51 52 for (testIndex = 0; testIndex < sizeof(kTestLength) / sizeof(const char*); 53 testIndex++) 54 { 55 NextSubTest(); 56 57 testUrl.SetUrlString(kTestLength[testIndex]); 58 CPPUNIT_ASSERT_EQUAL(BString(kTestLength[testIndex]), 59 testUrl.UrlString()); 60 } 61 } 62 63 64 void UrlTest::TestIsValid() 65 { 66 BUrl url("http:"); 67 CPPUNIT_ASSERT_MESSAGE("Created with a scheme but no hierarchical segment.", 68 !url.IsValid()); 69 70 url.SetHost("<invalid>"); 71 CPPUNIT_ASSERT_MESSAGE("Set to an invalid host", !url.IsValid()); 72 73 url.SetUrlString(""); 74 url.SetProtocol("\t \n"); 75 CPPUNIT_ASSERT_MESSAGE("Set a protocol with whitespace", !url.IsValid()); 76 url.SetProtocol("123"); 77 CPPUNIT_ASSERT_MESSAGE("Set an all-digits protocol", !url.IsValid()); 78 79 url.SetUserName("user"); 80 CPPUNIT_ASSERT_MESSAGE("Retain invalid state on user change", 81 !url.IsValid()); 82 url.SetPassword("pass"); 83 CPPUNIT_ASSERT_MESSAGE("Retain invalid state on password change", 84 !url.IsValid()); 85 86 url.SetProtocol("http"); 87 url.SetFragment("fragment"); 88 CPPUNIT_ASSERT_MESSAGE("Only protocol and fragment are set", 89 !url.IsValid()); 90 url.SetFragment("fragment"); 91 url.SetProtocol("http"); 92 CPPUNIT_ASSERT_MESSAGE("Only protocol and fragment are set", 93 !url.IsValid()); 94 } 95 96 97 void UrlTest::TestGettersSetters() 98 { 99 BUrl url; 100 url.SetProtocol("http"); 101 url.SetUserName("user"); 102 url.SetPassword("password"); 103 url.SetHost("example.com"); 104 url.SetPort(8080); 105 url.SetPath("/path"); 106 url.SetRequest("query=value"); 107 url.SetFragment("fragment"); 108 109 CPPUNIT_ASSERT_EQUAL(BString("http"), url.Protocol()); 110 CPPUNIT_ASSERT_EQUAL(BString("user"), url.UserName()); 111 CPPUNIT_ASSERT_EQUAL(BString("password"), url.Password()); 112 CPPUNIT_ASSERT_EQUAL(BString("user:password"), url.UserInfo()); 113 CPPUNIT_ASSERT_EQUAL(BString("example.com"), url.Host()); 114 CPPUNIT_ASSERT_EQUAL(BString("user:password@example.com:8080"), 115 url.Authority()); 116 CPPUNIT_ASSERT_EQUAL(8080, url.Port()); 117 CPPUNIT_ASSERT_EQUAL(BString("/path"), url.Path()); 118 CPPUNIT_ASSERT_EQUAL(BString("query=value"), url.Request()); 119 CPPUNIT_ASSERT_EQUAL(BString("fragment"), url.Fragment()); 120 CPPUNIT_ASSERT_EQUAL(BString( 121 "http://user:password@example.com:8080/path?query=value#fragment"), 122 url.UrlString()); 123 } 124 125 126 void UrlTest::TestNullity() 127 { 128 BUrl url; 129 url.SetProtocol("http"); 130 url.SetHost("example.com"); 131 132 CPPUNIT_ASSERT(url.HasAuthority()); 133 CPPUNIT_ASSERT(url.HasHost()); 134 135 CPPUNIT_ASSERT(!url.HasUserName()); 136 CPPUNIT_ASSERT(!url.HasPassword()); 137 CPPUNIT_ASSERT(!url.HasUserInfo()); 138 CPPUNIT_ASSERT(!url.HasPort()); 139 CPPUNIT_ASSERT(!url.HasPath()); 140 CPPUNIT_ASSERT(!url.HasRequest()); 141 CPPUNIT_ASSERT(!url.HasFragment()); 142 } 143 144 145 void UrlTest::TestCopy() 146 { 147 BUrl url1("http://example.com"); 148 BUrl url2(url1); 149 150 url2.SetHost("www.example.com"); 151 152 CPPUNIT_ASSERT_EQUAL(BString("www.example.com"), url2.Host()); 153 CPPUNIT_ASSERT_EQUAL(BString("http://www.example.com"), url2.UrlString()); 154 CPPUNIT_ASSERT_EQUAL(BString("example.com"), url1.Host()); 155 CPPUNIT_ASSERT_EQUAL(BString("http://example.com"), url1.UrlString()); 156 } 157 158 159 typedef struct 160 { 161 const char* url; 162 163 struct 164 { 165 const char* protocol; 166 const char* userName; 167 const char* password; 168 const char* host; 169 int16 port; 170 const char* path; 171 const char* request; 172 const char* fragment; 173 } expected; 174 } ExplodeTest; 175 176 177 const ExplodeTest kTestExplode[] = 178 // Url 179 // Protocol User Password Hostname Port Path Request Fragment 180 // -------- --------- --------- --------- ---- ---------- ---------- ------------ 181 { 182 { "http://user:pass@host:80/path?query#fragment", 183 { "http", "user", "pass", "host", 80, "/path", "query", "fragment" } }, 184 { "http://www.host.tld/path?query#fragment", 185 { "http", "", "", "www.host.tld",0, "/path", "query", "fragment" } }, 186 { "", 187 { "", "", "", "", 0, "", "", ""} }, 188 { "mailto:John.Doe@example.com", 189 { "mailto", "", "", "", 0, "John.Doe@example.com", "", "" } }, 190 { "mailto:?to=addr1@an.example,addr2@an.example", 191 { "mailto", "", "", "", 0, "", "to=addr1@an.example,addr2@an.example", "" } }, 192 { "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", 193 { "urn", "", "", "", 0, "oasis:names:specification:docbook:dtd:xml:4.1.2", "", "" } }, 194 { "http://www.goodsearch.com/login?return_path=/", 195 { "http", "", "", "www.goodsearch.com", 0, "/login", "return_path=/", "" } }, 196 { "ldap://[2001:db8::7]:389/c=GB?objectClass?one", 197 { "ldap", "", "", "[2001:db8::7]",389,"/c=GB", "objectClass?one", "" } }, 198 { "ldap://[2001:db8::7]/c=GB?objectClass?one", 199 { "ldap", "", "", "[2001:db8::7]",0, "/c=GB", "objectClass?one", "" } }, 200 { "HTTP://example.com.:80/%70a%74%68?a=%31#1%323", 201 { "HTTP", "", "", "example.com.",80, "/%70a%74%68","a=%31","1%323"} }, 202 { "/boot/home/Desktop/index.html", 203 { "", "", "", "", 0, "/boot/home/Desktop/index.html","",""} }, 204 { "//remote.host/boot/home/Desktop", 205 { "", "", "", "remote.host", 0, "/boot/home/Desktop","",""} }, 206 { "tag:haiku-os.org,2020:repositories/haiku/r1beta2/x86_gcc2", 207 { "tag", "", "", "", 0, "haiku-os.org,2020:repositories/haiku/r1beta2/x86_gcc2" } } 208 }; 209 210 void UrlTest::ExplodeImplodeTest() 211 { 212 uint8 testIndex; 213 BUrl testUrl; 214 215 for (testIndex = 0; testIndex < (sizeof(kTestExplode) / sizeof(ExplodeTest)); testIndex++) 216 { 217 NextSubTest(); 218 testUrl.SetUrlString(kTestExplode[testIndex].url); 219 220 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].url), 221 BString(testUrl.UrlString())); 222 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.protocol), 223 BString(testUrl.Protocol())); 224 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.userName), 225 BString(testUrl.UserName())); 226 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.password), 227 BString(testUrl.Password())); 228 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.host), 229 BString(testUrl.Host())); 230 CPPUNIT_ASSERT_EQUAL(kTestExplode[testIndex].expected.port, 231 testUrl.Port()); 232 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.path), 233 BString(testUrl.Path())); 234 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.request), 235 BString(testUrl.Request())); 236 CPPUNIT_ASSERT_EQUAL(BString(kTestExplode[testIndex].expected.fragment), 237 BString(testUrl.Fragment())); 238 } 239 } 240 241 242 void 243 UrlTest::PathOnly() 244 { 245 BUrl test = "lol"; 246 CPPUNIT_ASSERT(test.HasPath()); 247 CPPUNIT_ASSERT_EQUAL(BString("lol"), test.Path()); 248 CPPUNIT_ASSERT_EQUAL(BString("lol"), test.UrlString()); 249 } 250 251 252 void 253 UrlTest::RelativeUriTest() 254 { 255 // http://skew.org/uri/uri%5Ftests.html 256 struct RelativeUrl { 257 const char* base; 258 const char* relative; 259 const char* absolute; 260 }; 261 262 const RelativeUrl tests[] = { 263 // The port must be preserved 264 {"http://host:81/", "/path", "http://host:81/path"}, 265 266 // Tests from http://skew.org/uri/uri_tests.html 267 {"http://example.com/path?query#frag", "", 268 "http://example.com/path?query"}, 269 // The fragment must be dropped when changing the path, but the 270 // query must be preserved 271 {"foo:a/b", "../c", "foo:/c"}, 272 // foo:c would be more intuitive, and is what skew.org tests. 273 // However, foo:/c is what the RFC says we should get. 274 {"foo:a", "foo:.", "foo:"}, 275 {"zz:abc", "/foo/../../../bar", "zz:/bar"}, 276 {"zz:abc", "/foo/../bar", "zz:/bar"}, 277 {"zz:abc", "foo/../../../bar", "zz:/bar"}, 278 // zz:bar would be more intuitive, ... 279 {"zz:abc", "zz:.", "zz:"}, 280 {"http://a/b/c/d;p?q", "/.", "http://a/"}, 281 {"http://a/b/c/d;p?q", "/.foo", "http://a/.foo"}, 282 {"http://a/b/c/d;p?q", ".foo", "http://a/b/c/.foo"}, 283 {"http://a/b/c/d;p?q", "g:h", "g:h"}, 284 285 {"http://a/b/c/d;p?q", "g:h", "g:h"}, 286 {"http://a/b/c/d;p?q", "g", "http://a/b/c/g"}, 287 {"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"}, 288 {"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"}, 289 {"http://a/b/c/d;p?q", "/g", "http://a/g"}, 290 {"http://a/b/c/d;p?q", "//g", "http://g"}, 291 {"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"}, 292 {"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"}, 293 {"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"}, 294 {"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"}, 295 {"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"}, 296 {"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"}, 297 {"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"}, 298 {"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"}, 299 {"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"}, 300 {"http://a/b/c/d;p?q", ".", "http://a/b/c/"}, 301 {"http://a/b/c/d;p?q", "./", "http://a/b/c/"}, 302 {"http://a/b/c/d;p?q", "..", "http://a/b/"}, 303 {"http://a/b/c/d;p?q", "../", "http://a/b/"}, 304 {"http://a/b/c/d;p?q", "../g", "http://a/b/g"}, 305 {"http://a/b/c/d;p?q", "../..", "http://a/"}, 306 {"http://a/b/c/d;p?q", "../../", "http://a/"}, 307 {"http://a/b/c/d;p?q", "../../g", "http://a/g"}, 308 309 // Parsers must be careful in handling cases where there are more 310 // relative path ".." segments than there are hierarchical levels in the 311 // base URI's path. Note that the ".." syntax cannot be used to change 312 // the authority component of a URI. 313 {"http://a/b/c/d;p?q", "../../../g", "http://a/g"}, 314 {"http://a/b/c/d;p?q", "../../../../g", "http://a/g"}, 315 316 // Similarly, parsers must remove the dot-segments "." and ".." when 317 // they are complete components of a path, but not when they are only 318 // part of a segment. 319 {"http://a/b/c/d;p?q", "/./g", "http://a/g"}, 320 {"http://a/b/c/d;p?q", "/../g", "http://a/g"}, 321 {"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."}, 322 {"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"}, 323 {"http://a/b/c/d;p?q", "g..", "http://a/b/c/g.."}, 324 {"http://a/b/c/d;p?q", "..g", "http://a/b/c/..g"}, 325 326 // Less likely are cases where the relative URI reference uses 327 // unnecessary or nonsensical forms of the "." and ".." complete path 328 // segments. 329 {"http://a/b/c/d;p?q", "./../g", "http://a/b/g"}, 330 {"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"}, 331 {"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"}, 332 {"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"}, 333 {"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"}, 334 {"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"}, 335 336 // Some applications fail to separate the reference's query and/or 337 // fragment components from a relative path before merging it with the 338 // base path and removing dot-segments. This error is rarely noticed, 339 // since typical usage of a fragment never includes the hierarchy ("/") 340 // character, and the query component is not normally used within 341 // relative references. 342 {"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"}, 343 {"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"}, 344 {"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"}, 345 {"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"}, 346 347 // Some parsers allow the scheme name to be present in a relative URI 348 // reference if it is the same as the base URI scheme. This is 349 // considered to be a loophole in prior specifications of partial URI 350 // [RFC1630]. Its use should be avoided, but is allowed for backward 351 // compatibility. 352 {"http://a/b/c/d;p?q", "http:g", "http:g"}, 353 {"http://a/b/c/d;p?q", "http:", "http:"}, 354 355 {"http://a/b/c/d;p?q", "./g:h", "http://a/b/c/g:h"}, 356 {"http://a/b/c/d;p?q", "/a/b/c/./../../g", "http://a/a/g"}, 357 358 {"http://a/b/c/d;p?q=1/2", "g", "http://a/b/c/g"}, 359 {"http://a/b/c/d;p?q=1/2", "./g", "http://a/b/c/g"}, 360 {"http://a/b/c/d;p?q=1/2", "g/", "http://a/b/c/g/"}, 361 {"http://a/b/c/d;p?q=1/2", "/g", "http://a/g"}, 362 {"http://a/b/c/d;p?q=1/2", "//g", "http://g"}, 363 {"http://a/b/c/d;p?q=1/2", "?y", "http://a/b/c/d;p?y"}, 364 {"http://a/b/c/d;p?q=1/2", "g?y", "http://a/b/c/g?y"}, 365 {"http://a/b/c/d;p?q=1/2", "g?y/./x", "http://a/b/c/g?y/./x"}, 366 {"http://a/b/c/d;p?q=1/2", "g?y/../x", "http://a/b/c/g?y/../x"}, 367 {"http://a/b/c/d;p?q=1/2", "g#s", "http://a/b/c/g#s"}, 368 {"http://a/b/c/d;p?q=1/2", "g#s/./x", "http://a/b/c/g#s/./x"}, 369 {"http://a/b/c/d;p?q=1/2", "g#s/../x", "http://a/b/c/g#s/../x"}, 370 {"http://a/b/c/d;p?q=1/2", "./", "http://a/b/c/"}, 371 {"http://a/b/c/d;p?q=1/2", "../", "http://a/b/"}, 372 {"http://a/b/c/d;p?q=1/2", "../g", "http://a/b/g"}, 373 {"http://a/b/c/d;p?q=1/2", "../../", "http://a/"}, 374 {"http://a/b/c/d;p?q=1/2", "../../g", "http://a/g"}, 375 376 {"http://a/b/c/d;p=1/2?q", "g", "http://a/b/c/d;p=1/g"}, 377 {"http://a/b/c/d;p=1/2?q", "./g", "http://a/b/c/d;p=1/g"}, 378 {"http://a/b/c/d;p=1/2?q", "g/", "http://a/b/c/d;p=1/g/"}, 379 {"http://a/b/c/d;p=1/2?q", "g?y", "http://a/b/c/d;p=1/g?y"}, 380 {"http://a/b/c/d;p=1/2?q", ";x", "http://a/b/c/d;p=1/;x"}, 381 {"http://a/b/c/d;p=1/2?q", "g;x", "http://a/b/c/d;p=1/g;x"}, 382 {"http://a/b/c/d;p=1/2?q", "g;x=1/./y", "http://a/b/c/d;p=1/g;x=1/y"}, 383 {"http://a/b/c/d;p=1/2?q", "g;x=1/../y", "http://a/b/c/d;p=1/y"}, 384 {"http://a/b/c/d;p=1/2?q", "./", "http://a/b/c/d;p=1/"}, 385 {"http://a/b/c/d;p=1/2?q", "../", "http://a/b/c/"}, 386 {"http://a/b/c/d;p=1/2?q", "../g", "http://a/b/c/g"}, 387 {"http://a/b/c/d;p=1/2?q", "../../", "http://a/b/"}, 388 {"http://a/b/c/d;p=1/2?q", "../../g", "http://a/b/g"}, 389 390 // Empty host and directory 391 {"fred:///s//a/b/c", "g:h", "g:h"}, 392 {"fred:///s//a/b/c", "g", "fred:///s//a/b/g"}, 393 {"fred:///s//a/b/c", "./g", "fred:///s//a/b/g"}, 394 {"fred:///s//a/b/c", "g/", "fred:///s//a/b/g/"}, 395 {"fred:///s//a/b/c", "/g", "fred:///g"}, 396 {"fred:///s//a/b/c", "//g", "fred://g"}, 397 {"fred:///s//a/b/c", "//g/x", "fred://g/x"}, 398 {"fred:///s//a/b/c", "///g", "fred:///g"}, 399 {"fred:///s//a/b/c", "./", "fred:///s//a/b/"}, 400 {"fred:///s//a/b/c", "../", "fred:///s//a/"}, 401 {"fred:///s//a/b/c", "../g", "fred:///s//a/g"}, 402 {"fred:///s//a/b/c", "../..", "fred:///s//"}, 403 {"fred:///s//a/b/c", "../../g", "fred:///s//g"}, 404 {"fred:///s//a/b/c", "../../../g", "fred:///s/g"}, 405 {"fred:///s//a/b/c", "../../../g", "fred:///s/g"}, 406 407 {"http:///s//a/b/c", "g:h", "g:h"}, 408 {"http:///s//a/b/c", "g", "http:///s//a/b/g"}, 409 {"http:///s//a/b/c", "./g", "http:///s//a/b/g"}, 410 {"http:///s//a/b/c", "g/", "http:///s//a/b/g/"}, 411 {"http:///s//a/b/c", "/g", "http:///g"}, 412 {"http:///s//a/b/c", "//g", "http://g"}, 413 {"http:///s//a/b/c", "//g/x", "http://g/x"}, 414 {"http:///s//a/b/c", "///g", "http:///g"}, 415 {"http:///s//a/b/c", "./", "http:///s//a/b/"}, 416 {"http:///s//a/b/c", "../", "http:///s//a/"}, 417 {"http:///s//a/b/c", "../g", "http:///s//a/g"}, 418 {"http:///s//a/b/c", "../..", "http:///s//"}, 419 {"http:///s//a/b/c", "../../g", "http:///s//g"}, 420 {"http:///s//a/b/c", "../../../g", "http:///s/g"}, 421 {"http:///s//a/b/c", "../../../g", "http:///s/g"}, 422 423 {"foo:xyz", "bar:abc", "bar:abc"}, 424 {"http://example/x/y/z","../abc", "http://example/x/abc"}, 425 {"http://example2/x/y/z","http://example/x/abc","http://example/x/abc"}, 426 {"http://ex/x/y/z", "../r", "http://ex/x/r"}, 427 {"http://ex/x/y", "q/r", "http://ex/x/q/r"}, 428 {"http://ex/x/y", "q/r#s", "http://ex/x/q/r#s"}, 429 {"http://ex/x/y", "q/r#s/t", "http://ex/x/q/r#s/t"}, 430 {"http://ex/x/y", "ftp://ex/x/q/r", "ftp://ex/x/q/r"}, 431 {"http://ex/x/y", "", "http://ex/x/y"}, 432 {"http://ex/x/y/", "", "http://ex/x/y/"}, 433 {"http://ex/x/y/pdq", "", "http://ex/x/y/pdq"}, 434 {"http://ex/x/y/", "z/", "http://ex/x/y/z/"}, 435 436 {"file:/swap/test/animal.rdf", "#Animal", 437 "file:/swap/test/animal.rdf#Animal"}, 438 {"file:/e/x/y/z", "../abc", "file:/e/x/abc"}, 439 {"file:/example2/x/y/z","/example/x/abc", "file:/example/x/abc"}, 440 {"file:/e/x/y/z", "../r", "file:/e/x/r"}, 441 {"file:/e/x/y/z", "/r", "file:/r"}, 442 {"file:/e/x/y", "q/r", "file:/e/x/q/r"}, 443 {"file:/e/x/y", "q/r#s", "file:/e/x/q/r#s"}, 444 {"file:/e/x/y", "q/r#", "file:/e/x/q/r#"}, 445 {"file:/e/x/y", "q/r#s/t", "file:/e/x/q/r#s/t"}, 446 {"file:/e/x/y", "ftp://ex/x/q/r", "ftp://ex/x/q/r"}, 447 {"file:/e/x/y", "", "file:/e/x/y"}, 448 {"file:/e/x/y/", "", "file:/e/x/y/"}, 449 {"file:/e/x/y/pdq", "", "file:/e/x/y/pdq"}, 450 {"file:/e/x/y/", "z/", "file:/e/x/y/z/"}, 451 {"file:/devel/WWW/2000/10/swap/test/reluri-1.n3", 452 "file://meetings.example.com/cal#m1", 453 "file://meetings.example.com/cal#m1"}, 454 {"file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3", 455 "file://meetings.example.com/cal#m1", 456 "file://meetings.example.com/cal#m1"}, 457 {"file:/some/dir/foo", "./#blort", "file:/some/dir/#blort"}, 458 {"file:/some/dir/foo", "./#", "file:/some/dir/#"}, 459 460 {"http://example/x/abc.efg", "./", "http://example/x/"}, 461 {"http://example2/x/y/z", "//example/x/abc","http://example/x/abc"}, 462 {"http://ex/x/y/z", "/r", "http://ex/r"}, 463 {"http://ex/x/y", "./q:r", "http://ex/x/q:r"}, 464 {"http://ex/x/y", "./p=q:r", "http://ex/x/p=q:r"}, 465 {"http://ex/x/y?pp/qq", "?pp/rr", "http://ex/x/y?pp/rr"}, 466 {"http://ex/x/y?pp/qq", "y/z", "http://ex/x/y/z"}, 467 468 {"mailto:local", "local/qual@domain.org#frag", 469 "mailto:local/qual@domain.org#frag"}, 470 {"mailto:local/qual1@domain1.org", "more/qual2@domain2.org#frag", 471 "mailto:local/more/qual2@domain2.org#frag"}, 472 473 {"http://ex/x/y?q", "y?q", "http://ex/x/y?q"}, 474 {"http://ex?p", "x/y?q", "http://ex/x/y?q"}, 475 {"foo:a/b", "c/d", "foo:a/c/d"}, 476 {"foo:a/b", "/c/d", "foo:/c/d"}, 477 {"foo:a/b?c#d", "", "foo:a/b?c"}, 478 {"foo:a", "b/c", "foo:b/c"}, 479 {"foo:/a/y/z", "../b/c", "foo:/a/b/c"}, 480 {"foo:a", "./b/c", "foo:b/c"}, 481 {"foo:a", "/./b/c", "foo:/b/c"}, 482 {"foo://a//b/c", "../../d", "foo://a/d"}, 483 {"foo:a", ".", "foo:"}, 484 {"foo:a", "..", "foo:"}, 485 486 {"http://example/x/y%2Fz", "abc", "http://example/x/abc"}, 487 {"http://example/a/x/y/z", "../../x%2Fabc", "http://example/a/x%2Fabc"}, 488 {"http://example/a/x/y%2Fz", "../x%2Fabc", "http://example/a/x%2Fabc"}, 489 {"http://example/x%2Fy/z", "abc", "http://example/x%2Fy/abc"}, 490 {"http://ex/x/y", "q%3Ar", "http://ex/x/q%3Ar"}, 491 {"http://example/x/y%2Fz", "/x%2Fabc", "http://example/x%2Fabc"}, 492 {"http://example/x/y/z", "/x%2Fabc", "http://example/x%2Fabc"}, 493 494 {"mailto:local1@domain1?query1", "local2@domain2", 495 "mailto:local2@domain2"}, 496 {"mailto:local1@domain1", "local2@domain2?query2", 497 "mailto:local2@domain2?query2"}, 498 {"mailto:local1@domain1?query1", "local2@domain2?query2", 499 "mailto:local2@domain2?query2"}, 500 {"mailto:local@domain?query1", "?query2", 501 "mailto:local@domain?query2"}, 502 {"mailto:?query1", "local2@domain2?query2", 503 "mailto:local2@domain2?query2"}, 504 {"mailto:local1@domain1?query1", "?query2", 505 "mailto:local1@domain1?query2"}, 506 507 {"foo:bar", "http://example/a/b?c/../d", "http://example/a/b?c/../d"}, 508 {"foo:bar", "http://example/a/b#c/../d", "http://example/a/b#c/../d"}, 509 {"http://example.org/base/uri", "http:this", "http:this"}, 510 {"http:base", "http:this", "http:this"}, 511 {"f:/a", ".//g", "f://g"}, 512 {"f://example.org/base/a", "b/c//d/e", "f://example.org/base/b/c//d/e"}, 513 {"mid:m@example.ord/c@example.org", "m2@example.ord/c2@example.org", 514 "mid:m@example.ord/m2@example.ord/c2@example.org"}, 515 {"file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/", "mini1.xml", 516 "file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml"}, 517 {"foo:a/y/z", "../b/c", "foo:a/b/c"}, 518 {"foo:", "b", "foo:b"}, 519 {"foo://a", "b", "foo://a/b"}, 520 {"foo://a?q", "b", "foo://a/b"}, 521 {"foo://a", "b?q", "foo://a/b?q"}, 522 {"foo://a?r", "b?q", "foo://a/b?q"}, 523 }; 524 525 BString message(" Base: "); 526 for (unsigned int index = 0; index < sizeof(tests) / sizeof(RelativeUrl); 527 index++) 528 { 529 NextSubTest(); 530 531 BUrl baseUrl(tests[index].base); 532 533 message.Truncate(7, true); 534 message << tests[index].base; 535 message << " Relative: "; 536 message << tests[index].relative; 537 538 CPPUNIT_ASSERT_EQUAL_MESSAGE(message.String(), 539 BString(tests[index].absolute), 540 BUrl(baseUrl, tests[index].relative).UrlString()); 541 } 542 } 543 544 545 void 546 UrlTest::IDNTest() 547 { 548 // http://www.w3.org/2004/04/uri-rel-test.html 549 // TODO We need to decide wether to store them as UTF-8 or IDNA/punycode. 550 551 struct Test { 552 const char* escaped; 553 const char* decoded; 554 }; 555 556 Test tests[] = { 557 { "http://www.w%33.org", "http://www.w3.org" }, 558 { "http://r%C3%A4ksm%C3%B6rg%C3%A5s.josefsson.org", 559 "http://xn--rksmrgs-5wao1o.josefsson.org" }, 560 { "http://%E7%B4%8D%E8%B1%86.w3.mag.keio.ac.jp", 561 "http://xn--99zt52a.w3.mag.keio.ac.jp" }, 562 { "http://www.%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA" 563 "%E3%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3" 564 "%82%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82" 565 "%81%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0" 566 "%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3" 567 "%81%9F%E3%82%8A%E3%81%AA%E3%81%84.w3.mag.keio.ac.jp/", 568 "http://www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9b" 569 "ya3kc6lra.w3.mag.keio.ac.jp/" }, 570 571 { "http://%E3%81%BB%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3" 572 "%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82" 573 "%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81" 574 "%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3" 575 "%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81" 576 "%9F%E3%82%8A%E3%81%AA%E3%81%84.%E3%81%BB%E3%82%93%E3%81%A8%E3%81" 577 "%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81%84%E3%82%8F%E3%81%91%E3%81%AE" 578 "%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E3%81%A9%E3%82%81%E3" 579 "%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE%E3%82%89%E3%81%B9%E3%82" 580 "%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3%81%8F%E3%81%97%E3%81%AA" 581 "%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81%AA%E3%81%84.%E3%81%BB" 582 "%E3%82%93%E3%81%A8%E3%81%86%E3%81%AB%E3%81%AA%E3%81%8C%E3%81%84%E3" 583 "%82%8F%E3%81%91%E3%81%AE%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81" 584 "%84%E3%81%A9%E3%82%81%E3%81%84%E3%82%93%E3%82%81%E3%81%84%E3%81%AE" 585 "%E3%82%89%E3%81%B9%E3%82%8B%E3%81%BE%E3%81%A0%E3%81%AA%E3%81%8C%E3" 586 "%81%8F%E3%81%97%E3%81%AA%E3%81%84%E3%81%A8%E3%81%9F%E3%82%8A%E3%81" 587 "%AA%E3%81%84.w3.mag.keio.ac.jp/", 588 "http://xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3k" 589 "c6lra.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6" 590 "lra.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3fg11amb5gzdb4wi9bya3kc6lr" 591 "a.w3.mag.keio.ac.jp/" }, 592 { NULL, NULL } 593 }; 594 595 for (int i = 0; tests[i].escaped != NULL; i++) 596 { 597 NextSubTest(); 598 599 BUrl url(tests[i].escaped); 600 url.UrlDecode(); 601 602 BUrl idn(tests[i].decoded); 603 status_t success = idn.IDNAToUnicode(); 604 605 CPPUNIT_ASSERT_EQUAL(B_OK, success); 606 CPPUNIT_ASSERT_EQUAL(url.UrlString(), idn.UrlString()); 607 } 608 } 609 610 611 /* static */ void 612 UrlTest::AddTests(BTestSuite& parent) 613 { 614 CppUnit::TestSuite& suite = *new CppUnit::TestSuite("UrlTest"); 615 616 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::ParseTest", 617 &UrlTest::ParseTest)); 618 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::TestIsValid", 619 &UrlTest::TestIsValid)); 620 suite.addTest(new CppUnit::TestCaller<UrlTest>( 621 "UrlTest::TestGettersSetters", &UrlTest::TestGettersSetters)); 622 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::TestNullity", 623 &UrlTest::TestNullity)); 624 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::TestCopy", 625 &UrlTest::TestCopy)); 626 suite.addTest(new CppUnit::TestCaller<UrlTest>( 627 "UrlTest::ExplodeImplodeTest", &UrlTest::ExplodeImplodeTest)); 628 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::PathOnly", 629 &UrlTest::PathOnly)); 630 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::RelativeUriTest", 631 &UrlTest::RelativeUriTest)); 632 suite.addTest(new CppUnit::TestCaller<UrlTest>("UrlTest::IDNTest", 633 &UrlTest::IDNTest)); 634 635 parent.addTest("UrlTest", &suite); 636 } 637