P1-1(会静默开错图):取消「书库从列表推导图片下标」。 - 新增 mr_reader_open_dir_at_name(dir, name):阅读器打开目录册后,在**自己刚枚举出的 全量目录册**(mr_dir_book,与翻页同一份枚举)里按 basename 定位起始页(先精确, 再大小写不敏感兜底;找不到退回续读/首页)。书库点击只传文件名。 - 删除 mr_library_image_index(不再有跨枚举的隐式契约)。 - 截断提示文案改为「仅显示前 2000 项(图片可在阅读器中翻到)」。 - 修复前(cb02722 探针,2100 张 4 位名夹具):rows=2000/truncated=1/ library_images=2000/reader_pages=2100/**mismatched=590**(首行 p1412 → 打开 p1411); 评审夹具为 1995/2000。修复后:2000 个可见图片行按名定位 **0 mismatch**, 2100 页全量可翻;26 次真实 UI 点开(首尾各 3 + 20 个确定性伪随机)逐张命中。 连带处理(评审 r1): 1. 删掉 reader_page_enter 的 c->view 死写(reopen 判定用 activity active/depth); 顺手删掉 library_page_create 里同款死写。 2. 阅读器册间衔接根锁定:新增 mr_path_within_root + mr_book_adjacent_rooted, book_switch 用浏览根(默认 MR_ROOT_DIR,测试可注入)钳制相邻册; 根外的兄弟册按「没有相邻册」处理。test_cont_adjacent A7 验证「明文 _ex 会越根、 rooted 拒绝、根内翻册不受影响」;test_mode_switch_host H6 验证条漫末页不越根。 3. test_reader_perf 不再依赖顶栏 tap 命中时序:模式切换改走测试钩子 mr_reader_test_toggle_mode()(与顶栏动作同一个 mode_set),负载下稳定。 4. page_cache 注释去掉「缩略图缓存/缩略图线程」过时描述(只改文字)。 回归:run_tests.sh(library 81 / nav 74 / lazy 57 / perf 25 / … / open_book 含 2100 用例) 与 run_webtoon_tests.sh(含 A7/H6)全 PASS;交叉构建零警告。
426 lines
18 KiB
C
426 lines
18 KiB
C
/* test_browse_ui_host.c — 书库浏览页宿主集成测试(可选,需 SDK 源码树)。
|
||
*
|
||
* 目的(M18 文件管理器模型):
|
||
* U1 行模型:目录 + 图片 + 档案册混排(目录优先+自然序),行副标题是扫描期生成的
|
||
* 类型·大小(不再有占位/后台补位),图标/右侧箭头按类型区分;
|
||
* U2 行合成零 IO:反复取可见行不触发 opendir/readdir/lstat(渲染期无文件访问);
|
||
* U3 点击语义:点目录=进入(空目录也进);点图片=从该图开始读
|
||
* (mr_reader_open_dir_at_name(当前目录, 文件名);阅读器自己定位下标);
|
||
* 点档案册=打开档案册;
|
||
* U4 根目录锁定:根不显示顶栏返回、library_go_up 在根返回 0(app.c 退出);
|
||
* 子目录返回=回根;不存在越过浏览根的导航;
|
||
* U5 无 SD 卡容错:浏览根不可访问(不存在/不是目录)→ 友好空态「未检测到 SD 卡」,
|
||
* 不弹错误横幅;顶栏刷新/重扫可重试成功;
|
||
* U6 子目录不可读:停留原目录 + 顶部提示横幅;
|
||
* U7 书库不再碰缓存目录:CBZ 解压页缓存 mrpage_* 原样保留,且不写任何 bmp。
|
||
*
|
||
* 编译:见 test/run_tests.sh(链 SDK 源码树 + 打桩;只发布 meow_ui.a 的环境自动 SKIP)。
|
||
* ui/library/library.c 以 -DMR_LIBRARY_UI_TEST_HOOKS 编译出测试接缝;logic/library.c
|
||
* 以 -DMR_LIBRARY_TEST_HOOKS 编译出 IO 计数。
|
||
*/
|
||
#include "tuku_internal.h"
|
||
#include "logic/library.h"
|
||
#include "logic/reader/page_cache.h"
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
#include <dirent.h>
|
||
|
||
/* ui/library/library.c 的测试接缝(仅 -DMR_LIBRARY_UI_TEST_HOOKS 时定义) */
|
||
int library_test_load(const char *dir);
|
||
void library_test_set_root(const char *root);
|
||
const char *library_test_root(void);
|
||
const char *library_test_dir(void);
|
||
void library_test_set_dir(const char *dir);
|
||
int library_test_rows(void);
|
||
int library_test_src(int idx, meow_list_item *item);
|
||
int library_test_hint_active(void);
|
||
const char *library_test_hint(void);
|
||
int library_test_topbar_back(void);
|
||
const char *library_test_empty_text(void);
|
||
int library_test_sd_missing(void);
|
||
int library_test_retry(void);
|
||
int library_test_go_up(void);
|
||
int library_test_drag_active(void);
|
||
int library_test_has_list(void);
|
||
|
||
/* 阅读器入口打桩:记录被打开的册路径 / 起始页 / 按名定位的文件名(本测试不建 reader 页)。 */
|
||
#define MR_READER_START_RESUME (-1)
|
||
void mr_reader_open_dir_at(const char *dir, int start_index);
|
||
void mr_reader_open_dir_at_name(const char *dir, const char *name);
|
||
static char g_opened[MR_PATH_MAX];
|
||
static char g_opened_name[MR_NAME_MAX];
|
||
static int g_opened_start;
|
||
static int g_opened_n;
|
||
void mr_reader_open_dir_at(const char *dir, int start_index)
|
||
{
|
||
snprintf(g_opened, sizeof g_opened, "%s", dir ? dir : "");
|
||
g_opened_name[0] = '\0';
|
||
g_opened_start = start_index;
|
||
g_opened_n++;
|
||
}
|
||
void mr_reader_open_dir_at_name(const char *dir, const char *name)
|
||
{
|
||
snprintf(g_opened, sizeof g_opened, "%s", dir ? dir : "");
|
||
snprintf(g_opened_name, sizeof g_opened_name, "%s", name ? name : "");
|
||
g_opened_start = MR_READER_START_RESUME;
|
||
g_opened_n++;
|
||
}
|
||
|
||
static int g_fail;
|
||
#define CHECK(cond, ...) do { \
|
||
if (cond) { printf(" PASS\n"); } \
|
||
else { g_fail++; printf(" FAIL %s:%d ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } \
|
||
} while (0)
|
||
|
||
/* app.c 的导航上报在测试里不需要网络侧实现。 */
|
||
void mr_nav_report(void) {}
|
||
|
||
static int mkdir_p(const char *path)
|
||
{
|
||
char tmp[MR_PATH_MAX];
|
||
snprintf(tmp, sizeof tmp, "%s", path);
|
||
for (size_t i = 1; i <= strlen(tmp); i++) {
|
||
if (tmp[i] == '/' || tmp[i] == '\0') {
|
||
char ch = tmp[i];
|
||
tmp[i] = '\0';
|
||
if (mkdir(tmp, 0755) != 0) {
|
||
struct stat st;
|
||
if (stat(tmp, &st) != 0 || !S_ISDIR(st.st_mode)) return -1;
|
||
}
|
||
tmp[i] = ch;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int touch_file(const char *path)
|
||
{
|
||
FILE *f = fopen(path, "wb");
|
||
if (!f) return -1;
|
||
fputs("x", f);
|
||
return fclose(f);
|
||
}
|
||
|
||
static int rm_rf(const char *path)
|
||
{
|
||
struct stat st;
|
||
if (lstat(path, &st) != 0) return -1;
|
||
if (!S_ISDIR(st.st_mode)) { chmod(path, 0700); return unlink(path); }
|
||
chmod(path, 0700);
|
||
DIR *d = opendir(path);
|
||
if (!d) return -1;
|
||
struct dirent *de;
|
||
while ((de = readdir(d)) != NULL) {
|
||
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
|
||
char full[MR_PATH_MAX];
|
||
snprintf(full, sizeof full, "%s/%s", path, de->d_name);
|
||
rm_rf(full);
|
||
}
|
||
closedir(d);
|
||
return rmdir(path);
|
||
}
|
||
|
||
static void joinp(char *out, int outsz, const char *a, const char *b)
|
||
{
|
||
snprintf(out, (size_t)outsz, "%s/%s", a, b);
|
||
}
|
||
|
||
static int row_index_for(const char *name)
|
||
{
|
||
int rows = library_test_rows();
|
||
for (int i = 0; i < rows; i++) {
|
||
meow_list_item it;
|
||
if (!library_test_src(i, &it)) continue;
|
||
if (it.title && strcmp(it.title, name) == 0) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static void tap_row(const char *name)
|
||
{
|
||
int idx = row_index_for(name);
|
||
if (idx < 0) { CHECK(0, "row %s not found", name); return; }
|
||
int list_top = meow_ui_statusbar_height() + 56;
|
||
library_on_tap(120, list_top + idx * 56 + 28);
|
||
}
|
||
|
||
/* 统计缓存目录里的 bmp(缩略图/解压页)。 */
|
||
static int count_bmp(const char *dir, int *pages)
|
||
{
|
||
DIR *d = opendir(dir);
|
||
if (!d) return -1;
|
||
int thumbs = 0, pg = 0;
|
||
struct dirent *de;
|
||
while ((de = readdir(d)) != NULL) {
|
||
size_t len = strlen(de->d_name);
|
||
if (len <= 4 || strcmp(de->d_name + len - 4, ".bmp") != 0) continue;
|
||
if (mr_cache_is_page_name(de->d_name)) pg++;
|
||
else thumbs++;
|
||
}
|
||
closedir(d);
|
||
if (pages) *pages = pg;
|
||
return thumbs;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
const char *base = getenv("TMPDIR");
|
||
if (!base || !base[0]) base = ".";
|
||
char tmpl[512];
|
||
snprintf(tmpl, sizeof tmpl, "%s/mr_browse_ui_XXXXXX", base);
|
||
char *data = mkdtemp(tmpl);
|
||
if (!data) { printf("SKIP: mkdtemp failed\n"); return 0; }
|
||
setenv("MEOW_DATA_DIR", data, 1);
|
||
|
||
char root[MR_PATH_MAX], p[MR_PATH_MAX];
|
||
snprintf(root, sizeof root, "%s/library", data);
|
||
const char *dirs[] = { "album", "empty", "noimg", "deep/sub", "Pictures", "Pictures/sub" };
|
||
for (unsigned i = 0; i < sizeof dirs / sizeof dirs[0]; i++) {
|
||
joinp(p, sizeof p, root, dirs[i]);
|
||
if (mkdir_p(p) != 0) { printf("SKIP: mkdir %s failed\n", p); return 0; }
|
||
}
|
||
joinp(p, sizeof p, root, "album/001.png"); touch_file(p);
|
||
joinp(p, sizeof p, root, "album/002.png"); touch_file(p);
|
||
joinp(p, sizeof p, root, "noimg/readme.txt"); touch_file(p);
|
||
joinp(p, sizeof p, root, "deep/sub/001.png"); touch_file(p);
|
||
joinp(p, sizeof p, root, "root.png"); touch_file(p);
|
||
joinp(p, sizeof p, root, "book.cbz"); touch_file(p);
|
||
|
||
/* Pictures:大小写混排 + 数字序号 + 大写扩展名 + 档案册(图片排序契约夹具)。 */
|
||
const char *pics[] = { "A.jpg", "a.jpg", "img2.png", "IMG3.JPG", "img10.png",
|
||
"page01.bmp", "page1.jpeg", "cover.cbz", "notes.txt" };
|
||
for (unsigned i = 0; i < sizeof pics / sizeof pics[0]; i++) {
|
||
joinp(p, sizeof p, root, "Pictures");
|
||
size_t dl = strlen(p);
|
||
snprintf(p + dl, sizeof p - dl, "/%s", pics[i]);
|
||
touch_file(p);
|
||
}
|
||
|
||
meow_ui_opts o = { 480, 720 };
|
||
if (meow_ui_init(&o) != 0) { printf("SKIP: meow_ui_init failed (no host backend)\n"); return 0; }
|
||
|
||
/* ---- U5:浏览根不可访问 —— 先指向不存在的根,再 create ---- */
|
||
const char *bad_root = "/no/such/tuku/m18_root";
|
||
library_test_set_root(bad_root);
|
||
CHECK(strcmp(library_test_root(), bad_root) == 0, "bad root installed");
|
||
meow_view *lib = library_view_create();
|
||
CHECK(library_test_sd_missing() == 1, "unreadable root -> SD missing state");
|
||
CHECK(library_test_rows() == 0, "empty list rows=%d", library_test_rows());
|
||
CHECK(library_test_has_list(), "startup still builds a listview");
|
||
CHECK(strcmp(library_test_dir(), bad_root) == 0, "startup dir=%s", library_test_dir());
|
||
CHECK(strstr(library_test_empty_text(), "SD") != NULL,
|
||
"friendly empty state: %s", library_test_empty_text());
|
||
CHECK(library_test_hint_active() == 0, "no error banner for missing SD");
|
||
CHECK(library_test_topbar_back() == 0, "no back arrow at storage root");
|
||
CHECK(library_test_retry() == 0 && library_test_sd_missing() == 1,
|
||
"retry while still missing stays in empty state (no crash)");
|
||
|
||
/* 根路径不是目录(普通文件)同样走 SD 空态。 */
|
||
char notadir[MR_PATH_MAX];
|
||
joinp(notadir, sizeof notadir, data, "notadir");
|
||
touch_file(notadir);
|
||
library_test_set_root(notadir);
|
||
library_test_set_dir(notadir);
|
||
CHECK(library_test_retry() == 0 && library_test_sd_missing() == 1,
|
||
"root pointing at a file -> SD missing state");
|
||
|
||
/* 插卡/挂载恢复:同一路径变成可读目录后,刷新即恢复列表。 */
|
||
library_test_set_root(root);
|
||
library_test_set_dir(root);
|
||
CHECK(library_test_retry() > 0, "retry after storage available loads rows");
|
||
CHECK(library_test_sd_missing() == 0, "SD missing state cleared");
|
||
CHECK(strcmp(library_test_dir(), root) == 0, "now browsing root");
|
||
CHECK(library_test_topbar_back() == 0, "still no back arrow at root");
|
||
|
||
meow_view_page_push(ACT_LIBRARY);
|
||
CHECK(meow_ui_activity_active() == ACT_LIBRARY, "library active");
|
||
|
||
/* ---- U1:行模型(目录优先 + 自然序 + 类型图标/箭头) ---- */
|
||
CHECK(library_test_load(root) == 1, "load fixture root");
|
||
CHECK(library_test_rows() == 7, "7 rows (got %d)", library_test_rows());
|
||
static const char *want[7] = {
|
||
"album", "deep", "empty", "noimg", "Pictures", "book.cbz", "root.png"
|
||
};
|
||
for (int i = 0; i < 7; i++) {
|
||
meow_list_item it;
|
||
CHECK(library_test_src(i, &it) == 1, "src row %d", i);
|
||
CHECK(it.title && strcmp(it.title, want[i]) == 0, "row[%d]=%s (want %s)",
|
||
i, it.title ? it.title : "(null)", want[i]);
|
||
CHECK(it.subtitle && it.subtitle[0] && strcmp(it.subtitle, "…") != 0,
|
||
"row[%d] subtitle pre-generated (%s)", i, it.subtitle ? it.subtitle : "");
|
||
if (i < 5) {
|
||
CHECK(it.icon_kind == MEOW_LIST_ICON_FOLDER && it.right == MEOW_LIST_RIGHT_ARROW,
|
||
"row[%d] is a folder row with enter arrow", i);
|
||
} else {
|
||
CHECK(it.icon_kind == MEOW_LIST_ICON_FILE && it.right == MEOW_LIST_RIGHT_NONE,
|
||
"row[%d] is a file row without child arrow", i);
|
||
}
|
||
}
|
||
int ic = row_index_for("book.cbz");
|
||
meow_list_item it;
|
||
CHECK(ic >= 0 && library_test_src(ic, &it) &&
|
||
strncmp(it.subtitle, "档案册 · ", strlen("档案册 · ")) == 0,
|
||
"archive subtitle=%s", it.subtitle ? it.subtitle : "");
|
||
int ir = row_index_for("root.png");
|
||
CHECK(ir >= 0 && library_test_src(ir, &it) &&
|
||
strncmp(it.subtitle, "图片 · ", strlen("图片 · ")) == 0,
|
||
"image subtitle=%s", it.subtitle ? it.subtitle : "");
|
||
|
||
/* ---- U2:行合成零 IO(渲染/滚动路径不访问文件系统) ---- */
|
||
mr_library_test_io_reset();
|
||
for (int pass = 0; pass < 500; pass++)
|
||
for (int i = 0; i < library_test_rows(); i++) {
|
||
meow_list_item tmp;
|
||
library_test_src(i, &tmp);
|
||
}
|
||
mr_library_io_stats io;
|
||
mr_library_test_io_get(&io);
|
||
CHECK(io.opendirs == 0 && io.readdirs == 0 && io.lstats == 0,
|
||
"row source does no IO (open=%ld rd=%ld stat=%ld)",
|
||
io.opendirs, io.readdirs, io.lstats);
|
||
|
||
/* ---- U3:点击语义 ---- */
|
||
char pics_dir[MR_PATH_MAX];
|
||
joinp(pics_dir, sizeof pics_dir, root, "Pictures");
|
||
CHECK(library_test_load(pics_dir) == 1, "entered Pictures");
|
||
CHECK(strcmp(library_test_dir(), pics_dir) == 0, "current dir=%s", library_test_dir());
|
||
|
||
/* 点图片:把**文件名**交给阅读器(阅读器用自己的全量目录册定位起始页;
|
||
* 书库列表可能被 MR_LIB_SCAN_MAX 截断,所以不再传下标)。 */
|
||
const char *target = "img10.png";
|
||
int ti = row_index_for(target);
|
||
CHECK(ti >= 0, "target image row present");
|
||
g_opened_n = 0;
|
||
tap_row(target);
|
||
CHECK(g_opened_n == 1 && strcmp(g_opened, pics_dir) == 0,
|
||
"tap image opens reader for current dir (%s)", g_opened);
|
||
CHECK(strcmp(g_opened_name, target) == 0, "reader gets file name (%s)", g_opened_name);
|
||
CHECK(g_opened_start == MR_READER_START_RESUME, "name-based open does not pass an index");
|
||
CHECK(strcmp(library_test_dir(), pics_dir) == 0, "tap image does not navigate");
|
||
|
||
g_opened_n = 0;
|
||
tap_row("cover.cbz");
|
||
char archive[MR_PATH_MAX];
|
||
joinp(archive, sizeof archive, pics_dir, "cover.cbz");
|
||
CHECK(g_opened_n == 1 && strcmp(g_opened, archive) == 0,
|
||
"tap archive opens archive book (%s)", g_opened);
|
||
CHECK(g_opened_start == MR_READER_START_RESUME, "archive uses resume start (%d)",
|
||
g_opened_start);
|
||
|
||
/* 点目录 = 进入;空目录也进(不打开阅读器)。 */
|
||
char sub_dir[MR_PATH_MAX];
|
||
joinp(sub_dir, sizeof sub_dir, pics_dir, "sub");
|
||
g_opened_n = 0;
|
||
tap_row("sub");
|
||
CHECK(g_opened_n == 0, "tap dir does not open reader");
|
||
CHECK(strcmp(library_test_dir(), sub_dir) == 0, "entered empty sub dir (%s)",
|
||
library_test_dir());
|
||
CHECK(library_test_rows() == 0, "empty dir shows empty list");
|
||
CHECK(strcmp(library_test_empty_text(), "空文件夹") == 0,
|
||
"empty dir state: %s", library_test_empty_text());
|
||
|
||
/* ---- U4:根目录锁定 ---- */
|
||
char album_dir[MR_PATH_MAX];
|
||
joinp(album_dir, sizeof album_dir, root, "album");
|
||
CHECK(library_test_load(album_dir) == 1, "load album");
|
||
CHECK(library_test_topbar_back() == 1, "back arrow shown in subdir");
|
||
CHECK(library_test_go_up() == 1, "go_up from subdir consumed");
|
||
CHECK(strcmp(library_test_dir(), root) == 0, "back to root");
|
||
|
||
/* 根:系统返回 = 退出(返回 0),并保持列表不变。 */
|
||
CHECK(library_test_go_up() == 0, "go_up at root returns 0 (app exits)");
|
||
CHECK(strcmp(library_test_dir(), root) == 0, "still at root after exit gesture");
|
||
CHECK(library_test_topbar_back() == 0, "no back arrow at root");
|
||
|
||
/* 顶栏返回:子目录点击返回箭头 = 回根;根上该区域不响应。 */
|
||
CHECK(library_test_load(album_dir) == 1, "load album again");
|
||
library_on_tap(20, meow_ui_statusbar_height() + 28);
|
||
CHECK(strcmp(library_test_dir(), root) == 0, "topbar back returns to root");
|
||
int root_rows_before = library_test_rows();
|
||
library_on_tap(20, meow_ui_statusbar_height() + 28);
|
||
CHECK(strcmp(library_test_dir(), root) == 0 && library_test_rows() == root_rows_before,
|
||
"topbar back is inert at root");
|
||
|
||
/* 嵌套:deep/sub 两级返回。 */
|
||
tap_row("deep");
|
||
tap_row("sub");
|
||
char deep_sub[MR_PATH_MAX], deep_dir[MR_PATH_MAX];
|
||
joinp(deep_dir, sizeof deep_dir, root, "deep");
|
||
joinp(deep_sub, sizeof deep_sub, root, "deep/sub");
|
||
CHECK(strcmp(library_test_dir(), deep_sub) == 0, "entered deep/sub (%s)", library_test_dir());
|
||
CHECK(library_test_go_up() == 1 && strcmp(library_test_dir(), deep_dir) == 0, "up to deep");
|
||
CHECK(library_test_go_up() == 1 && strcmp(library_test_dir(), root) == 0, "up to root");
|
||
|
||
/* ---- U3c:左缘 24px / 屏底热区不启动列表拖拽 ---- */
|
||
int list_top = meow_ui_statusbar_height() + 56;
|
||
int row_y = list_top + 28;
|
||
library_on_touch_down(120, row_y);
|
||
CHECK(library_test_drag_active() == 1, "touch inside list starts drag");
|
||
library_on_touch_down(5, row_y);
|
||
CHECK(library_test_drag_active() == 0, "left-edge touch does not start drag");
|
||
library_on_touch_down(23, row_y);
|
||
CHECK(library_test_drag_active() == 0, "left-edge (23px) does not start drag");
|
||
library_on_touch_down(24, row_y);
|
||
CHECK(library_test_drag_active() == 1, "just outside left band starts drag");
|
||
library_on_touch_down(120, meow_ui_height() - 4);
|
||
CHECK(library_test_drag_active() == 0, "bottom hotzone does not start drag");
|
||
|
||
/* ---- U6:不可读子目录 → 停留 + 提示 ---- */
|
||
if (geteuid() != 0) {
|
||
char noread[MR_PATH_MAX];
|
||
joinp(noread, sizeof noread, root, "noread");
|
||
CHECK(mkdir(noread, 0755) == 0, "mkdir noread");
|
||
chmod(noread, 0000);
|
||
int ok = library_test_load(noread);
|
||
CHECK(ok == 0, "unreadable dir load fails");
|
||
CHECK(strcmp(library_test_dir(), root) == 0, "stays in previous dir (%s)",
|
||
library_test_dir());
|
||
CHECK(library_test_hint_active(), "hint banner shown: %s", library_test_hint());
|
||
CHECK(strstr(library_test_hint(), "无法读取") != NULL,
|
||
"hint text mentions unreadable: %s", library_test_hint());
|
||
chmod(noread, 0700);
|
||
} else {
|
||
printf(" SKIP unreadable-dir case (running as root)\n");
|
||
}
|
||
|
||
/* ---- U7:书库不碰缓存目录(CBZ 解压页缓存原样保留,不写 bmp) ---- */
|
||
CHECK(mr_cache_init(data) == 0, "page cache init");
|
||
const char *cdir = mr_cache_dir();
|
||
CHECK(cdir && cdir[0], "page cache dir: %s", cdir ? cdir : "");
|
||
if (cdir && cdir[0]) {
|
||
char page[MR_PATH_MAX + 96];
|
||
snprintf(page, sizeof page, "%s/mrpage_0123456789abcdef.bmp", cdir);
|
||
CHECK(touch_file(page) == 0, "create page cache file");
|
||
int pages_before = 0;
|
||
count_bmp(cdir, &pages_before);
|
||
CHECK(pages_before == 1, "precondition 1 page (got %d)", pages_before);
|
||
|
||
CHECK(library_test_load(root) == 1, "reload root");
|
||
for (int i = 0; i < library_test_rows(); i++) {
|
||
meow_list_item tmp;
|
||
library_test_src(i, &tmp);
|
||
}
|
||
tap_row("album");
|
||
tap_row("001.png"); /* 打开阅读器入口(打桩) */
|
||
library_test_go_up();
|
||
|
||
int pages_after = 0;
|
||
int thumbs_after = count_bmp(cdir, &pages_after);
|
||
CHECK(access(page, R_OK) == 0, "mrpage_* survives book browsing");
|
||
CHECK(pages_after == 1 && thumbs_after == 0,
|
||
"library writes no bmp to cache (pages=%d thumbs=%d)", pages_after, thumbs_after);
|
||
}
|
||
|
||
meow_view_free(lib);
|
||
meow_ui_shutdown();
|
||
rm_rf(data);
|
||
printf("%s: %s\n", g_fail ? "FAIL" : "PASS",
|
||
g_fail ? "see failures above" : "browse UI checks passed");
|
||
return g_fail ? 1 : 0;
|
||
}
|