Add ensure_required_types constructor
[platform/upstream/glib.git] / glib / tests / uri.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include <glib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 typedef struct
33 {
34   char *filename;
35   char *hostname;
36   char *expected_result;
37   gint expected_error; /* If failed */
38 } FileToUriTest;
39
40 FileToUriTest
41 file_to_uri_tests[] = {
42   { "/etc", NULL, "file:///etc", 0 },
43   { "/etc", "", "file:///etc", 0 },
44   { "/etc", "otherhost", "file://otherhost/etc", 0 },
45 #ifdef G_OS_WIN32
46   { "/etc", "localhost", "file:///etc", 0 },
47   { "c:\\windows", NULL, "file:///c:/windows", 0 },
48   { "c:\\windows", "localhost", "file:///c:/windows", 0 },
49   { "c:\\windows", "otherhost", "file://otherhost/c:/windows", 0 },
50   { "\\\\server\\share\\dir", NULL, "file:////server/share/dir", 0 },
51   { "\\\\server\\share\\dir", "localhost", "file:////server/share/dir", 0 },
52 #else
53   { "/etc", "localhost", "file://localhost/etc", 0 },
54   { "c:\\windows", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH}, /* it's important to get this error on Unix */
55   { "c:\\windows", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
56   { "c:\\windows", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
57 #endif
58   { "etc", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
59 #ifndef G_PLATFORM_WIN32
60   { "/etc/\xE5\xE4\xF6", NULL, "file:///etc/%E5%E4%F6", 0 },
61   { "/etc/\xC3\xB6\xC3\xA4\xC3\xA5", NULL, "file:///etc/%C3%B6%C3%A4%C3%A5", 0 },
62 #endif
63   { "/etc", "\xC3\xB6\xC3\xA4\xC3\xA5", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
64   { "/etc", "\xE5\xE4\xF6", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
65   { "/etc/file with #%", NULL, "file:///etc/file%20with%20%23%25", 0 },
66   { "", NULL, NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
67   { "", "", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
68   { "", "localhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
69   { "", "otherhost", NULL, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH},
70   { "/0123456789", NULL, "file:///0123456789", 0 },
71   { "/ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL, "file:///ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 },
72   { "/abcdefghijklmnopqrstuvwxyz", NULL, "file:///abcdefghijklmnopqrstuvwxyz", 0 },
73   { "/-_.!~*'()", NULL, "file:///-_.!~*'()", 0 },
74 #ifdef G_OS_WIN32
75   /* As '\\' is a path separator on Win32, it gets turned into '/' in the URI */
76   { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B/%5D%5E%60%7B%7C%7D%7F", 0 },
77 #else
78   /* On Unix, '\\' is a normal character in the file name */
79   { "/\"#%<>[\\]^`{|}\x7F", NULL, "file:///%22%23%25%3C%3E%5B%5C%5D%5E%60%7B%7C%7D%7F", 0 },
80 #endif
81   { "/;@+$,", NULL, "file:///%3B@+$,", 0 },
82   /* This and some of the following are of course as such illegal file names on Windows,
83    * and would not occur in real life.
84    */
85   { "/:", NULL, "file:///:", 0 },
86   { "/?&=", NULL, "file:///%3F&=", 0 },
87   { "/", "0123456789-", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
88   { "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/", 0 },
89   { "/", "abcdefghijklmnopqrstuvwxyz", "file://abcdefghijklmnopqrstuvwxyz/", 0 },
90   { "/", "_.!~*'()", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
91   { "/", "\"#%<>[\\]^`{|}\x7F", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
92   { "/", ";?&=+$,", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
93   { "/", "/", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
94   { "/", "@:", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
95   { "/", "\x80\xFF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
96   { "/", "\xC3\x80\xC3\xBF", NULL, G_CONVERT_ERROR_ILLEGAL_SEQUENCE},
97 };
98
99
100 typedef struct
101 {
102   char *uri;
103   char *expected_filename;
104   char *expected_hostname;
105   gint expected_error; /* If failed */
106 } FileFromUriTest;
107
108 FileFromUriTest
109 file_from_uri_tests[] = {
110   { "file:///etc", "/etc", NULL, 0 },
111   { "FILE:///etc", "/etc", NULL, 0 },
112   { "file:/etc", "/etc", NULL, 0 },
113 #ifdef G_OS_WIN32
114   /* On Win32 we don't return "localhost" hostames, just in case
115    * it isn't recognized anyway.
116    */
117   { "file://localhost/etc", "/etc", NULL, 0 },
118   { "file://localhost/etc/%23%25%20file", "/etc/#% file", NULL, 0 },
119   { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", NULL, 0 },
120   { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", NULL, 0 },
121 #else
122   { "file://localhost/etc", "/etc", "localhost", 0 },
123   { "file://localhost/etc/%23%25%20file", "/etc/#% file", "localhost", 0 },
124   { "file://localhost/\xE5\xE4\xF6", "/\xe5\xe4\xf6", "localhost", 0 },
125   { "file://localhost/%E5%E4%F6", "/\xe5\xe4\xf6", "localhost", 0 },
126 #endif
127   { "file://otherhost/etc", "/etc", "otherhost", 0 },
128   { "file://otherhost/etc/%23%25%20file", "/etc/#% file", "otherhost", 0 },
129   { "file://%C3%B6%C3%A4%C3%A5/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
130   { "file:////etc/%C3%B6%C3%C3%C3%A5", "//etc/\xc3\xb6\xc3\xc3\xc3\xa5", NULL, 0 },
131   { "file://\xE5\xE4\xF6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
132   { "file://%E5%E4%F6/etc", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
133   { "file:///some/file?query", "/some/file", NULL, 0 },
134   { "file:///some/file#bad", "/some/file", NULL, 0 },
135   { "file://some", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
136   { "", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
137   { "file:test", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
138   { "http://www.yahoo.com/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
139   { "file:////etc", "//etc", NULL, 0 },
140   { "file://///etc", "///etc", NULL, 0 },
141 #ifdef G_OS_WIN32
142   /* URIs with backslashes come from some nonstandard application, but accept them anyhow */
143   { "file:///c:\\foo", "c:\\foo", NULL, 0 },
144   { "file:///c:/foo\\bar", "c:\\foo\\bar", NULL, 0 },
145   /* Accept also the old Netscape drive-letter-and-vertical bar convention */
146   { "file:///c|/foo", "c:\\foo", NULL, 0 },
147   { "file:////server/share/dir", "\\\\server\\share\\dir", NULL, 0 },
148   { "file://localhost//server/share/foo", "\\\\server\\share\\foo", NULL, 0 },
149   { "file://otherhost//server/share/foo", "\\\\server\\share\\foo", "otherhost", 0 },
150 #else
151   { "file:///c:\\foo", "/c:\\foo", NULL, 0 },
152   { "file:///c:/foo", "/c:/foo", NULL, 0 },
153   { "file:////c:/foo", "//c:/foo",  NULL, 0 },
154 #endif
155   { "file://0123456789/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
156   { "file://ABCDEFGHIJKLMNOPQRSTUVWXYZ/", "/", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0 },
157   { "file://abcdefghijklmnopqrstuvwxyz/", "/", "abcdefghijklmnopqrstuvwxyz", 0 },
158   { "file://-_.!~*'()/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
159   { "file://\"<>[\\]^`{|}\x7F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
160   { "file://;?&=+$,/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
161   { "file://%C3%80%C3%BF/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
162   { "file://@/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
163   { "file://:/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
164   { "file://#/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
165   { "file://%23/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
166   { "file://%2F/", NULL, NULL, G_CONVERT_ERROR_BAD_URI},
167 };
168
169 static void
170 run_file_to_uri_tests (void)
171 {
172   gsize i;
173   gchar *res;
174   GError *error;
175
176   for (i = 0; i < G_N_ELEMENTS (file_to_uri_tests); i++)
177     {
178       error = NULL;
179       res = g_filename_to_uri (file_to_uri_tests[i].filename,
180                                file_to_uri_tests[i].hostname,
181                                &error);
182
183       if (res)
184         g_assert_cmpstr (res, ==, file_to_uri_tests[i].expected_result);
185       else
186         g_assert_error (error, G_CONVERT_ERROR, file_to_uri_tests[i].expected_error);
187
188       g_free (res);
189       g_clear_error (&error);
190     }
191 }
192
193 static void
194 run_file_from_uri_tests (void)
195 {
196   gsize i;
197   gchar *res;
198   gchar *hostname;
199   GError *error;
200
201   for (i = 0; i < G_N_ELEMENTS (file_from_uri_tests); i++)
202     {
203       error = NULL;
204       res = g_filename_from_uri (file_from_uri_tests[i].uri,
205                                  &hostname,
206                                  &error);
207
208 #ifdef G_OS_WIN32
209       if (file_from_uri_tests[i].expected_filename)
210         {
211           gchar *p, *slash;
212           p = file_from_uri_tests[i].expected_filename =
213             g_strdup (file_from_uri_tests[i].expected_filename);
214           while ((slash = strchr (p, '/')) != NULL)
215             {
216               *slash = '\\';
217               p = slash + 1;
218             }
219         }
220 #endif
221       if (res)
222         g_assert_cmpstr (res, ==, file_from_uri_tests[i].expected_filename);
223       else
224         g_assert_error (error, G_CONVERT_ERROR, file_from_uri_tests[i].expected_error);
225       g_assert_cmpstr (hostname, ==, file_from_uri_tests[i].expected_hostname);
226
227       g_free (res);
228       g_free (hostname);
229       g_clear_error (&error);
230     }
231 }
232
233 static gint
234 safe_strcmp_filename (const gchar *a, const gchar *b)
235 {
236 #ifndef G_OS_WIN32
237   return g_strcmp0 (a, b);
238 #else
239   if (!a || !b)
240     return g_strcmp0 (a, b);
241   else
242     {
243       while (*a && *b)
244         {
245           if ((G_IS_DIR_SEPARATOR (*a) && G_IS_DIR_SEPARATOR (*b)) ||
246               *a == *b)
247             a++, b++;
248           else
249             return (*a - *b);
250         }
251       return (*a - *b);
252     }
253 #endif
254 }
255
256 static gint
257 safe_strcmp_hostname (const gchar *a, const gchar *b)
258 {
259   if (a == NULL)
260     a = "";
261   if (b == NULL)
262     b = "";
263 #ifndef G_OS_WIN32
264   return strcmp (a, b);
265 #else
266   if (strcmp (a, "localhost") == 0 && !*b)
267     return 0;
268   else
269     return strcmp (a, b);
270 #endif
271 }
272
273 static void
274 run_file_roundtrip_tests (void)
275 {
276   gsize i;
277   gchar *uri, *hostname, *res;
278   GError *error;
279
280   for (i = 0; i < G_N_ELEMENTS (file_to_uri_tests); i++)
281     {
282       if (file_to_uri_tests[i].expected_error != 0)
283         continue;
284
285       error = NULL;
286       uri = g_filename_to_uri (file_to_uri_tests[i].filename,
287                                file_to_uri_tests[i].hostname,
288                                &error);
289       g_assert_no_error (error);
290
291       hostname = NULL;
292       res = g_filename_from_uri (uri, &hostname, &error);
293       g_assert_no_error (error);
294
295       g_assert_cmpint (safe_strcmp_filename (file_to_uri_tests[i].filename, res), ==, 0);
296       g_assert_cmpint (safe_strcmp_hostname (file_to_uri_tests[i].hostname, hostname), ==, 0);
297       g_free (res);
298       g_free (uri);
299       g_free (hostname);
300     }
301 }
302
303 static void
304 run_uri_list_tests (void)
305 {
306   /* straight from the RFC */
307   gchar *list =
308     "# urn:isbn:0-201-08372-8\r\n"
309     "http://www.huh.org/books/foo.html\r\n"
310     "http://www.huh.org/books/foo.pdf   \r\n"
311     "   ftp://ftp.foo.org/books/foo.txt\r\n";
312   gchar *expected_uris[] = {
313     "http://www.huh.org/books/foo.html",
314     "http://www.huh.org/books/foo.pdf",
315     "ftp://ftp.foo.org/books/foo.txt"
316   };
317
318   gchar **uris;
319   gint j;
320
321   uris = g_uri_list_extract_uris (list);
322   g_assert_cmpint (g_strv_length (uris), ==, 3);
323
324   for (j = 0; j < 3; j++)
325     g_assert_cmpstr (uris[j], ==, expected_uris[j]);
326
327   g_strfreev (uris);
328
329   uris = g_uri_list_extract_uris ("# just hot air\r\n# more hot air");
330   g_assert_cmpint (g_strv_length (uris), ==, 0);
331   g_strfreev (uris);
332 }
333
334 static void
335 test_uri_unescape_string (void)
336 {
337   const struct
338     {
339       /* Inputs */
340       const gchar *escaped;  /* (nullable) */
341       const gchar *illegal_characters;  /* (nullable) */
342       /* Outputs */
343       const gchar *expected_unescaped;  /* (nullable) */
344     }
345   tests[] =
346     {
347       { "%2Babc %4F", NULL, "+abc O" },
348       { "%2Babc %4F", "+", NULL },
349       { "%00abc %4F", "+/", NULL },
350       { "/cursors/none.png", "/", "/cursors/none.png" },
351       { "/cursors%2fbad-subdir/none.png", "/", NULL },
352       { "%0", NULL, NULL },
353       { "%ra", NULL, NULL },
354       { "%2r", NULL, NULL },
355       { "Timm B\303\244der", NULL, "Timm B\303\244der" },
356       { NULL, NULL, NULL },  /* actually a valid test, not a delimiter */
357     };
358   gsize i;
359
360   for (i = 0; i < G_N_ELEMENTS (tests); i++)
361     {
362       gchar *s = NULL;
363
364       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
365
366       s = g_uri_unescape_string (tests[i].escaped, tests[i].illegal_characters);
367       g_assert_cmpstr (s, ==, tests[i].expected_unescaped);
368       g_free (s);
369     }
370 }
371
372 static void
373 test_uri_unescape_bytes (gconstpointer test_data)
374 {
375   GError *error = NULL;
376   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
377   const struct
378     {
379       /* Inputs */
380       const gchar *escaped;  /* (nullable) */
381       const gchar *illegal;
382       /* Outputs */
383       gssize expected_unescaped_len;  /* -1 => error expected */
384       const guint8 *expected_unescaped;  /* (nullable) */
385     }
386   tests[] =
387     {
388       { "%00%00", NULL, 2, (const guint8 *) "\x00\x00" },
389       { "/cursors/none.png", "/", 17, (const guint8 *) "/cursors/none.png" },
390       { "/cursors%2fbad-subdir/none.png", "/", -1, NULL },
391       { "%%", NULL, -1, NULL },
392       { "%", NULL, -1, NULL },
393     };
394   gsize i;
395
396   for (i = 0; i < G_N_ELEMENTS (tests); i++)
397     {
398       gssize escaped_len = 0;
399       gchar *escaped = NULL;
400       GBytes *bytes = NULL;
401
402       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, tests[i].escaped);
403
404       /* The tests get run twice: once with the length unspecified, using a
405        * nul-terminated string; and once with the length specified and a copy of
406        * the string with the trailing nul explicitly removed (to help catch
407        * buffer overflows). */
408       if (use_nul_terminated)
409         {
410           escaped_len = -1;
411           escaped = g_strdup (tests[i].escaped);
412         }
413       else
414         {
415           escaped_len = strlen (tests[i].escaped);  /* no trailing nul */
416           escaped = g_memdup2 (tests[i].escaped, escaped_len);
417         }
418
419       bytes = g_uri_unescape_bytes (escaped, escaped_len, tests[i].illegal, &error);
420
421       if (tests[i].expected_unescaped_len < 0)
422         {
423           g_assert_null (bytes);
424           g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
425           g_clear_error (&error);
426         }
427       else
428         {
429           g_assert_no_error (error);
430           g_assert_cmpmem (g_bytes_get_data (bytes, NULL),
431                            g_bytes_get_size (bytes),
432                            tests[i].expected_unescaped,
433                            tests[i].expected_unescaped_len);
434         }
435
436       g_clear_pointer (&bytes, g_bytes_unref);
437       g_free (escaped);
438     }
439 }
440
441 static void
442 test_uri_unescape_segment (void)
443 {
444   const gchar *escaped_segment = "%2Babc %4F---";
445   gchar *s = NULL;
446
447   s = g_uri_unescape_segment (escaped_segment, escaped_segment + 10, NULL);
448   g_assert_cmpstr (s, ==, "+abc O");
449   g_free (s);
450
451   s = g_uri_unescape_segment ("%2Babc%00cde", NULL, NULL);
452   g_assert_null (s);
453 }
454
455 static void
456 test_uri_escape_string (void)
457 {
458   const struct
459     {
460       /* Inputs */
461       const gchar *unescaped;
462       const gchar *reserved_chars_allowed;
463       gboolean allow_utf8;
464       /* Outputs */
465       const gchar *expected_escaped;
466     }
467   tests[] =
468     {
469       { "abcdefgABCDEFG._~", NULL, FALSE, "abcdefgABCDEFG._~" },
470       { ":+ \\?#", NULL, FALSE, "%3A%2B%20%5C%3F%23" },
471       { "a+b:c", "+", FALSE, "a+b%3Ac" },
472       { "a+b:c\303\234", "+", TRUE, "a+b%3Ac\303\234" },
473       /* Incomplete UTF-8 sequence: */
474       { "\xfc\x3b\xd2", NULL, TRUE, "%FC%3B%D2" },
475       /* Invalid sequence: */
476       { "\xc3\xb1\xc3\x28", NULL, TRUE, "ñ%C3%28" },
477     };
478   gsize i;
479
480   for (i = 0; i < G_N_ELEMENTS (tests); i++)
481     {
482       gchar *s = NULL;
483       gchar *escaped = g_strescape (tests[i].unescaped, NULL);
484
485       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, escaped);
486
487       s = g_uri_escape_string (tests[i].unescaped,
488                                tests[i].reserved_chars_allowed,
489                                tests[i].allow_utf8);
490       g_assert_cmpstr (s, ==, tests[i].expected_escaped);
491       g_free (s);
492       g_free (escaped);
493     }
494 }
495
496 static void
497 test_uri_escape_bytes (void)
498 {
499   gchar *s = NULL;
500
501   s = g_uri_escape_bytes ((guchar*)"\0\0", 2, NULL);
502   g_assert_cmpstr (s, ==, "%00%00");
503   g_free (s);
504 }
505
506 static void
507 test_uri_scheme (void)
508 {
509   const gchar *s1, *s2;
510   gchar *s;
511
512   s = g_uri_parse_scheme ("ftp://ftp.gtk.org");
513   g_assert_cmpstr (s, ==, "ftp");
514   g_free (s);
515
516   s = g_uri_parse_scheme ("good-scheme.but+weird:gtk.org");
517   g_assert_cmpstr (s, ==, "good-scheme.but+weird");
518   g_free (s);
519
520   s = g_uri_parse_scheme ("1bad:");
521   g_assert_null (s);
522   s = g_uri_parse_scheme ("bad");
523   g_assert_null (s);
524   s = g_uri_parse_scheme ("99http://host/path");
525   g_assert_null (s);
526   s = g_uri_parse_scheme (".http://host/path");
527   g_assert_null (s);
528   s = g_uri_parse_scheme ("+http://host/path");
529   g_assert_null (s);
530
531   s1 = g_uri_peek_scheme ("ftp://ftp.gtk.org");
532   g_assert_cmpstr (s1, ==, "ftp");
533   s2 = g_uri_peek_scheme ("FTP://ftp.gtk.org");
534   g_assert_cmpstr (s2, ==, "ftp");
535   g_assert_true (s1 == s2);
536   s1 = g_uri_peek_scheme ("1bad:");
537   g_assert_null (s1);
538   s1 = g_uri_peek_scheme ("bad");
539   g_assert_null (s1);
540 }
541
542 typedef struct {
543   const gchar *scheme;
544   const gchar *userinfo;
545   const gchar *host;
546   gint         port;
547   const gchar *path;
548   const gchar *query;
549   const gchar *fragment;
550 } UriParts;
551
552 typedef struct {
553   /* Inputs */
554   const gchar *orig;
555   GUriFlags flags;
556   /* Outputs */
557   gboolean expected_success;
558   gint expected_error_code;       /* unused if @expected_success is true */
559   const UriParts expected_parts;  /* unused if @expected_success is false */
560 } UriAbsoluteTest;
561
562 static const UriAbsoluteTest absolute_tests[] = {
563   { "foo:", G_URI_FLAGS_NONE, TRUE, 0,
564     { "foo", NULL, NULL, -1, "", NULL, NULL }
565   },
566   { "file:/dev/null", G_URI_FLAGS_NONE, TRUE, 0,
567     { "file", NULL, NULL, -1, "/dev/null", NULL, NULL }
568   },
569   { "file:///dev/null", G_URI_FLAGS_NONE, TRUE, 0,
570     { "file", NULL, "", -1, "/dev/null", NULL, NULL }
571   },
572   { "ftp://user@host/path", G_URI_FLAGS_NONE, TRUE, 0,
573     { "ftp", "user", "host", -1, "/path", NULL, NULL }
574   },
575   { "ftp://user@host:9999/path", G_URI_FLAGS_NONE, TRUE, 0,
576     { "ftp", "user", "host", 9999, "/path", NULL, NULL }
577   },
578   { "ftp://user:password@host/path", G_URI_FLAGS_NONE, TRUE, 0,
579     { "ftp", "user:password", "host", -1, "/path", NULL, NULL }
580   },
581   { "ftp://user:password@host:9999/path", G_URI_FLAGS_NONE, TRUE, 0,
582     { "ftp", "user:password", "host", 9999, "/path", NULL, NULL }
583   },
584   { "ftp://user:password@host", G_URI_FLAGS_NONE, TRUE, 0,
585     { "ftp", "user:password", "host", -1, "", NULL, NULL }
586   },
587   { "http://us%65r@host", G_URI_FLAGS_NONE, TRUE, 0,
588     { "http", "user", "host", -1, "", NULL, NULL }
589   },
590   { "http://us%40r@host", G_URI_FLAGS_NONE, TRUE, 0,
591     { "http", "us@r", "host", -1, "", NULL, NULL }
592   },
593   { "http://us%3ar@host", G_URI_FLAGS_NONE, TRUE, 0,
594     { "http", "us:r", "host", -1, "", NULL, NULL }
595   },
596   { "http://us%2fr@host", G_URI_FLAGS_NONE, TRUE, 0,
597     { "http", "us/r", "host", -1, "", NULL, NULL }
598   },
599   { "http://us%3fr@host", G_URI_FLAGS_NONE, TRUE, 0,
600     { "http", "us?r", "host", -1, "", NULL, NULL }
601   },
602   { "http://host?query", G_URI_FLAGS_NONE, TRUE, 0,
603     { "http", NULL, "host", -1, "", "query", NULL }
604   },
605   { "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value", G_URI_FLAGS_NONE, TRUE, 0,
606     { "http", NULL, "host", -1, "/path", "query=http://host/path?childparam=childvalue&param=value", NULL }
607   },
608   { "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", G_URI_FLAGS_NONE, TRUE, 0,
609     { "http", NULL, "control-chars", -1, "/\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F", NULL, NULL }
610   },
611   { "http://space/%20", G_URI_FLAGS_NONE, TRUE, 0,
612     { "http", NULL, "space", -1, "/ ", NULL, NULL }
613   },
614   { "http://delims/%3C%3E%23%25%22", G_URI_FLAGS_NONE, TRUE, 0,
615     { "http", NULL, "delims", -1, "/<>#%\"", NULL, NULL }
616   },
617   { "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", G_URI_FLAGS_NONE, TRUE, 0,
618     { "http", NULL, "unwise-chars", -1, "/{}|\\^[]`", NULL, NULL }
619   },
620
621   /* From RFC 2732 */
622   { "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", G_URI_FLAGS_NONE, TRUE, 0,
623     { "http", NULL, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", 80, "/index.html", NULL, NULL }
624   },
625   { "http://[1080:0:0:0:8:800:200C:417A]/index.html", G_URI_FLAGS_NONE, TRUE, 0,
626     { "http", NULL, "1080:0:0:0:8:800:200C:417A", -1, "/index.html", NULL, NULL }
627   },
628   { "http://[3ffe:2a00:100:7031::1]", G_URI_FLAGS_NONE, TRUE, 0,
629     { "http", NULL, "3ffe:2a00:100:7031::1", -1, "", NULL, NULL }
630   },
631   { "http://[1080::8:800:200C:417A]/foo", G_URI_FLAGS_NONE, TRUE, 0,
632     { "http", NULL, "1080::8:800:200C:417A", -1, "/foo", NULL, NULL }
633   },
634   { "http://[::192.9.5.5]/ipng", G_URI_FLAGS_NONE, TRUE, 0,
635     { "http", NULL, "::192.9.5.5", -1, "/ipng", NULL, NULL }
636   },
637   { "http://[::FFFF:129.144.52.38]:80/index.html", G_URI_FLAGS_NONE, TRUE, 0,
638     { "http", NULL, "::FFFF:129.144.52.38", 80, "/index.html", NULL, NULL }
639   },
640   { "http://[2010:836B:4179::836B:4179]", G_URI_FLAGS_NONE, TRUE, 0,
641     { "http", NULL, "2010:836B:4179::836B:4179", -1, "", NULL, NULL }
642   },
643
644   /* some problematic URIs that are handled differently in libsoup */
645   { "http://host/path with spaces", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
646     { "http", NULL, "host", -1, "/path with spaces", NULL, NULL }
647   },
648   { "  http://host/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
649     { "http", NULL, "host", -1, "/path", NULL, NULL }
650   },
651   { "http://host/path  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
652     { "http", NULL, "host", -1, "/path", NULL, NULL }
653   },
654   { "http://host  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
655     { "http", NULL, "host", -1, "", NULL, NULL }
656   },
657   { "http://host:999  ", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
658     { "http", NULL, "host", 999, "", NULL, NULL }
659   },
660   { "http://host/pa\nth", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
661     { "http", NULL, "host", -1, "/path", NULL, NULL }
662   },
663   { "http:\r\n//host/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
664     { "http", NULL, "host", -1, "/path", NULL, NULL }
665   },
666   { "http://\thost/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
667     { "http", NULL, "host", -1, "/path", NULL, NULL }
668   },
669
670   /* Bug 594405; 0-length is different from not-present */
671   { "http://host/path?", G_URI_FLAGS_NONE, TRUE, 0,
672     { "http", NULL, "host", -1, "/path", "", NULL }
673   },
674   { "http://host/path#", G_URI_FLAGS_NONE, TRUE, 0,
675     { "http", NULL, "host", -1, "/path", NULL, "" },
676   },
677
678   /* Bug 590524; ignore bad %-encoding */
679   { "http://host/path%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
680     { "http", NULL, "host", -1, "/path%", NULL, NULL }
681   },
682   { "http://h%ost/path", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
683     { "http", NULL, "h%ost", -1, "/path", NULL, NULL }
684   },
685   { "http://host/path%%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
686     { "http", NULL, "host", -1, "/path%%", NULL, NULL }
687   },
688   { "http://host/path%%%", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
689     { "http", NULL, "host", -1, "/path%%%", NULL, NULL }
690   },
691   { "http://host/path%/x/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
692     { "http", NULL, "host", -1, "/path%/x/", NULL, NULL }
693   },
694   { "http://host/path%0x/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
695     { "http", NULL, "host", -1, "/path%0x/", NULL, NULL }
696   },
697   { "http://host/path%ax", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
698     { "http", NULL, "host", -1, "/path%ax", NULL, NULL }
699   },
700
701   /* GUri doesn't %-encode non-ASCII characters */
702   { "http://host/p\xc3\xa4th/", G_URI_FLAGS_NONE, TRUE, 0,
703     { "http", NULL, "host", -1, "/p\xc3\xa4th/", NULL, NULL }
704   },
705
706   { "HTTP:////////////////", G_URI_FLAGS_NONE, TRUE, 0,
707     { "http", NULL, "", -1, "//////////////", NULL, NULL }
708   },
709
710   { "http://@host", G_URI_FLAGS_NONE, TRUE, 0,
711     { "http", "", "host", -1, "", NULL, NULL }
712   },
713   { "http://:@host", G_URI_FLAGS_NONE, TRUE, 0,
714     { "http", ":", "host", -1, "", NULL, NULL }
715   },
716   { "scheme://foo%3Abar._webdav._tcp.local", G_URI_FLAGS_NONE, TRUE, 0,
717     { "scheme", NULL, "foo:bar._webdav._tcp.local", -1, "", NULL, NULL}
718   },
719
720   /* ".." past top */
721   { "http://example.com/..", G_URI_FLAGS_NONE, TRUE, 0,
722     { "http", NULL, "example.com", -1, "/", NULL, NULL }
723   },
724
725   /* scheme parsing */
726   { "foo0://host/path", G_URI_FLAGS_NONE, TRUE, 0,
727     { "foo0", NULL, "host", -1, "/path", NULL, NULL } },
728   { "f0.o://host/path", G_URI_FLAGS_NONE, TRUE, 0,
729     { "f0.o", NULL, "host", -1, "/path", NULL, NULL } },
730   { "http++://host/path", G_URI_FLAGS_NONE, TRUE, 0,
731     { "http++", NULL, "host", -1, "/path", NULL, NULL } },
732   { "http-ish://host/path", G_URI_FLAGS_NONE, TRUE, 0,
733     { "http-ish", NULL, "host", -1, "/path", NULL, NULL } },
734
735   /* IPv6 scope ID parsing (both correct and incorrect) */
736   { "http://[fe80::dead:beef%]/", G_URI_FLAGS_PARSE_RELAXED, FALSE, G_URI_ERROR_BAD_HOST,
737     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
738   { "http://[fe80::dead:beef%em1]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
739     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
740   { "http://[fe80::dead:beef%em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
741     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
742   { "http://[fe80::dead:beef%25em1]/", G_URI_FLAGS_NONE, TRUE, 0,
743     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
744   { "http://[fe80::dead:beef%25em1%20]/", G_URI_FLAGS_NONE, TRUE, 0,
745     { "http", NULL, "fe80::dead:beef%em1 ", -1, "/", NULL, NULL } },
746   { "http://[fe80::dead:beef%25em%31]/", G_URI_FLAGS_NONE, TRUE, 0,
747     { "http", NULL, "fe80::dead:beef%em1", -1, "/", NULL, NULL } },
748   { "http://[fe80::dead:beef%10]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
749     { "http", NULL, "fe80::dead:beef%10", -1, "/", NULL, NULL } },
750   { "http://[fe80::dead:beef%10]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
751     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
752   { "http://[fe80::dead:beef%25]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
753     { "http", NULL, "fe80::dead:beef%25", -1, "/", NULL, NULL } },
754   { "http://[fe80::dead:beef%25]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
755     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
756   { "http://[192.168.0.1%25em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
757     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
758   { "http://[fe80::dead:beef%2em1]/", G_URI_FLAGS_PARSE_RELAXED, TRUE, 0,
759     { "http", NULL, "fe80::dead:beef%2em1", -1, "/", NULL, NULL } },
760   { "http://[fe80::dead:beef%2em1]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
761     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
762   { "http://[fe80::dead:beef%25em1%00]/", G_URI_FLAGS_PARSE_RELAXED, FALSE, G_URI_ERROR_BAD_HOST,
763     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
764   { "http://[fe80::dead:beef%25em1%00]/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
765     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
766
767   /* Invalid IDN hostname */
768   { "http://xn--mixed-\xc3\xbcp/", G_URI_FLAGS_NONE, FALSE, G_URI_ERROR_BAD_HOST,
769     { NULL, NULL, NULL, -1, NULL, NULL, NULL } },
770 };
771
772 static void
773 test_uri_parsing_absolute (void)
774 {
775   gsize i;
776
777   for (i = 0; i < G_N_ELEMENTS (absolute_tests); i++)
778     {
779       const UriAbsoluteTest *test = &absolute_tests[i];
780       GError *error = NULL;
781       GUri *uri;
782
783       g_test_message ("Test %" G_GSIZE_FORMAT ": %s", i, test->orig);
784
785       uri = g_uri_parse (test->orig, test->flags, &error);
786       if (test->expected_success)
787         {
788           g_assert_no_error (error);
789
790           g_assert_cmpstr (g_uri_get_scheme (uri),   ==, test->expected_parts.scheme);
791           g_assert_cmpstr (g_uri_get_userinfo (uri), ==, test->expected_parts.userinfo);
792           g_assert_cmpstr (g_uri_get_host (uri),     ==, test->expected_parts.host);
793           g_assert_cmpint (g_uri_get_port (uri),     ==, test->expected_parts.port);
794           g_assert_cmpstr (g_uri_get_path (uri),     ==, test->expected_parts.path);
795           g_assert_cmpstr (g_uri_get_query (uri),    ==, test->expected_parts.query);
796           g_assert_cmpstr (g_uri_get_fragment (uri), ==, test->expected_parts.fragment);
797         }
798       else
799         {
800           g_assert_error (error, G_URI_ERROR, test->expected_error_code);
801           g_assert_null (uri);
802         }
803
804       g_clear_pointer (&uri, g_uri_unref);
805       g_clear_error (&error);
806     }
807 }
808
809 typedef struct {
810   const gchar *orig, *resolved;
811   UriParts parts;
812 } UriRelativeTest;
813
814 /* This all comes from RFC 3986 */
815 static const char *relative_test_base = "http://a/b/c/d;p?q";
816 static const UriRelativeTest relative_tests[] = {
817   { "g:h", "g:h",
818     { "g", NULL, NULL, -1, "h", NULL, NULL } },
819   { "g", "http://a/b/c/g",
820     { "http", NULL, "a", -1, "/b/c/g", NULL, NULL } },
821   { "./g", "http://a/b/c/g",
822     { "http", NULL, "a", -1, "/b/c/g", NULL, NULL } },
823   { "g/", "http://a/b/c/g/",
824     { "http", NULL, "a", -1, "/b/c/g/", NULL, NULL } },
825   { "/g", "http://a/g",
826     { "http", NULL, "a", -1, "/g", NULL, NULL } },
827   { "//g", "http://g",
828     { "http", NULL, "g", -1, "", NULL, NULL } },
829   { "?y", "http://a/b/c/d;p?y",
830     { "http", NULL, "a", -1, "/b/c/d;p", "y", NULL } },
831   { "g?y", "http://a/b/c/g?y",
832     { "http", NULL, "a", -1, "/b/c/g", "y", NULL } },
833   { "#s", "http://a/b/c/d;p?q#s",
834     { "http", NULL, "a", -1, "/b/c/d;p", "q", "s" } },
835   { "g#s", "http://a/b/c/g#s",
836     { "http", NULL, "a", -1, "/b/c/g", NULL, "s" } },
837   { "g?y#s", "http://a/b/c/g?y#s",
838     { "http", NULL, "a", -1, "/b/c/g", "y", "s" } },
839   { ";x", "http://a/b/c/;x",
840     { "http", NULL, "a", -1, "/b/c/;x", NULL, NULL } },
841   { "g;x", "http://a/b/c/g;x",
842     { "http", NULL, "a", -1, "/b/c/g;x", NULL, NULL } },
843   { "g;x?y#s", "http://a/b/c/g;x?y#s",
844     { "http", NULL, "a", -1, "/b/c/g;x", "y", "s" } },
845   { ".", "http://a/b/c/",
846     { "http", NULL, "a", -1, "/b/c/", NULL, NULL } },
847   { "./", "http://a/b/c/",
848     { "http", NULL, "a", -1, "/b/c/", NULL, NULL } },
849   { "..", "http://a/b/",
850     { "http", NULL, "a", -1, "/b/", NULL, NULL } },
851   { "../", "http://a/b/",
852     { "http", NULL, "a", -1, "/b/", NULL, NULL } },
853   { "../g", "http://a/b/g",
854     { "http", NULL, "a", -1, "/b/g", NULL, NULL } },
855   { "../..", "http://a/",
856     { "http", NULL, "a", -1, "/", NULL, NULL } },
857   { "../../", "http://a/",
858     { "http", NULL, "a", -1, "/", NULL, NULL } },
859   { "../../g", "http://a/g",
860     { "http", NULL, "a", -1, "/g", NULL, NULL } },
861   { "", "http://a/b/c/d;p?q",
862     { "http", NULL, "a", -1, "/b/c/d;p", "q", NULL } },
863   { "../../../g", "http://a/g",
864     { "http", NULL, "a", -1, "/g", NULL, NULL } },
865   { "../../../../g", "http://a/g",
866     { "http", NULL, "a", -1, "/g", NULL, NULL } },
867   { "/./g", "http://a/g",
868     { "http", NULL, "a", -1, "/g", NULL, NULL } },
869   { "/../g", "http://a/g",
870     { "http", NULL, "a", -1, "/g", NULL, NULL } },
871   { "g.", "http://a/b/c/g.",
872     { "http", NULL, "a", -1, "/b/c/g.", NULL, NULL } },
873   { ".g", "http://a/b/c/.g",
874     { "http", NULL, "a", -1, "/b/c/.g", NULL, NULL } },
875   { "g..", "http://a/b/c/g..",
876     { "http", NULL, "a", -1, "/b/c/g..", NULL, NULL } },
877   { "..g", "http://a/b/c/..g",
878     { "http", NULL, "a", -1, "/b/c/..g", NULL, NULL } },
879   { "./../g", "http://a/b/g",
880     { "http", NULL, "a", -1, "/b/g", NULL, NULL } },
881   { "./g/.", "http://a/b/c/g/",
882     { "http", NULL, "a", -1, "/b/c/g/", NULL, NULL } },
883   { "g/./h", "http://a/b/c/g/h",
884     { "http", NULL, "a", -1, "/b/c/g/h", NULL, NULL } },
885   { "g/../h", "http://a/b/c/h",
886     { "http", NULL, "a", -1, "/b/c/h", NULL, NULL } },
887   { "g;x=1/./y", "http://a/b/c/g;x=1/y",
888     { "http", NULL, "a", -1, "/b/c/g;x=1/y", NULL, NULL } },
889   { "g;x=1/../y", "http://a/b/c/y",
890     { "http", NULL, "a", -1, "/b/c/y", NULL, NULL } },
891   { "g?y/./x", "http://a/b/c/g?y/./x",
892     { "http", NULL, "a", -1, "/b/c/g", "y/./x", NULL } },
893   { "g?y/../x", "http://a/b/c/g?y/../x",
894     { "http", NULL, "a", -1, "/b/c/g", "y/../x", NULL } },
895   { "g#s/./x", "http://a/b/c/g#s/./x",
896     { "http", NULL, "a", -1, "/b/c/g", NULL, "s/./x" } },
897   { "g#s/../x", "http://a/b/c/g#s/../x",
898     { "http", NULL, "a", -1, "/b/c/g", NULL, "s/../x" } },
899   { "http:g", "http:g",
900     { "http", NULL, NULL, -1, "g", NULL, NULL } },
901   { "http://a/../..", "http://a/",
902     { "http", NULL, "a", -1, "/", NULL, NULL } },
903   { "ScHeMe://User:P%61ss@HOST.%63om:1234/path/./from/../to%7d/item%2dobj?qu%65ry=something#fr%61gment",
904     "scheme://User:Pass@HOST.com:1234/path/to%7D/item-obj?query=something#fragment",
905     { "scheme", "User:Pass", "HOST.com", 1234, "/path/to}/item-obj", "query=something", "fragment" } },
906   /* Test corner cases of remove_dot_segments */
907   { "http:..", "http:",
908     { "http", NULL, NULL, -1, "", NULL, NULL } },
909   { "http:../", "http:",
910     { "http", NULL, NULL, -1, "", NULL, NULL } },
911   { "http:.", "http:",
912     { "http", NULL, NULL, -1, "", NULL, NULL } },
913   { "http:./", "http:",
914     { "http", NULL, NULL, -1, "", NULL, NULL } },
915   { "http:a/..", "http:/",
916     { "http", NULL, NULL, -1, "/", NULL, NULL } },
917   { "http:a/../", "http:/",
918     { "http", NULL, NULL, -1, "/", NULL, NULL } },
919 };
920 static int num_relative_tests = G_N_ELEMENTS (relative_tests);
921
922 static void
923 test_uri_parsing_relative (void)
924 {
925   int i;
926   GUri *base, *uri;
927   GError *error = NULL;
928   gchar *resolved;
929
930   base = g_uri_parse (relative_test_base, G_URI_FLAGS_NONE, &error);
931   g_assert_no_error (error);
932
933   for (i = 0; i < num_relative_tests; i++)
934     {
935       const UriRelativeTest *test = &relative_tests[i];
936       gchar *tostring;
937
938       uri = g_uri_parse_relative (base, test->orig, G_URI_FLAGS_NONE, &error);
939       g_assert_no_error (error);
940
941       g_assert_cmpstr (g_uri_get_scheme (uri),   ==, test->parts.scheme);
942       g_assert_cmpstr (g_uri_get_userinfo (uri), ==, test->parts.userinfo);
943       g_assert_cmpstr (g_uri_get_host (uri),     ==, test->parts.host);
944       g_assert_cmpint (g_uri_get_port (uri),     ==, test->parts.port);
945       g_assert_cmpstr (g_uri_get_path (uri),     ==, test->parts.path);
946       g_assert_cmpstr (g_uri_get_query (uri),    ==, test->parts.query);
947       g_assert_cmpstr (g_uri_get_fragment (uri), ==, test->parts.fragment);
948
949       tostring = g_uri_to_string (uri);
950       g_assert_cmpstr (tostring, ==, test->resolved);
951       g_free (tostring);
952
953       g_uri_unref (uri);
954
955       resolved = g_uri_resolve_relative (relative_test_base, test->orig, G_URI_FLAGS_NONE, &error);
956       g_assert_no_error (error);
957       g_assert_cmpstr (resolved, ==, test->resolved);
958       g_free (resolved);
959     }
960   uri = g_uri_parse_relative (base, "%%", G_URI_FLAGS_NONE, &error);
961   g_assert_null (uri);
962   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PATH);
963   g_clear_error (&error);
964
965   g_uri_unref (base);
966
967   resolved = g_uri_resolve_relative (NULL, "http://a", G_URI_FLAGS_NONE, &error);
968   g_assert_no_error (error);
969   g_assert_cmpstr (resolved, ==, "http://a");
970   g_free (resolved);
971
972   resolved = g_uri_resolve_relative ("http://a", "b", G_URI_FLAGS_NONE, &error);
973   g_assert_no_error (error);
974   g_assert_cmpstr (resolved, ==, "http://a/b");
975   g_free (resolved);
976
977   resolved = g_uri_resolve_relative (NULL, "a", G_URI_FLAGS_NONE, &error);
978   g_assert_null (resolved);
979   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
980   g_clear_error (&error);
981
982   resolved = g_uri_resolve_relative ("../b", "a", G_URI_FLAGS_NONE, &error);
983   g_assert_null (resolved);
984   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
985   g_clear_error (&error);
986
987   resolved = g_uri_resolve_relative ("%%", "a", G_URI_FLAGS_PARSE_RELAXED, &error);
988   g_assert_null (resolved);
989   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_FAILED);
990   g_clear_error (&error);
991 }
992
993 static void
994 test_uri_to_string (void)
995 {
996   GUri *uri;
997   gchar *tostring;
998
999   uri = g_uri_build (G_URI_FLAGS_NONE, "scheme", "userinfo", "host", 1234,
1000                      "/path", "query", "fragment");
1001
1002   tostring = g_uri_to_string (uri);
1003   g_assert_cmpstr (tostring, ==, "scheme://userinfo@host:1234/path?query#fragment");
1004   g_free (tostring);
1005   g_uri_unref (uri);
1006
1007   uri = g_uri_build (G_URI_FLAGS_NONE, "scheme", NULL, "fe80::dead:beef%em1", -1, "", NULL, NULL);
1008   tostring = g_uri_to_string (uri);
1009   g_assert_cmpstr (tostring, ==, "scheme://[fe80::dead:beef%25em1]");
1010   g_free (tostring);
1011   g_uri_unref (uri);
1012
1013   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user", "pass", "auth", "host", 1234,
1014                                "/path", "query", "fragment");
1015   tostring = g_uri_to_string (uri);
1016   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path?query#fragment");
1017   g_free (tostring);
1018   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_USERINFO);
1019   g_assert_cmpstr (tostring, ==, "scheme://host:1234/path?query#fragment");
1020   g_free (tostring);
1021   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_QUERY);
1022   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path#fragment");
1023   g_free (tostring);
1024   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_FRAGMENT);
1025   g_assert_cmpstr (tostring, ==, "scheme://user:pass;auth@host:1234/path?query");
1026   g_free (tostring);
1027   g_uri_unref (uri);
1028
1029   uri = g_uri_build_with_user (G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1030                                "scheme", "us:er", "pass", "auth", "host", 1234,
1031                                "/path", "query", "fragment");
1032   tostring = g_uri_to_string (uri);
1033   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass;auth@host:1234/path?query#fragment");
1034   g_free (tostring);
1035   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
1036   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer;auth@host:1234/path?query#fragment");
1037   g_free (tostring);
1038   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_AUTH_PARAMS);
1039   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass@host:1234/path?query#fragment");
1040   g_free (tostring);
1041   tostring = g_uri_to_string_partial (uri, G_URI_HIDE_QUERY);
1042   g_assert_cmpstr (tostring, ==, "scheme://us%3Aer:pass;auth@host:1234/path#fragment");
1043   g_free (tostring);
1044   g_uri_unref (uri);
1045 }
1046
1047 static void
1048 test_uri_build (void)
1049 {
1050   GUri *uri;
1051
1052   uri = g_uri_build (G_URI_FLAGS_NON_DNS, "scheme", "userinfo", "host", 1234,
1053                      "/path", "query", "fragment");
1054
1055   /* check ref/unref */
1056   g_uri_ref (uri);
1057   g_uri_unref (uri);
1058
1059   g_assert_cmpint (g_uri_get_flags (uri), ==, G_URI_FLAGS_NON_DNS);
1060   g_assert_cmpstr (g_uri_get_scheme (uri), ==, "scheme");
1061   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "userinfo");
1062   g_assert_cmpstr (g_uri_get_host (uri), ==, "host");
1063   g_assert_cmpint (g_uri_get_port (uri), ==, 1234);
1064   g_assert_cmpstr (g_uri_get_path (uri), ==, "/path");
1065   g_assert_cmpstr (g_uri_get_query (uri), ==, "query");
1066   g_assert_cmpstr (g_uri_get_fragment (uri), ==, "fragment");
1067   g_assert_cmpstr (g_uri_get_user (uri), ==, NULL);
1068   g_assert_cmpstr (g_uri_get_password (uri), ==, NULL);
1069   g_uri_unref (uri);
1070
1071   uri = g_uri_build_with_user (G_URI_FLAGS_NON_DNS, "scheme", "user", "password",
1072                                "authparams", "host", 1234,
1073                                "/path", "query", "fragment");
1074
1075   g_assert_cmpint (g_uri_get_flags (uri), ==, G_URI_FLAGS_NON_DNS | G_URI_FLAGS_HAS_PASSWORD);
1076   g_assert_cmpstr (g_uri_get_scheme (uri), ==, "scheme");
1077   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user:password;authparams");
1078   g_assert_cmpstr (g_uri_get_host (uri), ==, "host");
1079   g_assert_cmpint (g_uri_get_port (uri), ==, 1234);
1080   g_assert_cmpstr (g_uri_get_path (uri), ==, "/path");
1081   g_assert_cmpstr (g_uri_get_query (uri), ==, "query");
1082   g_assert_cmpstr (g_uri_get_fragment (uri), ==, "fragment");
1083   g_assert_cmpstr (g_uri_get_user (uri), ==, "user");
1084   g_assert_cmpstr (g_uri_get_password (uri), ==, "password");
1085   g_assert_cmpstr (g_uri_get_auth_params (uri), ==, "authparams");
1086   g_uri_unref (uri);
1087
1088   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "password\002",
1089                                "authparams\003", "host", 1234,
1090                                "/path", "query", "fragment");
1091   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user\001:password\002;authparams\003");
1092   g_uri_unref (uri);
1093
1094   uri = g_uri_build_with_user (G_URI_FLAGS_ENCODED, "scheme", "user%01", "password%02",
1095                                "authparams%03", "host", 1234,
1096                                "/path", "query", "fragment");
1097   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user%01:password%02;authparams%03");
1098   g_uri_unref (uri);
1099
1100   uri = g_uri_build_with_user (G_URI_FLAGS_ENCODED, "scheme", NULL, NULL,
1101                                NULL, "host", 1234,
1102                                "/path", "query", "fragment");
1103   g_assert_null (g_uri_get_userinfo (uri));
1104   g_uri_unref (uri);
1105
1106   uri = g_uri_build_with_user (G_URI_FLAGS_NONE, "scheme", "user", NULL, NULL,
1107                                "host", 1234,
1108                                "/path", "query", "fragment");
1109   g_assert_cmpstr (g_uri_get_userinfo (uri), ==, "user");
1110   g_uri_unref (uri);
1111 }
1112
1113 static void
1114 test_uri_split (void)
1115 {
1116   gchar *scheme = NULL;
1117   gchar *userinfo = NULL;
1118   gchar *user = NULL;
1119   gchar *pass = NULL;
1120   gchar *authparams = NULL;
1121   gchar *host = NULL;
1122   gchar *path = NULL;
1123   gchar *query = NULL;
1124   gchar *fragment = NULL;
1125   GError *error = NULL;
1126   gint port;
1127
1128   g_uri_split ("scheme://user%3Apass%3Bauth@host:1234/path?query#fragment",
1129                G_URI_FLAGS_NONE,
1130                &scheme,
1131                &userinfo,
1132                &host,
1133                &port,
1134                &path,
1135                &query,
1136                &fragment,
1137                &error);
1138   g_assert_no_error (error);
1139   g_assert_cmpstr (scheme, ==, "scheme");
1140   g_assert_cmpstr (userinfo, ==, "user:pass;auth");
1141   g_assert_cmpstr (host, ==, "host");
1142   g_assert_cmpint (port, ==, 1234);
1143   g_assert_cmpstr (path, ==, "/path");
1144   g_assert_cmpstr (query, ==, "query");
1145   g_assert_cmpstr (fragment, ==, "fragment");
1146   g_free (scheme);
1147   g_free (userinfo);
1148   g_free (host);
1149   g_free (path);
1150   g_free (query);
1151   g_free (fragment);
1152
1153   g_uri_split ("scheme://user%3Apass%3Bauth@h%01st:1234/path?query#fragment",
1154                G_URI_FLAGS_ENCODED,
1155                NULL,
1156                NULL,
1157                &host,
1158                NULL,
1159                NULL,
1160                NULL,
1161                NULL,
1162                &error);
1163   g_assert_no_error (error);
1164   g_assert_cmpstr (host, ==, "h\001st");
1165   g_free (host);
1166
1167   g_uri_split ("scheme://@@@host:1234/path?query#fragment",
1168                G_URI_FLAGS_ENCODED | G_URI_FLAGS_PARSE_RELAXED,
1169                NULL,
1170                &userinfo,
1171                NULL,
1172                NULL,
1173                NULL,
1174                NULL,
1175                NULL,
1176                &error);
1177   g_assert_no_error (error);
1178   g_assert_cmpstr (userinfo, ==, "@@");
1179   g_free (userinfo);
1180
1181
1182   g_uri_split ("http://f;oo/",
1183                G_URI_FLAGS_NONE | G_URI_FLAGS_PARSE_RELAXED,
1184                NULL,
1185                NULL,
1186                NULL,
1187                NULL,
1188                &path,
1189                NULL,
1190                NULL,
1191                &error);
1192   g_assert_no_error (error);
1193   g_assert_cmpstr (path, ==, ";oo/");
1194   g_free (path);
1195
1196   g_uri_split ("http://h%01st/path?saisons=%C3%89t%C3%A9%2Bhiver",
1197                G_URI_FLAGS_NONE,
1198                NULL,
1199                NULL,
1200                &host,
1201                NULL,
1202                NULL,
1203                &query,
1204                NULL,
1205                &error);
1206   g_assert_no_error (error);
1207   g_assert_cmpstr (host, ==, "h\001st");
1208   g_assert_cmpstr (query, ==, "saisons=Été+hiver");
1209   g_free (host);
1210   g_free (query);
1211
1212   g_uri_split ("http://h%01st/path?saisons=%C3%89t%C3%A9%2Bhiver",
1213                G_URI_FLAGS_ENCODED_QUERY,
1214                NULL,
1215                NULL,
1216                &host,
1217                NULL,
1218                NULL,
1219                &query,
1220                NULL,
1221                &error);
1222   g_assert_no_error (error);
1223   g_assert_cmpstr (host, ==, "h\001st");
1224   g_assert_cmpstr (query, ==, "saisons=%C3%89t%C3%A9%2Bhiver");
1225   g_free (host);
1226   g_free (query);
1227
1228   g_uri_split ("http://h%01st/%C3%89t%C3%A9%2Bhiver",
1229                G_URI_FLAGS_ENCODED_PATH,
1230                NULL,
1231                NULL,
1232                NULL,
1233                NULL,
1234                &path,
1235                NULL,
1236                NULL,
1237                &error);
1238   g_assert_no_error (error);
1239   g_assert_cmpstr (path, ==, "/%C3%89t%C3%A9%2Bhiver");
1240   g_free (path);
1241
1242   g_uri_split ("file:///path/to/some%20file",
1243                G_URI_FLAGS_NONE,
1244                NULL,
1245                NULL,
1246                NULL,
1247                NULL,
1248                &path,
1249                NULL,
1250                NULL,
1251                &error);
1252   g_assert_no_error (error);
1253   g_assert_cmpstr (path, ==, "/path/to/some file");
1254   g_free (path);
1255
1256   g_uri_split ("http://h%01st/path#%C3%89t%C3%A9%2Bhiver",
1257                G_URI_FLAGS_ENCODED_FRAGMENT,
1258                NULL,
1259                NULL,
1260                NULL,
1261                NULL,
1262                NULL,
1263                NULL,
1264                &fragment,
1265                &error);
1266   g_assert_no_error (error);
1267   g_assert_cmpstr (fragment, ==, "%C3%89t%C3%A9%2Bhiver");
1268   g_free (fragment);
1269
1270   g_uri_split_with_user ("scheme://user:pass;auth@host:1234/path?query#fragment",
1271                          G_URI_FLAGS_HAS_AUTH_PARAMS|G_URI_FLAGS_HAS_PASSWORD,
1272                          NULL,
1273                          &user,
1274                          &pass,
1275                          &authparams,
1276                          NULL,
1277                          NULL,
1278                          NULL,
1279                          NULL,
1280                          NULL,
1281                          &error);
1282   g_assert_no_error (error);
1283   g_assert_cmpstr (user, ==, "user");
1284   g_assert_cmpstr (pass, ==, "pass");
1285   g_assert_cmpstr (authparams, ==, "auth");
1286   g_free (user);
1287   g_free (pass);
1288   g_free (authparams);
1289
1290   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?query#fragment",
1291                        G_URI_FLAGS_NONE,
1292                        NULL,
1293                        NULL,
1294                        NULL,
1295                        &error);
1296   g_assert_no_error (error);
1297
1298   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?query#fragment",
1299                        G_URI_FLAGS_NONE,
1300                        &scheme,
1301                        &host,
1302                        &port,
1303                        &error);
1304   g_assert_no_error (error);
1305   g_assert_cmpstr (scheme, ==, "scheme");
1306   g_assert_cmpstr (host, ==, "host");
1307   g_assert_cmpint (port, ==, 1234);
1308   g_free (scheme);
1309   g_free (host);
1310
1311   g_uri_split_network ("%00",
1312                        G_URI_FLAGS_NONE, NULL, NULL, NULL, &error);
1313   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PATH);
1314   g_clear_error (&error);
1315
1316   g_uri_split_network ("/a",
1317                        G_URI_FLAGS_NONE,
1318                        &scheme,
1319                        &host,
1320                        &port,
1321                        &error);
1322   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_SCHEME);
1323   g_clear_error (&error);
1324
1325   g_uri_split_network ("schme:#",
1326                        G_URI_FLAGS_NONE,
1327                        &scheme,
1328                        &host,
1329                        &port,
1330                        &error);
1331   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1332   g_clear_error (&error);
1333
1334   g_uri_split_network ("scheme://[]/a",
1335                        G_URI_FLAGS_NONE, NULL, NULL, NULL, &error);
1336   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1337   g_clear_error (&error);
1338
1339   g_uri_split_network ("scheme://user%00:pass;auth@host",
1340                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1341                        NULL, NULL, NULL, &error);
1342   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_USER);
1343   g_clear_error (&error);
1344
1345   g_uri_split_network ("scheme://user:pass%00;auth@host",
1346                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1347                        NULL, NULL, NULL, &error);
1348   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1349   g_clear_error (&error);
1350
1351   g_uri_split_network ("scheme://user:pass;auth@host:1234/path?quer%00y#fragment",
1352                        G_URI_FLAGS_NONE,
1353                        NULL, NULL, NULL, &error);
1354   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_QUERY);
1355   g_clear_error (&error);
1356
1357   g_uri_split_network ("scheme://use%00r:pass;auth@host:1234/path",
1358                        G_URI_FLAGS_NONE,
1359                        NULL, NULL, NULL, &error);
1360   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_USER);
1361   g_clear_error (&error);
1362
1363   g_uri_split ("scheme://user:pass;auth@host:1234/path?query#fragm%00ent",
1364                G_URI_FLAGS_NONE,
1365                &scheme,
1366                &userinfo,
1367                &host,
1368                &port,
1369                &path,
1370                &query,
1371                &fragment,
1372                &error);
1373   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_FRAGMENT);
1374   g_clear_error (&error);
1375
1376   g_uri_split_with_user ("scheme://user:pa%x0s;auth@host:1234/path?query#fragment",
1377                          G_URI_FLAGS_HAS_PASSWORD,
1378                          &scheme,
1379                          &user,
1380                          &pass,
1381                          &authparams,
1382                          &host,
1383                          &port,
1384                          &path,
1385                          &query,
1386                          &fragment,
1387                          &error);
1388   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1389   g_clear_error (&error);
1390
1391   g_uri_split_with_user ("scheme://user:pass;auth%00@host",
1392                          G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1393                          &scheme,
1394                          &user,
1395                          &pass,
1396                          &authparams,
1397                          &host,
1398                          &port,
1399                          &path,
1400                          &query,
1401                          &fragment,
1402                          &error);
1403   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_AUTH_PARAMS);
1404   g_clear_error (&error);
1405
1406   g_uri_split_network ("scheme://user:pass%00;auth@host",
1407                        G_URI_FLAGS_HAS_PASSWORD|G_URI_FLAGS_HAS_AUTH_PARAMS,
1408                        NULL, NULL, NULL, &error);
1409   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PASSWORD);
1410   g_clear_error (&error);
1411
1412   /* Path not started correctly */
1413   g_uri_split("scheme://hostname:123path?query#fragment",
1414               G_URI_FLAGS_NONE,
1415               &scheme,
1416               &userinfo,
1417               &host,
1418               &port,
1419               &path,
1420               &query,
1421               &fragment,
1422               &error);
1423   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1424   g_clear_error (&error);
1425
1426   /* Brackets that don't close */
1427   g_uri_split("scheme://[01:23:45:67:89:ab:cd:ef:123/path",
1428               G_URI_FLAGS_NONE,
1429               &scheme,
1430               &userinfo,
1431               &host,
1432               &port,
1433               &path,
1434               &query,
1435               &fragment,
1436               &error);
1437   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1438   g_clear_error (&error);
1439
1440   /* IPv6 hostname without brackets */
1441   g_uri_split("scheme://01:23:45:67:89:ab:cd:ef:123/path",
1442               G_URI_FLAGS_NONE,
1443               &scheme,
1444               &userinfo,
1445               &host,
1446               &port,
1447               &path,
1448               &query,
1449               &fragment,
1450               &error);
1451   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1452   g_clear_error (&error);
1453 }
1454
1455 static void
1456 test_uri_is_valid (void)
1457 {
1458   GError *error = NULL;
1459
1460   g_assert_true (g_uri_is_valid ("http://[::192.9.5.5]/ipng", G_URI_FLAGS_NONE, NULL));
1461   g_assert_true (g_uri_is_valid ("http://127.127.127.127/", G_URI_FLAGS_NONE, NULL));
1462   g_assert_true (g_uri_is_valid ("http://127.127.127.b/", G_URI_FLAGS_NONE, NULL));
1463   g_assert_true (g_uri_is_valid ("http://\xc3\x89XAMPLE.COM/", G_URI_FLAGS_NONE, NULL));
1464
1465   g_assert_true (g_uri_is_valid ("  \r http\t://f oo  \t\n ", G_URI_FLAGS_PARSE_RELAXED, NULL));
1466   g_assert_false (g_uri_is_valid ("  \r http\t://f oo  \t\n ", G_URI_FLAGS_NONE, &error));
1467   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_SCHEME);
1468   g_clear_error (&error);
1469
1470   g_assert_false (g_uri_is_valid ("http://[::192.9.5.5/ipng", G_URI_FLAGS_NONE, &error));
1471   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1472   g_clear_error (&error);
1473
1474   g_assert_true (g_uri_is_valid ("http://[fe80::dead:beef%25wef]/", G_URI_FLAGS_NONE, NULL));
1475   g_assert_false (g_uri_is_valid ("http://[fe80::dead:beef%wef%]/", G_URI_FLAGS_NONE, &error));
1476   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1477   g_clear_error (&error);
1478
1479   g_assert_false (g_uri_is_valid ("http://%00/", G_URI_FLAGS_NON_DNS, &error));
1480   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1481   g_clear_error (&error);
1482
1483   g_assert_true (g_uri_is_valid ("http://foo/", G_URI_FLAGS_NON_DNS, &error));
1484
1485   g_assert_false (g_uri_is_valid ("http://%00/", G_URI_FLAGS_NONE, &error));
1486   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1487   g_clear_error (&error);
1488
1489   g_assert_false (g_uri_is_valid ("http://%30.%30.%30.%30/", G_URI_FLAGS_NONE, &error));
1490   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_HOST);
1491   g_clear_error (&error);
1492
1493   g_assert_false (g_uri_is_valid ("http://host:port", G_URI_FLAGS_NONE, &error));
1494   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1495   g_clear_error (&error);
1496
1497   g_assert_false (g_uri_is_valid ("http://host:65536", G_URI_FLAGS_NONE, &error));
1498   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1499   g_clear_error (&error);
1500
1501   g_assert_false (g_uri_is_valid ("http://host:6553l", G_URI_FLAGS_NONE, &error));
1502   g_assert_error (error, G_URI_ERROR, G_URI_ERROR_BAD_PORT);
1503   g_clear_error (&error);
1504
1505   g_assert_true (g_uri_is_valid ("data:,Hello", G_URI_FLAGS_NONE, &error));
1506
1507   g_assert_true (g_uri_is_valid ("B:\\foo.txt", G_URI_FLAGS_NONE, &error));
1508   g_assert_true (g_uri_is_valid ("B:/foo.txt", G_URI_FLAGS_NONE, &error));
1509   g_assert_true (g_uri_is_valid ("B://foo.txt", G_URI_FLAGS_NONE, &error));
1510   g_assert_true (g_uri_is_valid ("B:foo.txt", G_URI_FLAGS_NONE, &error));
1511
1512   g_assert_true (g_uri_is_valid ("fd://0", G_URI_FLAGS_NONE, &error));
1513   g_assert_true (g_uri_is_valid ("AB:\\foo.txt", G_URI_FLAGS_NONE, &error));
1514   g_assert_true (g_uri_is_valid ("AB:/foo.txt", G_URI_FLAGS_NONE, &error));
1515   g_assert_true (g_uri_is_valid ("AB://foo.txt", G_URI_FLAGS_NONE, &error));
1516   g_assert_true (g_uri_is_valid ("AB:foo.txt", G_URI_FLAGS_NONE, &error));
1517
1518   g_assert_true (g_uri_is_valid ("ABC:/foo.txt", G_URI_FLAGS_NONE, &error));
1519   g_assert_true (g_uri_is_valid ("ABC://foo.txt", G_URI_FLAGS_NONE, &error));
1520   g_assert_true (g_uri_is_valid ("ABC:foo.txt", G_URI_FLAGS_NONE, &error));
1521
1522   g_assert_true (g_uri_is_valid ("ABCD:/foo.txt", G_URI_FLAGS_NONE, &error));
1523   g_assert_true (g_uri_is_valid ("ABCD://foo.txt", G_URI_FLAGS_NONE, &error));
1524   g_assert_true (g_uri_is_valid ("ABCD:foo.txt", G_URI_FLAGS_NONE, &error));
1525 }
1526
1527 static const struct
1528 {
1529   /* Inputs */
1530   const gchar *uri;
1531   gchar *separators;
1532   GUriParamsFlags flags;
1533   /* Outputs */
1534   /* key, value, key, value, â€¦, limited to length 2*expected_n_params */
1535   gssize expected_n_iter;  /* -1 => error expected */
1536   const gchar *expected_iter_key_values[6];
1537   gssize expected_n_params;  /* -1 => error expected */
1538   const gchar *expected_param_key_values[6];
1539 } params_tests[] =
1540   {
1541     { "p1=foo&p2=bar;p3=baz", "&;", G_URI_PARAMS_NONE,
1542       3, { "p1", "foo", "p2", "bar", "p3", "baz" },
1543       3, { "p1", "foo", "p2", "bar", "p3", "baz" }},
1544     { "p1=foo&p2=bar", "", G_URI_PARAMS_NONE,
1545       1, { "p1", "foo&p2=bar" },
1546       1, { "p1", "foo&p2=bar" }},
1547     { "p1=foo&&P1=bar", "&", G_URI_PARAMS_NONE,
1548       1, { "p1", "foo" },
1549       -1, { NULL, }},
1550     { "%00=foo", "&", G_URI_PARAMS_NONE,
1551       0, { NULL, },
1552       -1, { NULL, }},
1553     { "p1=%00", "&", G_URI_PARAMS_NONE,
1554       0, { NULL, },
1555       -1, { NULL, }},
1556     { "p1=foo&p1=bar", "&", G_URI_PARAMS_NONE,
1557       2, { "p1", "foo", "p1", "bar" },
1558       1, { "p1", "bar", NULL, }},
1559     { "p1=foo&P1=bar", "&", G_URI_PARAMS_CASE_INSENSITIVE,
1560       2, { "p1", "foo", "P1", "bar" },
1561       1, { "p1", "bar", NULL, }},
1562     { "=%", "&", G_URI_PARAMS_PARSE_RELAXED,
1563       1, { "", "%", NULL, },
1564       1, { "", "%", NULL, }},
1565     { "=", "&", G_URI_PARAMS_NONE,
1566       1, { "", "", NULL, },
1567       1, { "", "", NULL, }},
1568     { "foo", "&", G_URI_PARAMS_NONE,
1569       0, { NULL, },
1570       -1, { NULL, }},
1571     { "foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver", "&", G_URI_PARAMS_WWW_FORM,
1572       2, { "foo", "bar & baz", "saisons", "Été+hiver", NULL, },
1573       2, { "foo", "bar & baz", "saisons", "Été+hiver", NULL, }},
1574     { "foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver", "&", G_URI_PARAMS_NONE,
1575       2, { "foo", "bar+&+baz", "saisons", "Été+hiver", NULL, },
1576       2, { "foo", "bar+&+baz", "saisons", "Été+hiver", NULL, }},
1577     { "token=exp=123~acl=/QualityLevels(*~hmac=0cb", "&", G_URI_PARAMS_NONE,
1578       1, { "token", "exp=123~acl=/QualityLevels(*~hmac=0cb", NULL, },
1579       1, { "token", "exp=123~acl=/QualityLevels(*~hmac=0cb", NULL, }},
1580   };
1581
1582 static void
1583 test_uri_iter_params (gconstpointer test_data)
1584 {
1585   GError *err = NULL;
1586   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
1587   gsize i, n;
1588
1589   for (i = 0; i < G_N_ELEMENTS (params_tests); i++)
1590     {
1591       GUriParamsIter iter;
1592       gchar *uri, *attr, *value;
1593       gssize uri_len;
1594
1595       g_test_message ("URI %" G_GSIZE_FORMAT ": %s", i, params_tests[i].uri);
1596
1597       g_assert (params_tests[i].expected_n_params < 0 ||
1598                 params_tests[i].expected_n_params <= (gssize) G_N_ELEMENTS (params_tests[i].expected_param_key_values) / 2);
1599
1600       /* The tests get run twice: once with the length unspecified, using a
1601        * nul-terminated string; and once with the length specified and a copy of
1602        * the string with the trailing nul explicitly removed (to help catch
1603        * buffer overflows). */
1604       if (use_nul_terminated)
1605         {
1606           uri_len = -1;
1607           uri = g_strdup (params_tests[i].uri);
1608         }
1609       else
1610         {
1611           uri_len = strlen (params_tests[i].uri);  /* no trailing nul */
1612           uri = g_memdup2 (params_tests[i].uri, uri_len);
1613         }
1614
1615       /* Run once without extracting the attr or value, just to check the numbers. */
1616       n = 0;
1617       g_uri_params_iter_init (&iter, params_tests[i].uri, -1, params_tests[i].separators, params_tests[i].flags);
1618       while (g_uri_params_iter_next (&iter, NULL, NULL, &err))
1619         n++;
1620       g_assert_cmpint (n, ==, params_tests[i].expected_n_iter);
1621       if (err)
1622         {
1623           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1624           g_clear_error (&err);
1625         }
1626
1627       /* Run again and check the strings too. */
1628       n = 0;
1629       g_uri_params_iter_init (&iter, params_tests[i].uri, -1, params_tests[i].separators, params_tests[i].flags);
1630       while (g_uri_params_iter_next (&iter, &attr, &value, &err))
1631         {
1632           g_assert_cmpstr (attr, ==, params_tests[i].expected_iter_key_values[n * 2]);
1633           g_assert_cmpstr (value, ==, params_tests[i].expected_iter_key_values[n * 2 + 1]);
1634           n++;
1635           g_free (attr);
1636           g_free (value);
1637         }
1638       g_assert_cmpint (n, ==, params_tests[i].expected_n_iter);
1639       if (err)
1640         {
1641           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1642           g_clear_error (&err);
1643         }
1644
1645       g_free (uri);
1646     }
1647 }
1648
1649 static void
1650 test_uri_parse_params (gconstpointer test_data)
1651 {
1652   GError *err = NULL;
1653   gboolean use_nul_terminated = GPOINTER_TO_INT (test_data);
1654   gsize i;
1655
1656   for (i = 0; i < G_N_ELEMENTS (params_tests); i++)
1657     {
1658       GHashTable *params;
1659       gchar *uri = NULL;
1660       gssize uri_len;
1661
1662       g_test_message ("URI %" G_GSIZE_FORMAT ": %s", i, params_tests[i].uri);
1663
1664       g_assert (params_tests[i].expected_n_params < 0 ||
1665                 params_tests[i].expected_n_params <= (gssize) G_N_ELEMENTS (params_tests[i].expected_param_key_values) / 2);
1666
1667       /* The tests get run twice: once with the length unspecified, using a
1668        * nul-terminated string; and once with the length specified and a copy of
1669        * the string with the trailing nul explicitly removed (to help catch
1670        * buffer overflows). */
1671       if (use_nul_terminated)
1672         {
1673           uri_len = -1;
1674           uri = g_strdup (params_tests[i].uri);
1675         }
1676       else
1677         {
1678           uri_len = strlen (params_tests[i].uri);  /* no trailing nul */
1679           uri = g_memdup2 (params_tests[i].uri, uri_len);
1680         }
1681
1682       params = g_uri_parse_params (uri, uri_len, params_tests[i].separators, params_tests[i].flags, &err);
1683
1684       if (params_tests[i].expected_n_params < 0)
1685         {
1686           g_assert_null (params);
1687           g_assert_error (err, G_URI_ERROR, G_URI_ERROR_FAILED);
1688           g_clear_error (&err);
1689         }
1690       else
1691         {
1692           gsize j;
1693
1694           g_assert_no_error (err);
1695           g_assert_cmpint (g_hash_table_size (params), ==, params_tests[i].expected_n_params);
1696
1697           for (j = 0; j < (gsize) params_tests[i].expected_n_params; j += 2)
1698             g_assert_cmpstr (g_hash_table_lookup (params, params_tests[i].expected_param_key_values[j]), ==,
1699                              params_tests[i].expected_param_key_values[j + 1]);
1700         }
1701
1702       g_clear_pointer (&params, g_hash_table_unref);
1703       g_free (uri);
1704     }
1705 }
1706
1707 static void
1708 test_uri_join (void)
1709 {
1710   gchar *uri = NULL;
1711
1712   uri = g_uri_join (G_URI_FLAGS_NONE, "foo", "some:user@info", "bar", -1, "", NULL, NULL);
1713   g_assert_cmpstr (uri, ==, "foo://some:user%40info@bar");
1714   g_free (uri);
1715
1716   uri = g_uri_join (G_URI_FLAGS_NONE, NULL, NULL, NULL, -1, "/foo", "abc", NULL);
1717   g_assert_cmpstr (uri, ==, "/foo?abc");
1718   g_free (uri);
1719
1720   uri = g_uri_join (G_URI_FLAGS_NONE, NULL, NULL, "hostname", -1, "/foo", "abc", NULL);
1721   g_assert_cmpstr (uri, ==, "//hostname/foo?abc");
1722   g_free (uri);
1723
1724   uri = g_uri_join_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "pass\002", "authparams\003",
1725                               "host", 9876, "/path", "query", "fragment");
1726   g_assert_cmpstr (uri, ==, "scheme://user%01:pass%02;authparams%03@host:9876/path?query#fragment");
1727   g_free (uri);
1728
1729   uri = g_uri_join_with_user (G_URI_FLAGS_NONE, "scheme", "user\001", "pass\002", "authparams\003",
1730                               "::192.9.5.5", 9876, "/path", "query", "fragment");
1731   g_assert_cmpstr (uri, ==, "scheme://user%01:pass%02;authparams%03@[::192.9.5.5]:9876/path?query#fragment");
1732   g_free (uri);
1733
1734   uri = g_uri_join_with_user (G_URI_FLAGS_ENCODED,
1735                               "scheme", "user%01", "pass%02", "authparams%03",
1736                               "::192.9.5.5", 9876, "/path", "query", "fragment");
1737   g_assert_cmpstr (uri, ==,
1738                    "scheme://user%01:pass%02;authparams%03@[::192.9.5.5]:9876/path?query#fragment");
1739   g_free (uri);
1740
1741   uri = g_uri_join (G_URI_FLAGS_NONE, "scheme", NULL, "foo:bar._webdav._tcp.local", -1, "", NULL, NULL);
1742   g_assert_cmpstr (uri, ==, "scheme://foo%3Abar._webdav._tcp.local");
1743   g_free (uri);
1744 }
1745
1746 static void
1747 test_uri_join_split_round_trip (void)
1748 {
1749   GUriFlags flags = G_URI_FLAGS_HAS_PASSWORD | G_URI_FLAGS_HAS_AUTH_PARAMS;
1750   guint i;
1751
1752   g_test_summary ("Test that joining different URI components survives a round trip");
1753
1754   /* Each bit in @i indicates whether the corresponding URI field should be set
1755    * or %NULL. */
1756   for (i = 0; i < (1 << 8); i++)
1757     {
1758       gchar *uri = NULL;
1759       const gchar *scheme, *user, *password, *auth_params, *host, *path, *query, *fragment;
1760       gint port;
1761       gchar *scheme_out = NULL, *user_out = NULL, *password_out = NULL;
1762       gchar *auth_params_out = NULL, *host_out = NULL, *path_out = NULL;
1763       gchar *query_out = NULL, *fragment_out = NULL;
1764       gint port_out = -1;
1765       gboolean split_success;
1766       GError *local_error = NULL;
1767
1768       g_test_message ("Combination %u", i);
1769
1770       scheme = (i & (1 << 8)) ? "scheme" : NULL;
1771       host = (i & (1 << 4)) ? "host" : NULL;
1772       user = (host != NULL && i & (1 << 7)) ? "user" : NULL;  /* only supported if host is also set */
1773       password = (host != NULL && user != NULL && i & (1 << 6)) ? "password" : NULL;  /* only supported if host and user are also set */
1774       auth_params = (host != NULL && user != NULL && i & (1 << 5)) ? "auth_params" : NULL;  /* only supported if host and user are also set */
1775       port = (host != NULL && i & (1 << 3)) ? 123 : -1;  /* only supported if host is also set */
1776       path = (i & (1 << 2)) ? "/path" : "";  /* the only mandatory component */
1777       query = (i & (1 << 1)) ? "query" : NULL;
1778       fragment = (i & (1 << 0)) ? "fragment" : NULL;
1779
1780       uri = g_uri_join_with_user (flags, scheme, user, password, auth_params,
1781                                   host, port, path, query, fragment);
1782       g_assert_nonnull (uri);
1783
1784       split_success = g_uri_split_with_user (uri, flags, &scheme_out, &user_out,
1785                                              &password_out, &auth_params_out,
1786                                              &host_out, &port_out, &path_out,
1787                                              &query_out, &fragment_out,
1788                                              &local_error);
1789       g_assert_no_error (local_error);
1790       g_assert_true (split_success);
1791
1792       g_assert_cmpstr (scheme, ==, scheme_out);
1793       g_assert_cmpstr (user, ==, user_out);
1794       g_assert_cmpstr (password, ==, password_out);
1795       g_assert_cmpstr (auth_params, ==, auth_params_out);
1796       g_assert_cmpstr (host, ==, host_out);
1797       g_assert_cmpint (port, ==, port_out);
1798       g_assert_cmpstr (path, ==, path_out);
1799       g_assert_cmpstr (query, ==, query_out);
1800       g_assert_cmpstr (fragment, ==, fragment_out);
1801
1802       g_free (uri);
1803       g_free (scheme_out);
1804       g_free (user_out);
1805       g_free (password_out);
1806       g_free (auth_params_out);
1807       g_free (host_out);
1808       g_free (path_out);
1809       g_free (query_out);
1810       g_free (fragment_out);
1811     }
1812 }
1813
1814 static const struct
1815 {
1816   /* Inputs */
1817   const gchar *base;
1818   const gchar *uri;
1819   GUriFlags flags;
1820   /* Outputs */
1821   const gchar *uri_string;
1822   const gchar *path;
1823   int port;
1824 } normalize_parse_tests[] =
1825   {
1826     { NULL, "http://foo/path with spaces", G_URI_FLAGS_ENCODED,
1827       "http://foo/path%20with%20spaces", "/path%20with%20spaces", -1 },
1828     { NULL, "http://foo/path with spaces 2", G_URI_FLAGS_ENCODED_PATH,
1829       "http://foo/path%20with%20spaces%202", "/path%20with%20spaces%202", -1 },
1830     { NULL, "http://foo/%aa", G_URI_FLAGS_ENCODED,
1831       "http://foo/%AA", "/%AA", -1 },
1832     { NULL, "http://foo/p\xc3\xa4th/", G_URI_FLAGS_ENCODED | G_URI_FLAGS_PARSE_RELAXED,
1833       "http://foo/p%C3%A4th/", "/p%C3%A4th/", -1 },
1834     { NULL, "http://foo", G_URI_FLAGS_NONE,
1835       "http://foo", "", -1 },
1836     { NULL, "http://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1837       "http://foo/", "/", 80 },
1838     { NULL, "nothttp://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1839       "nothttp://foo", "", -1 },
1840     { NULL, "http://foo:80", G_URI_FLAGS_NONE,
1841       "http://foo:80", "", 80 },
1842     { NULL, "http://foo:80", G_URI_FLAGS_SCHEME_NORMALIZE,
1843       "http://foo/", "/", 80 },
1844     { NULL, "http://foo:8080", G_URI_FLAGS_SCHEME_NORMALIZE,
1845       "http://foo:8080/", "/", 8080 },
1846     { NULL, "https://foo:443", G_URI_FLAGS_SCHEME_NORMALIZE,
1847       "https://foo/", "/", 443 },
1848     { NULL, "https://foo:943", G_URI_FLAGS_SCHEME_NORMALIZE,
1849       "https://foo:943/", "/", 943 },
1850     { NULL, "ws://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1851       "ws://foo/", "/", 80 },
1852     { NULL, "wss://foo:443", G_URI_FLAGS_SCHEME_NORMALIZE,
1853       "wss://foo/", "/", 443 },
1854     { NULL, "ftp://foo", G_URI_FLAGS_NONE,
1855       "ftp://foo", "", -1 },
1856     { NULL, "ftp://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1857       "ftp://foo", "", 21 },
1858     { NULL, "ftp://foo:21", G_URI_FLAGS_SCHEME_NORMALIZE,
1859       "ftp://foo", "", 21 },
1860     { NULL, "ftp://foo:2100", G_URI_FLAGS_SCHEME_NORMALIZE,
1861       "ftp://foo:2100", "", 2100 },
1862     { NULL, "nothttp://foo:80", G_URI_FLAGS_SCHEME_NORMALIZE,
1863       "nothttp://foo:80", "", 80 },
1864     { "http://foo", "//bar", G_URI_FLAGS_SCHEME_NORMALIZE,
1865       "http://bar/", "/", 80 },
1866     { "http://foo", "//bar:80", G_URI_FLAGS_SCHEME_NORMALIZE,
1867       "http://bar/", "/", 80 },
1868     { "nothttp://foo", "//bar:80", G_URI_FLAGS_SCHEME_NORMALIZE,
1869       "nothttp://bar:80", "", 80 },
1870     { "http://foo", "//bar", G_URI_FLAGS_NONE,
1871       "http://bar", "", -1 },
1872     { "ScHeMe://User:P%61ss@HOST.%63om:1234/path",
1873       "ScHeMe://User:P%61ss@HOST.%63om:1234/path/./from/../to%7d/item%2dobj?qu%65ry=something#fr%61gment",
1874       G_URI_FLAGS_SCHEME_NORMALIZE,
1875       "scheme://User:Pass@HOST.com:1234/path/to%7D/item-obj?query=something#fragment",
1876       "/path/to}/item-obj", 1234 },
1877   };
1878
1879 static const struct
1880 {
1881   /* Inputs */
1882   const gchar *uri;
1883   GUriFlags flags;
1884   /* Outputs */
1885   const char *scheme;
1886   const gchar *path;
1887   int port;
1888 } normalize_split_tests[] =
1889   {
1890     { "HTTP://foo", G_URI_FLAGS_ENCODED,
1891       "http", "", -1 },
1892     { "HTTP://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1893       "http", "/", 80 },
1894     { "http://foo:80/", G_URI_FLAGS_SCHEME_NORMALIZE,
1895       "http", "/", 80 },
1896     { "http://foo:8080/bar", G_URI_FLAGS_SCHEME_NORMALIZE,
1897       "http", "/bar", 8080 },
1898     { "ws://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1899       "ws", "/", 80 },
1900     { "https://foo", G_URI_FLAGS_ENCODED,
1901       "https", "", -1 },
1902     { "https://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1903       "https", "/", 443 },
1904     { "https://foo:443/", G_URI_FLAGS_SCHEME_NORMALIZE,
1905       "https", "/", 443 },
1906     { "wss://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1907       "wss", "/", 443 },
1908     { "ftp://foo", G_URI_FLAGS_ENCODED,
1909       "ftp", "", -1 },
1910     { "ftp://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1911       "ftp", "", 21 },
1912     { "ftp://foo:21", G_URI_FLAGS_SCHEME_NORMALIZE,
1913       "ftp", "", 21 },
1914     { "scheme://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1915       "scheme", "", -1 },
1916     { "socks://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1917       "socks", "", 1080 },
1918     { "socks4://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1919       "socks4", "", 1080 },
1920     { "socks4a://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1921       "socks4a", "", 1080 },
1922     { "socks5://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1923       "socks5", "", 1080 },
1924     { "socks5h://foo", G_URI_FLAGS_SCHEME_NORMALIZE,
1925       "socks5h", "", 1080 },
1926   };
1927
1928 static const struct
1929 {
1930   /* Inputs */
1931   GUriFlags flags;
1932   const gchar *scheme;
1933   const gchar *host;
1934   int port;
1935   const gchar *path;
1936   /* Outputs */
1937   const gchar *uri;
1938 } normalize_join_tests[] =
1939   {
1940     { G_URI_FLAGS_NONE, "http", "foo", -1, "",
1941       "http://foo" },
1942     { G_URI_FLAGS_SCHEME_NORMALIZE, "http", "foo", -1, "",
1943       "http://foo/" },
1944     { G_URI_FLAGS_SCHEME_NORMALIZE, "http", "foo", 80, "",
1945       "http://foo/" },
1946     { G_URI_FLAGS_SCHEME_NORMALIZE, "http", "foo", 8080, "",
1947       "http://foo:8080/" },
1948     { G_URI_FLAGS_NONE, "http", "foo", 80, "",
1949       "http://foo:80" },
1950     { G_URI_FLAGS_SCHEME_NORMALIZE, "ws", "foo", 80, "",
1951       "ws://foo/" },
1952     { G_URI_FLAGS_NONE, "https", "foo", -1, "",
1953       "https://foo" },
1954     { G_URI_FLAGS_SCHEME_NORMALIZE, "https", "foo", -1, "",
1955       "https://foo/" },
1956     { G_URI_FLAGS_SCHEME_NORMALIZE, "https", "foo", 443, "",
1957       "https://foo/" },
1958     { G_URI_FLAGS_SCHEME_NORMALIZE, "https", "foo", 943, "",
1959       "https://foo:943/" },
1960     { G_URI_FLAGS_NONE, "https", "foo", 443, "",
1961       "https://foo:443" },
1962     { G_URI_FLAGS_SCHEME_NORMALIZE, "wss", "foo", 443, "",
1963       "wss://foo/" },
1964     { G_URI_FLAGS_NONE, "ftp", "foo", -1, "",
1965       "ftp://foo" },
1966     { G_URI_FLAGS_SCHEME_NORMALIZE, "ftp", "foo", -1, "",
1967       "ftp://foo" },
1968     { G_URI_FLAGS_SCHEME_NORMALIZE, "ftp", "foo", 21, "",
1969       "ftp://foo" },
1970     { G_URI_FLAGS_SCHEME_NORMALIZE, "ftp", "foo", 2020, "",
1971       "ftp://foo:2020" },
1972     { G_URI_FLAGS_NONE, "ftp", "foo", 21, "",
1973       "ftp://foo:21" },
1974     { G_URI_FLAGS_SCHEME_NORMALIZE, "scheme", "foo", 80, "",
1975       "scheme://foo:80" },
1976   };
1977
1978 static void
1979 test_uri_normalize (void)
1980 {
1981   gsize i;
1982   int port;
1983   char *path;
1984   char *uri_string;
1985
1986   for (i = 0; i < G_N_ELEMENTS (normalize_parse_tests); ++i)
1987     {
1988       GUri *uri, *base = NULL;
1989
1990       if (normalize_parse_tests[i].base)
1991         base = g_uri_parse (normalize_parse_tests[i].base, normalize_parse_tests[i].flags, NULL);
1992
1993       uri = g_uri_parse_relative (base,
1994                                   normalize_parse_tests[i].uri,
1995                                   normalize_parse_tests[i].flags,
1996                                   NULL);
1997       uri_string = g_uri_to_string (uri);
1998
1999       g_assert_nonnull (uri);
2000       g_assert_cmpstr (g_uri_get_path (uri), ==, normalize_parse_tests[i].path);
2001       g_assert_cmpint (g_uri_get_port (uri), ==, normalize_parse_tests[i].port);
2002       g_assert_cmpstr (uri_string, ==, normalize_parse_tests[i].uri_string);
2003
2004       g_free (uri_string);
2005       g_uri_unref (uri);
2006       if (base)
2007         g_uri_unref (base);
2008     }
2009
2010   for (i = 0; i < G_N_ELEMENTS (normalize_split_tests); ++i)
2011     {
2012       char *scheme;
2013
2014       /* Testing a codepath where scheme is NULL but internally we still normalize it. */
2015       g_assert_true (g_uri_split (normalize_split_tests[i].uri, normalize_split_tests[i].flags,
2016                                   NULL, NULL, NULL, &port, &path, NULL, NULL, NULL));
2017       g_assert_cmpstr (path, ==, normalize_split_tests[i].path);
2018       g_assert_cmpint (port, ==, normalize_split_tests[i].port);
2019       g_free (path);
2020
2021       g_assert_true (g_uri_split (normalize_split_tests[i].uri, normalize_split_tests[i].flags,
2022                                   &scheme, NULL, NULL, &port, &path, NULL, NULL, NULL));
2023       g_assert_cmpstr (scheme, ==, normalize_split_tests[i].scheme);
2024       g_assert_cmpstr (path, ==, normalize_split_tests[i].path);
2025       g_assert_cmpint (port, ==, normalize_split_tests[i].port);
2026       g_free (scheme);
2027       g_free (path);
2028     }
2029
2030   for (i = 0; i < G_N_ELEMENTS (normalize_join_tests); ++i)
2031     {
2032       uri_string = g_uri_join (normalize_join_tests[i].flags, normalize_join_tests[i].scheme, NULL,
2033                                normalize_join_tests[i].host, normalize_join_tests[i].port,
2034                                normalize_join_tests[i].path, NULL, NULL);
2035       g_assert_cmpstr (uri_string, ==, normalize_join_tests[i].uri);
2036       g_free (uri_string);
2037     }
2038 }
2039
2040 int
2041 main (int   argc,
2042       char *argv[])
2043 {
2044   g_test_init (&argc, &argv, NULL);
2045
2046   g_test_add_func ("/uri/file-to-uri", run_file_to_uri_tests);
2047   g_test_add_func ("/uri/file-from-uri", run_file_from_uri_tests);
2048   g_test_add_func ("/uri/file-roundtrip", run_file_roundtrip_tests);
2049   g_test_add_func ("/uri/list", run_uri_list_tests);
2050   g_test_add_func ("/uri/unescape-string", test_uri_unescape_string);
2051   g_test_add_data_func ("/uri/unescape-bytes/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_unescape_bytes);
2052   g_test_add_data_func ("/uri/unescape-bytes/length", GINT_TO_POINTER (FALSE), test_uri_unescape_bytes);
2053   g_test_add_func ("/uri/unescape-segment", test_uri_unescape_segment);
2054   g_test_add_func ("/uri/escape-string", test_uri_escape_string);
2055   g_test_add_func ("/uri/escape-bytes", test_uri_escape_bytes);
2056   g_test_add_func ("/uri/scheme", test_uri_scheme);
2057   g_test_add_func ("/uri/parsing/absolute", test_uri_parsing_absolute);
2058   g_test_add_func ("/uri/parsing/relative", test_uri_parsing_relative);
2059   g_test_add_func ("/uri/build", test_uri_build);
2060   g_test_add_func ("/uri/split", test_uri_split);
2061   g_test_add_func ("/uri/is_valid", test_uri_is_valid);
2062   g_test_add_func ("/uri/to-string", test_uri_to_string);
2063   g_test_add_func ("/uri/join", test_uri_join);
2064   g_test_add_func ("/uri/join-split-round-trip", test_uri_join_split_round_trip);
2065   g_test_add_func ("/uri/normalize", test_uri_normalize);
2066   g_test_add_data_func ("/uri/iter-params/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_iter_params);
2067   g_test_add_data_func ("/uri/iter-params/length", GINT_TO_POINTER (FALSE), test_uri_iter_params);
2068   g_test_add_data_func ("/uri/parse-params/nul-terminated", GINT_TO_POINTER (TRUE), test_uri_parse_params);
2069   g_test_add_data_func ("/uri/parse-params/length", GINT_TO_POINTER (FALSE), test_uri_parse_params);
2070
2071   return g_test_run ();
2072 }