#include #include #include "secsgem/gem/runtime.hpp" #include "secsgem/secs2/item.hpp" using namespace secsgem; namespace gem = secsgem::gem; namespace s2 = secsgem::secs2; #ifndef SECSGEM_DATA_DIR #error "SECSGEM_DATA_DIR not defined; see CMakeLists.txt" #endif // Port 0 binds an OS-chosen ephemeral port. These tests never call run()/ // run_async(), so the acceptor is opened but never armed — we exercise the // outbound API by posting and draining with poll(), no host involved. static gem::EquipmentRuntime::Config test_config() { gem::EquipmentRuntime::Config c; c.equipment_yaml = SECSGEM_DATA_DIR "/equipment.yaml"; c.control_state_yaml = SECSGEM_DATA_DIR "/control_state.yaml"; c.process_job_yaml = SECSGEM_DATA_DIR "/process_job_state.yaml"; c.control_job_yaml = SECSGEM_DATA_DIR "/control_job_state.yaml"; c.port = 0; return c; } TEST_CASE("EquipmentRuntime loads the data dictionary from config") { gem::EquipmentRuntime rt(test_config()); CHECK(rt.model().svids.all().size() == 3); CHECK(rt.model().alarms.all().size() == 2); CHECK(rt.control_state() == gem::ControlState::HostOffline); } TEST_CASE("EquipmentRuntime.set_variable posts onto the io thread and updates the model") { gem::EquipmentRuntime rt(test_config()); rt.set_variable(1, s2::Item::ascii("OnlineRemote")); CHECK(rt.model().svids.value(1) != s2::Item::ascii("OnlineRemote")); // not yet — posted rt.poll(); CHECK(rt.model().svids.value(1) == s2::Item::ascii("OnlineRemote")); } TEST_CASE("EquipmentRuntime.set_alarm / clear_alarm toggle the active flag") { gem::EquipmentRuntime rt(test_config()); rt.set_alarm(1); rt.poll(); CHECK(rt.model().alarms.active(1)); rt.clear_alarm(1); rt.poll(); CHECK_FALSE(rt.model().alarms.active(1)); } TEST_CASE("EquipmentRuntime.on_command registers the behaviour hook on the model") { gem::EquipmentRuntime rt(test_config()); bool ran = false; rt.on_command("START", [&](const std::string& rcmd, const std::vector&) { ran = (rcmd == "START"); return gem::HostCmdAck::Accept; }); CHECK(rt.model().commands.has_handler("START")); auto res = rt.model().commands.dispatch("START", {}); CHECK(ran); CHECK(res.ack == gem::HostCmdAck::Accept); }