2 * Copyright (c) 2020 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 #define TEST(uri, fail, exp, descr) \
24 if (!run_test(uri, fail, exp)) { \
25 fprintf(stderr, "%s:%d: error: %s\n", \
26 __FILE__, __LINE__, descr); \
30 #define URI(schema, host, port, path, query, frag) \
31 ((struct uri){schema, host, port, 0, path, query, frag})
33 #define DIFF(wanted, got, field) \
34 if (wanted->field == NULL || got->field == NULL || \
35 strcmp(wanted->field, got->field)) { \
36 fprintf(stderr, #field ":\n\tgot: %s\n\twanted: %s\n", \
37 got->field, wanted->field); \
45 diff_uri(struct uri *p, struct uri *exp)
52 DIFF(p, exp, fragment);
57 run_test(const char *uri, int should_fail, struct uri expected)
64 if ((uri_copy = strdup(uri)) == NULL)
67 fprintf(stderr, "=> %s\n", uri);
68 failed = !parse_uri(uri_copy, &parsed, &error);
70 if (failed && should_fail)
74 fprintf(stderr, "> %s\n", error);
76 ok = !failed && !should_fail;
78 ok = diff_uri(&expected, &parsed);
88 struct uri empty = {"", "", "", PASS, "", "", ""};
90 TEST("http://omarpolo.com",
92 URI("http", "omarpolo.com", "", "", "", ""),
93 "can parse uri with empty path");
96 TEST("omarpolo.com", FAIL, empty, "FAIL when the schema is missing");
97 TEST("gemini:/omarpolo.com", FAIL, empty, "FAIL with invalid marker");
98 TEST("gemini//omarpolo.com", FAIL, empty, "FAIL with invalid marker");
99 TEST("h!!p://omarpolo.com", FAIL, empty, "FAIL with invalid schema");
102 TEST("gemini://omarpolo.com",
104 URI("gemini", "omarpolo.com", "", "", "", ""),
105 "can parse authority with empty path");
106 TEST("gemini://omarpolo.com/",
108 URI("gemini", "omarpolo.com", "", "", "", ""),
109 "can parse authority with empty path (alt)")
110 TEST("gemini://omarpolo.com:1965",
112 URI("gemini", "omarpolo.com", "1965", "", "", ""),
113 "can parse with port and empty path");
114 TEST("gemini://omarpolo.com:1965/",
116 URI("gemini", "omarpolo.com", "1965", "", "", ""),
117 "can parse with port and empty path")
118 TEST("gemini://omarpolo.com:196s",
121 "FAIL with invalid port number");
124 TEST("gemini://omarpolo.com/foo/bar/baz",
126 URI("gemini", "omarpolo.com", "", "foo/bar/baz", "", ""),
127 "parse simple paths");
128 TEST("gemini://omarpolo.com/foo//bar///baz",
130 URI("gemini", "omarpolo.com", "", "foo/bar/baz", "", ""),
131 "parse paths with multiple slashes");
132 TEST("gemini://omarpolo.com/foo/./bar/./././baz",
134 URI("gemini", "omarpolo.com", "", "foo/bar/baz", "", ""),
135 "parse paths with . elements");
136 TEST("gemini://omarpolo.com/foo/bar/../bar/baz",
138 URI("gemini", "omarpolo.com", "", "foo/bar/baz", "", ""),
139 "parse paths with .. elements");
140 TEST("gemini://omarpolo.com/foo/../foo/bar/../bar/baz/../baz",
142 URI("gemini", "omarpolo.com", "", "foo/bar/baz", "", ""),
143 "parse paths with multiple .. elements");
144 TEST("gemini://omarpolo.com/foo/..",
146 URI("gemini", "omarpolo.com", "", "", "", ""),
147 "parse paths with a trailing ..");
148 TEST("gemini://omarpolo.com/foo/../",
150 URI("gemini", "omarpolo.com", "", "", "", ""),
151 "parse paths with a trailing ..");
152 TEST("gemini://omarpolo.com/foo/../..",
155 "reject paths that would escape the root");
158 TEST("foo://example.com/foo/?gne",
160 URI("foo", "example.com", "", "foo/", "gne", ""),
161 "parse query strings");
162 TEST("foo://example.com/foo/?gne&foo",
164 URI("foo", "example.com", "", "foo/", "gne&foo", ""),
165 "parse query strings");
166 TEST("foo://example.com/foo/?gne%2F",
168 URI("foo", "example.com", "", "foo/", "gne/", ""),
169 "parse query strings");
172 TEST("foo://bar.co/#foo",
174 URI("foo", "bar.co", "", "", "", "foo"),
175 "can recognize fragments");
177 /* percent encoding */
178 TEST("foo://bar.com/caf%C3%A8.gmi",
180 URI("foo", "bar.com", "", "cafè.gmi", "", ""),