1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstdint> 17 #include <cstring> 18 19 #include "fake_server_reader_writer.h" 20 #include "pw_rpc/internal/lock.h" 21 #include "pw_rpc/internal/method.h" 22 #include "pw_rpc/internal/method_union.h" 23 #include "pw_rpc/internal/packet.h" 24 #include "pw_rpc/internal/server_call.h" 25 #include "pw_rpc/method_type.h" 26 #include "pw_rpc/server.h" 27 #include "pw_span/span.h" 28 #include "pw_status/status_with_size.h" 29 30 namespace pw::rpc::internal { 31 32 // This is a fake RPC method implementation for testing only. It stores the 33 // channel ID, request, and payload buffer, and optionally provides a response. 34 class TestMethod : public Method { 35 public: 36 constexpr TestMethod(uint32_t id, MethodType type = MethodType::kUnary) Method(id,GetInvoker (type),type)37 : Method(id, GetInvoker(type), type), 38 last_channel_id_(0), 39 invocations_(0), 40 move_to_call_(nullptr) {} 41 last_channel_id()42 uint32_t last_channel_id() const { return last_channel_id_; } last_request()43 const Packet& last_request() const { return last_request_; } invocations()44 size_t invocations() const { return invocations_; } 45 46 // Sets a call object into which to move the call object when the RPC is 47 // invoked. This keeps the RPC active until the provided call object is 48 // finished or goes out of scope. keep_call_active(test::FakeServerReaderWriter & move_to_call)49 void keep_call_active(test::FakeServerReaderWriter& move_to_call) const { 50 move_to_call_ = &move_to_call; 51 } 52 53 private: 54 template <MethodType kType> InvokeForTest(const CallContext & context,const Packet & request)55 static void InvokeForTest(const CallContext& context, const Packet& request) 56 PW_UNLOCK_FUNCTION(rpc_lock()) { 57 const auto& test_method = static_cast<const TestMethod&>(context.method()); 58 test_method.last_channel_id_ = context.channel_id(); 59 test_method.last_request_ = request; 60 test_method.invocations_ += 1; 61 62 // Create a call object so it registers / unregisters with the server. 63 test::FakeServerReaderWriter fake_call(context.ClaimLocked(), kType); 64 65 context.server().CleanUpCalls(); 66 67 if (test_method.move_to_call_ != nullptr) { 68 *test_method.move_to_call_ = std::move(fake_call); 69 } 70 } 71 GetInvoker(MethodType type)72 static constexpr Invoker GetInvoker(MethodType type) { 73 switch (type) { 74 case MethodType::kUnary: 75 return InvokeForTest<MethodType::kUnary>; 76 case MethodType::kServerStreaming: 77 return InvokeForTest<MethodType::kServerStreaming>; 78 case MethodType::kClientStreaming: 79 return InvokeForTest<MethodType::kClientStreaming>; 80 case MethodType::kBidirectionalStreaming: 81 return InvokeForTest<MethodType::kBidirectionalStreaming>; 82 }; 83 } 84 85 // Make these mutable so they can be set in the Invoke method, which is const. 86 // The Method class is used exclusively in tests. Having these members mutable 87 // allows tests to verify that the Method is invoked correctly. 88 mutable uint32_t last_channel_id_; 89 mutable Packet last_request_; 90 mutable size_t invocations_; 91 mutable test::FakeServerReaderWriter* move_to_call_; 92 93 span<const std::byte> response_; 94 Status response_status_; 95 }; 96 97 class TestMethodUnion : public MethodUnion { 98 public: TestMethodUnion(TestMethod && method)99 constexpr TestMethodUnion(TestMethod&& method) : impl_({.test = method}) {} 100 method()101 constexpr const Method& method() const { return impl_.method; } test_method()102 constexpr const TestMethod& test_method() const { return impl_.test; } 103 104 private: 105 union { 106 Method method; 107 TestMethod test; 108 } impl_; 109 }; 110 111 } // namespace pw::rpc::internal 112