// Tests for uri.h - URI parsing and serialization #define URI_IMPLEMENTATION #include "../include/uri.h" #include #include #include #define ASSERT(cond, msg) \ do { \ if (!(cond)) { \ fprintf(stderr, "FAIL: %s (test_uri.c:%d)\n", msg, __LINE__); \ failures++; \ } \ } while (0) #define ASSERT_STR_EQ(a, b, msg) \ do { \ if ((a) == NULL || (b) == NULL || strcmp((a), (b)) != 0) { \ fprintf(stderr, "FAIL: %s (got '%s', expected '%s') (test_uri.c:%d)\n", \ msg, (a) ? (a) : "NULL", (b) ? (b) : "NULL", __LINE__); \ failures++; \ } \ } while (0) static int failures = 0; void test_init() { uri_t *u = uri_init(); ASSERT(u != NULL, "init returns non-NULL"); for (int i = 0; i < URI_END; i++) { ASSERT(u->data[i] == NULL, "all fields NULL"); } uri_free(u); } void test_set_and_get() { uri_t *u = uri_init(); uri_set(u, SCHEME, "https"); uri_set(u, HOST, "example.com"); uri_set(u, PATH, "/api/v1"); char *uri = uri_get(u); ASSERT(uri != NULL, "get returns non-NULL"); ASSERT_STR_EQ(uri, "https://example.com/api/v1", "constructed URI"); free(uri); uri_free(u); } void test_set_with_port() { uri_t *u = uri_init(); uri_set(u, SCHEME, "http"); uri_set(u, HOST, "localhost"); uri_set(u, PORT, "8080"); char *uri = uri_get(u); ASSERT_STR_EQ(uri, "http://localhost:8080", "URI with port"); free(uri); uri_free(u); } void test_set_with_query() { uri_t *u = uri_init(); uri_set(u, SCHEME, "https"); uri_set(u, HOST, "example.com"); uri_set(u, PATH, "/search"); uri_set(u, QUERY, "q=test"); char *uri = uri_get(u); ASSERT_STR_EQ(uri, "https://example.com/search?q=test", "URI with query"); free(uri); uri_free(u); } void test_set_with_fragment() { uri_t *u = uri_init(); uri_set(u, SCHEME, "https"); uri_set(u, HOST, "example.com"); uri_set(u, PATH, "/page"); uri_set(u, FRAGMENT, "section"); char *uri = uri_get(u); ASSERT_STR_EQ(uri, "https://example.com/page#section", "URI with fragment"); free(uri); uri_free(u); } void test_full_uri() { uri_t *u = uri_init(); uri_set(u, SCHEME, "https"); uri_set(u, USERINFO, "user:pass"); uri_set(u, HOST, "example.com"); uri_set(u, PORT, "443"); uri_set(u, PATH, "/path/to/resource"); uri_set(u, QUERY, "key=value"); uri_set(u, FRAGMENT, "anchor"); char *uri = uri_get(u); ASSERT(uri != NULL, "full URI constructed"); uri_free(u); free(uri); } void test_parse_basic() { uri_t *u = uri_parse("https://example.com/path"); ASSERT(u != NULL, "parse returns non-NULL"); ASSERT_STR_EQ(u->data[SCHEME], "https", "parsed scheme"); ASSERT_STR_EQ(u->data[HOST], "example.com", "parsed host"); ASSERT_STR_EQ(u->data[PATH], "/path", "parsed path"); uri_free(u); } void test_parse_with_port() { uri_t *u = uri_parse("http://localhost:8080/api"); ASSERT(u != NULL, "parse with port"); ASSERT_STR_EQ(u->data[SCHEME], "http", "scheme"); ASSERT_STR_EQ(u->data[HOST], "localhost", "host"); ASSERT_STR_EQ(u->data[PORT], "8080", "port"); ASSERT_STR_EQ(u->data[PATH], "/api", "path"); uri_free(u); } void test_parse_with_query() { uri_t *u = uri_parse("https://example.com/search?q=hello"); ASSERT(u != NULL, "parse with query"); ASSERT_STR_EQ(u->data[QUERY], "q=hello", "query parsed"); uri_free(u); } void test_parse_with_fragment() { uri_t *u = uri_parse("https://example.com/page#top"); ASSERT(u != NULL, "parse with fragment"); ASSERT_STR_EQ(u->data[FRAGMENT], "top", "fragment parsed"); uri_free(u); } void test_parse_full() { uri_t *u = uri_parse("https://user:pass@example.com:443/path?q=1#frag"); ASSERT(u != NULL, "parse full URI"); ASSERT_STR_EQ(u->data[SCHEME], "https", "scheme"); ASSERT_STR_EQ(u->data[USERINFO], "user:pass", "userinfo parsed"); ASSERT_STR_EQ(u->data[HOST], "example.com", "host parsed"); ASSERT_STR_EQ(u->data[PORT], "443", "port parsed"); ASSERT_STR_EQ(u->data[PATH], "/path", "path"); ASSERT_STR_EQ(u->data[QUERY], "q=1", "query"); ASSERT_STR_EQ(u->data[FRAGMENT], "frag", "fragment"); uri_free(u); } void test_parse_invalid() { uri_t *u = uri_parse("not-a-uri"); ASSERT(u == NULL, "invalid URI returns NULL"); } void test_set_override() { uri_t *u = uri_init(); uri_set(u, SCHEME, "http"); uri_set(u, HOST, "old.com"); uri_set(u, SCHEME, "https"); uri_set(u, HOST, "new.com"); char *uri = uri_get(u); ASSERT_STR_EQ(uri, "https://new.com", "override works"); free(uri); uri_free(u); } void test_roundtrip() { char const *original = "https://example.com:8080/path?q=test#anchor"; uri_t *u = uri_parse(original); ASSERT(u != NULL, "parse for roundtrip"); if (!u) return; char *rebuilt = uri_get(u); ASSERT(rebuilt != NULL, "rebuilt URI"); if (!rebuilt) { uri_free(u); return; } ASSERT(strcmp(rebuilt, original) == 0, "roundtrip matches original"); uri_free(u); free(rebuilt); } void test_minimal_uri() { uri_t *u = uri_init(); uri_set(u, SCHEME, "file"); uri_set(u, HOST, "/tmp/file.txt"); char *uri = uri_get(u); ASSERT(uri != NULL, "minimal URI"); free(uri); uri_free(u); } void test_set_null() { uri_t *u = uri_init(); uri_set(u, SCHEME, "http"); uri_set(u, HOST, "example.com"); uri_set(u, HOST, NULL); char *uri = uri_get(u); ASSERT(uri != NULL, "uri with NULL host"); ASSERT(strcmp(uri, "http:") == 0, "uri is just scheme"); free(uri); uri_free(u); } void test_remove_element() { uri_t *u = uri_init(); uri_set(u, SCHEME, "https"); uri_set(u, HOST, "example.com"); uri_set(u, PATH, "/path"); char *removed = uri_remove(u, SCHEME); ASSERT(removed != NULL, "remove returns old value"); ASSERT(strcmp(removed, "https") == 0, "removed value matches"); free(removed); char *uri = uri_get(u); ASSERT(uri != NULL, "uri after remove"); ASSERT(strcmp(uri, "//example.com/path") == 0, "uri without scheme"); free(uri); uri_free(u); } void test_null_guards() { uri_set(NULL, SCHEME, "http"); uri_print(NULL); uri_print_elements(NULL); ASSERT(uri_get(NULL) == NULL, "uri_get(NULL) returns NULL"); ASSERT(uri_remove(NULL, HOST) == NULL, "uri_remove(NULL) returns NULL"); ASSERT(uri_parse(NULL) == NULL, "uri_parse(NULL) returns NULL"); } void test_bounds_guards() { uri_t *u = uri_init(); uri_set(u, (uri_element_t)999, "http"); ASSERT(u->data[SCHEME] == NULL, "out-of-bounds set is no-op"); char *r = uri_remove(u, (uri_element_t)-1); ASSERT(r == NULL, "negative index remove returns NULL"); uri_free(u); } int main() { test_init(); test_set_and_get(); test_set_with_port(); test_set_with_query(); test_set_with_fragment(); test_full_uri(); test_parse_basic(); test_parse_with_port(); test_parse_with_query(); test_parse_with_fragment(); test_parse_full(); test_parse_invalid(); test_set_override(); test_roundtrip(); test_minimal_uri(); test_set_null(); test_remove_element(); test_null_guards(); test_bounds_guards(); printf("test_uri: %d failures\n", failures); return failures; }