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