test(ncm): M9 分类/登录门宿主单测(83 断言)+ 传输层归一 4 例 + REPORT §41

- bench/session_gate_sim.c(新):真实 core + ncm_http_request 桩,覆盖
  A 错误分类矩阵(-4/-5/-6/-17/裸 mbedtls vs -13/其它)、B HTTP 状态矩阵
  (401/403 vs 5xx/429 vs 3xx/4xx)、C 登录门 + 启动决策(无网→离线、无会话→扫码)、
  D 真实 ncm_api 状态/业务码映射(含 -460→SVC、301/302→AUTH)、
  E 真实 ncm_login_status_check/refresh 映射、F 会话密钥稳定性(换 MAC 仍可解、
  旧 MAC 密文可解且自动迁移)。83 PASS / 0 FAIL,0 真实请求。
- bench/http_truncate_sim.c:追加 4 例传输层归一(握手 fatal alert→NET、握手超时→TIMEOUT、
  读 fatal alert→NET、CA 失败→TLS),原 8 例截断守卫零回归(12 PASS)。
- build_ncm_sims.sh:window/search/plpage/apicache/cookie_refresh 链入 ncm_session.o
  (appstate/api/login 新依赖),新增 session_gate_sim 构建与运行。
- test_ncm_core.c:采用 M8 分支的 $TMPDIR 适配(Termux /tmp 不可写;合并时以 M8 为准)。
- 相关 sim 头注释同步链接命令(ncm_apicache.o/ncm_session.o)。
- REPORT §41:判定点调用链与行号、根因证据、实现、验证矩阵与诚实出入
  (mipsel/真机未跑;app.c 宿主不可编译的既有原因;403/-460 取舍)。

验证:test_ncm_core_host 139 PASS/0 FAIL;session_gate_sim 83 PASS;
http_truncate_sim 12 PASS;window/search/apicache/cookie_refresh sim 通过;
核心 12 TU + 应用 13 TU(-Wall -Wextra)0 warning。
This commit is contained in:
AskaEth committed 2026-11-05 11:45:44 +08:00
1 parent e0bd503261
commit 2ca386973b
8 files changed
+606 -12

No files matched your search

+51 -4
View File
@@ -15,6 +15,12 @@
* chunked 完整(0 分块末端) → BS_DONE → NCM_OK
* chunked 中途关闭 → 截断 → 非 NCM_OK(NCM_ERR_IO)
*
* M9 追加(传输层错误归一):mbedtls 裸负码不得原样穿透给上层——
* 握手失败(fatal alert) → NCM_ERR_NET
* 握手超时 → NCM_ERR_TIMEOUT
* 读阶段 fatal alert(对端 reset/拒绝) → NCM_ERR_NET
* CA 加载/校验失败(verify_ca=1) → NCM_ERR_TLS
*
* 编译(在 ncm_app 下):
* gcc -O2 -Wall -Wextra -DJSMN_HEADER -I. -Icore -Ivendor/mbedtls/include \
* -c bench/http_truncate_sim.c -o /tmp/t.o
@@ -48,6 +54,8 @@ static const tr_chunk_t *g_script;
static int g_script_n;
static int g_script_i;
static int g_close_rc; /* 脚本耗尽后的 ssl_read 返回值 */
static int g_handshake_rc; /* M9:0=正常;否则握手直接失败(模拟网络/TLS 异常) */
static int g_ca_fail; /* M9:1=CA 加载失败(verify_ca=1 时应判 NCM_ERR_TLS) */
static void tr_script(const tr_chunk_t *chunks, int n, int close_rc) {
g_script = chunks; g_script_n = n; g_script_i = 0; g_close_rc = close_rc;
@@ -110,13 +118,13 @@ void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl, void *p_bio,
mbedtls_ssl_recv_timeout_t *f_recv_timeout) {
(void)ssl; (void)p_bio; (void)f_send; (void)f_recv; (void)f_recv_timeout;
}
int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl) { (void)ssl; return 0; }
int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl) { (void)ssl; return g_handshake_rc; }
void mbedtls_ssl_free(mbedtls_ssl_context *ssl) { (void)ssl; }
void mbedtls_ssl_config_free(mbedtls_ssl_config *conf) { (void)conf; }
void mbedtls_x509_crt_init(mbedtls_x509_crt *crt) { (void)crt; }
int mbedtls_x509_crt_parse_file(mbedtls_x509_crt *crt, const char *path) {
(void)crt; (void)path; return 0; /* CA 加载成功(validate=0 时不走此径,桩兜底) */
(void)crt; (void)path; return g_ca_fail ? -1 : 0; /* CA 加载成功/失败(M9 用例可控) */
}
void mbedtls_x509_crt_free(mbedtls_x509_crt *crt) { (void)crt; }
@@ -136,12 +144,13 @@ static int on_body(void *ctx, const unsigned char *data, size_t len) {
/* 运行一个用例:装填脚本 → 发起 GET 下载 → 记录 rc / body_len / content_length */
typedef struct { int rc; size_t body_len; size_t content_length; } case_result_t;
static case_result_t run_case(const tr_chunk_t *script, int script_n, int close_rc) {
static case_result_t run_case_ex(const tr_chunk_t *script, int script_n, int close_rc,
int verify_ca) {
ncm_http_req_t req;
memset(&req, 0, sizeof req);
req.url = "https://ex.test/song.mp3";
req.method = "GET";
req.verify_ca = 0; /* 跳过 CA 校验,聚焦 body 终态判定 */
req.verify_ca = verify_ca; /* 0=跳过 CA 校验,聚焦 body 终态判定 */
req.timeout_ms = 3000;
sink_cb_t sink; memset(&sink, 0, sizeof sink);
@@ -157,6 +166,10 @@ static case_result_t run_case(const tr_chunk_t *script, int script_n, int close_
return r;
}
static case_result_t run_case(const tr_chunk_t *script, int script_n, int close_rc) {
return run_case_ex(script, script_n, close_rc, 0);
}
static int g_fail = 0;
static void check(const char *name, int ok, const char *detail) {
if (!ok) g_fail++;
@@ -286,6 +299,40 @@ int main(void) {
(r.rc != NCM_OK) && (r.rc == NCM_ERR_IO), dbg);
}
/* ---- 场景 9(M9):握手 fatal alert(对端 reset/拒绝)→ 归一为 NCM_ERR_NET ---- */
{
g_handshake_rc = MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE;
case_result_t r = run_case(NULL, 0, 0);
g_handshake_rc = 0;
snprintf(dbg, sizeof dbg, "rc=%d", r.rc);
check("handshake_fatal_alert -> NCM_ERR_NET", r.rc == NCM_ERR_NET, dbg);
}
/* ---- 场景 10(M9):握手超时 → NCM_ERR_TIMEOUT ---- */
{
g_handshake_rc = MBEDTLS_ERR_SSL_TIMEOUT;
case_result_t r = run_case(NULL, 0, 0);
g_handshake_rc = 0;
snprintf(dbg, sizeof dbg, "rc=%d", r.rc);
check("handshake_timeout -> NCM_ERR_TIMEOUT", r.rc == NCM_ERR_TIMEOUT, dbg);
}
/* ---- 场景 11(M9):读阶段 fatal alert(连接被 reset)→ NCM_ERR_NET ---- */
{
case_result_t r = run_case(NULL, 0, MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE);
snprintf(dbg, sizeof dbg, "rc=%d", r.rc);
check("read_fatal_alert -> NCM_ERR_NET", r.rc == NCM_ERR_NET, dbg);
}
/* ---- 场景 12(M9):CA 不可用(verify_ca=1)→ NCM_ERR_TLS(不静默放行) ---- */
{
g_ca_fail = 1;
case_result_t r = run_case_ex(NULL, 0, 0, 1);
g_ca_fail = 0;
snprintf(dbg, sizeof dbg, "rc=%d", r.rc);
check("ca_load_fail -> NCM_ERR_TLS", r.rc == NCM_ERR_TLS, dbg);
}
printf("\n== HTTP TRUNCATE SIM: %s ==\n", g_fail ? "FAIL" : "ALL PASS");
return g_fail ? 1 : 0;
}