pw_rpc: Generic method invocations and tests
- Restructure the BUILD.gn file to define the pw_rpc server library with
a template. This makes it simple to build different versions of the
pw_rpc server library.
- Define the ServerContext class, which provides context for a
particular RPC invocation.
- Have the server invoke RPCs if it finds a method with matching IDs.
- Implement a test version of the Method class and expand the server
tests.
Change-Id: Id1f46a88f2618a649d72f8c694ee829aef80588c
diff --git a/pw_rpc/server_test.cc b/pw_rpc/server_test.cc
index 984f5d1..b242088 100644
--- a/pw_rpc/server_test.cc
+++ b/pw_rpc/server_test.cc
@@ -14,15 +14,22 @@
#include "pw_rpc/server.h"
+#include <array>
+#include <cstdint>
+
#include "gtest/gtest.h"
+#include "pw_assert/assert.h"
#include "pw_rpc/internal/packet.h"
+#include "pw_rpc/internal/service.h"
namespace pw::rpc {
namespace {
+using std::byte;
+
+using internal::Method;
using internal::Packet;
using internal::PacketType;
-using std::byte;
template <size_t buffer_size>
class TestOutput : public ChannelOutput {
@@ -42,106 +49,153 @@
span<const byte> sent_packet_;
};
-Packet MakePacket(uint32_t channel_id,
- uint32_t service_id,
- uint32_t method_id,
- span<const byte> payload) {
- Packet packet(PacketType::RPC);
- packet.set_channel_id(channel_id);
- packet.set_service_id(service_id);
- packet.set_method_id(method_id);
- packet.set_payload(payload);
- return packet;
+class TestService : public internal::Service {
+ public:
+ TestService(uint32_t service_id)
+ : internal::Service(service_id, methods_),
+ methods_{
+ Method(100),
+ Method(200),
+ } {}
+
+ Method& method(uint32_t id) {
+ for (Method& method : methods_) {
+ if (method.id() == id) {
+ return method;
+ }
+ }
+
+ PW_CRASH("Invalid method ID %u", static_cast<unsigned>(id));
+ }
+
+ private:
+ std::array<Method, 2> methods_;
+};
+
+class BasicServer : public ::testing::Test {
+ protected:
+ static constexpr byte kDefaultPayload[] = {
+ byte(0x82), byte(0x02), byte(0xff), byte(0xff)};
+
+ BasicServer()
+ : output_(1),
+ channels_{
+ Channel::Create<1>(&output_),
+ Channel::Create<2>(&output_),
+ Channel(), // available for assignment
+ },
+ server_(channels_),
+ service_(42) {
+ server_.RegisterService(service_);
+ }
+
+ TestOutput<128> output_;
+ std::array<Channel, 3> channels_;
+ Server server_;
+ TestService service_;
+
+ span<const byte> EncodeRequest(PacketType type,
+ uint32_t channel_id,
+ uint32_t service_id,
+ uint32_t method_id,
+ span<const byte> payload = kDefaultPayload) {
+ auto sws = Packet(type, channel_id, service_id, method_id, payload)
+ .Encode(request_buffer_);
+ EXPECT_EQ(Status::OK, sws.status());
+ return span(request_buffer_, sws.size());
+ }
+
+ private:
+ byte request_buffer_[64];
+};
+
+TEST_F(BasicServer, ProcessPacket_ValidMethod_InvokesMethod) {
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 1, 42, 100), output_);
+
+ const Method& method = service_.method(100);
+ EXPECT_EQ(1u, method.last_channel_id());
+ EXPECT_EQ(sizeof(kDefaultPayload), method.last_request().size());
+ EXPECT_EQ(std::memcmp(kDefaultPayload,
+ method.last_request().data(),
+ method.last_request().size()),
+ 0);
}
-TEST(Server, ProcessPacket_SendsResponse) {
- TestOutput<128> output(1);
- Channel channels[] = {
- Channel::Create<1>(&output),
- Channel::Create<2>(&output),
- };
- Server server(channels);
- internal::Service service(42, {});
- server.RegisterService(service);
+TEST_F(BasicServer, ProcessPacket_ValidMethod_SendsOkResponse) {
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 1, 42, 100), output_);
- byte encoded_packet[64];
- constexpr byte payload[] = {byte(0x82), byte(0x02), byte(0xff), byte(0xff)};
- Packet request = MakePacket(1, 42, 27, payload);
- auto sws = request.Encode(encoded_packet);
-
- server.ProcessPacket(span(encoded_packet, sws.size()), output);
- Packet packet = Packet::FromBuffer(output.sent_packet());
- EXPECT_EQ(packet.status(), Status::OK);
+ Packet packet = Packet::FromBuffer(output_.sent_packet());
EXPECT_EQ(packet.channel_id(), 1u);
EXPECT_EQ(packet.service_id(), 42u);
+ EXPECT_EQ(packet.method_id(), 100u);
+ EXPECT_TRUE(packet.payload().empty());
+ EXPECT_EQ(packet.status(), Status::OK);
}
-TEST(Server, ProcessPacket_SendsNotFoundOnInvalidService) {
- TestOutput<128> output(1);
- Channel channels[] = {
- Channel::Create<1>(&output),
- Channel::Create<2>(&output),
- };
- Server server(channels);
- internal::Service service(42, {});
- server.RegisterService(service);
+TEST_F(BasicServer, ProcessPacket_ValidMethod_SendsErrorResponse) {
+ constexpr byte resp[] = {byte{0xf0}, byte{0x0d}};
+ service_.method(200).set_response(resp);
+ service_.method(200).set_status(Status::FAILED_PRECONDITION);
- byte encoded_packet[64];
- constexpr byte payload[] = {byte(0x82), byte(0x02), byte(0xff), byte(0xff)};
- Packet request = MakePacket(1, 43, 27, payload);
- auto sws = request.Encode(encoded_packet);
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 2, 42, 200), output_);
- server.ProcessPacket(span(encoded_packet, sws.size()), output);
- Packet packet = Packet::FromBuffer(output.sent_packet());
+ Packet packet = Packet::FromBuffer(output_.sent_packet());
+ EXPECT_EQ(packet.channel_id(), 2u);
+ EXPECT_EQ(packet.service_id(), 42u);
+ EXPECT_EQ(packet.method_id(), 200u);
+ EXPECT_EQ(packet.status(), Status::FAILED_PRECONDITION);
+ ASSERT_EQ(sizeof(resp), packet.payload().size());
+ EXPECT_EQ(std::memcmp(packet.payload().data(), resp, sizeof(resp)), 0);
+}
+
+TEST_F(BasicServer, ProcessPacket_InvalidMethod_NothingIsInvoked) {
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 1, 42, 101), output_);
+
+ EXPECT_EQ(0u, service_.method(100).last_channel_id());
+ EXPECT_EQ(0u, service_.method(200).last_channel_id());
+}
+
+TEST_F(BasicServer, ProcessPacket_InvalidMethod_SendsNotFound) {
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 1, 42, 27), output_);
+
+ Packet packet = Packet::FromBuffer(output_.sent_packet());
+ EXPECT_EQ(packet.channel_id(), 1u);
+ EXPECT_EQ(packet.service_id(), 42u);
+ EXPECT_EQ(packet.method_id(), 0u); // No method ID 27
+ EXPECT_EQ(packet.status(), Status::NOT_FOUND);
+}
+
+TEST_F(BasicServer, ProcessPacket_InvalidService_SendsNotFound) {
+ server_.ProcessPacket(EncodeRequest(PacketType::RPC, 1, 43, 27), output_);
+
+ Packet packet = Packet::FromBuffer(output_.sent_packet());
EXPECT_EQ(packet.status(), Status::NOT_FOUND);
EXPECT_EQ(packet.channel_id(), 1u);
EXPECT_EQ(packet.service_id(), 0u);
}
-TEST(Server, ProcessPacket_AssignsAnUnassignedChannel) {
- TestOutput<128> output(1);
- Channel channels[] = {
- Channel::Create<1>(&output),
- Channel::Create<2>(&output),
- Channel(),
- };
- Server server(channels);
- internal::Service service(42, {});
- server.RegisterService(service);
-
- byte encoded_packet[64];
- constexpr byte payload[] = {byte(0x82), byte(0x02), byte(0xff), byte(0xff)};
- Packet request = MakePacket(/*channel_id=*/99, 42, 27, payload);
- auto sws = request.Encode(encoded_packet);
-
+TEST_F(BasicServer, ProcessPacket_UnassignedChannel_AssignsToAvalableSlot) {
TestOutput<128> unassigned_output(2);
- server.ProcessPacket(span(encoded_packet, sws.size()), unassigned_output);
- ASSERT_EQ(channels[2].id(), 99u);
+ server_.ProcessPacket(
+ EncodeRequest(PacketType::RPC, /*channel_id=*/99, 42, 27),
+ unassigned_output);
+ ASSERT_EQ(channels_[2].id(), 99u);
Packet packet = Packet::FromBuffer(unassigned_output.sent_packet());
- EXPECT_EQ(packet.status(), Status::OK);
EXPECT_EQ(packet.channel_id(), 99u);
EXPECT_EQ(packet.service_id(), 42u);
+ EXPECT_EQ(packet.method_id(), 0u); // No method ID 27
+ EXPECT_EQ(packet.status(), Status::NOT_FOUND);
}
-TEST(Server, ProcessPacket_SendsResourceExhaustedWhenChannelCantBeAssigned) {
- TestOutput<128> output(1);
- Channel channels[] = {
- Channel::Create<1>(&output),
- Channel::Create<2>(&output),
- };
- Server server(channels);
- internal::Service service(42, {});
- server.RegisterService(service);
+TEST_F(BasicServer,
+ ProcessPacket_UnassignedChannel_SendsResourceExhaustedIfCannotAssign) {
+ channels_[2] = Channel::Create<3>(&output_); // Occupy only available channel
- byte encoded_packet[64];
- constexpr byte payload[] = {byte(0x82), byte(0x02), byte(0xff), byte(0xff)};
- Packet request = MakePacket(/*channel_id=*/99, 42, 27, payload);
- auto sws = request.Encode(encoded_packet);
+ server_.ProcessPacket(
+ EncodeRequest(PacketType::RPC, /*channel_id=*/99, 42, 27), output_);
- server.ProcessPacket(span(encoded_packet, sws.size()), output);
-
- Packet packet = Packet::FromBuffer(output.sent_packet());
+ Packet packet = Packet::FromBuffer(output_.sent_packet());
EXPECT_EQ(packet.status(), Status::RESOURCE_EXHAUSTED);
EXPECT_EQ(packet.channel_id(), 0u);
EXPECT_EQ(packet.service_id(), 0u);