- 搜索历史:进页自动弹键盘;meow_config 持久化 ≤12 去重最新在前;空态=历史列表(点搜/左滑删/清空) - 结果分类单曲/歌单页签:独立结果集+独立滚动;歌单行进详情(playlist_open_pl);单曲行播放 - 触底分页:offset 累加 limit=30/页 cloudsearch 同参数;每类各记 offset - 误触抑制:g_press_row + g_press_delta>10(对齐 home/playlist §13.3) - 结果改独立数组(不写 g_st.songs[]);播放搜索单曲建搜索队列(queue_src=SEARCH),不弄丢歌单窗口 - core cloudsearch 扩展 type/offset/limit/total + 歌单解析 - bench/search_sim.c 离线回归(历史/独立集/翻页 offset/队列上下文);host searchtap 误触自检 验证:mipsel -Wall -Wextra 0 warning;core host/mipsel PASS;search_sim PASS; 宿主离屏 search(单曲)/searchpl(歌单)/searchhist(空态历史)/searchtap(误触) 均正常;0 真实 NCM 请求。
213 lines
11 KiB
C
213 lines
11 KiB
C
/* bench/search_sim.c — 搜索增强(REPORT §23)数据层离线回归(0 网络请求)。
|
||
*
|
||
* 直接驱动 App 真实函数(app/logic/appstate.c):搜索历史(去重/≤12/最新在前)、
|
||
* 单曲/歌单两类独立结果集、翻页 offset 累加与 has_more、以及「搜索结果播放队列」
|
||
* 上下文(queue_src=SEARCH 读 search_songs[],不覆写歌单窗口)。
|
||
* meow_config_* 用内存 stub(历史持久化语义)。误触抑制(g_press_row+g_press_delta>10)
|
||
* 属 UI 手势层,另在宿主 test_ncm_app.c 的 searchtap 自检中验证(真实页面+真实 SDK)。
|
||
*
|
||
* 编译(宿主):$CC_HOST -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
|
||
* -Icore -c bench/search_sim.c -o /tmp/ss.o
|
||
* $CC_HOST -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \
|
||
* -Icore -c app/logic/appstate.c -o /tmp/ss_app.o
|
||
* $CC_HOST -O2 -Wl,--gc-sections -o search_sim /tmp/ss.o /tmp/ss_app.o -lpthread
|
||
* 运行:./search_sim (0 网络;PASS/FAIL 退出码)
|
||
*/
|
||
#include "../app/ncm_app_internal.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdint.h>
|
||
|
||
/* appstate.c 依赖的全局(app.c 提供;本 bench 只测搜索/历史路径) */
|
||
ncm_app_state_t g_st;
|
||
ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER, .state = NCM_PC_STOPPED, .cur_song = -1 };
|
||
volatile int g_app_quit = 0;
|
||
volatile int g_net_ready = 0;
|
||
int g_host_mock = 0;
|
||
|
||
size_t ncm_strlcpy(char *dst, const char *src, size_t sz);
|
||
|
||
/* appstate_queue_song 的 PLAYLIST 分支引用 song_fetch→songmeta/ncm_api_song_detail;
|
||
* 本 bench 只测 SEARCH 路径,这些分支不执行,提供 No-op 桩满足链接。 */
|
||
int songmeta_load(const char *cd, const char *id, ncm_song_t *out) { (void)cd;(void)id;(void)out; return NCM_ERR_IO; }
|
||
int songmeta_save(const char *cd, const char *id, const ncm_song_t *s){ (void)cd;(void)id;(void)s; return NCM_ERR_IO; }
|
||
int ncm_api_song_detail(const char *d, const char *c, const int64_t *ids, int n,
|
||
ncm_song_t *out, int max, int *out_n) {
|
||
(void)d;(void)c;(void)ids;(void)n;(void)out;(void)max; if (out_n) *out_n = 0; return NCM_ERR_IO;
|
||
}
|
||
|
||
/* meow_config 内存 stub:只存 ncm.search_history 单键,模拟持久化(历史读回)。 */
|
||
static char s_cfg[4096];
|
||
static int s_cfg_valid = 0;
|
||
int meow_config_set(const char *key, const char *val)
|
||
{
|
||
(void)key;
|
||
if (!val) return MEOW_CFG_ERR_INVAL;
|
||
size_t n = strlen(val);
|
||
if (n >= sizeof s_cfg) n = sizeof s_cfg - 1;
|
||
memcpy(s_cfg, val, n); s_cfg[n] = 0; s_cfg_valid = 1;
|
||
return MEOW_CFG_OK;
|
||
}
|
||
int meow_config_get(const char *key, char *out, int outsz)
|
||
{
|
||
(void)key;
|
||
if (!s_cfg_valid || outsz <= 0) return MEOW_CFG_ERR_NOENT;
|
||
size_t n = strlen(s_cfg);
|
||
if (n >= (size_t)outsz) n = (size_t)outsz - 1;
|
||
memcpy(out, s_cfg, n); out[n] = 0;
|
||
return (s_cfg[0] || n) ? MEOW_CFG_OK : MEOW_CFG_ERR_NOENT;
|
||
}
|
||
|
||
size_t ncm_strlcpy(char *dst, const char *src, size_t sz)
|
||
{
|
||
size_t n = 0;
|
||
if (sz) {
|
||
while (src[n] && n < sz - 1) { dst[n] = src[n]; n++; }
|
||
dst[n] = 0;
|
||
}
|
||
while (src[n]) n++;
|
||
return n;
|
||
}
|
||
|
||
static int g_fail = 0;
|
||
#define CHECK(cond, fmt, ...) do { \
|
||
if (cond) printf(" PASS: " fmt "\n", ##__VA_ARGS__); \
|
||
else { printf(" FAIL: " fmt "\n", ##__VA_ARGS__); g_fail = 1; } \
|
||
} while (0)
|
||
|
||
static ncm_song_t mk_song(int i)
|
||
{
|
||
ncm_song_t s; memset(&s, 0, sizeof s);
|
||
snprintf(s.id, sizeof s.id, "%d", 100000 + i);
|
||
snprintf(s.name, sizeof s.name, "Search Song %d", i);
|
||
snprintf(s.art_name, sizeof s.art_name, "Artist %d", i);
|
||
snprintf(s.album_name, sizeof s.album_name, "Album %d", i);
|
||
s.duration_ms = 180000 + i * 1000;
|
||
return s;
|
||
}
|
||
static ncm_playlist_t mk_pl(int i)
|
||
{
|
||
ncm_playlist_t p; memset(&p, 0, sizeof p);
|
||
snprintf(p.id, sizeof p.id, "pl%d", 200000 + i);
|
||
snprintf(p.name, sizeof p.name, "Playlist %d", i);
|
||
p.track_count = 10 + i;
|
||
return p;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
/* ===== 搜索历史:去重 + ≤12 + 最新在前 ===== */
|
||
printf("[1] search history dedup/cap/newest-first\n");
|
||
appstate_search_history_clear();
|
||
appstate_search_history_add("alpha");
|
||
appstate_search_history_add("beta");
|
||
appstate_search_history_add("alpha"); /* 去重:alpha 前置 */
|
||
CHECK(appstate_search_history_count() == 2, "dedup keeps 2 (alpha, beta)");
|
||
char b1[64], b2[64];
|
||
appstate_search_history_get(0, b1, sizeof b1);
|
||
appstate_search_history_get(1, b2, sizeof b2);
|
||
CHECK(strcmp(b1, "alpha") == 0 && strcmp(b2, "beta") == 0, "newest first after re-add");
|
||
for (int i = 0; i < NCM_SEARCH_HIST_MAX + 5; i++) { /* 超 12 → 截断 */
|
||
char k[32]; snprintf(k, sizeof k, "kw%d", i);
|
||
appstate_search_history_add(k);
|
||
}
|
||
CHECK(appstate_search_history_count() == NCM_SEARCH_HIST_MAX, "cap at %d", NCM_SEARCH_HIST_MAX);
|
||
char top[64]; appstate_search_history_get(0, top, sizeof top);
|
||
CHECK(strcmp(top, "kw16") == 0, "newest kw16 at front"); /* 15+2... kw16 为最后 add */
|
||
char mid[64]; appstate_search_history_get(8, mid, sizeof mid);
|
||
CHECK(strcmp(mid, "kw8") == 0, "kw8 at index 8 (truncation preserves order)");
|
||
appstate_search_history_remove(0);
|
||
CHECK(appstate_search_history_count() == NCM_SEARCH_HIST_MAX - 1, "remove index 0 shrinks");
|
||
char top2[64]; appstate_search_history_get(0, top2, sizeof top2);
|
||
CHECK(strcmp(top2, "kw15") == 0, "remove shifts next to front");
|
||
/* 持久化读回(stub meow_config 已落盘) */
|
||
appstate_search_history_clear();
|
||
CHECK(appstate_search_history_count() == 0, "clear to 0");
|
||
|
||
/* ===== 单曲/歌单两类独立结果集 ===== */
|
||
printf("[2] independent song/playlist result sets\n");
|
||
g_st.queue_src = NCM_QUEUE_PLAYLIST;
|
||
appstate_search_clear(0);
|
||
appstate_search_clear(1);
|
||
ncm_song_t s0[4];
|
||
for (int i = 0; i < 4; i++) s0[i] = mk_song(i);
|
||
appstate_search_append_songs(4, s0, 100);
|
||
CHECK(appstate_search_count(0) == 4, "song set count=4");
|
||
CHECK(appstate_search_count(1) == 0, "playlist set independent (=0)");
|
||
ncm_playlist_t p0[2];
|
||
for (int i = 0; i < 2; i++) p0[i] = mk_pl(i);
|
||
appstate_search_append_pls(2, p0, 50);
|
||
CHECK(appstate_search_count(1) == 2, "playlist set count=2");
|
||
CHECK(appstate_search_count(0) == 4, "song set unchanged after playlist append");
|
||
ncm_song_t cpy;
|
||
CHECK(appstate_search_song(1, &cpy) == 0 && strcmp(cpy.name, "Search Song 1") == 0, "copy song #1");
|
||
ncm_playlist_t pcpy;
|
||
CHECK(appstate_search_pl(0, &pcpy) == 0 && strcmp(pcpy.name, "Playlist 0") == 0, "copy playlist #0");
|
||
appstate_search_clear(0);
|
||
CHECK(appstate_search_count(0) == 0 && appstate_search_count(1) == 2, "clear song set leaves playlist");
|
||
|
||
/* ===== 翻页 offset 累加 / has_more ===== */
|
||
printf("[3] pagination offset accumulation / has_more\n");
|
||
appstate_search_clear(0);
|
||
CHECK(appstate_search_has_more(0) == 1 && appstate_search_offset(0) == 0, "clear resets offset=0 more=1");
|
||
ncm_song_t page[30];
|
||
for (int i = 0; i < NCM_SEARCH_PAGE; i++) page[i] = mk_song(100 + i);
|
||
appstate_search_append_songs(NCM_SEARCH_PAGE, page, 300); /* page1: total 300 */
|
||
CHECK(appstate_search_count(0) == NCM_SEARCH_PAGE, "page1 count=30");
|
||
CHECK(appstate_search_offset(0) == NCM_SEARCH_PAGE, "offset=30 after page1");
|
||
CHECK(appstate_search_has_more(0) == 1, "more=1 (30<300)");
|
||
appstate_search_append_songs(NCM_SEARCH_PAGE, page, 300); /* page2 */
|
||
CHECK(appstate_search_count(0) == 2 * NCM_SEARCH_PAGE && appstate_search_offset(0) == 60, "offset=60 after page2");
|
||
ncm_song_t tail[30];
|
||
for (int i = 0; i < NCM_SEARCH_PAGE; i++) tail[i] = mk_song(200 + i);
|
||
appstate_search_append_songs(NCM_SEARCH_PAGE, tail, 240); /* page3 → 90/240 */
|
||
CHECK(appstate_search_has_more(0) == 1 && appstate_search_offset(0) == 90, "more=1 at 90/240");
|
||
for (int i = 0; i < 5; i++) appstate_search_append_songs(30, tail, 240); /* +150 → 240/240 */
|
||
CHECK(appstate_search_count(0) == 240 && appstate_search_offset(0) == 240, "reached total 240");
|
||
CHECK(appstate_search_has_more(0) == 0, "more=0 at total");
|
||
/* 总量 > 数组上限:clamp 到 NCM_SEARCH_SONG_MAX,has_more 仍按服务端总数反映 */
|
||
appstate_search_clear(0);
|
||
for (int i = 0; i < NCM_SEARCH_SONG_MAX / NCM_SEARCH_PAGE + 3; i++)
|
||
appstate_search_append_songs(NCM_SEARCH_PAGE, page, 2000);
|
||
CHECK(appstate_search_count(0) == NCM_SEARCH_SONG_MAX, "clamp at NCM_SEARCH_SONG_MAX");
|
||
CHECK(appstate_search_has_more(0) == 1, "more=1 while server total (2000) > cap");
|
||
/* 服务端 total 恰好等于 cap → 加满后 more=0 */
|
||
appstate_search_clear(0);
|
||
for (int i = 0; i < NCM_SEARCH_SONG_MAX / NCM_SEARCH_PAGE + 1; i++)
|
||
appstate_search_append_songs(NCM_SEARCH_PAGE, page, NCM_SEARCH_SONG_MAX);
|
||
CHECK(appstate_search_count(0) == NCM_SEARCH_SONG_MAX && appstate_search_has_more(0) == 0,
|
||
"more=0 when server total == cap (clamped full)");
|
||
/* 无 total(-1)时回落「返回条数==页大小」判 more */
|
||
appstate_search_clear(1);
|
||
ncm_playlist_t pp[NCM_SEARCH_PAGE];
|
||
for (int i = 0; i < NCM_SEARCH_PAGE; i++) pp[i] = mk_pl(i);
|
||
appstate_search_append_pls(NCM_SEARCH_PAGE, pp, -1);
|
||
CHECK(appstate_search_has_more(1) == 1, "unknown total + full page => more=1");
|
||
appstate_search_append_pls(10, pp, -1);
|
||
CHECK(appstate_search_has_more(1) == 0, "unknown total + short page => more=0");
|
||
|
||
/* ===== 搜索结果播放队列上下文(§23:不覆写歌单窗口) ===== */
|
||
printf("[4] search queue context (queue_src=SEARCH)\n");
|
||
appstate_search_clear(0);
|
||
for (int i = 0; i < 3; i++) { ncm_song_t t = mk_song(300 + i); appstate_search_append_songs(1, &t, 3); }
|
||
/* 歌单窗口预置(模拟「打开的歌单还没被打扰」) */
|
||
g_st.track_ids_count = 7; g_st.song_count = 7; g_st.window_base = 0;
|
||
memset(g_st.song_valid, 0, sizeof g_st.song_valid);
|
||
for (int i = 0; i < 7; i++) { g_st.song_ids[i] = 50 + i; g_st.song_valid[i] = 1; }
|
||
appstate_set_queue(NCM_QUEUE_PLAYLIST);
|
||
CHECK(appstate_queue_count() == 7, "queue=playlist window count(7)");
|
||
appstate_set_queue(NCM_QUEUE_SEARCH);
|
||
CHECK(appstate_queue_count() == 3, "queue=search count(3)");
|
||
ncm_song_t qs;
|
||
CHECK(appstate_queue_song(2, &qs) == 0 && strcmp(qs.name, "Search Song 302") == 0, "queue_song from search set");
|
||
/* 关键:搜索起播不改写歌单窗口(track_ids_count/song_ids 原样) */
|
||
CHECK(g_st.track_ids_count == 7 && g_st.song_valid[3] == 1 && g_st.song_ids[3] == 53,
|
||
"playlist window untouched by search queue");
|
||
appstate_set_queue(NCM_QUEUE_PLAYLIST);
|
||
CHECK(appstate_queue_count() == 7, "queue back to playlist count(7)");
|
||
|
||
printf("\n%s\n", g_fail ? "search_sim FAIL" : "search_sim ALL PASS");
|
||
return g_fail ? 1 : 0;
|
||
}
|