From fad4ee8e3884b0c188872aefe4609aa7aa8f8760 Mon Sep 17 00:00:00 2001 From: Huamonarch Date: Fri, 15 May 2026 13:27:27 +0800 Subject: [PATCH] =?UTF-8?q?test:=20FbStateMachine=20=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eqpalg/CMakeLists.txt | 1 + eqpalg/test/test_fb_state_machine.cc | 197 +++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 eqpalg/test/test_fb_state_machine.cc diff --git a/eqpalg/CMakeLists.txt b/eqpalg/CMakeLists.txt index c94c913..9c84539 100644 --- a/eqpalg/CMakeLists.txt +++ b/eqpalg/CMakeLists.txt @@ -101,6 +101,7 @@ aux_source_directory(./test TEST_SOURCES) add_executable(eqpalg_test ${TEST_SOURCES} ./utility/expression_engine.cpp + ./utility/fb_state_machine.cpp ) target_include_directories(eqpalg_test PUBLIC diff --git a/eqpalg/test/test_fb_state_machine.cc b/eqpalg/test/test_fb_state_machine.cc new file mode 100644 index 0000000..a3a47c9 --- /dev/null +++ b/eqpalg/test/test_fb_state_machine.cc @@ -0,0 +1,197 @@ +// eqpalg/test/test_fb_state_machine.cc +#include "test_harness.h" +#include +#include +#include +#include + +using namespace std::chrono; + +// 测试辅助:构建最小环境 +struct FbTestEnv { + std::map vars; + FbStateMachine fsm; + TimePoint now; + + FbTestEnv(size_t tag_count = 2) { + // 模拟已初始化的变量状态 + for (size_t i = 0; i < tag_count; i++) { + std::string idx = std::to_string(i + 1); + vars["tag" + idx] = 0.0; + vars["p" + idx] = 0.0; + vars["now"] = 0; + } + now = std::chrono::system_clock::now(); + vars["now"] = duration_cast(now.time_since_epoch()).count(); + } + + void setTag(int n, double val) { + std::string idx = std::to_string(n); + vars["p" + idx] = vars["tag" + idx]; // p = old tag + vars["tag" + idx] = val; + } + + void advanceTime(int ms) { + now += milliseconds(ms); + vars["now"] = duration_cast(now.time_since_epoch()).count(); + } + + FbUpdateResult step(bool trigger) { + return fsm.update(trigger, now, vars, 2); + } +}; + +// ==================== 初始状态 ==================== + +TEST(initial_state_is_idle) { + FbTestEnv env; + CHECK(env.fsm.currentState() == FbState::Idle); + CHECK_EQ(env.fsm.isActive(), false); +} + +// ==================== Idle → Started ==================== + +TEST(idle_to_started_when_triggered) { + FbTestEnv env; + auto result = env.step(true); + CHECK(result.state == FbState::Started); + CHECK_EQ(env.fsm.isActive(), true); + CHECK_EQ(result.funVarsNeedReset, false); +} + +TEST(idle_stays_idle_when_not_triggered) { + FbTestEnv env; + auto result = env.step(false); + CHECK(result.state == FbState::Idle); + CHECK_EQ(result.funVarsNeedReset, true); // 空闲时重置 fun_vars +} + +// ==================== Started → InProgress → Done ==================== + +TEST(started_proceeds_to_done_via_feedback) { + FbTestEnv env; + + // 首次触发 → Started + auto r1 = env.step(true); + CHECK(r1.state == FbState::Started); + + // Started 自动推进到 InProgress(第二次 update) + auto r2 = env.step(true); + CHECK(r2.state == FbState::InProgress || r2.state == FbState::Started); + // 如果第二次还在 Started,第三次一定到 InProgress + if (r2.state == FbState::Started) { + r2 = env.step(true); + } + CHECK(r2.state == FbState::InProgress); + + // 反馈条件满足 → Done + bool done = env.fsm.checkFeedback(true, env.now, env.vars); + CHECK_EQ(done, true); + CHECK(env.fsm.currentState() == FbState::Idle); // 自动回到 Idle +} + +// ==================== NotHold ==================== + +TEST(keep_mode_not_hold_when_trigger_lost) { + FbTestEnv env; + env.fsm.configure(true, milliseconds(600000)); // keep_mode=true + + // 触发 → Started + env.step(true); + // → InProgress + env.step(true); + // 触发丢失 → NotHold + auto r = env.step(false); + CHECK(r.state == FbState::NotHold); + CHECK_EQ(r.funVarsNeedReset, true); +} + +// ==================== Timeout ==================== + +TEST(timeout_when_exceeds_limit) { + FbTestEnv env; + env.fsm.configure(false, milliseconds(100)); // 100ms timeout + + // 触发 → Started → InProgress + env.step(true); + env.step(true); + + // 超时 + env.advanceTime(200); + auto r = env.step(true); + CHECK(r.state == FbState::Timeout); + CHECK_EQ(r.funVarsNeedReset, true); +} + +// ==================== 变量快照 ==================== + +TEST(snapshot_on_action_start) { + FbTestEnv env; + env.setTag(1, 3.0); + env.setTag(2, 7.0); + + env.step(true); // → Started(快照发生) + + CHECK_FLOAT_EQ(env.vars["s1"], 3.0, 0.001); + CHECK_FLOAT_EQ(env.vars["s2"], 7.0, 0.001); + CHECK_FLOAT_EQ(env.vars["time"], 0.0, 0.001); + CHECK_FLOAT_EQ(env.vars["mv2_tag1"], 0.0, 0.001); + // stime 应该被设置 + CHECK(env.vars["stime"] > 0); +} + +// ==================== 变量累积 ==================== + +TEST(action_vars_accumulate_in_progress) { + FbTestEnv env; + env.setTag(1, 1.0); // tag1=1 + env.setTag(2, 0.0); + + env.step(true); // Started + env.step(true); // InProgress (累积发生) + + CHECK_EQ(env.vars["mv2_tag1"] >= 1.0, true); // tag1==1 被计数 + CHECK_FLOAT_EQ(env.vars["mx_tag1"], 1.0, 0.001); + CHECK_FLOAT_EQ(env.vars["mi_tag2"], 0.0, 0.001); +} + +// ==================== forceReset ==================== + +TEST(force_reset_returns_to_idle) { + FbTestEnv env; + env.step(true); // Started + CHECK_EQ(env.fsm.isActive(), true); + + env.fsm.forceReset(); + CHECK(env.fsm.currentState() == FbState::Idle); + CHECK_EQ(env.fsm.isActive(), false); +} + +// ==================== 非 keep 模式下触发丢失 ==================== + +TEST(no_keep_mode_trigger_loss_stays_in_progress) { + FbTestEnv env; + env.fsm.configure(false, milliseconds(600000)); // keep_mode=false + + env.step(true); // Started + env.step(true); // InProgress + auto r = env.step(false); // 触发丢失但 keep_mode=false + CHECK(r.state == FbState::InProgress); // 不退出 +} + +// ==================== 终端状态自动回归 Idle ==================== + +TEST(terminal_state_returns_to_idle_next_cycle) { + FbTestEnv env; + env.fsm.configure(false, milliseconds(10)); + + env.step(true); + env.step(true); + env.advanceTime(20); + auto r = env.step(true); + CHECK(r.state == FbState::Timeout); + + // 下一周期自动回到 Idle + auto r2 = env.step(false); + CHECK(r2.state == FbState::Idle); +}