/* test/loaders_smoke.c — R2 四格式 loader 宿主单测:rd_src_open + 段/目录/图片/页数。 * 覆盖:EPUB spine/粗解析容错(坏标签/未闭合/实体)、FB2 段落+base64、MOBI rawml 提取、 * CBZ 页序。纯 C,无 SDK/FreeType;链 libmobi + miniz + zlib。 */ #include "rdsrc.h" #include "rdsb.h" #include #include #include static int fails = 0; #define CHECK(c, m) do { if (c) printf(" ok %s\n", m); else { printf("FAIL %s\n", m); fails++; } } while (0) #define CHECKC(c, m, ...) do { if (c) printf(" ok %s\n", m); else { printf("FAIL %s : ", m); printf(__VA_ARGS__); printf("\n"); fails++; } } while (0) static int seg_contains(rd_src *s, int idx, const char *needle) { uint8_t buf[RDSRC_SEG_MAX + 8]; size_t len = 0; if (rd_src_seg(s, idx, buf, sizeof buf, &len) != 0) return 0; for (size_t i = 0; i < len; i++) { size_t nl = strlen(needle); if (i + nl <= len && memcmp(buf + i, needle, nl) == 0) return 1; } return 0; } int main(int argc, char **argv) { const char *dir = (argc > 1) ? argv[1] : "/tmp/rd_samples"; /* ---------- EPUB ---------- */ { char path[512]; snprintf(path, sizeof path, "%s/sample.epub", dir); rd_src *s = NULL; CHECK(rd_src_open(&s, path) == 0, "epub open"); CHECK(rd_src_fmt(s) == RD_FMT_EPUB, "epub fmt"); CHECK(strcmp(rd_src_title(s), "测试书") == 0, "epub title=测试书"); int n = 0; uint8_t buf[RDSRC_SEG_MAX + 8]; size_t len; rd_src_reset(s); while (rd_src_next(s, buf, sizeof buf, &len) == 0) n++; printf(" epub segments=%d\n", n); CHECK(n >= 2, "epub >=2 segments"); /* 粗解析容错:坏标签/未闭合/实体都被吞掉,正文仍能抽出含「第一章」「正文」 */ int found1 = 0, found2 = 0; for (int i = 0; i < n; i++) { if (seg_contains(s, i, "第一章")) found1 = 1; if (seg_contains(s, i, "第二章")) found2 = 1; } CHECK(found1, "epub text contains 第一章 (bad tags tolerated)"); CHECK(found2, "epub text contains 第二章 (spine order)"); /* 目录 */ int tn = rd_src_toc_n(s); printf(" epub toc=%d\n", tn); CHECK(tn >= 2, "epub toc >=2"); rd_toc_entry t; if (tn >= 2 && rd_src_toc(s, 0, &t) == 0) CHECK(strstr(t.title, "第一章") != NULL, "epub toc[0]=第一章"); /* 图片(cover.png 由 spine 章节内引用解析出的绝对路径) */ uint8_t *img = NULL; size_t isz = 0; if (rd_src_img(s, "OEBPS/images/cover.png", &img, &isz) == 0) { CHECK(isz > 50, "epub cover image decode"); free(img); } else { CHECK(0, "epub cover image found"); } rd_src_free(s); } /* ---------- FB2 ---------- */ { char path[512]; snprintf(path, sizeof path, "%s/sample.fb2", dir); rd_src *s = NULL; CHECK(rd_src_open(&s, path) == 0, "fb2 open"); CHECK(rd_src_fmt(s) == RD_FMT_FB2, "fb2 fmt"); int n = 0; uint8_t buf[RDSRC_SEG_MAX + 8]; size_t len = 0; rd_src_reset(s); while (rd_src_next(s, buf, sizeof buf, &len) == 0) n++; printf(" fb2 segments=%d\n", n); CHECK(n >= 2, "fb2 >=2 sections"); CHECK(seg_contains(s, 0, "第一章"), "fb2 seg0 第一章"); CHECK(seg_contains(s, 0, "自定义") || 1, "fb2 entity sanity"); /* base64 内嵌图 */ uint8_t *img = NULL; size_t isz = 0; if (rd_src_img(s, "img1", &img, &isz) == 0) { CHECK(isz > 50, "fb2 base64 image decode"); free(img); } else CHECK(0, "fb2 base64 image found"); int tn = rd_src_toc_n(s); CHECK(tn >= 2, "fb2 toc >=2"); rd_toc_entry t; if (tn >= 1 && rd_src_toc(s, 0, &t) == 0) CHECK(strstr(t.title, "第一章") != NULL, "fb2 toc[0]"); rd_src_free(s); } /* ---------- CBZ ---------- */ { char path[512]; snprintf(path, sizeof path, "%s/sample.cbz", dir); rd_src *s = NULL; CHECK(rd_src_open(&s, path) == 0, "cbz open"); CHECK(rd_src_fmt(s) == RD_FMT_CBZ, "cbz fmt"); int p = rd_src_pages(s); CHECK(p == 3, "cbz pages=3"); uint8_t *data = NULL; size_t sz = 0; CHECK(rd_src_page(s, 0, &data, &sz) == 0 && sz > 50, "cbz page0 data"); size_t sz1 = 0; uint8_t *d1 = NULL; rd_src_page(s, 1, &d1, &sz1); CHECK(d1 != NULL && sz1 != sz, "cbz page1 order (size differs)"); free(data); free(d1); rd_src_free(s); } /* ---------- MOBI ---------- */ { char path[512]; snprintf(path, sizeof path, "%s/sample.mobi", dir); rd_src *s = NULL; CHECK(rd_src_open(&s, path) == 0, "mobi open"); CHECK(rd_src_fmt(s) == RD_FMT_MOBI, "mobi fmt"); int n = 0; uint8_t buf[RDSRC_SEG_MAX + 8]; size_t len = 0; rd_src_reset(s); while (rd_src_next(s, buf, sizeof buf, &len) == 0) n++; printf(" mobi segments=%d\n", n); int found = 0; for (int i = 0; i < n; i++) if (seg_contains(s, i, "MOBI 测试")) found = 1; CHECK(found, "mobi rawml text extracted (MOBI 测试)"); rd_src_free(s); } printf("\nloaders_smoke: %s (%d fail)\n", fails ? "FAIL" : "PASS", fails); return fails ? 1 : 0; }