// 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/internal/base_server_writer.h" #include #include #include #include #include "gtest/gtest.h" #include "pw_rpc/internal/test_method.h" #include "pw_rpc/server_context.h" #include "pw_rpc/service.h" #include "pw_rpc_private/internal_test_utils.h" namespace pw::rpc { class TestService : public Service { public: constexpr TestService(uint32_t id) : Service(id, method) {} static constexpr internal::TestMethodUnion method = internal::TestMethod(8); }; namespace internal { namespace { using std::byte; TEST(BaseServerWriter, ConstructWithContext_StartsOpen) { ServerContextForTest context(TestService::method.method()); BaseServerWriter writer(context.get()); EXPECT_TRUE(writer.open()); } TEST(BaseServerWriter, Move_ClosesOriginal) { ServerContextForTest context(TestService::method.method()); BaseServerWriter moved(context.get()); BaseServerWriter writer(std::move(moved)); #ifndef __clang_analyzer__ EXPECT_FALSE(moved.open()); #endif // ignore use-after-move EXPECT_TRUE(writer.open()); } class FakeServerWriter : public BaseServerWriter { public: FakeServerWriter(ServerCall& context) : BaseServerWriter(context) {} constexpr FakeServerWriter() = default; Status Write(std::span response) { std::span buffer = AcquirePayloadBuffer(); std::memcpy(buffer.data(), response.data(), std::min(buffer.size(), response.size())); return ReleasePayloadBuffer(buffer.first(response.size())); } ByteSpan PayloadBuffer() { return AcquirePayloadBuffer(); } const Channel::OutputBuffer& output_buffer() { return buffer(); } }; TEST(ServerWriter, DefaultConstruct_Closed) { FakeServerWriter writer; EXPECT_FALSE(writer.open()); } TEST(ServerWriter, Construct_RegistersWithServer) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); auto& writers = context.server().writers(); EXPECT_FALSE(writers.empty()); auto it = std::find_if( writers.begin(), writers.end(), [&](auto& w) { return &w == &writer; }); ASSERT_NE(it, writers.end()); } TEST(ServerWriter, Destruct_RemovesFromServer) { ServerContextForTest context(TestService::method.method()); { FakeServerWriter writer(context.get()); } auto& writers = context.server().writers(); EXPECT_TRUE(writers.empty()); } TEST(ServerWriter, Finish_RemovesFromServer) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); EXPECT_EQ(OkStatus(), writer.Finish()); auto& writers = context.server().writers(); EXPECT_TRUE(writers.empty()); } TEST(ServerWriter, Finish_SendsCancellationPacket) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); EXPECT_EQ(OkStatus(), writer.Finish()); const Packet& packet = context.output().sent_packet(); EXPECT_EQ(packet.type(), PacketType::SERVER_STREAM_END); EXPECT_EQ(packet.channel_id(), context.channel_id()); EXPECT_EQ(packet.service_id(), context.service_id()); EXPECT_EQ(packet.method_id(), context.get().method().id()); EXPECT_TRUE(packet.payload().empty()); EXPECT_EQ(packet.status(), OkStatus()); } TEST(ServerWriter, Finish_ReturnsStatusFromChannelSend) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); context.output().set_send_status(Status::Unauthenticated()); EXPECT_EQ(Status::Unauthenticated(), writer.Finish()); } TEST(ServerWriter, Close) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); ASSERT_TRUE(writer.open()); EXPECT_EQ(OkStatus(), writer.Finish()); EXPECT_FALSE(writer.open()); EXPECT_EQ(Status::FailedPrecondition(), writer.Finish()); } TEST(ServerWriter, Close_ReleasesBuffer) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); ASSERT_TRUE(writer.open()); auto buffer = writer.PayloadBuffer(); buffer[0] = std::byte{0}; EXPECT_FALSE(writer.output_buffer().empty()); EXPECT_EQ(OkStatus(), writer.Finish()); EXPECT_FALSE(writer.open()); EXPECT_TRUE(writer.output_buffer().empty()); } TEST(ServerWriter, Open_SendsPacketWithPayload) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); constexpr byte data[] = {byte{0xf0}, byte{0x0d}}; ASSERT_EQ(OkStatus(), writer.Write(data)); byte encoded[64]; auto result = context.packet(data).Encode(encoded); ASSERT_EQ(OkStatus(), result.status()); EXPECT_EQ(result.value().size(), context.output().sent_data().size()); EXPECT_EQ( 0, std::memcmp( encoded, context.output().sent_data().data(), result.value().size())); } TEST(ServerWriter, Closed_IgnoresFinish) { ServerContextForTest context(TestService::method.method()); FakeServerWriter writer(context.get()); EXPECT_EQ(OkStatus(), writer.Finish()); EXPECT_EQ(Status::FailedPrecondition(), writer.Finish()); } } // namespace } // namespace internal } // namespace pw::rpc