// Copyright 2011-2023 David Robillard // SPDX-License-Identifier: ISC #undef NDEBUG #include "serd/memory.h" #include "serd/node.h" #include "serd/string_view.h" #include "serd/uri.h" #include #include #include #include static void test_uri_string_has_scheme(void) { assert(!serd_uri_string_has_scheme("relative")); assert(!serd_uri_string_has_scheme("http")); assert(!serd_uri_string_has_scheme("5nostartdigit")); assert(!serd_uri_string_has_scheme("+nostartplus")); assert(!serd_uri_string_has_scheme("-nostartminus")); assert(!serd_uri_string_has_scheme(".nostartdot")); assert(!serd_uri_string_has_scheme(":missing")); assert(!serd_uri_string_has_scheme("a/slash/is/not/a/scheme/char")); assert(serd_uri_string_has_scheme("http://example.org/")); assert(serd_uri_string_has_scheme("https://example.org/")); assert(serd_uri_string_has_scheme("allapha:path")); assert(serd_uri_string_has_scheme("w1thd1g1t5:path")); assert(serd_uri_string_has_scheme("with.dot:path")); assert(serd_uri_string_has_scheme("with+plus:path")); assert(serd_uri_string_has_scheme("with-minus:path")); } static void test_file_uri(const char* const hostname, const char* const path, const char* const expected_uri, const char* expected_path) { if (!expected_path) { expected_path = path; } SerdNode node = serd_node_new_file_uri(path, hostname, 0); char* out_hostname = NULL; char* out_path = serd_file_uri_parse(node.buf, &out_hostname); assert(!strcmp(node.buf, expected_uri)); assert((hostname && out_hostname) || (!hostname && !out_hostname)); assert(!hostname || !strcmp(hostname, out_hostname)); assert(!strcmp(out_path, expected_path)); serd_free(out_path); serd_free(out_hostname); serd_node_free(&node); } static void test_uri_parsing(void) { test_file_uri(NULL, "C:/My 100%", "file:///C:/My%20100%%", NULL); test_file_uri(NULL, "/foo/bar", "file:///foo/bar", NULL); test_file_uri("bhost", "/foo/bar", "file://bhost/foo/bar", NULL); test_file_uri(NULL, "a/relative ", "a/relative%20%3Cpath%3E", NULL); #ifdef _WIN32 test_file_uri(NULL, "C:\\My 100%", "file:///C:/My%20100%%", "C:/My 100%"); test_file_uri( NULL, "\\drive\\relative", "file:///drive/relative", "/drive/relative"); test_file_uri(NULL, "C:\\Program Files\\Serd", "file:///C:/Program%20Files/Serd", "C:/Program Files/Serd"); test_file_uri("ahost", "C:\\Pointless Space", "file://ahost/C:/Pointless%20Space", "C:/Pointless Space"); #else /* What happens with Windows paths on other platforms is a bit weird, but more or less unavoidable. It doesn't work to interpret backslashes as path separators on any other platform. */ test_file_uri("ahost", "C:\\Pointless Space", "file://ahost/C:%5CPointless%20Space", "/C:\\Pointless Space"); test_file_uri( NULL, "\\drive\\relative", "%5Cdrive%5Crelative", "\\drive\\relative"); test_file_uri(NULL, "C:\\Program Files\\Serd", "file:///C:%5CProgram%20Files%5CSerd", "/C:\\Program Files\\Serd"); test_file_uri("ahost", "C:\\Pointless Space", "file://ahost/C:%5CPointless%20Space", "/C:\\Pointless Space"); #endif // Test tolerance of parsing junk URI escapes char* out_path = serd_file_uri_parse("file:///foo/%0Xbar", NULL); assert(!strcmp(out_path, "/foo/bar")); serd_free(out_path); } static void test_uri_from_string(void) { SerdNode nonsense = serd_node_new_uri_from_string(NULL, NULL, NULL); assert(nonsense.type == SERD_NOTHING); SerdURIView base_uri; SerdNode base = serd_node_new_uri_from_string("http://example.org/", NULL, &base_uri); SerdNode nil = serd_node_new_uri_from_string(NULL, &base_uri, NULL); SerdNode nil2 = serd_node_new_uri_from_string("", &base_uri, NULL); assert(nil.type == SERD_URI); assert(!strcmp(nil.buf, base.buf)); assert(nil2.type == SERD_URI); assert(!strcmp(nil2.buf, base.buf)); serd_node_free(&nil); serd_node_free(&nil2); serd_node_free(&base); } static inline bool chunk_equals(const SerdStringView* a, const SerdStringView* b) { return (!a->len && !b->len && !a->buf && !b->buf) || (a->len && b->len && a->buf && b->buf && !strncmp((const char*)a->buf, (const char*)b->buf, a->len)); } static void check_relative_uri(const char* const uri_string, const char* const base_string, const char* const root_string, const char* const expected_string) { assert(uri_string); assert(base_string); assert(expected_string); SerdURIView uri = SERD_URI_NULL; SerdURIView base = SERD_URI_NULL; SerdURIView result = SERD_URI_NULL; SerdNode uri_node = serd_node_new_uri_from_string(uri_string, NULL, &uri); SerdNode base_node = serd_node_new_uri_from_string(base_string, NULL, &base); SerdNode result_node = SERD_NODE_NULL; if (root_string) { SerdURIView root = SERD_URI_NULL; SerdNode root_node = serd_node_new_uri_from_string(root_string, NULL, &root); result_node = serd_node_new_relative_uri(&uri, &base, &root, &result); serd_node_free(&root_node); } else { result_node = serd_node_new_relative_uri(&uri, &base, NULL, &result); } assert(!strcmp((const char*)result_node.buf, expected_string)); SerdURIView expected = SERD_URI_NULL; assert(!serd_uri_parse(expected_string, &expected)); assert(chunk_equals(&result.scheme, &expected.scheme)); assert(chunk_equals(&result.authority, &expected.authority)); assert(chunk_equals(&result.path_base, &expected.path_base)); assert(chunk_equals(&result.path, &expected.path)); assert(chunk_equals(&result.query, &expected.query)); assert(chunk_equals(&result.fragment, &expected.fragment)); serd_node_free(&result_node); serd_node_free(&base_node); serd_node_free(&uri_node); } static void test_relative_uri(void) { // Unrelated base check_relative_uri("http://example.org/a/b", "ftp://example.org/", NULL, "http://example.org/a/b"); check_relative_uri("http://example.org/a/b", "http://example.com/", NULL, "http://example.org/a/b"); // Related base check_relative_uri( "http://example.org/a/b", "http://example.org/", NULL, "a/b"); check_relative_uri( "http://example.org/a/b", "http://example.org/a/", NULL, "b"); check_relative_uri( "http://example.org/a/b", "http://example.org/a/b", NULL, ""); check_relative_uri( "http://example.org/a/b", "http://example.org/a/b/", NULL, "../b"); check_relative_uri( "http://example.org/a/b/", "http://example.org/a/b/", NULL, ""); check_relative_uri("http://example.org/", "http://example.org/", NULL, ""); check_relative_uri("http://example.org/", "http://example.org/a", NULL, ""); check_relative_uri( "http://example.org/", "http://example.org/a/", NULL, "../"); check_relative_uri( "http://example.org/", "http://example.org/a/b", NULL, "../"); check_relative_uri( "http://example.org/", "http://example.org/a/b/", NULL, "../../"); // Unrelated root check_relative_uri("http://example.org/", "http://example.org/a/b", "relative", "http://example.org/"); check_relative_uri("http://example.org/", "http://example.org/a/b", "ftp://example.org/", "http://example.org/"); check_relative_uri("http://example.org/", "http://example.org/a/b", "http://example.com/", "http://example.org/"); // Related root check_relative_uri("http://example.org/a/b", "http://example.org/", "http://example.org/c/d", "http://example.org/a/b"); check_relative_uri("http://example.org/", "http://example.org/a/b", "http://example.org/a/b", "http://example.org/"); check_relative_uri("http://example.org/a/b", "http://example.org/a/b", "http://example.org/a/b", ""); check_relative_uri("http://example.org/a/", "http://example.org/a/", "http://example.org/a/", ""); check_relative_uri("http://example.org/a/b", "http://example.org/a/b/c", "http://example.org/a/b", "../b"); check_relative_uri("http://example.org/a", "http://example.org/a/b/c", "http://example.org/a/b", "http://example.org/a"); } int main(void) { test_uri_string_has_scheme(); test_uri_parsing(); test_uri_from_string(); test_relative_uri(); printf("Success\n"); return 0; }