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 <tuple> 17 #include <type_traits> 18 19 #include "pw_rpc/internal/packet.h" 20 #include "pw_rpc/raw/internal/method.h" 21 #include "pw_unit_test/framework.h" 22 23 namespace pw::rpc::internal { 24 25 template <typename...> 26 struct MatchesTypes {}; 27 28 // This class tests Method implementation classes and MethodTraits 29 // specializations. It verifies that they provide the expected functions and 30 // that they correctly identify and construct the various method types. 31 // 32 // The TestService class must inherit from Service and provide the following 33 // methods with valid signatures for RPCs: 34 // 35 // - Unary: synchronous unary RPC member function 36 // - StaticUnary: synchronous unary RPC static member function 37 // - AsyncUnary: asynchronous unary RPC member function 38 // - StaticAsyncUnary: asynchronous unary RPC static member function 39 // - ServerStreaming: server streaming RPC member function 40 // - StaticServerStreaming: server streaming static RPC member function 41 // - ClientStreaming: client streaming RPC member function 42 // - StaticClientStreaming: client streaming static RPC member function 43 // - BidirectionalStreaming: bidirectional streaming RPC member function 44 // - StaticBidirectionalStreaming: bidirectional streaming static RPC 45 // member function 46 // 47 template <typename MethodImpl, typename TestService> 48 class MethodImplTests { 49 public: 50 template <typename... ExtraTypes, typename... CreationArgs> 51 constexpr bool Pass( 52 const MatchesTypes<ExtraTypes...>& = {}, 53 const std::tuple<CreationArgs...>& creation_args = {}) const { 54 return Matches<ExtraTypes...>().Pass() && Type().Pass() && 55 Creation().Pass(creation_args); 56 } 57 58 private: 59 template <typename... ExtraTypes> 60 struct Matches { PassMatches61 constexpr bool Pass() const { return true; } 62 63 // Test that the matches() function matches valid signatures. 64 static_assert( 65 MethodImpl::template matches<&TestService::Unary, ExtraTypes...>()); 66 static_assert(MethodImpl::template matches<&TestService::StaticUnary, 67 ExtraTypes...>()); 68 69 static_assert(MethodImpl::template matches<&TestService::AsyncUnary, 70 ExtraTypes...>()); 71 static_assert(MethodImpl::template matches<&TestService::StaticAsyncUnary, 72 ExtraTypes...>()); 73 74 static_assert(MethodImpl::template matches<&TestService::ServerStreaming, 75 ExtraTypes...>()); 76 static_assert( 77 MethodImpl::template matches<&TestService::StaticServerStreaming, 78 ExtraTypes...>()); 79 80 static_assert(MethodImpl::template matches<&TestService::ClientStreaming, 81 ExtraTypes...>()); 82 static_assert( 83 MethodImpl::template matches<&TestService::StaticClientStreaming, 84 ExtraTypes...>()); 85 86 static_assert( 87 MethodImpl::template matches<&TestService::BidirectionalStreaming, 88 ExtraTypes...>()); 89 static_assert( 90 MethodImpl::template matches<&TestService::StaticBidirectionalStreaming, 91 ExtraTypes...>()); 92 93 // Test that the matches() function does not match the wrong method type. 94 static_assert(!MethodImpl::template matches<&TestService::UnaryWrongArg, 95 ExtraTypes...>()); 96 static_assert( 97 !MethodImpl::template matches<&TestService::StaticUnaryVoidReturn, 98 ExtraTypes...>()); 99 100 static_assert( 101 !MethodImpl::template matches<&TestService::ServerStreamingBadReturn, 102 ExtraTypes...>()); 103 static_assert(!MethodImpl::template matches< 104 &TestService::StaticServerStreamingMissingArg, 105 ExtraTypes...>()); 106 107 static_assert( 108 !MethodImpl::template matches<&TestService::ClientStreamingBadReturn, 109 ExtraTypes...>()); 110 static_assert(!MethodImpl::template matches< 111 &TestService::StaticClientStreamingMissingArg, 112 ExtraTypes...>()); 113 114 static_assert(!MethodImpl::template matches< 115 &TestService::BidirectionalStreamingBadReturn, 116 ExtraTypes...>()); 117 static_assert(!MethodImpl::template matches< 118 &TestService::StaticBidirectionalStreamingMissingArg, 119 ExtraTypes...>()); 120 }; 121 122 // Check that MethodTraits resolves to the correct value for kType. 123 struct Type { PassType124 constexpr bool Pass() const { return true; } 125 126 // Don't check kSynchronous for Unary since not all method implementations 127 // support synchronous unary. 128 static_assert(MethodTraits<decltype(&TestService::Unary)>::kType == 129 MethodType::kUnary); 130 static_assert(MethodTraits<decltype(&TestService::StaticUnary)>::kType == 131 MethodType::kUnary); 132 static_assert(MethodTraits<decltype(&TestService::AsyncUnary)>::kType == 133 MethodType::kUnary); 134 static_assert( 135 !MethodTraits<decltype(&TestService::AsyncUnary)>::kSynchronous); 136 static_assert( 137 MethodTraits<decltype(&TestService::StaticAsyncUnary)>::kType == 138 MethodType::kUnary); 139 static_assert( 140 !MethodTraits<decltype(&TestService::StaticAsyncUnary)>::kSynchronous); 141 142 static_assert( 143 MethodTraits<decltype(&TestService::ServerStreaming)>::kType == 144 MethodType::kServerStreaming); 145 static_assert( 146 MethodTraits<decltype(&TestService::StaticServerStreaming)>::kType == 147 MethodType::kServerStreaming); 148 149 static_assert( 150 MethodTraits<decltype(&TestService::ClientStreaming)>::kType == 151 MethodType::kClientStreaming); 152 static_assert( 153 MethodTraits<decltype(&TestService::StaticClientStreaming)>::kType == 154 MethodType::kClientStreaming); 155 156 static_assert( 157 MethodTraits<decltype(&TestService::BidirectionalStreaming)>::kType == 158 MethodType::kBidirectionalStreaming); 159 static_assert( 160 MethodTraits< 161 decltype(&TestService::StaticBidirectionalStreaming)>::kType == 162 MethodType::kBidirectionalStreaming); 163 }; 164 165 // Test method creation. 166 class Creation { 167 public: 168 template <typename... Args> Pass(const std::tuple<Args...> & args)169 constexpr bool Pass(const std::tuple<Args...>& args) const { 170 return AsyncUnaryMethod(args).id() == 3 && 171 StaticAsyncUnaryMethod(args).id() == 4 && 172 ServerStreamingMethod(args).id() == 5 && 173 StaticServerStreamingMethod(args).id() == 6 && 174 ClientStreamingMethod(args).id() == 7 && 175 StaticClientStreamingMethod(args).id() == 8 && 176 BidirectionalStreamingMethod(args).id() == 9 && 177 StaticBidirectionalStreamingMethod(args).id() == 10 && 178 InvalidMethod().id() == 0; 179 } 180 181 private: 182 // Do not check synchronous unary since not all method implementations 183 // support it. 184 185 template <typename... Args> AsyncUnaryMethod(const std::tuple<Args...> & args)186 constexpr MethodImpl AsyncUnaryMethod( 187 const std::tuple<Args...>& args) const { 188 return Call( 189 MethodImpl::template AsynchronousUnary<&TestService::AsyncUnary>, 190 3, 191 args); 192 } 193 194 template <typename... Args> StaticAsyncUnaryMethod(const std::tuple<Args...> & args)195 constexpr MethodImpl StaticAsyncUnaryMethod( 196 const std::tuple<Args...>& args) const { 197 return Call(MethodImpl::template AsynchronousUnary< 198 &TestService::StaticAsyncUnary>, 199 4, 200 args); 201 } 202 203 template <typename... Args> ServerStreamingMethod(const std::tuple<Args...> & args)204 constexpr MethodImpl ServerStreamingMethod( 205 const std::tuple<Args...>& args) const { 206 return Call( 207 MethodImpl::template ServerStreaming<&TestService::ServerStreaming>, 208 5, 209 args); 210 } 211 212 template <typename... Args> StaticServerStreamingMethod(const std::tuple<Args...> & args)213 constexpr MethodImpl StaticServerStreamingMethod( 214 const std::tuple<Args...>& args) const { 215 return Call(MethodImpl::template ServerStreaming< 216 &TestService::StaticServerStreaming>, 217 6, 218 args); 219 } 220 221 template <typename... Args> ClientStreamingMethod(const std::tuple<Args...> & args)222 constexpr MethodImpl ClientStreamingMethod( 223 const std::tuple<Args...>& args) const { 224 return Call( 225 MethodImpl::template ClientStreaming<&TestService::ClientStreaming>, 226 7, 227 args); 228 } 229 230 template <typename... Args> StaticClientStreamingMethod(const std::tuple<Args...> & args)231 constexpr MethodImpl StaticClientStreamingMethod( 232 const std::tuple<Args...>& args) const { 233 return Call(MethodImpl::template ClientStreaming< 234 &TestService::StaticClientStreaming>, 235 8, 236 args); 237 } 238 239 template <typename... Args> BidirectionalStreamingMethod(const std::tuple<Args...> & args)240 constexpr MethodImpl BidirectionalStreamingMethod( 241 const std::tuple<Args...>& args) const { 242 return Call(MethodImpl::template BidirectionalStreaming< 243 &TestService::BidirectionalStreaming>, 244 9, 245 args); 246 } 247 248 template <typename... Args> StaticBidirectionalStreamingMethod(const std::tuple<Args...> & args)249 constexpr MethodImpl StaticBidirectionalStreamingMethod( 250 const std::tuple<Args...>& args) const { 251 return Call(MethodImpl::template BidirectionalStreaming< 252 &TestService::StaticBidirectionalStreaming>, 253 10, 254 args); 255 } 256 257 // Test that there is an Invalid method creation function. InvalidMethod()258 constexpr MethodImpl InvalidMethod() const { return MethodImpl::Invalid(); } 259 260 // Invokes the method creation function with the ID and extra args. 261 template <typename Function, typename... Args> Call(Function && function,uint32_t id,const std::tuple<Args...> & args)262 static constexpr MethodImpl Call(Function&& function, 263 uint32_t id, 264 const std::tuple<Args...>& args) { 265 return std::apply(function, std::tuple_cat(std::tuple(id), args)); 266 } 267 }; 268 }; 269 270 } // namespace pw::rpc::internal 271