文件
phonerail/firmware/test/test_hubctl.c
T

238 行
8.7 KiB
C
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
/* 0.1.14:Hub 按需启停(hubctl.h)。
口径同其它测试:判据用 `hub_decide()` 直接喂输入;引脚与事件用 `hubctl_tick()`
打在假 GPIO / 事件缓冲上。R716 的上拉用 `sim_gpio_drive(HUB_RST#, 1)` 模拟 ——
放成输入时读回 1 = Hub 在工作,配成输出拉低时读回 0 = 按住复位。 */
#include <string.h>
#include "board_pins.h"
#include "evlog.h"
#include "hal_time.h"
#include "hubctl.h"
#include "netcfg.h"
#include "sim_hal.h"
#include "unity.h"
#define PIN BOARD_PIN_HUB_RST_N
static hub_mode_t s_saved;
static int s_saves;
static void save(void *ctx, hub_mode_t m)
{
(void)ctx;
s_saved = m;
s_saves++;
}
static void link(bool up)
{
net_status_t st;
memset(&st, 0, sizeof(st));
st.mqtt = up ? NET_UP : NET_CONNECTING;
net_status_set(&st);
}
static uint32_t now(void) { return hal_time_now_ms(); }
/* 每 100 ms 一拍(执行器空闲时的节奏),推 ms 毫秒。 */
static void run_ms(uint32_t ms)
{
for (uint32_t t = 0; t < ms; t += 100) {
sim_time_advance_ms(100);
hubctl_tick(now());
}
}
static int hub_events(uint8_t arg)
{
(void)evlog_flush(0);
evlog_rec_t r[16];
const uint32_t n = evlog_read(0, r, 16);
int k = 0;
for (uint32_t i = 0; i < n; i++) {
k += (r[i].type == LOG_EV_HUB && r[i].arg == arg) ? 1 : 0;
}
return k;
}
void setUp(void)
{
sim_hal_reset();
TEST_ASSERT_EQUAL_INT(HAL_OK, evlog_init());
sim_gpio_drive(PIN, 1); /* R716 */
s_saves = 0;
link(true);
hubctl_init(HUB_MODE_AUTO, save, NULL, now());
}
void tearDown(void) {}
static const hub_in_t k_idle = { .link_up = true };
/* ---- 判据 ---------------------------------------------------------------- */
/* 启动算一次需要:HUB_IDLE_MS 之内开着(idle),到点就关。 */
static void test_boot_window_then_off(void)
{
TEST_ASSERT_TRUE(hubctl_is_on());
TEST_ASSERT_EQUAL_INT(1, sim_gpio_level(PIN));
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&k_idle, now() + HUB_IDLE_MS - 1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, now() + HUB_IDLE_MS));
}
/* 优先级:mode > vbus > flash > act > hold > link。任一条都刷新「最后一次需要」。 */
static void test_reasons_and_their_order(void)
{
hub_in_t in = k_idle;
in.vbus_any = in.flash_any = in.act_any = true;
TEST_ASSERT_EQUAL_INT(HUB_WHY_VBUS, hub_decide(&in, now()));
in.vbus_any = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_FLASH, hub_decide(&in, now()));
in.flash_any = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_ACT, hub_decide(&in, now()));
in.act_any = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, now()));
/* VBUS 一直开着 30 分钟,关掉之后还要再开 HUB_IDLE_MS。 */
const uint32_t t0 = now() + 30u * 60000u;
in.vbus_any = true;
TEST_ASSERT_EQUAL_INT(HUB_WHY_VBUS, hub_decide(&in, t0));
in.vbus_any = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0 + HUB_IDLE_MS - 1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_IDLE_MS));
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
TEST_ASSERT_EQUAL_INT(HUB_WHY_MODE, hub_decide(&in, t0 + 10u * HUB_IDLE_MS));
}
/* MQTT 断满 HUB_LINK_LOST_MS 才算(WiFi 抖一下不开);恢复后再给一个 idle 窗口。 */
static void test_link_lost_forces_on(void)
{
const uint32_t t0 = now() + HUB_IDLE_MS; /* 启动窗口已过 */
hub_in_t in = k_idle;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0));
in.link_up = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_LINK_LOST_MS - 1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hub_decide(&in, t0 + HUB_LINK_LOST_MS));
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hub_decide(&in, t0 + 5u * HUB_IDLE_MS));
in.link_up = true;
const uint32_t t1 = t0 + 5u * HUB_IDLE_MS + 1;
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS));
/* 断了不到一分钟又连上:计时重来,不累计。 */
in.link_up = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 1));
in.link_up = true;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 30000u));
in.link_up = false;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t1 + HUB_IDLE_MS + 30001u));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF,
hub_decide(&in, t1 + HUB_IDLE_MS + 30001u + HUB_LINK_LOST_MS - 1));
}
/* 电脑插上(UP_VBUS_DET 上升沿)与 USB 命令都算一次需要;电平一直高不算。 */
static void test_host_edge_and_usb_touch(void)
{
const uint32_t t0 = now() + HUB_IDLE_MS;
hub_in_t in = k_idle;
in.host = true;
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0)); /* 上升沿 */
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + HUB_IDLE_MS)); /* 电平不算 */
hubctl_touch(t0 + 2u * HUB_IDLE_MS);
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&in, t0 + 3u * HUB_IDLE_MS - 1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&in, t0 + 3u * HUB_IDLE_MS));
}
/* 保持窗口;min=0 取消保持并结束 idle 窗口(下一拍就关)。 */
static void test_hold(void)
{
const uint32_t t0 = now();
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, hubctl_hold(HUB_HOLD_MAX_MIN + 1, t0));
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_hold(30, t0));
TEST_ASSERT_EQUAL_UINT32(1800, hubctl_hold_left_s(t0));
TEST_ASSERT_EQUAL_INT(HUB_WHY_HOLD, hub_decide(&k_idle, t0 + 30u * 60000u - 1));
TEST_ASSERT_EQUAL_INT(HUB_WHY_IDLE, hub_decide(&k_idle, t0 + 30u * 60000u));
TEST_ASSERT_EQUAL_UINT32(0, hubctl_hold_left_s(t0 + 30u * 60000u));
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_hold(0, t0 + 30u * 60000u));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 30u * 60000u + 1));
}
/* 关着 49.7 天,32 位时钟回绕之后 idle 窗口不许「复活」。 */
static void test_idle_window_does_not_revive_after_wrap(void)
{
const uint32_t t0 = now() + HUB_IDLE_MS;
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 0x80000000u));
TEST_ASSERT_EQUAL_INT(HUB_WHY_OFF, hub_decide(&k_idle, t0 + 0xFFFFFFF0u));
}
/* ---- 引脚与事件 ---------------------------------------------------------- */
/* 关 = 配成输出拉低;开 = 放成输入(R716 拉高),**从不主动写 1**。每次翻转一条事件。 */
static void test_tick_drives_the_pin_and_logs(void)
{
run_ms(HUB_IDLE_MS + 200);
TEST_ASSERT_FALSE(hubctl_is_on());
TEST_ASSERT_EQUAL_INT(0, sim_gpio_level(PIN));
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_OFF));
TEST_ASSERT_EQUAL_UINT64(0, sim_gpio_rise_us(PIN)); /* 固件没写过 1 */
/* 断网一分钟 ⇒ 自己回来。 */
link(false);
run_ms(HUB_LINK_LOST_MS + 200);
TEST_ASSERT_TRUE(hubctl_is_on());
TEST_ASSERT_EQUAL_INT(HUB_WHY_LINK, hubctl_why());
TEST_ASSERT_EQUAL_INT(1, sim_gpio_level(PIN));
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_ON));
TEST_ASSERT_EQUAL_UINT64(0, sim_gpio_rise_us(PIN));
/* 一直开着不重复记。 */
run_ms(5000);
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_ON));
}
static void test_mode_is_saved_and_logged(void)
{
TEST_ASSERT_EQUAL_INT(HAL_ERR_ARG, hubctl_set_mode(HUB_MODE_COUNT));
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
TEST_ASSERT_EQUAL_INT(1, s_saves);
TEST_ASSERT_EQUAL_INT(HUB_MODE_ON, s_saved);
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_MODE | HUB_MODE_ON));
run_ms(HUB_IDLE_MS + 200);
TEST_ASSERT_TRUE(hubctl_is_on()); /* 常开 */
TEST_ASSERT_EQUAL_INT(HAL_OK, hubctl_set_mode(HUB_MODE_ON));
TEST_ASSERT_EQUAL_INT(1, hub_events(HUB_EV_MODE | HUB_MODE_ON)); /* 没变不记 */
hub_mode_t m = HUB_MODE_ON;
TEST_ASSERT_TRUE(hub_mode_parse("auto", &m));
TEST_ASSERT_EQUAL_INT(HUB_MODE_AUTO, m);
TEST_ASSERT_FALSE(hub_mode_parse("off", &m));
}
/* 从 NVS 读到的是垃圾 ⇒ 按 AUTO。 */
static void test_bad_mode_falls_back_to_auto(void)
{
hubctl_init((hub_mode_t)7, NULL, NULL, now());
TEST_ASSERT_EQUAL_INT(HUB_MODE_AUTO, hubctl_mode());
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_boot_window_then_off);
RUN_TEST(test_reasons_and_their_order);
RUN_TEST(test_link_lost_forces_on);
RUN_TEST(test_host_edge_and_usb_touch);
RUN_TEST(test_hold);
RUN_TEST(test_idle_window_does_not_revive_after_wrap);
RUN_TEST(test_tick_drives_the_pin_and_logs);
RUN_TEST(test_mode_is_saved_and_logged);
RUN_TEST(test_bad_mode_falls_back_to_auto);
return UNITY_END();
}