test: probe existing SYS clock access and AT time queries from APP

This commit is contained in:
chenjintao
2026-10-26 16:25:19 +08:00
parent 07525225cb
commit f8df812f45
7 changed files with 277 additions and 21 deletions
+18 -8
View File
@@ -32,7 +32,8 @@ int ql_rtc_get_time(ql_rtc_time_t *);
#include <assert.h>
#include <stdlib.h>
#include "APP_SOURCE"
static int mode, creates, queries, sets;
static int mode, creates, queries, sets, probes;
void app_sys_time_probe_run(void) {++probes;}
static unsigned int ticks=100;
static time_t app_epoch=100;
static int zone_minutes=480;
@@ -53,7 +54,10 @@ int ql_nw_get_nitz_time_info(QL_NW_NITZ_TIME_INFO_T *info) {
if (mode==3) return 0;
if (mode==4) {memset(info->nitz_time,'X',32); return 0;}
if (mode==5) {strcpy(info->nitz_time,"26/02/30 13:00:00 +32 00"); return 0;}
if (mode==6) {strcpy(info->nitz_time,"26/10/26 13:00:00 +32 00"); return 0;}
if (mode==6 || mode==22) {
strcpy(info->nitz_time,mode==22 ? "26/10/26 08:00:10 32 0" : "26/10/26 13:00:00 +32 00");
return 0;
}
if (mode==16 && queries==1) return 1;
if (mode==17) ticks+=600; /* slow query must be rejected */
if (mode==14) value=1793059198; /* near midnight UTC */
@@ -63,7 +67,7 @@ int ql_nw_get_nitz_time_info(QL_NW_NITZ_TIME_INFO_T *info) {
/* Compute relative elapsed correctly in wrap scenario. */
if (mode==13) value=1793019600+(unsigned int)(ticks-(UINT32_MAX-199U))/200U;
utc=gmtime(&value); assert(utc);
snprintf(info->nitz_time,32,"%02d/%02d/%02d %02d:%02d:%02d +32 00",
snprintf(info->nitz_time,32,mode==21 ? "%02d/%02d/%02d %02d:%02d:%02d 32 0" : "%02d/%02d/%02d %02d:%02d:%02d +32 00",
utc->tm_year%100,utc->tm_mon+1,utc->tm_mday,utc->tm_hour,utc->tm_min,utc->tm_sec);
return 0;
}
@@ -100,6 +104,11 @@ static void check_parser(void) {
assert(parse_nitz("26/10/26,13:00:00-22",&epoch,&zone)==0 &&
epoch==1793019600 && zone==-22);
assert(parse_nitz("26/10/26 13:00:00 +56 02",&epoch,&zone)==0);
assert(parse_nitz("26/10/26 08:00:10 32 0",&epoch,&zone)==0 && epoch==1793001610 && zone==32);
assert(parse_nitz("26/10/26 13:00:00 -22 0",&epoch,&zone)==0 && zone==-22);
assert(parse_nitz("26/10/26 13:00:00 0 0",&epoch,&zone)==0 && zone==0);
assert(parse_nitz("26/10/26 13:00:00 0",&epoch,&zone)!=0);
assert(parse_nitz("26/10/26 13:00:00 320 0",&epoch,&zone)!=0);
assert(parse_nitz("24/02/29 23:59:59 +00 00",&epoch,&zone)==0);
assert(parse_nitz("26/02/29 00:00:00 +00 00",&epoch,&zone)!=0);
assert(parse_nitz("38/01/19 03:14:07 +00 00",&epoch,&zone)==0 && epoch==2147483647);
@@ -110,7 +119,7 @@ static void check_parser(void) {
"26/10/26 24:00:00 +00 00", "26/10/26 13:60:00 +00 00",
"26/10/26 13:00:60 +00 00", "26/10/26 13:00:00 +57 00",
"26/10/26 13:00:00 -49 00", "26/10/26 13:00:00 +32 03",
"26/10/26 13:00:00 +32 0", "26/10/26 13:00:00 +32 junk",
"26/10/26 13:00:00 +32 junk",
"26/10/26 13:00:00 +32 00junk", "x6/10/26 13:00:00 +32 00"};
for (unsigned int i=0;i<sizeof(bad)/sizeof(bad[0]);++i)
assert(parse_nitz(bad[i],&epoch,&zone)!=0);
@@ -124,10 +133,11 @@ int main(int argc,char **argv) {
if (mode==13) ticks=UINT32_MAX-199U;
app_nitz_test_start(); app_nitz_test_start();
assert(creates==(mode==19 ? 2 : 1));
if ((mode>=1 && mode<=6) || mode==15 || mode==17 || mode==18 || mode==19 || mode==7)
if ((mode>=1 && mode<=6) || mode==15 || mode==17 || mode==18 || mode==19 || mode==7 || mode==22)
assert(sets==0);
else assert(sets==1);
assert(queries<=6);
assert(probes==(mode==19 ? 0 : 1));
return 0;
}
'''.replace('APP_SOURCE', str(APP / 'app_nitz_test.c'))
@@ -147,10 +157,10 @@ int main(int argc,char **argv) {
12: 'PASS source=nitz', 13: 'PASS source=nitz', 14: 'PASS source=nitz',
15: 'FAIL stage=no_fresh_nitz', 16: 'PASS source=nitz',
17: 'FAIL stage=no_fresh_nitz', 18: 'cached_or_inconsistent_nitz=1',
19: 'FAIL stage=create_task', 20: 'FAIL stage=app_clock_verification'}
19: 'FAIL stage=create_task', 20: 'FAIL stage=app_clock_verification', 21: 'PASS source=nitz', 22: 'FAIL stage=no_fresh_nitz'}
for mode, expected in cases.items():
log = subprocess.check_output([str(work / 'test'), str(mode)], text=True)
assert expected in log, log
if mode not in (0, 12, 13, 14, 16):
if mode not in (0, 12, 13, 14, 16, 21):
assert '[APP-NITZ-TEST] PASS ' not in log, log
print('PASS: Gregorian/parser validation and 21 API/freshness/RTC scenarios (ASan/UBSan)')
print('PASS: Gregorian/parser validation and 23 API/freshness/RTC scenarios (ASan/UBSan)')
+90
View File
@@ -0,0 +1,90 @@
"""Exercise the actual read-only SYS/AT probe without a modem or kernel image."""
from pathlib import Path
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
APP = ROOT / 'ql-application/threadx/interface/custom_app'
with tempfile.TemporaryDirectory(prefix='app-sys-probe-') as directory:
work = Path(directory)
headers = {
'ql_rtos.h': '''unsigned int ql_rtos_get_systicks(void);
void ql_rtos_task_sleep_ms(unsigned int);
''',
'ql_func.h': '#include <stdint.h>\nextern intptr_t (*m_get_api_ptr)(char *);\n',
'ql_rtc.h': '''typedef struct {int tm_sec,tm_min,tm_hour,tm_mday,tm_mon,tm_year,tm_wday;} ql_rtc_time_t;
int ql_rtc_get_time_ex(ql_rtc_time_t *);
''',
'ql_atcmd.h': 'int ql_atcmd_send_sync(char *,char *,int,char *,int);\n',
}
for name, content in headers.items():
(work / name).write_text(content)
harness = r'''
#include <assert.h>
#include <stdlib.h>
#include "APP_SOURCE"
static int mode, sends, reads, sys_reads;
static unsigned int ticks=100, origin=100;
unsigned int ql_rtos_get_systicks(void) {return ticks;}
void ql_rtos_task_sleep_ms(unsigned int ms) {assert(ms==3000); ticks+=ms/5;}
static unsigned int get_count(int offset) {
assert(offset==0); ++sys_reads;
if (mode==4) return 100;
return 1793030410U+(mode==5 ? 0 : (ticks-origin)/200U);
}
static int get_zone(void) {return 480;}
static intptr_t lookup(char *name) {
assert(strstr(name,"diag")==NULL);
if (!strcmp(name,"PMIC_RTC_GetTime_Count")) return mode==1 ? 0 : (intptr_t)get_count;
if (!strcmp(name,"GetTimeZone")) return mode==1 || mode==2 ? 0 : (intptr_t)get_zone;
if (!strcmp(name,"ql_rtc_get_time_ex")) return mode==7 ? 0 : 1;
if (!strcmp(name,"ql_atcmd_send_sync")) return mode==8 ? 0 : 1;
assert(0); return 0;
}
intptr_t (*m_get_api_ptr)(char *)=lookup;
time_t time(time_t *out) {(void)out; return 100;}
int ql_rtc_get_time_ex(ql_rtc_time_t *out) {
++reads; out->tm_year=1970; out->tm_mon=1; out->tm_mday=1; return 0;
}
int ql_atcmd_send_sync(char *cmd,char *resp,int len,char *filter,int timeout) {
static const char *commands[]={"AT+QLTS=0\r\n","AT+QLTS=1\r\n","AT+QLTS=2\r\n","AT+CCLK?\r\n"};
assert(!strcmp(cmd,commands[sends%4]));
assert(len==256 && filter==NULL && timeout==2);
++sends; ticks+=20;
if (mode==1) {strcpy(resp,"\r\nERROR\r\n"); return 1;}
if (mode==6) {memset(resp,'X',len); return 0;}
strcpy(resp,"\r\n+QLTS: \"2026/10/26,08:00:10+32,0\"\r\nOK\r\n"); return 0;
}
int main(int argc,char **argv) {
assert(argc==2); mode=atoi(argv[1]);
if (mode==3) m_get_api_ptr=NULL;
if (mode==9) ticks=origin=UINT32_MAX-199U;
app_sys_time_probe_run();
assert(sends==(mode==3 || mode==8 ? 0 : 8));
assert(reads==(mode==3 || mode==7 || mode==8 ? 0 : 2));
assert(sys_reads==(mode==1 || mode==2 || mode==3 ? 0 : 2));
return 0;
}
'''.replace('APP_SOURCE', str(APP / 'app_sys_time_probe.c'))
(work / 'test.c').write_text(harness)
compiler = shutil.which('clang') or shutil.which('cc')
if not compiler:
raise SystemExit('A native C compiler is required')
subprocess.run([compiler, '-std=c99', '-Wall', '-Wextra', '-Werror',
'-fsanitize=address,undefined', '-I', str(work), '-I', str(APP / 'inc'),
str(work / 'test.c'), '-o', str(work / 'test')], check=True)
cases = {0: 'direct_sys=candidate', 1: 'direct_sys=unavailable',
2: 'direct_sys=unavailable', 3: 'FAIL stage=api_lookup_unavailable',
4: 'direct_sys=rejected', 5: 'direct_sys=rejected',
6: 'terminated=0', 7: 'rtc_ex=unavailable', 8: 'at=unavailable',
9: 'direct_sys=candidate'}
for mode, expected in cases.items():
log = subprocess.check_output([str(work / 'test'), str(mode)], text=True)
assert expected in log, log
assert '[APP-SYS-PROBE] finished rtc_not_written=1' in log, log
if mode == 1:
assert 'query=QLTS_1 rc=1' in log, log
if mode not in (0, 6, 7, 8, 9):
assert 'direct_sys=candidate' not in log, log
print('PASS: 10 SYS/AT probe scenarios (ASan/UBSan); no setter or diagnostic API linked')