blob: 171ae285b03393cafdb90615a9b9df623a13370f [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_rpc/nanopb_client_call.h"
#include "gtest/gtest.h"
#include "pw_rpc_nanopb_private/internal_test_utils.h"
#include "pw_rpc_private/internal_test_utils.h"
#include "pw_rpc_test_protos/test.pb.h"
namespace pw::rpc {
namespace {
constexpr uint32_t kServiceId = 16;
constexpr uint32_t kUnaryMethodId = 111;
constexpr uint32_t kServerStreamingMethodId = 112;
class FakeGeneratedServiceClient {
public:
static NanopbClientCall<UnaryResponseHandler<pw_rpc_test_TestResponse>>
TestRpc(Channel& channel,
const pw_rpc_test_TestRequest& request,
UnaryResponseHandler<pw_rpc_test_TestResponse>& callback) {
auto call = NanopbClientCall(&channel,
kServiceId,
kUnaryMethodId,
callback,
pw_rpc_test_TestRequest_fields,
pw_rpc_test_TestResponse_fields);
call.SendRequest(&request);
return call;
}
static NanopbClientCall<
ServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse>>
TestStreamRpc(Channel& channel,
const pw_rpc_test_TestRequest& request,
ServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse>&
callback) {
auto call = NanopbClientCall(&channel,
kServiceId,
kServerStreamingMethodId,
callback,
pw_rpc_test_TestRequest_fields,
pw_rpc_test_TestStreamResponse_fields);
call.SendRequest(&request);
return call;
}
};
using internal::TestServerStreamingResponseHandler;
using internal::TestUnaryResponseHandler;
TEST(NanopbClientCall, Unary_SendsRequestPacket) {
ClientContextForTest context;
TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
auto call = FakeGeneratedServiceClient::TestRpc(
context.channel(), {.integer = 123, .status_code = 0}, handler);
EXPECT_EQ(context.output().packet_count(), 1u);
auto packet = context.output().sent_packet();
EXPECT_EQ(packet.channel_id(), context.channel().id());
EXPECT_EQ(packet.service_id(), kServiceId);
EXPECT_EQ(packet.method_id(), kUnaryMethodId);
PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
EXPECT_EQ(sent_proto.integer, 123);
}
TEST(NanopbClientCall, Unary_InvokesCallbackOnValidResponse) {
ClientContextForTest context;
TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
auto call = FakeGeneratedServiceClient::TestRpc(
context.channel(), {.integer = 123, .status_code = 0}, handler);
PW_ENCODE_PB(pw_rpc_test_TestResponse, response, .value = 42);
context.SendResponse(OkStatus(), response);
ASSERT_EQ(handler.responses_received(), 1u);
EXPECT_EQ(handler.last_status(), OkStatus());
EXPECT_EQ(handler.last_response().value, 42);
}
TEST(NanopbClientCall, Unary_InvokesErrorCallbackOnInvalidResponse) {
ClientContextForTest context;
TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
auto call = FakeGeneratedServiceClient::TestRpc(
context.channel(), {.integer = 123, .status_code = 0}, handler);
constexpr std::byte bad_payload[]{
std::byte{0xab}, std::byte{0xcd}, std::byte{0xef}};
context.SendResponse(OkStatus(), bad_payload);
EXPECT_EQ(handler.responses_received(), 0u);
EXPECT_EQ(handler.rpc_error(), Status::DataLoss());
}
TEST(NanopbClientCall, Unary_InvokesErrorCallbackOnServerError) {
ClientContextForTest context;
TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
auto call = FakeGeneratedServiceClient::TestRpc(
context.channel(), {.integer = 123, .status_code = 0}, handler);
context.SendPacket(internal::PacketType::SERVER_ERROR, Status::NotFound());
EXPECT_EQ(handler.responses_received(), 0u);
EXPECT_EQ(handler.rpc_error(), Status::NotFound());
}
TEST(NanopbClientCall, Unary_OnlyReceivesOneResponse) {
ClientContextForTest context;
TestUnaryResponseHandler<pw_rpc_test_TestResponse> handler;
auto call = FakeGeneratedServiceClient::TestRpc(
context.channel(), {.integer = 123, .status_code = 0}, handler);
PW_ENCODE_PB(pw_rpc_test_TestResponse, r1, .value = 42);
context.SendResponse(Status::Unimplemented(), r1);
PW_ENCODE_PB(pw_rpc_test_TestResponse, r2, .value = 44);
context.SendResponse(Status::OutOfRange(), r2);
PW_ENCODE_PB(pw_rpc_test_TestResponse, r3, .value = 46);
context.SendResponse(Status::Internal(), r3);
EXPECT_EQ(handler.responses_received(), 1u);
EXPECT_EQ(handler.last_status(), Status::Unimplemented());
EXPECT_EQ(handler.last_response().value, 42);
}
TEST(NanopbClientCall, ServerStreaming_SendsRequestPacket) {
ClientContextForTest<128, 128, 99, kServiceId, kServerStreamingMethodId>
context;
TestServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse> handler;
auto call = FakeGeneratedServiceClient::TestStreamRpc(
context.channel(), {.integer = 71, .status_code = 0}, handler);
EXPECT_EQ(context.output().packet_count(), 1u);
auto packet = context.output().sent_packet();
EXPECT_EQ(packet.channel_id(), context.channel().id());
EXPECT_EQ(packet.service_id(), kServiceId);
EXPECT_EQ(packet.method_id(), kServerStreamingMethodId);
PW_DECODE_PB(pw_rpc_test_TestRequest, sent_proto, packet.payload());
EXPECT_EQ(sent_proto.integer, 71);
}
TEST(NanopbClientCall, ServerStreaming_InvokesCallbackOnValidResponse) {
ClientContextForTest<128, 128, 99, kServiceId, kServerStreamingMethodId>
context;
TestServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse> handler;
auto call = FakeGeneratedServiceClient::TestStreamRpc(
context.channel(), {.integer = 71, .status_code = 0}, handler);
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r1, .chunk = {}, .number = 11u);
context.SendResponse(OkStatus(), r1);
EXPECT_TRUE(handler.active());
EXPECT_EQ(handler.responses_received(), 1u);
EXPECT_EQ(handler.last_response().number, 11u);
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r2, .chunk = {}, .number = 22u);
context.SendResponse(OkStatus(), r2);
EXPECT_TRUE(handler.active());
EXPECT_EQ(handler.responses_received(), 2u);
EXPECT_EQ(handler.last_response().number, 22u);
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r3, .chunk = {}, .number = 33u);
context.SendResponse(OkStatus(), r3);
EXPECT_TRUE(handler.active());
EXPECT_EQ(handler.responses_received(), 3u);
EXPECT_EQ(handler.last_response().number, 33u);
}
TEST(NanopbClientCall, ServerStreaming_ClosesOnFinish) {
ClientContextForTest<128, 128, 99, kServiceId, kServerStreamingMethodId>
context;
TestServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse> handler;
auto call = FakeGeneratedServiceClient::TestStreamRpc(
context.channel(), {.integer = 71, .status_code = 0}, handler);
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r1, .chunk = {}, .number = 11u);
context.SendResponse(OkStatus(), r1);
EXPECT_TRUE(handler.active());
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r2, .chunk = {}, .number = 22u);
context.SendResponse(OkStatus(), r2);
EXPECT_TRUE(handler.active());
// Close the stream.
context.SendPacket(internal::PacketType::SERVER_STREAM_END,
Status::NotFound());
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r3, .chunk = {}, .number = 33u);
context.SendResponse(OkStatus(), r3);
EXPECT_FALSE(handler.active());
EXPECT_EQ(handler.responses_received(), 2u);
}
TEST(NanopbClientCall, ServerStreaming_InvokesErrorCallbackOnInvalidResponses) {
ClientContextForTest<128, 128, 99, kServiceId, kServerStreamingMethodId>
context;
TestServerStreamingResponseHandler<pw_rpc_test_TestStreamResponse> handler;
auto call = FakeGeneratedServiceClient::TestStreamRpc(
context.channel(), {.integer = 71, .status_code = 0}, handler);
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r1, .chunk = {}, .number = 11u);
context.SendResponse(OkStatus(), r1);
EXPECT_TRUE(handler.active());
EXPECT_EQ(handler.responses_received(), 1u);
EXPECT_EQ(handler.last_response().number, 11u);
constexpr std::byte bad_payload[]{
std::byte{0xab}, std::byte{0xcd}, std::byte{0xef}};
context.SendResponse(OkStatus(), bad_payload);
EXPECT_EQ(handler.responses_received(), 1u);
EXPECT_EQ(handler.rpc_error(), Status::DataLoss());
PW_ENCODE_PB(pw_rpc_test_TestStreamResponse, r2, .chunk = {}, .number = 22u);
context.SendResponse(OkStatus(), r2);
EXPECT_TRUE(handler.active());
EXPECT_EQ(handler.responses_received(), 2u);
EXPECT_EQ(handler.last_response().number, 22u);
context.SendPacket(internal::PacketType::SERVER_ERROR, Status::NotFound());
EXPECT_EQ(handler.responses_received(), 2u);
EXPECT_EQ(handler.rpc_error(), Status::NotFound());
}
} // namespace
} // namespace pw::rpc