pw_rpc: Generic test for Method and MethodTraits implementations
Change-Id: Iaef3a3b15cbd1c9f6fb7e2f897011f02592466cb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/25642
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_rpc/BUILD b/pw_rpc/BUILD
index 260f2d3..fe34d2d 100644
--- a/pw_rpc/BUILD
+++ b/pw_rpc/BUILD
@@ -87,6 +87,7 @@
hdrs = [
"public/pw_rpc/internal/test_method.h",
"pw_rpc_private/internal_test_utils.h",
+ "pw_rpc_private/method_impl_tester.h",
],
visibility = ["//visibility:private"],
deps = [
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index a04c2e6..1dee6ca 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -90,6 +90,7 @@
public = [
"public/pw_rpc/internal/test_method.h",
"pw_rpc_private/internal_test_utils.h",
+ "pw_rpc_private/method_impl_tester.h",
]
public_configs = [ ":private_includes" ]
public_deps = [
diff --git a/pw_rpc/nanopb/nanopb_method_test.cc b/pw_rpc/nanopb/nanopb_method_test.cc
index 6c0b736..98e3d54 100644
--- a/pw_rpc/nanopb/nanopb_method_test.cc
+++ b/pw_rpc/nanopb/nanopb_method_test.cc
@@ -22,6 +22,7 @@
#include "pw_rpc/service.h"
#include "pw_rpc_nanopb_private/internal_test_utils.h"
#include "pw_rpc_private/internal_test_utils.h"
+#include "pw_rpc_private/method_impl_tester.h"
#include "pw_rpc_test_protos/test.pb.h"
namespace pw::rpc::internal {
@@ -29,6 +30,43 @@
using std::byte;
+struct FakePb {};
+
+// Create a fake service for use with the MethodImplTester.
+class TestNanopbService final : public Service {
+ public:
+ Status Unary(ServerContext&, const FakePb&, FakePb&) { return Status(); }
+
+ static Status StaticUnary(ServerContext&, const FakePb&, FakePb&) {
+ return Status();
+ }
+
+ void ServerStreaming(ServerContext&, const FakePb&, ServerWriter<FakePb>&) {}
+
+ static void StaticServerStreaming(ServerContext&,
+ const FakePb&,
+ ServerWriter<FakePb>&) {}
+
+ Status UnaryWrongArg(ServerContext&, FakePb&, FakePb&) { return Status(); }
+
+ static void StaticUnaryVoidReturn(ServerContext&, const FakePb&, FakePb&) {}
+
+ int ServerStreamingBadReturn(ServerContext&,
+ const FakePb&,
+ ServerWriter<FakePb>&) {
+ return 5;
+ }
+
+ static void StaticServerStreamingMissingArg(const FakePb&,
+ ServerWriter<FakePb>&) {}
+};
+
+TEST(MethodImplTester, NanopbMethod) {
+ constexpr MethodImplTester<NanopbMethod, TestNanopbService, nullptr, nullptr>
+ method_tester;
+ EXPECT_TRUE(method_tester.MethodImplIsValid());
+}
+
pw_rpc_test_TestRequest last_request;
ServerWriter<pw_rpc_test_TestResponse> last_writer;
diff --git a/pw_rpc/pw_rpc_private/method_impl_tester.h b/pw_rpc/pw_rpc_private/method_impl_tester.h
new file mode 100644
index 0000000..13ef880
--- /dev/null
+++ b/pw_rpc/pw_rpc_private/method_impl_tester.h
@@ -0,0 +1,106 @@
+// 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/internal/raw_method.h"
+#include "pw_rpc/server_context.h"
+
+namespace pw::rpc::internal {
+namespace {
+
+// 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: a valid unary RPC member function
+// - StaticUnary: valid unary RPC static member function
+// - ServerStreaming: valid server streaming RPC member function
+// - StaticServerStreaming: valid server streaming static RPC member function
+//
+// The class must also provide methods with errors as described in their names:
+//
+// - UnaryWrongArg
+// - StaticUnaryVoidReturn
+// - ServerStreamingBadReturn
+// - StaticServerStreamingMissingArg
+//
+template <typename MethodImpl, typename TestService, auto... extra_method_args>
+struct MethodImplTester {
+ // Test that the matches() function matches valid signatures.
+ static_assert(MethodImpl::template matches<&TestService::Unary>());
+ static_assert(MethodImpl::template matches<&TestService::ServerStreaming>());
+ static_assert(MethodImpl::template matches<&TestService::StaticUnary>());
+ static_assert(
+ MethodImpl::template matches<&TestService::StaticServerStreaming>());
+
+ // Test that the matches() function doesn't match invalid signatures.
+ static_assert(!MethodImpl::template matches<&TestService::UnaryWrongArg>());
+ static_assert(
+ !MethodImpl::template matches<&TestService::StaticUnaryVoidReturn>());
+ static_assert(
+ !MethodImpl::template matches<&TestService::ServerStreamingBadReturn>());
+ static_assert(!MethodImpl::template matches<
+ &TestService::StaticServerStreamingMissingArg>());
+
+ // Test the MethodTraits::kType member.
+ static_assert(MethodTraits<decltype(&TestService::Unary)>::kType ==
+ MethodType::kUnary);
+ static_assert(MethodTraits<decltype(&TestService::StaticUnary)>::kType ==
+ MethodType::kUnary);
+ static_assert(MethodTraits<decltype(&TestService::ServerStreaming)>::kType ==
+ MethodType::kServerStreaming);
+ static_assert(
+ MethodTraits<decltype(&TestService::StaticServerStreaming)>::kType ==
+ MethodType::kServerStreaming);
+
+ // Test method creation.
+ static constexpr MethodImpl kUnaryMethod =
+ MethodImpl::template Unary<&TestService::Unary>(1, extra_method_args...);
+ static_assert(kUnaryMethod.id() == 1);
+
+ static constexpr MethodImpl kStaticUnaryMethod =
+ MethodImpl::template Unary<&TestService::StaticUnary>(
+ 2, extra_method_args...);
+ static_assert(kStaticUnaryMethod.id() == 2);
+
+ static constexpr MethodImpl kServerStreamingMethod =
+ MethodImpl::template ServerStreaming<&TestService::ServerStreaming>(
+ 3, extra_method_args...);
+ static_assert(kServerStreamingMethod.id() == 3);
+
+ static constexpr MethodImpl kStaticServerStreamingMethod =
+ MethodImpl::template ServerStreaming<&TestService::StaticServerStreaming>(
+ 4, extra_method_args...);
+ static_assert(kStaticServerStreamingMethod.id() == 4);
+
+ // Test that there is an Invalid method creation function.
+ static constexpr MethodImpl kInvalidMethod = MethodImpl::Invalid();
+ static_assert(kInvalidMethod.id() == 0);
+
+ // Provide a method that tests can call to ensure this class is instantiated.
+ bool MethodImplIsValid() const {
+ return true; // If this class compiles, the MethodImpl passes this test.
+ }
+};
+
+} // namespace
+} // namespace pw::rpc::internal
diff --git a/pw_rpc/raw/raw_method_test.cc b/pw_rpc/raw/raw_method_test.cc
index c2dfb3e..0008222 100644
--- a/pw_rpc/raw/raw_method_test.cc
+++ b/pw_rpc/raw/raw_method_test.cc
@@ -24,11 +24,50 @@
#include "pw_rpc/server_context.h"
#include "pw_rpc/service.h"
#include "pw_rpc_private/internal_test_utils.h"
+#include "pw_rpc_private/method_impl_tester.h"
#include "pw_rpc_test_protos/test.pwpb.h"
namespace pw::rpc::internal {
namespace {
+// Create a fake service for use with the MethodImplTester.
+class TestRawService final : public Service {
+ public:
+ StatusWithSize Unary(ServerContext&, ConstByteSpan, ByteSpan) {
+ return StatusWithSize(0);
+ }
+
+ static StatusWithSize StaticUnary(ServerContext&, ConstByteSpan, ByteSpan) {
+ return StatusWithSize(0);
+ }
+
+ void ServerStreaming(ServerContext&, ConstByteSpan, RawServerWriter&) {}
+
+ static void StaticServerStreaming(ServerContext&,
+ ConstByteSpan,
+ RawServerWriter&) {}
+
+ StatusWithSize UnaryWrongArg(ServerContext&, ConstByteSpan, ConstByteSpan) {
+ return StatusWithSize(0);
+ }
+
+ static void StaticUnaryVoidReturn(ServerContext&, ConstByteSpan, ByteSpan) {}
+
+ Status ServerStreamingBadReturn(ServerContext&,
+ ConstByteSpan,
+ RawServerWriter&) {
+ return Status();
+ }
+
+ static void StaticServerStreamingMissingArg(ConstByteSpan, RawServerWriter&) {
+ }
+};
+
+TEST(MethodImplTester, RawMethod) {
+ constexpr MethodImplTester<RawMethod, TestRawService> method_tester;
+ EXPECT_TRUE(method_tester.MethodImplIsValid());
+}
+
struct {
int64_t integer;
uint32_t status_code;