924ac3d428
用户裁定「统一」。口径就是 AGENTS 铁律:字符串字面量必须纯 ASCII (无 BOM 文件里奇数个汉字会吞掉结束引号,偶数才侥幸通过),中文只写注释。 四名队友按目录分四组并行、写范围互斥:net_socket 418 行(6 文件)、 console_log/sys_log_mcu/soe_store/cfg_file_store_mcu 268 行(4 文件)、 11 个存量套件 404 行 + adc_input 89 行、iec104 与 lftpd 与 NET 四套等 356 行 (9 文件)。runner 的 .py 文档串不在口径内(Python 源不经 ARMCC 代码页)。 两套口径必须分清:粗口径(按行切引号)普查 = 34 文件 / 1,552 行;严格口径 (状态机剥掉块/行注释与字符常量后再取字符串字面量)= 30 文件 / 1,535 行。 差值那 4 个文件的命中全在注释里(ns_peers.c、net_socket/stub/ns_rtos.h、 common/ch395f_fake.h、common/fatfs_fake.c)——按口径一处都不该改,改了等于 把中文从注释里搬走、正好违背口径。粗口径绝不能当交付判据,否则要么白改 注释、要么留下「这行为什么没改」的悬案。 Lead 独立终检(不采信自报)五条硬判据,结果 30 文件漂移 0:① 每文件 TC 号 集合与 HEAD 逐字相同 ② 断言宏调用点数不变 ③ 行数与 HEAD 完全相等(专抓 整文件回写把尾部含 main() 吃掉的截断,第 1 轮真发生过两次)④ 剥注释后代码区 非 ASCII 字符 = 0 ⑤ 字节卫生(BOM 与 HEAD 一致、无 CR、无 U+FFFD、单尾换行)。 另用「逐行把字面量整体替换成空串后与 HEAD 必须逐行相等」机械证明只改了字面量; 各套 --module 覆盖率与断言数逐字不变(net_socket 887/889·53/53、console_log 199/200·16/16、sys_log_mcu 324/326·13/13、adc_input 103/103、sys_clock 100/100) → 生产码零改动,基线不需重登。 闸拦下一处真实回归(我的验收器没抓到):ns_s6_edges.c 翻译时把一个上一轮 已整改成平实描述的编号又写回了 CHECK 标签。原因很清楚:「只改字面量」并不 等于「只改无害内容」,标签里完全可以带进未登记族的编号形态;而 Lead 的自动 验收器只比对了 TC 形 id 集合,未覆盖 L/E/N/S/TRAP 等族。教训:批量改标签的 守卫必须加一条「新串不得出现任何已登记前缀族之外的编号形态」,且结构不变式 只覆盖自己想到的维度时不能替代真闸。 Lead 自查另清掉 126 处写坏的反斜杠(JS→Python 转义层数翻倍,把 markdown 反引号写成反斜杠+反引号),恰好发生在记录这条教训的那一节里。 文档:docs/测试说明/主机单测覆盖率口径.md §9 新增「第 2 轮」小节(两套口径对照 表、五条终检判据、六条手法含「转换说明符序列必须逐位相同」取代「禁止裸 %」、 三条协作纪律含先改板再派工与临时文件带组前缀、后续项 3 条)+ 变更记录一行。 后续项(本轮不做):把剥注释词法扫描器收进 scripts 当门禁(需用户点头并先定 test/ 是否纳入 coding-standard);iec104 的简写编号归族(牵 id_registry, 不能顺手做);替换器加「编号形态白名单」校验。 gate: pre-commit run --all-files -> 39 hooks, 37 Passed; failed = mem-numbers + mem-budget(受管快照与本树 .map 的漂移,属第 1 轮生产码改动的遗留,非本批 增量)。本批只碰 test/host/**,不进 Keil 编译面也不进模拟器 CMake,**RAM/Flash 增量 = 0**;实测水位 RW_IRAM1 57.4%、RW_CCM 87.9%,均低于 92% 红线。 id-check 0 台账外新账(可判定 84.63% > 基线 82.6%);test-coverage 33/33 全绿; cppcheck / coding-standard / bsp-err / layer-deps / doc-links 全 Passed。 按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
377 lines
14 KiB
C
377 lines
14 KiB
C
/*
|
||
* 模块名称:任务耗时探针(task_prof)主机侧单元测试
|
||
* 模块功能:在 PC 上离线验证 App/util/task_prof.c 的**样本有效性闸门、enter/exit
|
||
* 配对、上报节流与通道选择、周期→微秒换算、glitch 汇总**。
|
||
*
|
||
* 被测对象:真实源文件 ../../../App/util/task_prof.c(#include 原样纳入)。桩:
|
||
* - stub/main.h:DWT/CoreDebug/SystemCoreClock/HAL_GetTick(本目录,
|
||
* 真实 main.h 拉整棵 HAL 头树无法在 PC 上编);
|
||
* - console_log.h 用**真实头**(App/log),两个出口函数
|
||
* dbg_console_write_line / dbg_console_write_raw 由本文件实现并**留档
|
||
* 内容**——报告行是要被人读的,所以断言直接盯字符串本身。
|
||
*
|
||
* 为什么要主机侧单测(而不是只靠真机日志):
|
||
* 这个模块是"性能问题的唯一现场证据",它自己错了所有 PROF 行就都是假的:
|
||
* 1. **无效样本闸门**(cyc==0 或 ≥ SystemCoreClock×10):漏了它,一次 DWT 复位
|
||
* 就把 max 抬到 ~2^32 周期,之后所有"最坏耗时"永久失真(真机上没人会去翻
|
||
* gl= 计数发现)。这条只能构造样本测。
|
||
* 2. **未 enter 就 exit 必须直接返回**:周期测量写成"先 exit 再 enter"时,首轮
|
||
* 会把 0 当起点,记下一次几秒的虚假超大样本——真机上表现为"偶发一条离谱
|
||
* PROF 行",查半年。
|
||
* 3. 节流契约(首次 5 s、之后每 2 s)与**两条通道的选择**(report 走环形缓冲、
|
||
* report_raw 走裸发):错了要么刷屏挤掉正事,要么停摆现场那一行发不出去。
|
||
* 4. 周期→微秒换算依赖 SystemCoreClock,改时钟树(比如 168→180 MHz)忘了同步
|
||
* 就会整表偏移;这条在真机上看不出来(没人对着示波器量 PROF)。
|
||
*
|
||
* 覆盖点:
|
||
* TC-TASKPROF-001 有效样本:count+1 / last=样本 / max=样本
|
||
* TC-TASKPROF-002 更小样本不改 max(max 只增不减)
|
||
* TC-TASKPROF-003 cyc=0 只累加 glitch,不动 count/last/max(计数器复位防线)
|
||
* TC-TASKPROF-004 cyc ≥ SystemCoreClock×10 同样只累加 glitch
|
||
* TC-TASKPROF-005 id 越界(=COUNT 与 +1)安全返回,不碰任何槽
|
||
* TC-TASKPROF-006 首次取时基即惰性使能 DWT(TRCENA + CYCCNTENA 读回)
|
||
* TC-TASKPROF-007 enter 置 in_seg 并记下起点
|
||
* TC-TASKPROF-008 exit 结算 Δ、清 in_seg、count+1
|
||
* TC-TASKPROF-009 **未 enter 直接 exit 不记账**(防首轮 0 起点造出假超大样本)
|
||
* TC-TASKPROF-010 重复 exit 不重复记账
|
||
* TC-TASKPROF-011 首次 5 s 之前一条都不发
|
||
* TC-TASKPROF-012 到点后发两行([PROF] + [PROF2]),走环形缓冲通道
|
||
* TC-TASKPROF-013 之后不足 2 s 不发(节流)
|
||
* TC-TASKPROF-014 满 2 s 再发两行
|
||
* TC-TASKPROF-015 report_raw 无视节流,恒发两行且走**裸发**通道
|
||
* TC-TASKPROF-016 行格式:以 [PROF]/[PROF2] 开头、以 \r\n 结尾
|
||
* TC-TASKPROF-017 周期→微秒换算按 SystemCoreClock(168 MHz 下 1680 周期 = 10 us)
|
||
* TC-TASKPROF-018 gl= 是所有槽 glitch 的汇总,不是单槽
|
||
* TC-TASKPROF-019 段内进行时的 n= 字段非零(此刻仍在段内的可视性)
|
||
* TC-TASKPROF-020 count 是累计值,上报不清零
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||
*
|
||
* 创建日期:2026-10-23
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "console_log.h" /* 真实头:DBG_CON_LINE_MAX + 两个出口 */
|
||
#include "task_prof.h"
|
||
|
||
#include "task_prof.c"
|
||
|
||
/* ===================== 断言框架 ===================== */
|
||
|
||
static int s_total;
|
||
static int s_passed;
|
||
static int s_failed;
|
||
|
||
#define CHECK(cond, name) do { \
|
||
s_total++; \
|
||
if (cond) { s_passed++; printf(" PASS %s\n", name); } \
|
||
else { s_failed++; printf(" FAIL %s (line %d)\n", name, __LINE__); } \
|
||
} while (0)
|
||
|
||
/* ===================== 桩实体 ===================== */
|
||
|
||
static tp_coredebug_t g_coredebug;
|
||
static tp_dwt_t g_dwt;
|
||
tp_coredebug_t *const CoreDebug = &g_coredebug;
|
||
tp_dwt_t *const DWT = &g_dwt;
|
||
|
||
uint32_t SystemCoreClock = 168000000u; /* 真机口径:168 MHz */
|
||
|
||
static uint32_t g_tick; /* HAL_GetTick 的可控值 */
|
||
|
||
uint32_t HAL_GetTick(void)
|
||
{
|
||
return g_tick;
|
||
}
|
||
|
||
/* 出口留档:两条通道分别计数;**每一行都单独留档**(一次上报会连发
|
||
* [PROF] 与 [PROF2] 两行,只存"最后一行"就没法断言第一行的字段) */
|
||
#define CAP_MAX 8
|
||
static int g_line_calls;
|
||
static int g_raw_calls;
|
||
static char g_line[CAP_MAX][DBG_CON_LINE_MAX];
|
||
|
||
static void cap(int slot, const char *src, unsigned len)
|
||
{
|
||
unsigned i;
|
||
|
||
if (slot >= CAP_MAX) {
|
||
return;
|
||
}
|
||
for (i = 0u; (i < len) && (i + 1u < DBG_CON_LINE_MAX); i++) {
|
||
g_line[slot][i] = src[i];
|
||
}
|
||
g_line[slot][i] = '\0';
|
||
}
|
||
|
||
int dbg_console_write_line(const char *line, unsigned len)
|
||
{
|
||
cap(g_line_calls + g_raw_calls, line, len);
|
||
g_line_calls++;
|
||
return (int)len;
|
||
}
|
||
|
||
void dbg_console_write_raw(const char *buf, unsigned len)
|
||
{
|
||
cap(g_line_calls + g_raw_calls, buf, len);
|
||
g_raw_calls++;
|
||
}
|
||
|
||
void dbg_console_writer_attach(void) { }
|
||
uint8_t dbg_console_writer_ready(void) { return 1u; }
|
||
|
||
/* ===================== 辅助 ===================== */
|
||
|
||
static void reset_all(void)
|
||
{
|
||
(void)memset(s_prof, 0, sizeof(s_prof));
|
||
(void)memset(&g_coredebug, 0, sizeof(g_coredebug));
|
||
(void)memset(&g_dwt, 0, sizeof(g_dwt));
|
||
s_dwt_ok = 0u;
|
||
s_prof_reported = 0u;
|
||
s_prof_last = 0u;
|
||
g_tick = 0u;
|
||
g_line_calls = 0;
|
||
g_raw_calls = 0;
|
||
(void)memset(g_line, 0, sizeof(g_line));
|
||
}
|
||
|
||
static void set_cyc(uint32_t v)
|
||
{
|
||
g_dwt.CYCCNT = v;
|
||
}
|
||
|
||
/* 从指定行的键上取整数值;键不存在或其后不是数字则返回 -1(断言因此会响)。
|
||
* 手写解析而不用 sscanf:后者在 MSVC 下触发 C4996(弃用告警),门禁输出要干净 */
|
||
static long field_of(int slot, const char *p_key)
|
||
{
|
||
const char *p;
|
||
long v = 0;
|
||
int digits = 0;
|
||
|
||
if (slot >= CAP_MAX) {
|
||
return -1L;
|
||
}
|
||
p = strstr(g_line[slot], p_key);
|
||
if (p == NULL) {
|
||
return -1L;
|
||
}
|
||
p += strlen(p_key);
|
||
while ((*p >= '0') && (*p <= '9')) {
|
||
v = (v * 10) + (long)(*p - '0');
|
||
p++;
|
||
digits++;
|
||
}
|
||
return (digits > 0) ? v : -1L;
|
||
}
|
||
|
||
/* ===================== 样本闸门 ===================== */
|
||
|
||
static void tc_sample_gate(void)
|
||
{
|
||
printf("-- TC-TASKPROF-001..005 sample gate --\n");
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, 500u);
|
||
CHECK((s_prof[TASK_PROF_UI_PERIOD].count == 1u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].last == 500u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].max == 500u),
|
||
"TC-TASKPROF-001 valid sample records count/last/max");
|
||
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, 300u);
|
||
CHECK((s_prof[TASK_PROF_UI_PERIOD].max == 500u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].last == 300u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].count == 2u),
|
||
"TC-TASKPROF-002 smaller sample does not change max");
|
||
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, 0u);
|
||
CHECK((s_prof[TASK_PROF_UI_PERIOD].glitch == 1u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].count == 0u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].max == 0u),
|
||
"TC-TASKPROF-003 cyc=0 only accumulates glitch (DWT reset defence)");
|
||
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, SystemCoreClock * 10u);
|
||
CHECK((s_prof[TASK_PROF_UI_PERIOD].glitch == 1u) &&
|
||
(s_prof[TASK_PROF_UI_PERIOD].count == 0u),
|
||
"TC-TASKPROF-004 over-upper-bound sample only accumulates glitch");
|
||
|
||
reset_all();
|
||
task_prof_sample((task_prof_id_t)TASK_PROF_ID_COUNT, 100u);
|
||
task_prof_sample((task_prof_id_t)(TASK_PROF_ID_COUNT + 1), 100u);
|
||
task_prof_enter((task_prof_id_t)TASK_PROF_ID_COUNT);
|
||
task_prof_exit((task_prof_id_t)TASK_PROF_ID_COUNT);
|
||
CHECK((s_prof[0].count == 0u) && (s_prof[TASK_PROF_ID_COUNT - 1].count == 0u),
|
||
"TC-TASKPROF-005 out-of-range id returns safely, touches no slot");
|
||
}
|
||
|
||
/* ===================== 时基与 enter/exit 配对 ===================== */
|
||
|
||
static void tc_enter_exit(void)
|
||
{
|
||
printf("-- TC-TASKPROF-006..010 dwt enable + enter/exit --\n");
|
||
reset_all();
|
||
set_cyc(1000u);
|
||
(void)task_prof_cyc();
|
||
CHECK(((g_coredebug.DEMCR & CoreDebug_DEMCR_TRCENA_Msk) != 0u) &&
|
||
((g_dwt.CTRL & DWT_CTRL_CYCCNTENA_Msk) != 0u) &&
|
||
(s_dwt_ok == 1u),
|
||
"TC-TASKPROF-006 first timebase read lazily enables DWT");
|
||
|
||
reset_all();
|
||
set_cyc(2000u);
|
||
task_prof_enter(TASK_PROF_UI_WORK);
|
||
CHECK((s_prof[TASK_PROF_UI_WORK].in_seg == 1u) &&
|
||
(s_prof[TASK_PROF_UI_WORK].t_start == 2000u),
|
||
"TC-TASKPROF-007 enter sets in_seg and records the start point");
|
||
|
||
set_cyc(2640u);
|
||
task_prof_exit(TASK_PROF_UI_WORK);
|
||
CHECK((s_prof[TASK_PROF_UI_WORK].in_seg == 0u) &&
|
||
(s_prof[TASK_PROF_UI_WORK].last == 640u) &&
|
||
(s_prof[TASK_PROF_UI_WORK].count == 1u),
|
||
"TC-TASKPROF-008 exit settles the delta and clears in_seg");
|
||
|
||
reset_all();
|
||
task_prof_exit(TASK_PROF_ADC_BEAT);
|
||
CHECK((s_prof[TASK_PROF_ADC_BEAT].count == 0u) &&
|
||
(s_prof[TASK_PROF_ADC_BEAT].glitch == 0u),
|
||
"TC-TASKPROF-009 exit without enter records nothing (guards fake huge sample from 0 start)");
|
||
|
||
reset_all();
|
||
set_cyc(100u);
|
||
task_prof_enter(TASK_PROF_NET_POLL);
|
||
set_cyc(150u);
|
||
task_prof_exit(TASK_PROF_NET_POLL);
|
||
task_prof_exit(TASK_PROF_NET_POLL);
|
||
CHECK(s_prof[TASK_PROF_NET_POLL].count == 1u, "TC-TASKPROF-010 repeated exit does not double count");
|
||
}
|
||
|
||
/* ===================== 上报节流与通道 ===================== */
|
||
|
||
static void tc_report_throttle(void)
|
||
{
|
||
printf("-- TC-TASKPROF-011..015 report throttle + channels --\n");
|
||
reset_all();
|
||
g_tick = 4999u;
|
||
task_prof_report();
|
||
CHECK((g_line_calls == 0) && (g_raw_calls == 0), "TC-TASKPROF-011 nothing sent before the first 5 s");
|
||
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
CHECK((g_line_calls == 2) && (g_raw_calls == 0),
|
||
"TC-TASKPROF-012 on schedule: sends two lines via the ring buffer");
|
||
|
||
g_tick = 6999u;
|
||
task_prof_report();
|
||
CHECK(g_line_calls == 2, "TC-TASKPROF-013 less than 2 s apart sends nothing");
|
||
|
||
g_tick = 7000u;
|
||
task_prof_report();
|
||
CHECK(g_line_calls == 4, "TC-TASKPROF-014 after a full 2 s sends two lines again");
|
||
|
||
g_line_calls = 0;
|
||
g_raw_calls = 0;
|
||
task_prof_report_raw();
|
||
CHECK((g_raw_calls == 2) && (g_line_calls == 0),
|
||
"TC-TASKPROF-015 report_raw ignores throttling and uses the raw channel");
|
||
}
|
||
|
||
/* ===================== 行内容与换算 ===================== */
|
||
|
||
static void tc_line_format(void)
|
||
{
|
||
printf("-- TC-TASKPROF-016..020 line content --\n");
|
||
|
||
/* 016:一次上报连发两行;行首前缀是采集脚本的抓取依据,行尾必须是 CRLF */
|
||
reset_all();
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
CHECK((g_line_calls == 2) &&
|
||
(strncmp(g_line[0], "[PROF] ", 7) == 0) &&
|
||
(strncmp(g_line[1], "[PROF2] ", 8) == 0),
|
||
"TC-TASKPROF-016 the two lines start with [PROF] / [PROF2] respectively");
|
||
CHECK((strstr(g_line[0], "\r\n") != NULL) &&
|
||
(strstr(g_line[1], "\r\n") != NULL),
|
||
"TC-TASKPROF-016b both lines end with CRLF");
|
||
CHECK(field_of(0, "uip=") == 0L, "TC-TASKPROF-016c an unsampled slot prints 0");
|
||
|
||
/* 017:打印的是**微秒**,分母是 SystemCoreClock —— 断言落在打印字段上 */
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, 1680u); /* 168 MHz → 10 us */
|
||
task_prof_sample(TASK_PROF_ADC_BEAT, 3360u); /* 168 MHz → 20 us */
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
CHECK(field_of(0, "uip=") == 10L,
|
||
"TC-TASKPROF-017 168 MHz: max of 1680 cycles prints as uip=10");
|
||
CHECK(field_of(0, "adcm=") == 20L,
|
||
"TC-TASKPROF-017b same clock: max of 3360 cycles prints as adcm=20");
|
||
SystemCoreClock = 84000000u; /* 改时钟树而忘了同步 → 整表偏移,这里必须显式跟着变 */
|
||
g_tick = 7000u;
|
||
task_prof_report();
|
||
CHECK(field_of(2, "uip=") == 20L,
|
||
"TC-TASKPROF-017c at 84 MHz the same cycle counts print as 20");
|
||
SystemCoreClock = 168000000u;
|
||
|
||
/* 018:gl= 是**所有槽**的 glitch 汇总,不是当前槽 */
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_PERIOD, 0u);
|
||
task_prof_sample(TASK_PROF_ADC_BEAT, 0u);
|
||
task_prof_sample(TASK_PROF_NET_POLL, 0u);
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
CHECK(field_of(0, "gl=") == 3L, "TC-TASKPROF-018 gl= sums the glitch of all slots");
|
||
|
||
/* 019:段未结算时,"此刻已在段内多久"要看得见(uipn / netn 两个字段) */
|
||
/* 字段口径 = 上报时刻的 CYCCNT 减去各自的进段时刻(**不是**样本值):
|
||
* UI_PERIOD 进段 @1000、NET_POLL 进段 @10000,上报时 CYCCNT=51680
|
||
* uipn = (51680-1000)/168 = 301 netn = (51680-10000)/168 = 248 */
|
||
reset_all();
|
||
set_cyc(1000u);
|
||
task_prof_enter(TASK_PROF_UI_PERIOD);
|
||
set_cyc(10000u);
|
||
task_prof_enter(TASK_PROF_NET_POLL);
|
||
set_cyc(51680u);
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
CHECK(field_of(0, "uipn=") == 301L,
|
||
"TC-TASKPROF-019 inside UI_PERIOD segment: uip= reports the us elapsed so far");
|
||
CHECK(field_of(1, "netn=") == 248L,
|
||
"TC-TASKPROF-019b inside NET_POLL segment: netn= of [PROF2] is visible too");
|
||
CHECK(field_of(0, "adcn=") == 0L,
|
||
"TC-TASKPROF-019c ADC_BEAT not in segment prints 0 (no cross-slot leakage)");
|
||
CHECK(s_prof[TASK_PROF_UI_PERIOD].in_seg == 1u,
|
||
"TC-TASKPROF-019d reporting does not consume segment state (observe only)");
|
||
|
||
/* 020:count 是累计量,两轮上报之间不能清零 */
|
||
reset_all();
|
||
task_prof_sample(TASK_PROF_UI_WORK, 100u);
|
||
g_tick = 5000u;
|
||
task_prof_report();
|
||
g_tick = 7000u;
|
||
task_prof_report();
|
||
CHECK((s_prof[TASK_PROF_UI_WORK].count == 1u) && (field_of(2, "uic=") == 0L) &&
|
||
(field_of(0, "uic=") == 0L),
|
||
"TC-TASKPROF-020 reporting does not clear counters (UI_WORK not in uic= field, still cumulative 1)");
|
||
}
|
||
|
||
|
||
/* ===================== 入口 ===================== */
|
||
|
||
int main(void)
|
||
{
|
||
printf("=== Task Prof Tests ===\n");
|
||
|
||
tc_sample_gate();
|
||
tc_enter_exit();
|
||
tc_report_throttle();
|
||
tc_line_format();
|
||
|
||
printf("\n=== Task Prof Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|