// Copyright 2021 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/server_reader_writer.h" #include "gtest/gtest.h" #include "pw_rpc/nanopb/fake_channel_output.h" #include "pw_rpc/nanopb/test_method_context.h" #include "pw_rpc/service.h" #include "pw_rpc_test_protos/test.rpc.pb.h" namespace pw::rpc { class TestServiceImpl final : public test::pw_rpc::nanopb::TestService::Service { public: Status TestUnaryRpc(const pw_rpc_test_TestRequest&, pw_rpc_test_TestResponse&) { return OkStatus(); } void TestAnotherUnaryRpc(const pw_rpc_test_TestRequest&, NanopbUnaryResponder&) {} void TestServerStreamRpc( const pw_rpc_test_TestRequest&, NanopbServerWriter&) {} void TestClientStreamRpc( NanopbServerReader&) {} void TestBidirectionalStreamRpc( NanopbServerReaderWriter&) {} }; template struct ReaderWriterTestContext { using Info = internal::MethodInfo; static constexpr uint32_t kChannelId = 1; ReaderWriterTestContext() : channel(Channel::Create(&output)), server(std::span(&channel, 1)) {} TestServiceImpl service; NanopbFakeChannelOutput<4> output; Channel channel; Server server; }; using test::pw_rpc::nanopb::TestService; TEST(NanopbUnaryResponder, DefaultConstructed) { NanopbUnaryResponder call; ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Finish({}, OkStatus())); call.set_on_error([](Status) {}); } TEST(NanopbServerWriter, DefaultConstructed) { NanopbServerWriter call; ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Write({})); EXPECT_EQ(Status::FailedPrecondition(), call.Finish(OkStatus())); call.set_on_error([](Status) {}); } TEST(NanopbServerReader, DefaultConstructed) { NanopbServerReader call; ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Finish({}, OkStatus())); call.set_on_next([](const pw_rpc_test_TestRequest&) {}); call.set_on_error([](Status) {}); } TEST(NanopbServerReaderWriter, DefaultConstructed) { NanopbServerReaderWriter call; ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Write({})); EXPECT_EQ(Status::FailedPrecondition(), call.Finish(OkStatus())); call.set_on_next([](const pw_rpc_test_TestRequest&) {}); call.set_on_error([](Status) {}); } TEST(NanopbUnaryResponder, Closed) { ReaderWriterTestContext ctx; NanopbUnaryResponder call = NanopbUnaryResponder::Open< TestService::TestUnaryRpc>(ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), call.Finish({}, OkStatus())); ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Finish({}, OkStatus())); call.set_on_error([](Status) {}); } TEST(NanopbServerWriter, Closed) { ReaderWriterTestContext ctx; NanopbServerWriter call = NanopbServerWriter::Open< TestService::TestServerStreamRpc>( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), call.Finish(OkStatus())); ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Write({})); EXPECT_EQ(Status::FailedPrecondition(), call.Finish(OkStatus())); call.set_on_error([](Status) {}); } TEST(NanopbServerReader, Closed) { ReaderWriterTestContext ctx; NanopbServerReader call = NanopbServerReader:: Open( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), call.Finish({}, OkStatus())); ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Finish({}, OkStatus())); call.set_on_next([](const pw_rpc_test_TestRequest&) {}); call.set_on_error([](Status) {}); } TEST(NanopbServerReaderWriter, Closed) { ReaderWriterTestContext ctx; NanopbServerReaderWriter call = NanopbServerReaderWriter:: Open( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), call.Finish(OkStatus())); ASSERT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); EXPECT_EQ(Status::FailedPrecondition(), call.Write({})); EXPECT_EQ(Status::FailedPrecondition(), call.Finish(OkStatus())); call.set_on_next([](const pw_rpc_test_TestRequest&) {}); call.set_on_error([](Status) {}); } TEST(NanopbUnaryResponder, Open_ReturnsUsableResponder) { ReaderWriterTestContext ctx; NanopbUnaryResponder responder = NanopbUnaryResponder::Open< TestService::TestUnaryRpc>(ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), responder.Finish({.value = 4321, .repeated_field = {}})); EXPECT_EQ(ctx.output.last_response().value, 4321); EXPECT_EQ(ctx.output.last_status(), OkStatus()); } TEST(NanopbServerWriter, Open_ReturnsUsableWriter) { ReaderWriterTestContext ctx; NanopbServerWriter responder = NanopbServerWriter::Open< TestService::TestServerStreamRpc>( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), responder.Write({.chunk = {}, .number = 321})); ASSERT_EQ(OkStatus(), responder.Finish()); EXPECT_EQ(ctx.output.last_response().number, 321u); EXPECT_EQ(ctx.output.last_status(), OkStatus()); } TEST(NanopbServerReader, Open_ReturnsUsableReader) { ReaderWriterTestContext ctx; NanopbServerReader responder = NanopbServerReader:: Open( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), responder.Finish({.chunk = {}, .number = 321})); EXPECT_EQ(ctx.output.last_response().number, 321u); } TEST(NanopbServerReaderWriter, Open_ReturnsUsableReaderWriter) { ReaderWriterTestContext ctx; NanopbServerReaderWriter responder = NanopbServerReaderWriter:: Open( ctx.server, ctx.channel.id(), ctx.service); ASSERT_EQ(OkStatus(), responder.Write({.chunk = {}, .number = 321})); ASSERT_EQ(OkStatus(), responder.Finish(Status::NotFound())); EXPECT_EQ(ctx.output.last_response() .number, 321u); EXPECT_EQ(ctx.output.last_status(), Status::NotFound()); } TEST(RawServerReaderWriter, Open_UnknownChannel) { ReaderWriterTestContext ctx; ASSERT_EQ(OkStatus(), ctx.server.CloseChannel(ctx.kChannelId)); NanopbServerReaderWriter call = NanopbServerReaderWriter:: Open( ctx.server, ctx.kChannelId, ctx.service); EXPECT_TRUE(call.active()); EXPECT_EQ(call.channel_id(), ctx.kChannelId); EXPECT_EQ(Status::Unavailable(), call.Write({})); ASSERT_EQ(OkStatus(), ctx.server.OpenChannel(ctx.kChannelId, ctx.output)); EXPECT_EQ(OkStatus(), call.Write({})); EXPECT_TRUE(call.active()); EXPECT_EQ(OkStatus(), call.Finish()); EXPECT_FALSE(call.active()); EXPECT_EQ(call.channel_id(), Channel::kUnassignedChannelId); } TEST(NanopbServerReader, CallbacksMoveCorrectly) { PW_NANOPB_TEST_METHOD_CONTEXT(TestServiceImpl, TestClientStreamRpc) ctx; NanopbServerReader call_1 = ctx.reader(); ASSERT_TRUE(call_1.active()); pw_rpc_test_TestRequest received_request = {.integer = 12345678, .status_code = 1}; call_1.set_on_next([&received_request](const pw_rpc_test_TestRequest& value) { received_request = value; }); NanopbServerReader call_2; call_2 = std::move(call_1); constexpr pw_rpc_test_TestRequest request{.integer = 600613, .status_code = 2}; ctx.SendClientStream(request); EXPECT_EQ(request.integer, received_request.integer); EXPECT_EQ(request.status_code, received_request.status_code); } } // namespace pw::rpc