#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include #include #include "equipment_service.hpp" using namespace secsgem; namespace gem = secsgem::gem; namespace s2 = secsgem::secs2; namespace pb = secsgem::v1; namespace dmn = secsgem::daemon; #ifndef SECSGEM_DATA_DIR #error "SECSGEM_DATA_DIR not defined; see CMakeLists.txt" #endif 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; // ephemeral; the engine isn't run() here, only poll()ed return c; } // Exercises the real gRPC service over an in-process channel: client stub -> // service -> EquipmentRuntime, proving the RPCs move data, not just compile. TEST_CASE("Equipment gRPC service over an in-process channel") { gem::EquipmentRuntime rt(test_config()); dmn::EquipmentService svc(rt); grpc::ServerBuilder builder; builder.RegisterService(&svc); std::unique_ptr server(builder.BuildAndStart()); REQUIRE(server); auto stub = pb::Equipment::NewStub(server->InProcessChannel(grpc::ChannelArguments{})); SUBCASE("GetControlState returns the initial control state") { grpc::ClientContext ctx; pb::Empty req; pb::ControlState resp; auto st = stub->GetControlState(&ctx, req, &resp); CHECK(st.ok()); CHECK(resp.state() == pb::ControlState::HOST_OFFLINE); } SUBCASE("SetVariables accepts a known name and updates the model") { grpc::ClientContext ctx; pb::VariableUpdate req; pb::Ack resp; (*req.mutable_values())["ChamberPressure"].set_real(2.5); auto st = stub->SetVariables(&ctx, req, &resp); CHECK(st.ok()); CHECK(resp.code() == pb::Ack::ACCEPT); rt.poll(); // drain the posted set_variable onto this thread CHECK(rt.model().dvids.value(101) == s2::Item::f8(2.5)); } SUBCASE("SetVariables rejects an unknown variable name") { grpc::ClientContext ctx; pb::VariableUpdate req; pb::Ack resp; (*req.mutable_values())["definitely_not_a_var"].set_real(1.0); auto st = stub->SetVariables(&ctx, req, &resp); CHECK(st.ok()); CHECK(resp.code() == pb::Ack::PARAMETER_INVALID); } SUBCASE("FireEvent accepts a known event and rejects an unknown one") { { grpc::ClientContext ctx; pb::Event req; pb::Ack resp; req.set_name("ProcessStarted"); CHECK(stub->FireEvent(&ctx, req, &resp).ok()); CHECK(resp.code() == pb::Ack::ACCEPT); } { grpc::ClientContext ctx; pb::Event req; pb::Ack resp; req.set_name("NoSuchEvent"); CHECK(stub->FireEvent(&ctx, req, &resp).ok()); CHECK(resp.code() == pb::Ack::PARAMETER_INVALID); } } server->Shutdown(); }