test: calibrate APP RTC from first valid NITZ snapshot after PDP
This commit is contained in:
+36
-52
@@ -1,72 +1,56 @@
|
||||
# 纯 APP NITZ 校时实机测试
|
||||
# 纯 APP:开机首次有效 NITZ 校时测试
|
||||
|
||||
当前 `test/time-fix` 在 PDP 首次激活后自动启动 NITZ 测试,替换上一版自动 NTP 测试。只改 APP;本次没有修改、编译或烧录内核,不依赖之前添加的内核诊断接口。NTP 测试源文件保留但不参与当前 APP 构建。此模块不访问时间服务器域名或 UDP 123,使用设备已有的运营商 NITZ 查询接口。
|
||||
当前 `test/time-fix` 在 PDP 首次激活后启动一次独立测试任务,尽早读取有效的运营商 NITZ 快照,写入 APP RTC 后读回验证。不再要求两次查询值每隔 3 秒推进;这符合实机已观察到的“最近一次授时快照”行为。
|
||||
|
||||
## 新增 SYS 读取探测
|
||||
只改 APP,没有修改内核,也不依赖新增的 `talkingq_time_diag_snapshot_v1`。前置 SYS/AT 探测及 APP NTP 测试均已关闭,相关源文件保留但不参与当前 APP 构建。原有内核功能不因本 APP 测试而关闭。
|
||||
|
||||
当前版本在 NITZ 测试之前运行 `[APP-SYS-PROBE]`,只使用当前源码声明过的函数签名和 APP 已有 AT 接口,不读取旧 AXF,不修改内核,不调用 `talkingq_time_diag_snapshot_v1`。原有 TIME-DIAG 日志是否可用,不影响本探测。
|
||||
## 编译与实测
|
||||
|
||||
- 动态查找 `PMIC_RTC_GetTime_Count` 和 `GetTimeZone`。如果原内核本来就导出这两个名字,调用 `SYS_OFFSET=0`,减去网络时区分钟数,间隔 3 秒读取两次,检查合理范围和推进量。找不到就输出 `direct_sys=unavailable`,不添加内核导出、不尝试固定地址。
|
||||
- 输出 `time(NULL)`、`ql_rtc_get_time_ex()` 作为对照,不把 `get_time_ex` 的名称当成读 SYS 的证据。
|
||||
- 通过 `ql_atcmd_send_sync` 连续两轮读取 `AT+QLTS=0`、`AT+QLTS=1`、`AT+QLTS=2`、`AT+CCLK?`。两轮间隔 3 秒,每个 AT 请求超时参数为 2 秒。记录返回码、运行计数和原始响应;本型号不支持时允许返回 ERROR。两个 QLTS 候选模式是否提供有效当前时间,需根据原始日志对照确认。
|
||||
- 本探测没有 RTC 设置调用。`direct_sys=candidate` 仅表示现有 SYS 函数可读、时间范围和推进正常,仍需与实际 UTC 核对;AT 返回成功也不等于已确认来源。
|
||||
在现有 Windows 环境执行 `build.bat app`,构建成功并核对 APP 镜像容量后,只更新 APP。保持原内核和故障现场 RTC/NVM 状态。
|
||||
|
||||
刷入后提供从 `[APP-SYS-PROBE] begin` 到 `finished` 的完整输出。若 QLTS 返回正确且推进的当前时间,可进一步实现 APP 校时;若所有接口缺失/返回错误或只返回 APP 旧时间,该路线仍未得到可用依据。后续 NITZ 测试保持原有写入条件,快照冻结时不写 RTC。
|
||||
|
||||
## 编译和测试
|
||||
|
||||
在现有 Windows 环境执行 `build.bat app`,按现有流程更新 APP 镜像。保持原内核、原 RTC/NVM 状态。核对 APP 构建成功及镜像容量后启动设备,保留从开机到测试结束、再到一次实际 WSS 连接尝试的完整串口日志。
|
||||
|
||||
应看到:
|
||||
启动时应出现:
|
||||
|
||||
```text
|
||||
[TIME-DIAG] nitz_enabled=1 app_nitz_test=once_after_pdp ntp_test=off
|
||||
[APP-SYS-PROBE] begin version=1 read_only=1 kernel_diag_used=0
|
||||
[APP-SYS-PROBE] exports sys_count=... network_zone=...
|
||||
...
|
||||
[APP-SYS-PROBE] finished rtc_not_written=1
|
||||
[APP-NITZ-TEST] begin version=2 source=operator_nitz ntp=off
|
||||
[TIME-DIAG] nitz_enabled=1 app_nitz_test=first_valid_after_pdp ntp_test=off sys_probe=off
|
||||
[APP-NITZ-TEST] begin version=3 source=operator_nitz ntp=off policy=first_valid_after_pdp sys_probe=off
|
||||
```
|
||||
|
||||
本机只进行了主机模拟测试和 ARM 目标语法检查,尚未完成 Windows 完整固件构建或实机测试。
|
||||
|
||||
## 实际流程
|
||||
|
||||
1. 检查原内核是否导出公开 SDK 接口 `ql_nw_get_nitz_time_info`、`ql_localtime_r`、`ql_rtc_set_time`、`ql_rtc_get_time`。
|
||||
2. 查询运营商 NITZ,输出完整原始字符串、返回值、`abs_time`、`leap_sec` 和查询耗时。支持 SDK 文档格式 `YY/MM/DD HH:MM:SS +32 00` 、紧凑格式 `YY/MM/DD,HH:MM:SS+32`,以及实机已返回的无正号、一位夏令时格式 `YY/MM/DD HH:MM:SS 32 0`。严格检查日期、闰年、时区及 UTC 范围(2024 年起,至 signed 32 位秒数上限)。不把语义未明确的 `abs_time` 当 Unix 时间戳使用。
|
||||
3. 间隔 3 秒再次查询。两次 UTC 必须向前推进,并与系统运行计数的增量相差不超过 1 秒,时区相同;单次查询超过 2 秒不采用。冻结的 NITZ 快照不写入 APP。最多尝试 3 组,组间等待 5 秒。
|
||||
4. 使用 NITZ 的 UT 日期字段生成 UTC 秒数。时区字段以 15 分钟为单位,只记录,不再次从 UT 扣除。这个解释依据仓库旧 SDK 镜像的 NITZ 转换逻辑:UT 日期转换后加时区生成 SYS 本地日历。原始值和解释会保留在日志中,供实机核对。
|
||||
5. 通过 SDK `ql_localtime_r` 按现有 PMIC 时区把 UTC 转成 APP RTC 日历,再调用 `ql_rtc_set_time`。不改时区、不操作 `app_ever` 或内部跟随标记、不直接写 NVM 文件。
|
||||
6. 读回 `ql_rtc_get_time` 日历和 `time(NULL)` 的 UTC 返回值,两者都必须与目标相差不超过 3 秒,并且 SDK 返回成功,才输出 `PASS`。设置后无论成功失败,都结束本次测试,避免反复写 RTC。
|
||||
|
||||
测试任务独立运行,不等待它就可以继续 MQTT 流程。每次启动完成一次测试;仅任务创建失败时允许下一次 PDP 激活重试。此版本没有周期重校、WSS 启动等待或主动重连逻辑,不是完整发布方案。
|
||||
|
||||
## 日志判断
|
||||
|
||||
以下是格式示例,不是设备实测结果:
|
||||
保留从启动到 `[APP-NITZ-TEST] finished` 的完整日志,随后按语音键触发一次实际 WSS 连接,并保留连接日志。以下是预期格式示例,不是本次实机结果:
|
||||
|
||||
```text
|
||||
[APP-NITZ-TEST] sample attempt=1 index=1 rc=0 raw="26/10/26 06:00:00 +32 00" abs_time=0 leap_sec=0 query_ms=...
|
||||
[APP-NITZ-TEST] parsed utc=... zone_quarters=32 zone_minutes=480
|
||||
[APP-NITZ-TEST] freshness advance_s=3 elapsed_s=3 drift_s=0
|
||||
[APP-NITZ-TEST] set_begin app_before=... expected_utc=... pmic_zone_minutes=480 app_calendar=2026-10-26T14:00:03
|
||||
[APP-NITZ-TEST] readback set_rc=0 rtc_rc=0 app_after=... expected_utc=... delta_s=0 calendar=...
|
||||
[APP-NITZ-TEST] sample attempt=1 index=1 rc=0 raw="26/10/26 08:00:10 32 0" abs_time=... leap_sec=0 query_ms=5
|
||||
[APP-NITZ-TEST] parsed utc=1793001610 zone_quarters=32 zone_minutes=480
|
||||
[APP-NITZ-TEST] snapshot_selected attempt=1 utc=1793001610 age=unknown first_valid_after_pdp=1 max_rtc_writes=1
|
||||
[APP-NITZ-TEST] set_begin app_before=... expected_utc=1793001610 pmic_zone_minutes=480 app_calendar=2026-10-26T16:00:10
|
||||
[APP-NITZ-TEST] readback set_rc=0 rtc_rc=0 app_after=1793001610 expected_utc=1793001610 delta_s=0 calendar=2026-10-26T16:00:10
|
||||
[APP-NITZ-TEST] PASS source=nitz app_utc=verified wss_requires_separate_handshake_check=1
|
||||
[APP-NITZ-TEST] finished one_test_per_boot=1
|
||||
```
|
||||
|
||||
- `sdk_api_unavailable`:原内核缺少所需公开接口,本次不写 RTC。
|
||||
- `stage=parse`:时间为空、格式不支持或日期/范围无效。保留 `sample` 原文以便分析。
|
||||
- `stage=freshness`:两次时间冻结、跳变或与运行计时不符,不采用。若三组都失败,会输出 `no_fresh_nitz rtc_not_written=1`。
|
||||
- `stage=local_calendar`:SDK 时区转换未产生可用日历,不写 RTC。
|
||||
- `stage=app_clock_verification`:设置返回失败,或者读回 UTC/日历不符。需要分析是否有时区或 APP RTC 写入问题。
|
||||
## 校时逻辑
|
||||
|
||||
`PASS` 只证明 NITZ 候选值推进正常、APP 写入和读回相符,无法单独证明运营商绝对时间准确或当前固件的 NITZ UT 语义与旧 SDK 完全一致。实机仍需把原始 NITZ、APP UTC 和实际北京时间对照,尤其检查整 8 小时偏差。它也不等于 WSS 握手或 OTA 下载成功;这两项继续看后续实际连接日志,TLS 校验保持开启。
|
||||
1. 检查原内核是否提供既有公开 SDK 接口 `ql_nw_get_nitz_time_info`、`ql_localtime_r`、`ql_rtc_set_time`、`ql_rtc_get_time`,缺失则退出。
|
||||
2. 立即读取 NITZ。支持实机格式 `YY/MM/DD HH:MM:SS 32 0`、文档中的带符号格式和紧凑格式。严格检查终止符、日期、闰年、时区和 UTC 范围:2024 年起至 signed 32 位秒数上限。
|
||||
3. 查询失败、空值、无效格式或查询耗时超过 2 秒,不写入,间隔 5 秒重试,最多 3 次。首次有效后立即采用,不等待第二个快照,也不运行前置 AT/SYS 探测。
|
||||
4. NITZ UT 日期字段按 UTC 解析;`32` 表示 32 个 15 分钟,即 UTC+8。UTC 时间戳本身不加 8 小时。使用 SDK `ql_localtime_r` 按现有 PMIC 时区转换成 RTC 本地日历,再调用 `ql_rtc_set_time`。不直接改时区、跟随标记或 NVM 文件。
|
||||
5. 读回 RTC 日历和 `time(NULL)` 返回的 UTC 秒数,要求 SDK 返回成功、日期有效,且 UTC 和本地日历均与写入目标相差不超过 3 秒,才输出 `PASS`。
|
||||
6. 设置尝试后不再重试写入,即使读回失败;任务结束后本次启动不再校时。仅任务创建失败允许下一次 PDP 激活重试创建。
|
||||
|
||||
SDK 可能只提供 NITZ 最近一次通知的快照;如出现冻结拒绝,不应去掉新鲜度检查直接写入,应据日志评估带接收时刻的 NITZ 事件或已有业务服务器授时路径。本测试不注册新网络回调,不抢占现有网络事件处理。
|
||||
## 结果与边界
|
||||
|
||||
## 主机回归
|
||||
- `sdk_api_unavailable`:缺少所需公开 SDK 接口。
|
||||
- `stage=parse` / `stage=slow_query`:本次查询未采用。
|
||||
- `no_valid_nitz rtc_not_written=1`:三次均未得到可采用值。
|
||||
- `snapshot_selected`:已选择本次启动首次读到的有效快照。
|
||||
- `stage=local_calendar`:SDK 时区转换未产生可用 RTC 日历,不写入。
|
||||
- `app_clock_verification`:设置或读回验证失败。
|
||||
|
||||
`python3 tools/test_app_nitz_test.py` 编译实际 C 模块,模拟 API 缺失、查询失败、空串、无终止符、无效日期、冻结时间、UTC 跨天、时区转换、运行计数回绕、signed 32 位边界、首次查询失败恢复、写入失败、读回偏差及任务创建失败。启用 ASan/UBSan;不发送设备或公网数据。
|
||||
本版本按用户要求验证“开机联网后尽早采用首次有效快照”。首次读取不等于已证明基站刚刚下发;接口没有提供本测试可确认的快照接收时刻,所以记录 `age=unknown`。只补偿本次查询返回后至写入的运行时间,不把设备开机时间或 PDP 激活时间冒充快照接收时间。
|
||||
|
||||
`python3 tools/test_app_sys_time_probe.py` 对实际 SYS/AT 探测模块验证 10 种场景:接口缺失、动态读取、无效/冻结时间、AT 错误、无终止符响应、tick 回绕等。测试不链接 RTC setter 或新增诊断接口;主机测试不证明本型号支持 QLTS。
|
||||
`PASS` 证明 APP 设置、时区换算和读回与所选快照一致,不独立证明快照绝对新鲜。需把原始 NITZ、APP 日历和实际北京时间对照,并继续看 WSS 实际握手。TLS 校验保持开启。本版没有增加 NITZ 更新事件处理、周期重校、WSS 启动等待或主动重连;先验证一次 APP 校时能否恢复当前故障设备。
|
||||
|
||||
## 本机验证
|
||||
|
||||
`python3 tools/test_app_nitz_test.py` 使用实际 C 源码验证解析、首次有效快照(含实机冻结快照)、失败后重试、最多一次写入、日期跨天、PMIC 时区转换、UTC 不重复偏移、tick 回绕、signed 32 位边界、设置失败及读回不符。启用 ASan/UBSan,不发送设备或公网数据。
|
||||
|
||||
同时对实际 SDK 头文件执行 ARM 目标语法检查。当前 macOS 环境不能运行仓库的 Windows 完整构建工具链,完整 APP 构建和实机验证待用户执行。SYS/AT 探测代码仍可通过 `tools/test_app_sys_time_probe.py` 单独回归,但不在本次开机流程中运行。
|
||||
|
||||
@@ -84,7 +84,6 @@ SRC_FILES+= \
|
||||
mqtt_connect_diag.c \
|
||||
time_diag.c \
|
||||
app_nitz_test.c \
|
||||
app_sys_time_probe.c \
|
||||
vad_det.c \
|
||||
sleep_wake.c \
|
||||
battery.c \
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
#include "ql_nw.h"
|
||||
#include "ql_rtc.h"
|
||||
#include "app_nitz_test.h"
|
||||
#include "app_sys_time_probe.h"
|
||||
|
||||
/* SDK wrapper uses the same PMIC timezone as time(); no libc TZ assumptions. */
|
||||
extern struct tm *ql_localtime_r(const time_t *, struct tm *);
|
||||
#define MIN_EPOCH 1704067200LL
|
||||
#define MAX_EPOCH 2147483647LL
|
||||
#define ATTEMPTS 3
|
||||
#define SAMPLE_MS 3000U
|
||||
|
||||
static ql_task_t test_task;
|
||||
static int test_started;
|
||||
@@ -110,8 +108,11 @@ static int get_sample(int attempt, int index, nitz_sample *sample)
|
||||
printf("[APP-NITZ-TEST] rejected stage=parse reason=%d\r\n", parsed);
|
||||
return -1;
|
||||
}
|
||||
/* A long blocking query makes the timestamp's age ambiguous. */
|
||||
if (sample->ticks - began > 400U) return -1;
|
||||
/* Bound query delay; this does not establish the age of a cached NITZ. */
|
||||
if (sample->ticks - began > 400U) {
|
||||
printf("[APP-NITZ-TEST] rejected stage=slow_query rtc_not_written=1\r\n");
|
||||
return -1;
|
||||
}
|
||||
printf("[APP-NITZ-TEST] parsed utc=%ld zone_quarters=%d zone_minutes=%d\r\n",
|
||||
(long)sample->utc, sample->zone_quarters, sample->zone_quarters * 15);
|
||||
return 0;
|
||||
@@ -181,42 +182,31 @@ static int write_and_verify(const nitz_sample *reference)
|
||||
|
||||
static void nitz_test_worker(void *arg)
|
||||
{
|
||||
nitz_sample first, second;
|
||||
nitz_sample sample;
|
||||
int attempt;
|
||||
long long advance, elapsed, drift;
|
||||
unsigned int span;
|
||||
(void)arg;
|
||||
app_sys_time_probe_run();
|
||||
printf("[APP-NITZ-TEST] begin version=2 source=operator_nitz ntp=off\r\n");
|
||||
printf("[APP-NITZ-TEST] begin version=3 source=operator_nitz ntp=off "
|
||||
"policy=first_valid_after_pdp sys_probe=off\r\n");
|
||||
if (!m_get_api_ptr || !m_get_api_ptr("ql_nw_get_nitz_time_info") ||
|
||||
!m_get_api_ptr("ql_localtime_r") || !m_get_api_ptr("ql_rtc_set_time") ||
|
||||
!m_get_api_ptr("ql_rtc_get_time")) {
|
||||
printf("[APP-NITZ-TEST] FAIL stage=sdk_api_unavailable\r\n");
|
||||
goto done;
|
||||
}
|
||||
/* Device experiment: use the first valid operator snapshot promptly after
|
||||
* PDP activation. A snapshot need not advance between API reads. Its actual
|
||||
* network reception time is unknown; readback alone cannot establish age. */
|
||||
for (attempt = 1; attempt <= ATTEMPTS; ++attempt) {
|
||||
if (get_sample(attempt, 1, &first) == 0) {
|
||||
ql_rtos_task_sleep_ms(SAMPLE_MS);
|
||||
if (get_sample(attempt, 2, &second) == 0) {
|
||||
span = second.ticks - first.ticks; /* unsigned handles tick wrap */
|
||||
elapsed = span / 200U;
|
||||
advance = (long long)second.utc - first.utc;
|
||||
drift = advance - elapsed;
|
||||
printf("[APP-NITZ-TEST] freshness advance_s=%lld elapsed_s=%lld "
|
||||
"drift_s=%lld\r\n", advance, elapsed, drift);
|
||||
if (span >= 400U && span <= 2000U && advance > 0 &&
|
||||
drift >= -1 && drift <= 1 &&
|
||||
first.zone_quarters == second.zone_quarters) {
|
||||
write_and_verify(&second);
|
||||
goto done; /* Never repeat an uncertain RTC write. */
|
||||
}
|
||||
printf("[APP-NITZ-TEST] rejected stage=freshness "
|
||||
"cached_or_inconsistent_nitz=1 rtc_not_written=1\r\n");
|
||||
}
|
||||
if (get_sample(attempt, 1, &sample) == 0) {
|
||||
printf("[APP-NITZ-TEST] snapshot_selected attempt=%d utc=%ld "
|
||||
"age=unknown first_valid_after_pdp=1 max_rtc_writes=1\r\n",
|
||||
attempt, (long)sample.utc);
|
||||
write_and_verify(&sample);
|
||||
goto done; /* No repeated write, even on setter/readback failure. */
|
||||
}
|
||||
if (attempt < ATTEMPTS) ql_rtos_task_sleep_ms(5000);
|
||||
}
|
||||
printf("[APP-NITZ-TEST] FAIL stage=no_fresh_nitz rtc_not_written=1\r\n");
|
||||
printf("[APP-NITZ-TEST] FAIL stage=no_valid_nitz rtc_not_written=1\r\n");
|
||||
done:
|
||||
printf("[APP-NITZ-TEST] finished one_test_per_boot=1\r\n");
|
||||
ql_rtos_task_delete(NULL);
|
||||
|
||||
@@ -201,7 +201,7 @@ int app_net_mgt_init()
|
||||
// ql_rtos_semaphore_create(&g_ql_ntp_sem,0);
|
||||
|
||||
ql_rtc_set_nitz_mode(1);
|
||||
printf("[TIME-DIAG] nitz_enabled=1 app_nitz_test=once_after_pdp ntp_test=off\r\n");
|
||||
printf("[TIME-DIAG] nitz_enabled=1 app_nitz_test=first_valid_after_pdp ntp_test=off sys_probe=off\r\n");
|
||||
talkingq_time_diag_log("nitz-enabled");
|
||||
|
||||
ql_task_t appTaskRef = NULL;
|
||||
|
||||
+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