test: calibrate APP RTC from first valid NITZ snapshot after PDP
This commit is contained in:
+31
-16
@@ -32,14 +32,13 @@ int ql_rtc_get_time(ql_rtc_time_t *);
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "APP_SOURCE"
|
||||
static int mode, creates, queries, sets, probes;
|
||||
void app_sys_time_probe_run(void) {++probes;}
|
||||
static int mode, creates, queries, sets, sleeps;
|
||||
static unsigned int ticks=100;
|
||||
static time_t app_epoch=100;
|
||||
static int zone_minutes=480;
|
||||
static ql_rtc_time_t saved;
|
||||
unsigned int ql_rtos_get_systicks(void) {return ticks;}
|
||||
void ql_rtos_task_sleep_ms(unsigned int ms) {ticks+=ms/5;}
|
||||
void ql_rtos_task_sleep_ms(unsigned int ms) {assert(ms==5000); ++sleeps; ticks+=ms/5;}
|
||||
static intptr_t lookup(char *name) {
|
||||
assert(!strcmp(name,"ql_nw_get_nitz_time_info") || !strcmp(name,"ql_localtime_r") ||
|
||||
!strcmp(name,"ql_rtc_set_time") || !strcmp(name,"ql_rtc_get_time"));
|
||||
@@ -59,10 +58,11 @@ int ql_nw_get_nitz_time_info(QL_NW_NITZ_TIME_INFO_T *info) {
|
||||
return 0;
|
||||
}
|
||||
if (mode==16 && queries==1) return 1;
|
||||
if (mode==23 && queries<3) return 1;
|
||||
if (mode==17) ticks+=600; /* slow query must be rejected */
|
||||
if (mode==14) value=1793059198; /* near midnight UTC */
|
||||
if (mode==15) value=2147483646; /* would advance past signed32 limit */
|
||||
if (mode==18) value+=(queries%2==0 ? 60 : 0); /* discontinuity */
|
||||
if (mode==15) value=2147483646; /* PMIC +8 local calendar exceeds signed32 range */
|
||||
if (mode==18) value=1704067200; /* Old but valid snapshot: age cannot be proven. */
|
||||
else value+=(ticks-100U)/200U;
|
||||
/* Compute relative elapsed correctly in wrap scenario. */
|
||||
if (mode==13) value=1793019600+(unsigned int)(ticks-(UINT32_MAX-199U))/200U;
|
||||
@@ -75,6 +75,7 @@ struct tm *ql_localtime_r(const time_t *epoch,struct tm *out) {
|
||||
time_t local=*epoch+zone_minutes*60;
|
||||
struct tm *value;
|
||||
if (mode==7) return NULL;
|
||||
if (mode==25) local+=28800; /* Invalid PMIC conversion (+16 hours total). */
|
||||
value=gmtime(&local); assert(value); *out=*value; return out;
|
||||
}
|
||||
time_t time(time_t *out) {(void)out; return app_epoch;}
|
||||
@@ -130,14 +131,22 @@ static void check_parser(void) {
|
||||
int main(int argc,char **argv) {
|
||||
assert(argc==2); mode=atoi(argv[1]); check_parser();
|
||||
if (mode==12) zone_minutes=-330;
|
||||
if (mode==24) zone_minutes=0;
|
||||
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 || mode==22)
|
||||
if ((mode>=1 && mode<=5) || mode==7 || mode==15 || mode==17 || mode==19 || mode==25)
|
||||
assert(sets==0);
|
||||
else assert(sets==1);
|
||||
assert(queries<=6);
|
||||
assert(probes==(mode==19 ? 0 : 1));
|
||||
if (mode==1 || mode==19) {assert(queries==0 && sleeps==0);}
|
||||
else if ((mode>=2 && mode<=5) || mode==17 || mode==23) {assert(queries==3 && sleeps==2);}
|
||||
else if (mode==16) {assert(queries==2 && sleeps==1);}
|
||||
else {assert(queries==1 && sleeps==0);}
|
||||
if (mode==22) {
|
||||
assert(saved.tm_year==2026 && saved.tm_mon==10 && saved.tm_mday==26);
|
||||
assert(saved.tm_hour==16 && saved.tm_min==0 && saved.tm_sec==10);
|
||||
assert(app_epoch==1793001610); /* Local RTC +8, UTC remains unshifted. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
'''.replace('APP_SOURCE', str(APP / 'app_nitz_test.c'))
|
||||
@@ -149,18 +158,24 @@ int main(int argc,char **argv) {
|
||||
'-fsanitize=address,undefined', '-I', str(work), '-I', str(APP / 'inc'),
|
||||
str(work / 'test.c'), '-o', str(work / 'test')], check=True)
|
||||
cases = {0: 'PASS source=nitz', 1: 'FAIL stage=sdk_api_unavailable',
|
||||
2: 'FAIL stage=no_fresh_nitz', 3: 'FAIL stage=no_fresh_nitz',
|
||||
4: 'FAIL stage=no_fresh_nitz', 5: 'FAIL stage=no_fresh_nitz',
|
||||
6: 'cached_or_inconsistent_nitz=1', 7: 'FAIL stage=local_calendar',
|
||||
2: 'FAIL stage=no_valid_nitz', 3: 'FAIL stage=no_valid_nitz',
|
||||
4: 'FAIL stage=no_valid_nitz', 5: 'FAIL stage=no_valid_nitz',
|
||||
6: 'PASS source=nitz', 7: 'FAIL stage=local_calendar',
|
||||
8: 'FAIL stage=app_clock_verification', 9: 'FAIL stage=app_clock_verification',
|
||||
10: 'FAIL stage=app_clock_verification', 11: 'FAIL stage=app_clock_verification',
|
||||
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', 21: 'PASS source=nitz', 22: 'FAIL stage=no_fresh_nitz'}
|
||||
15: 'FAIL stage=local_calendar', 16: 'PASS source=nitz',
|
||||
17: 'FAIL stage=no_valid_nitz', 18: 'PASS source=nitz',
|
||||
19: 'FAIL stage=create_task', 20: 'FAIL stage=app_clock_verification',
|
||||
21: 'PASS source=nitz', 22: 'PASS source=nitz', 23: 'PASS source=nitz',
|
||||
24: 'PASS source=nitz', 25: 'FAIL stage=local_calendar'}
|
||||
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, 21):
|
||||
assert '[APP-SYS-PROBE]' not in log, log
|
||||
assert 'stage=freshness' not in log, log
|
||||
if expected.startswith('PASS'):
|
||||
assert 'age=unknown first_valid_after_pdp=1 max_rtc_writes=1' in log, log
|
||||
else:
|
||||
assert '[APP-NITZ-TEST] PASS ' not in log, log
|
||||
print('PASS: Gregorian/parser validation and 23 API/freshness/RTC scenarios (ASan/UBSan)')
|
||||
print('PASS: parser validation and 26 first-snapshot/retry/timezone/RTC scenarios (ASan/UBSan)')
|
||||
|
||||
Reference in New Issue
Block a user