Files
MeowOS-For-R3P2/patch_v2/ncm_app/bench/search_sim.c
T
MeowOS 1e97dbf54a ncm search: 历史+单曲/歌单分类+分页+误触修复+不覆写歌单上下文 (§23)
- 搜索历史:进页自动弹键盘;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 请求。
2026-09-01 19:21:16 +08:00

213 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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;
}