blob: 6498a99c31cbf61dc86f0e16247627963daf515d [file] [log] [blame]
// 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.
#pragma once
#include <tuple>
#include <type_traits>
#include "gtest/gtest.h"
#include "pw_rpc/internal/packet.h"
#include "pw_rpc/raw/internal/method.h"
namespace pw::rpc::internal {
template <typename...>
struct MatchesTypes {};
// This class tests Method implementation classes and MethodTraits
// specializations. It verifies that they provide the expected functions and
// that they correctly identify and construct the various method types.
//
// The TestService class must inherit from Service and provide the following
// methods with valid signatures for RPCs:
//
// - Unary: synchronous unary RPC member function
// - StaticUnary: synchronous unary RPC static member function
// - AsyncUnary: asynchronous unary RPC member function
// - StaticAsyncUnary: asynchronous unary RPC static member function
// - ServerStreaming: server streaming RPC member function
// - StaticServerStreaming: server streaming static RPC member function
// - ClientStreaming: client streaming RPC member function
// - StaticClientStreaming: client streaming static RPC member function
// - BidirectionalStreaming: bidirectional streaming RPC member function
// - StaticBidirectionalStreaming: bidirectional streaming static RPC
// member function
//
template <typename MethodImpl, typename TestService>
class MethodImplTests {
public:
template <typename... ExtraTypes, typename... CreationArgs>
constexpr bool Pass(
const MatchesTypes<ExtraTypes...>& = {},
const std::tuple<CreationArgs...>& creation_args = {}) const {
return Matches<ExtraTypes...>().Pass() && Type().Pass() &&
Creation().Pass(creation_args);
}
private:
template <typename... ExtraTypes>
struct Matches {
constexpr bool Pass() const { return true; }
// Test that the matches() function matches valid signatures.
static_assert(
MethodImpl::template matches<&TestService::Unary, ExtraTypes...>());
static_assert(MethodImpl::template matches<&TestService::StaticUnary,
ExtraTypes...>());
static_assert(MethodImpl::template matches<&TestService::AsyncUnary,
ExtraTypes...>());
static_assert(MethodImpl::template matches<&TestService::StaticAsyncUnary,
ExtraTypes...>());
static_assert(MethodImpl::template matches<&TestService::ServerStreaming,
ExtraTypes...>());
static_assert(
MethodImpl::template matches<&TestService::StaticServerStreaming,
ExtraTypes...>());
static_assert(MethodImpl::template matches<&TestService::ClientStreaming,
ExtraTypes...>());
static_assert(
MethodImpl::template matches<&TestService::StaticClientStreaming,
ExtraTypes...>());
static_assert(
MethodImpl::template matches<&TestService::BidirectionalStreaming,
ExtraTypes...>());
static_assert(
MethodImpl::template matches<&TestService::StaticBidirectionalStreaming,
ExtraTypes...>());
// Test that the matches() function does not match the wrong method type.
static_assert(!MethodImpl::template matches<&TestService::UnaryWrongArg,
ExtraTypes...>());
static_assert(
!MethodImpl::template matches<&TestService::StaticUnaryVoidReturn,
ExtraTypes...>());
static_assert(
!MethodImpl::template matches<&TestService::ServerStreamingBadReturn,
ExtraTypes...>());
static_assert(!MethodImpl::template matches<
&TestService::StaticServerStreamingMissingArg,
ExtraTypes...>());
static_assert(
!MethodImpl::template matches<&TestService::ClientStreamingBadReturn,
ExtraTypes...>());
static_assert(!MethodImpl::template matches<
&TestService::StaticClientStreamingMissingArg,
ExtraTypes...>());
static_assert(!MethodImpl::template matches<
&TestService::BidirectionalStreamingBadReturn,
ExtraTypes...>());
static_assert(!MethodImpl::template matches<
&TestService::StaticBidirectionalStreamingMissingArg,
ExtraTypes...>());
};
// Check that MethodTraits resolves to the correct value for kType.
struct Type {
constexpr bool Pass() const { return true; }
static_assert(MethodTraits<decltype(&TestService::Unary)>::kType ==
MethodType::kUnary);
static_assert(MethodTraits<decltype(&TestService::Unary)>::kSynchronous);
static_assert(MethodTraits<decltype(&TestService::StaticUnary)>::kType ==
MethodType::kUnary);
static_assert(
MethodTraits<decltype(&TestService::StaticUnary)>::kSynchronous);
static_assert(MethodTraits<decltype(&TestService::AsyncUnary)>::kType ==
MethodType::kUnary);
static_assert(
!MethodTraits<decltype(&TestService::AsyncUnary)>::kSynchronous);
static_assert(
MethodTraits<decltype(&TestService::StaticAsyncUnary)>::kType ==
MethodType::kUnary);
static_assert(
!MethodTraits<decltype(&TestService::StaticAsyncUnary)>::kSynchronous);
static_assert(
MethodTraits<decltype(&TestService::ServerStreaming)>::kType ==
MethodType::kServerStreaming);
static_assert(
MethodTraits<decltype(&TestService::StaticServerStreaming)>::kType ==
MethodType::kServerStreaming);
static_assert(
MethodTraits<decltype(&TestService::ClientStreaming)>::kType ==
MethodType::kClientStreaming);
static_assert(
MethodTraits<decltype(&TestService::StaticClientStreaming)>::kType ==
MethodType::kClientStreaming);
static_assert(
MethodTraits<decltype(&TestService::BidirectionalStreaming)>::kType ==
MethodType::kBidirectionalStreaming);
static_assert(
MethodTraits<
decltype(&TestService::StaticBidirectionalStreaming)>::kType ==
MethodType::kBidirectionalStreaming);
};
// Test method creation.
class Creation {
public:
template <typename... Args>
constexpr bool Pass(const std::tuple<Args...>& args) const {
return UnaryMethod(args).id() == 1 && StaticUnaryMethod(args).id() == 2 &&
AsyncUnaryMethod(args).id() == 3 &&
StaticAsyncUnaryMethod(args).id() == 4 &&
ServerStreamingMethod(args).id() == 5 &&
StaticServerStreamingMethod(args).id() == 6 &&
ClientStreamingMethod(args).id() == 7 &&
StaticClientStreamingMethod(args).id() == 8 &&
BidirectionalStreamingMethod(args).id() == 9 &&
StaticBidirectionalStreamingMethod(args).id() == 10 &&
InvalidMethod().id() == 0;
}
private:
template <typename... Args>
constexpr MethodImpl UnaryMethod(const std::tuple<Args...>& args) const {
return Call(
MethodImpl::template SynchronousUnary<&TestService::Unary>, 1, args);
}
template <typename... Args>
constexpr MethodImpl StaticUnaryMethod(
const std::tuple<Args...>& args) const {
return Call(
MethodImpl::template SynchronousUnary<&TestService::StaticUnary>,
2,
args);
}
template <typename... Args>
constexpr MethodImpl AsyncUnaryMethod(
const std::tuple<Args...>& args) const {
return Call(
MethodImpl::template AsynchronousUnary<&TestService::AsyncUnary>,
3,
args);
}
template <typename... Args>
constexpr MethodImpl StaticAsyncUnaryMethod(
const std::tuple<Args...>& args) const {
return Call(MethodImpl::template AsynchronousUnary<
&TestService::StaticAsyncUnary>,
4,
args);
}
template <typename... Args>
constexpr MethodImpl ServerStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(
MethodImpl::template ServerStreaming<&TestService::ServerStreaming>,
5,
args);
}
template <typename... Args>
constexpr MethodImpl StaticServerStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(MethodImpl::template ServerStreaming<
&TestService::StaticServerStreaming>,
6,
args);
}
template <typename... Args>
constexpr MethodImpl ClientStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(
MethodImpl::template ClientStreaming<&TestService::ClientStreaming>,
7,
args);
}
template <typename... Args>
constexpr MethodImpl StaticClientStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(MethodImpl::template ClientStreaming<
&TestService::StaticClientStreaming>,
8,
args);
}
template <typename... Args>
constexpr MethodImpl BidirectionalStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(MethodImpl::template BidirectionalStreaming<
&TestService::BidirectionalStreaming>,
9,
args);
}
template <typename... Args>
constexpr MethodImpl StaticBidirectionalStreamingMethod(
const std::tuple<Args...>& args) const {
return Call(MethodImpl::template BidirectionalStreaming<
&TestService::StaticBidirectionalStreaming>,
10,
args);
}
// Test that there is an Invalid method creation function.
constexpr MethodImpl InvalidMethod() const { return MethodImpl::Invalid(); }
// Invokes the method creation function with the ID and extra args.
template <typename Function, typename... Args>
static constexpr MethodImpl Call(Function&& function,
uint32_t id,
const std::tuple<Args...>& args) {
return std::apply(function, std::tuple_cat(std::tuple(id), args));
}
};
};
} // namespace pw::rpc::internal