// Copyright 2011-2023 David Robillard // SPDX-License-Identifier: ISC #undef NDEBUG #include "serd/serd.h" #include #include #include #include #include #define USTR(s) ((const uint8_t*)(s)) static void test_uri_string_has_scheme(void) { assert(!serd_uri_string_has_scheme(USTR("relative"))); assert(!serd_uri_string_has_scheme(USTR("http"))); assert(!serd_uri_string_has_scheme(USTR("5nostartdigit"))); assert(!serd_uri_string_has_scheme(USTR("+nostartplus"))); assert(!serd_uri_string_has_scheme(USTR("-nostartminus"))); assert(!serd_uri_string_has_scheme(USTR(".nostartdot"))); assert(!serd_uri_string_has_scheme(USTR(":missing"))); assert(!serd_uri_string_has_scheme(USTR("a/slash/is/not/a/scheme/char"))); assert(serd_uri_string_has_scheme(USTR("http://example.org/"))); assert(serd_uri_string_has_scheme(USTR("https://example.org/"))); assert(serd_uri_string_has_scheme(USTR("allapha:path"))); assert(serd_uri_string_has_scheme(USTR("w1thd1g1t5:path"))); assert(serd_uri_string_has_scheme(USTR("with.dot:path"))); assert(serd_uri_string_has_scheme(USTR("with+plus:path"))); assert(serd_uri_string_has_scheme(USTR("with-minus:path"))); } static void test_file_uri(const char* const hostname, const char* const path, const bool escape, const char* const expected_uri, const char* expected_path) { if (!expected_path) { expected_path = path; } SerdNode node = serd_node_new_file_uri(USTR(path), USTR(hostname), 0, escape); uint8_t* out_hostname = NULL; uint8_t* out_path = serd_file_uri_parse(node.buf, &out_hostname); assert(!strcmp((const char*)node.buf, expected_uri)); assert((hostname && out_hostname) || (!hostname && !out_hostname)); assert(!hostname || !strcmp(hostname, (const char*)out_hostname)); assert(!strcmp((const char*)out_path, (const char*)expected_path)); serd_free(out_path); serd_free(out_hostname); serd_node_free(&node); } #if defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif static void test_uri_to_path(void) { assert(!strcmp( (const char*)serd_uri_to_path((const uint8_t*)"file:///home/user/foo.ttl"), "/home/user/foo.ttl")); assert(!strcmp((const char*)serd_uri_to_path( (const uint8_t*)"file://localhost/home/user/foo.ttl"), "/home/user/foo.ttl")); assert(!serd_uri_to_path((const uint8_t*)"file:illegal/file/uri")); assert(!strcmp( (const char*)serd_uri_to_path((const uint8_t*)"file:///c:/awful/system"), "c:/awful/system")); assert(!strcmp( (const char*)serd_uri_to_path((const uint8_t*)"file:///c:awful/system"), "/c:awful/system")); assert(!strcmp((const char*)serd_uri_to_path((const uint8_t*)"file:///0/1"), "/0/1")); assert( !strcmp((const char*)serd_uri_to_path((const uint8_t*)"C:\\Windows\\Sucks"), "C:\\Windows\\Sucks")); assert( !strcmp((const char*)serd_uri_to_path((const uint8_t*)"C|/Windows/Sucks"), "C|/Windows/Sucks")); assert(!serd_uri_to_path((const uint8_t*)"http://example.org/path")); } #if defined(__GNUC__) # pragma GCC diagnostic pop #endif static void test_uri_parsing(void) { test_file_uri(NULL, "C:/My 100%", true, "file:///C:/My%20100%%", NULL); test_file_uri(NULL, "/foo/bar", true, "file:///foo/bar", NULL); test_file_uri("bhost", "/foo/bar", true, "file://bhost/foo/bar", NULL); test_file_uri(NULL, "a/relative path", false, "a/relative path", NULL); test_file_uri( NULL, "a/relative ", true, "a/relative%20%3Cpath%3E", NULL); #ifdef _WIN32 test_file_uri( NULL, "C:\\My 100%", true, "file:///C:/My%20100%%", "C:/My 100%"); test_file_uri(NULL, "\\drive\\relative", true, "file:///drive/relative", "/drive/relative"); test_file_uri(NULL, "C:\\Program Files\\Serd", true, "file:///C:/Program%20Files/Serd", "C:/Program Files/Serd"); test_file_uri("ahost", "C:\\Pointless Space", true, "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", true, "file://ahost/C:%5CPointless%20Space", "/C:\\Pointless Space"); test_file_uri(NULL, "\\drive\\relative", true, "%5Cdrive%5Crelative", "\\drive\\relative"); test_file_uri(NULL, "C:\\Program Files\\Serd", true, "file:///C:%5CProgram%20Files%5CSerd", "/C:\\Program Files\\Serd"); test_file_uri("ahost", "C:\\Pointless Space", true, "file://ahost/C:%5CPointless%20Space", "/C:\\Pointless Space"); #endif // Test tolerance of parsing junk URI escapes uint8_t* out_path = serd_file_uri_parse(USTR("file:///foo/%0Xbar"), NULL); assert(!strcmp((const char*)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); SerdURI base_uri; SerdNode base = serd_node_new_uri_from_string(USTR("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(USTR(""), &base_uri, NULL); assert(nil.type == SERD_URI); assert(!strcmp((const char*)nil.buf, (const char*)base.buf)); assert(nil2.type == SERD_URI); assert(!strcmp((const char*)nil2.buf, (const char*)base.buf)); serd_node_free(&nil); serd_node_free(&nil2); serd_node_free(&base); } static inline bool chunk_equals(const SerdChunk* a, const SerdChunk* 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); SerdURI uri = SERD_URI_NULL; SerdURI base = SERD_URI_NULL; SerdURI result = SERD_URI_NULL; SerdNode uri_node = serd_node_new_uri_from_string(USTR(uri_string), NULL, &uri); SerdNode base_node = serd_node_new_uri_from_string(USTR(base_string), NULL, &base); SerdNode result_node = SERD_NODE_NULL; if (root_string) { SerdURI root = SERD_URI_NULL; SerdNode root_node = serd_node_new_uri_from_string(USTR(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)); SerdURI expected = SERD_URI_NULL; assert(!serd_uri_parse(USTR(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 /* Expected: check_relative_uri( "http://example.org/a/b", "http://example.org/", NULL, "a/b"); Actual: */ 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, ""); /* Expected: check_relative_uri("http://example.org/", "http://example.org/a", NULL, ""); Actual: */ 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/", /* Expected: ""); Actual: */ "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", /* Expected: "http://example.org/a"); Actual: */ "../../a"); } int main(void) { test_uri_to_path(); test_uri_string_has_scheme(); test_uri_parsing(); test_uri_from_string(); test_relative_uri(); printf("Success\n"); return 0; }