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 "pw_rpc/internal/lock.h" 20 #include "pw_rpc/internal/method.h" 21 #include "pw_rpc/internal/method_union.h" 22 #include "pw_rpc/internal/packet.h" 23 #include "pw_rpc/internal/server_call.h" 24 #include "pw_rpc/method_type.h" 25 #include "pw_rpc/server.h" 26 #include "pw_span/span.h" 27 #include "pw_status/status_with_size.h" 28 29 namespace pw::rpc::internal { 30 31 // This is a fake RPC method implementation for testing only. It stores the 32 // channel ID, request, and payload buffer, and optionally provides a response. 33 class TestMethod : public Method { 34 public: 35 class FakeServerCall : public ServerCall { 36 public: 37 constexpr FakeServerCall() = default; FakeServerCall(const LockedCallContext & context,MethodType type)38 FakeServerCall(const LockedCallContext& context, MethodType type) 39 PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) 40 : ServerCall(context, CallProperties(type, kServerCall, kRawProto)) {} 41 42 FakeServerCall(FakeServerCall&&) = default; 43 FakeServerCall& operator=(FakeServerCall&&) = default; 44 45 using internal::Call::set_on_error; 46 }; 47 48 constexpr TestMethod(uint32_t id, MethodType type = MethodType::kUnary) Method(id,GetInvoker (type))49 : Method(id, GetInvoker(type)), 50 last_channel_id_(0), 51 invocations_(0), 52 move_to_call_(nullptr) {} 53 last_channel_id()54 uint32_t last_channel_id() const { return last_channel_id_; } last_request()55 const Packet& last_request() const { return last_request_; } invocations()56 size_t invocations() const { return invocations_; } 57 58 // Sets a call object into which to move the call object when the RPC is 59 // invoked. This keeps the RPC active until the provided call object is 60 // finished or goes out of scope. keep_call_active(FakeServerCall & move_to_call)61 void keep_call_active(FakeServerCall& move_to_call) const { 62 move_to_call_ = &move_to_call; 63 } 64 65 private: 66 template <MethodType kType> InvokeForTest(const CallContext & context,const Packet & request)67 static void InvokeForTest(const CallContext& context, const Packet& request) 68 PW_UNLOCK_FUNCTION(rpc_lock()) { 69 const auto& test_method = static_cast<const TestMethod&>(context.method()); 70 test_method.last_channel_id_ = context.channel_id(); 71 test_method.last_request_ = request; 72 test_method.invocations_ += 1; 73 74 // Create a call object so it registers / unregisters with the server. 75 FakeServerCall fake_call(context.ClaimLocked(), kType); 76 77 context.server().CleanUpCalls(); 78 79 if (test_method.move_to_call_ != nullptr) { 80 *test_method.move_to_call_ = std::move(fake_call); 81 } 82 } 83 GetInvoker(MethodType type)84 static constexpr Invoker GetInvoker(MethodType type) { 85 switch (type) { 86 case MethodType::kUnary: 87 return InvokeForTest<MethodType::kUnary>; 88 case MethodType::kServerStreaming: 89 return InvokeForTest<MethodType::kServerStreaming>; 90 case MethodType::kClientStreaming: 91 return InvokeForTest<MethodType::kClientStreaming>; 92 case MethodType::kBidirectionalStreaming: 93 return InvokeForTest<MethodType::kBidirectionalStreaming>; 94 }; 95 } 96 97 // Make these mutable so they can be set in the Invoke method, which is const. 98 // The Method class is used exclusively in tests. Having these members mutable 99 // allows tests to verify that the Method is invoked correctly. 100 mutable uint32_t last_channel_id_; 101 mutable Packet last_request_; 102 mutable size_t invocations_; 103 mutable FakeServerCall* move_to_call_; 104 105 span<const std::byte> response_; 106 Status response_status_; 107 }; 108 109 class TestMethodUnion : public MethodUnion { 110 public: TestMethodUnion(TestMethod && method)111 constexpr TestMethodUnion(TestMethod&& method) : impl_({.test = method}) {} 112 method()113 constexpr const Method& method() const { return impl_.method; } test_method()114 constexpr const TestMethod& test_method() const { return impl_.test; } 115 116 private: 117 union { 118 Method method; 119 TestMethod test; 120 } impl_; 121 }; 122 123 } // namespace pw::rpc::internal 124