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
UrlTest()21 UrlTest::UrlTest()
22 {
23 }
24
25
~UrlTest()26 UrlTest::~UrlTest()
27 {
28 }
29
30
31 // Test that parsing a valid URL and converting back to string doesn't alter it
ParseTest()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
TestIsValid()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
TestGettersSetters()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
TestNullity()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
TestCopy()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
ExplodeImplodeTest()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
PathOnly()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
RelativeUriTest()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
IDNTest()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
AddTests(BTestSuite & parent)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