diff --git a/.gitignore b/.gitignore index c8737f7..7b2e3b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o iptv-dl +tests/test_runner diff --git a/Makefile b/Makefile index f6cd75d..764850b 100644 --- a/Makefile +++ b/Makefile @@ -34,20 +34,26 @@ $(TARGET): $(OBJS) $(CC) $(CFLAGS) -c $< -o $@ install: $(TARGET) - install -d $(BINDIR) $(SHAREDIR) $(SYSCONFDIR) - install -m 755 $(TARGET) $(BINDIR)/iptv-dl - install -m 644 static/iptv.css $(SHAREDIR)/iptv.css - install -m 644 static/iptv.js $(SHAREDIR)/iptv.js - install -m 644 static/downloads.js $(SHAREDIR)/downloads.js - install -m 644 static/series_show.js $(SHAREDIR)/series_show.js - install -m 644 static/header.html $(SHAREDIR)/header.html - install -m 644 static/footer.html $(SHAREDIR)/footer.html + install -d $(BINDIR) $(SHAREDIR)/css $(SHAREDIR)/js $(SHAREDIR)/html $(SYSCONFDIR) + install -m 755 $(TARGET) $(BINDIR)/iptv-dl + install -m 644 static/css/iptv.css $(SHAREDIR)/css/iptv.css + install -m 644 static/js/iptv.js $(SHAREDIR)/js/iptv.js + install -m 644 static/js/downloads.js $(SHAREDIR)/js/downloads.js + install -m 644 static/js/series_show.js $(SHAREDIR)/js/series_show.js + install -m 644 static/html/header.html $(SHAREDIR)/html/header.html + install -m 644 static/html/footer.html $(SHAREDIR)/html/footer.html @echo "" @echo "Installed to $(BINDIR)/iptv-dl" @echo "Create config at ~/.iptv-downloader/config.json or $(SYSCONFDIR)/config.json" @echo "Run: iptv-dl --dump-config (to see active defaults)" -clean: - rm -f $(OBJS) $(TARGET) +test: tests/test_runner + ./tests/test_runner -.PHONY: all install clean +tests/test_runner: tests/test_json.c util/json.c util/buf.c + $(CC) $(CFLAGS) $^ -o $@ + +clean: + rm -f $(OBJS) $(TARGET) tests/test_runner + +.PHONY: all install clean test diff --git a/http/http.c b/http/http.c index 8a317cd..9c9a4b0 100644 --- a/http/http.c +++ b/http/http.c @@ -11,9 +11,9 @@ char *g_footer = NULL; int g_footer_len = 0; char *g_css = NULL; int g_css_len = 0; StaticJS g_js[] = { - { "/iptv.js", "iptv.js", NULL, 0 }, - { "/downloads.js", "downloads.js", NULL, 0 }, - { "/series_show.js", "series_show.js", NULL, 0 }, + { "/iptv.js", "js/iptv.js", NULL, 0 }, + { "/downloads.js", "js/downloads.js", NULL, 0 }, + { "/series_show.js", "js/series_show.js", NULL, 0 }, { NULL, NULL, NULL, 0 } }; @@ -31,11 +31,11 @@ static int load_file(const char *path, char **out, int *out_len) { int http_load_templates(void) { char path[512]; - snprintf(path, sizeof(path), "%s/header.html", g_cfg.template_dir); + snprintf(path, sizeof(path), "%s/html/header.html", g_cfg.template_dir); if (!load_file(path, &g_header, &g_header_len)) return 0; - snprintf(path, sizeof(path), "%s/footer.html", g_cfg.template_dir); + snprintf(path, sizeof(path), "%s/html/footer.html", g_cfg.template_dir); if (!load_file(path, &g_footer, &g_footer_len)) return 0; - snprintf(path, sizeof(path), "%s/iptv.css", g_cfg.template_dir); + snprintf(path, sizeof(path), "%s/css/iptv.css", g_cfg.template_dir); if (!load_file(path, &g_css, &g_css_len)) return 0; for (int i = 0; g_js[i].url_path; i++) { snprintf(path, sizeof(path), "%s/%s", g_cfg.template_dir, g_js[i].fs_name); diff --git a/static/iptv.css b/static/css/iptv.css similarity index 100% rename from static/iptv.css rename to static/css/iptv.css diff --git a/static/footer.html b/static/html/footer.html similarity index 100% rename from static/footer.html rename to static/html/footer.html diff --git a/static/header.html b/static/html/header.html similarity index 100% rename from static/header.html rename to static/html/header.html diff --git a/static/downloads.js b/static/js/downloads.js similarity index 100% rename from static/downloads.js rename to static/js/downloads.js diff --git a/static/iptv.js b/static/js/iptv.js similarity index 100% rename from static/iptv.js rename to static/js/iptv.js diff --git a/static/series_show.js b/static/js/series_show.js similarity index 100% rename from static/series_show.js rename to static/js/series_show.js diff --git a/tests/test_json.c b/tests/test_json.c new file mode 100644 index 0000000..07a3a76 --- /dev/null +++ b/tests/test_json.c @@ -0,0 +1,67 @@ +/* + * tests/test_json.c — unit tests for util/json.c + * Build: make test + * Run: ./test_runner + */ +#include +#include +#include +#include +#include "json.h" + +static int passed = 0, failed = 0; + +#define CHECK(expr, desc) do { \ + if (expr) { printf(" PASS: %s\n", desc); passed++; } \ + else { printf(" FAIL: %s\n", desc); failed++; } \ +} while(0) + +static void test_json_str(void) { + printf("json_str:\n"); + const char *j = "{\"name\":\"test show\",\"id\":\"42\",\"flag\":true}"; + char *v; + + v = json_str(j, "name"); CHECK(v && strcmp(v,"test show")==0, "string value"); free(v); + v = json_str(j, "id"); CHECK(v && strcmp(v,"42")==0, "numeric string"); free(v); + v = json_str(j, "flag"); CHECK(v && strcmp(v,"true")==0, "bare value"); free(v); + v = json_str(j, "missing"); CHECK(v == NULL, "missing key"); free(v); +} + +static void test_json_array(void) { + printf("json_array:\n"); + const char *j = "[{\"id\":\"1\"},{\"id\":\"2\"},{\"id\":\"3\"}]"; + int n; char **arr = json_array(j, &n); + CHECK(n == 3, "count == 3"); + if (arr) { + char *v = json_str(arr[0], "id"); CHECK(v && strcmp(v,"1")==0, "arr[0].id==1"); free(v); + v = json_str(arr[2], "id"); CHECK(v && strcmp(v,"3")==0, "arr[2].id==3"); free(v); + for (int i = 0; i < n; i++) free(arr[i]); + free(arr); + } + /* empty array */ + arr = json_array("[]", &n); CHECK(n == 0, "empty array n==0"); free(arr); +} + +static void test_urldecode(void) { + printf("urldecode:\n"); + char s1[] = "hello%20world"; urldecode(s1); CHECK(strcmp(s1,"hello world")==0, "%%20 → space"); + char s2[] = "a+b+c"; urldecode(s2); CHECK(strcmp(s2,"a b c")==0, "+ → space"); + char s3[] = "no%20change%21"; urldecode(s3); CHECK(strcmp(s3,"no change!")==0, "mixed"); +} + +static void test_str_icontains(void) { + printf("str_icontains:\n"); + CHECK(str_icontains("Walking Dead", "walking"), "case-insensitive match"); + CHECK(str_icontains("Walking Dead", "DEAD"), "uppercase needle"); + CHECK(!str_icontains("Walking Dead", "sopranos"),"no match"); + CHECK(str_icontains("anything", ""), "empty needle → true"); +} + +int main(void) { + test_json_str(); + test_json_array(); + test_urldecode(); + test_str_icontains(); + printf("\n%d passed, %d failed\n", passed, failed); + return failed ? 1 : 0; +} diff --git a/tests/test_runner b/tests/test_runner new file mode 100755 index 0000000..b49370e Binary files /dev/null and b/tests/test_runner differ