// E84PortStore: per-port E84 FSMs. // // Each load port owns its own E84 handshake state. These tests // confirm the store keys correctly, that per-port FSMs are // independent, and that the change handler reports the right // port_id alongside the from/to state. #include #include #include #include "secsgem/gem/store/e84_ports.hpp" using namespace secsgem::gem; TEST_CASE("E84PortStore: create + get + has + size") { E84PortStore s; CHECK(s.size() == 0); REQUIRE(s.create(1)); REQUIRE(s.create(2)); CHECK(s.size() == 2); CHECK(s.has(1)); CHECK(s.has(2)); CHECK_FALSE(s.has(3)); CHECK(s.get(1) != nullptr); CHECK(s.get(99) == nullptr); CHECK_FALSE(s.create(1)); // duplicate } TEST_CASE("E84PortStore: implicit-create on first signal-change") { // Convenience pass-through: on_signal_change auto-creates the port // if it doesn't exist yet. E84PortStore s; CHECK(s.on_signal_change(7, E84Signal::CS_0, true)); CHECK(s.has(7)); CHECK(s.get(7)->signal(E84Signal::CS_0)); } TEST_CASE("E84PortStore: per-port FSMs run independently") { E84PortStore s; REQUIRE(s.create(1)); REQUIRE(s.create(2)); // LP1 walks all the way to Complete; LP2 stays idle. s.on_signal_change(1, E84Signal::CS_0, true); s.on_signal_change(1, E84Signal::VALID, true); s.on_signal_change(1, E84Signal::L_REQ, true); s.on_signal_change(1, E84Signal::BUSY, true); s.on_signal_change(1, E84Signal::BUSY, false); s.on_signal_change(1, E84Signal::COMPT, true); CHECK(s.get(1)->state() == E84State::Complete); CHECK(s.get(2)->state() == E84State::Idle); // Reset LP1; LP2 is unaffected. s.reset(1); CHECK(s.get(1)->state() == E84State::Idle); CHECK(s.get(2)->state() == E84State::Idle); } TEST_CASE("E84PortStore: change handler reports port_id") { E84PortStore s; std::vector> seen; s.set_state_change_handler( [&](uint8_t pid, E84State, E84State to, E84Signal) { seen.emplace_back(pid, to); }); REQUIRE(s.create(1)); REQUIRE(s.create(2)); s.on_signal_change(1, E84Signal::CS_0, true); s.on_signal_change(2, E84Signal::CS_0, true); s.on_signal_change(2, E84Signal::VALID, true); s.on_signal_change(2, E84Signal::U_REQ, true); // Should have seen events tagged with the right port_id. bool saw_lp1_carrier_present = false; bool saw_lp2_unload_ready = false; for (auto& [pid, to] : seen) { if (pid == 1 && to == E84State::CarrierPresent) saw_lp1_carrier_present = true; if (pid == 2 && to == E84State::UnloadReady) saw_lp2_unload_ready = true; } CHECK(saw_lp1_carrier_present); CHECK(saw_lp2_unload_ready); } TEST_CASE("E84PortStore: ids() returns the registered port set") { E84PortStore s; s.create(3); s.create(1); s.create(2); auto ids = s.ids(); // std::map iteration is ordered by key, so the returned vector is // sorted. REQUIRE(ids.size() == 3); CHECK(ids[0] == 1); CHECK(ids[1] == 2); CHECK(ids[2] == 3); }