• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 #include "pw_rpc/raw/internal/method_union.h"
16 
17 #include <array>
18 
19 #include "gtest/gtest.h"
20 #include "pw_bytes/array.h"
21 #include "pw_protobuf/decoder.h"
22 #include "pw_protobuf/encoder.h"
23 #include "pw_rpc/internal/test_utils.h"
24 #include "pw_rpc/service.h"
25 #include "pw_rpc_test_protos/test.pwpb.h"
26 
27 namespace pw::rpc::internal {
28 namespace {
29 
30 namespace TestRequest = ::pw::rpc::test::TestRequest;
31 namespace TestResponse = ::pw::rpc::test::TestResponse;
32 
33 template <typename Implementation>
34 class FakeGeneratedService : public Service {
35  public:
FakeGeneratedService(uint32_t id)36   constexpr FakeGeneratedService(uint32_t id) : Service(id, kMethods) {}
37 
38   static constexpr std::array<RawMethodUnion, 3> kMethods = {
39       GetRawMethodFor<&Implementation::DoNothing, MethodType::kUnary>(10u),
40       GetRawMethodFor<&Implementation::AddFive, MethodType::kUnary>(11u),
41       GetRawMethodFor<&Implementation::StartStream,
42                       MethodType::kServerStreaming>(12u),
43   };
44 };
45 
46 class FakeGeneratedServiceImpl
47     : public FakeGeneratedService<FakeGeneratedServiceImpl> {
48  public:
FakeGeneratedServiceImpl(uint32_t id)49   FakeGeneratedServiceImpl(uint32_t id) : FakeGeneratedService(id) {}
50 
DoNothing(ConstByteSpan,RawUnaryResponder &)51   void DoNothing(ConstByteSpan, RawUnaryResponder&) {}
52 
AddFive(ConstByteSpan request,RawUnaryResponder & responder)53   void AddFive(ConstByteSpan request, RawUnaryResponder& responder) {
54     DecodeRawTestRequest(request);
55 
56     std::byte response[32] = {};
57     TestResponse::MemoryEncoder test_response(response);
58     ASSERT_EQ(OkStatus(), test_response.WriteValue(last_request.integer + 5));
59 
60     ASSERT_EQ(OkStatus(),
61               responder.Finish(std::span(response).first(test_response.size()),
62                                Status::Unauthenticated()));
63   }
64 
StartStream(ConstByteSpan request,RawServerWriter & writer)65   void StartStream(ConstByteSpan request, RawServerWriter& writer) {
66     DecodeRawTestRequest(request);
67     last_writer = std::move(writer);
68   }
69 
70   struct {
71     int64_t integer;
72     uint32_t status_code;
73   } last_request;
74   RawServerWriter last_writer;
75 
76  private:
DecodeRawTestRequest(ConstByteSpan request)77   void DecodeRawTestRequest(ConstByteSpan request) {
78     protobuf::Decoder decoder(request);
79 
80     while (decoder.Next().ok()) {
81       TestRequest::Fields field =
82           static_cast<TestRequest::Fields>(decoder.FieldNumber());
83 
84       switch (field) {
85         case TestRequest::Fields::INTEGER:
86           decoder.ReadInt64(&last_request.integer)
87               .IgnoreError();  // TODO(pwbug/387): Handle Status properly
88           break;
89         case TestRequest::Fields::STATUS_CODE:
90           decoder.ReadUint32(&last_request.status_code)
91               .IgnoreError();  // TODO(pwbug/387): Handle Status properly
92           break;
93       }
94     }
95   }
96 };
97 
TEST(RawMethodUnion,InvokesUnary)98 TEST(RawMethodUnion, InvokesUnary) {
99   std::byte buffer[16];
100 
101   TestRequest::MemoryEncoder test_request(buffer);
102   test_request.WriteInteger(456)
103       .IgnoreError();  // TODO(pwbug/387): Handle Status properly
104   test_request.WriteStatusCode(7)
105       .IgnoreError();  // TODO(pwbug/387): Handle Status properly
106 
107   const Method& method =
108       std::get<1>(FakeGeneratedServiceImpl::kMethods).method();
109   ServerContextForTest<FakeGeneratedServiceImpl> context(method);
110   rpc_lock().lock();
111   method.Invoke(context.get(), context.request(test_request));
112 
113   EXPECT_EQ(context.service().last_request.integer, 456);
114   EXPECT_EQ(context.service().last_request.status_code, 7u);
115 
116   const Packet& response = context.output().last_packet();
117   EXPECT_EQ(response.status(), Status::Unauthenticated());
118 
119   protobuf::Decoder decoder(response.payload());
120   ASSERT_TRUE(decoder.Next().ok());
121   int64_t value;
122   EXPECT_EQ(decoder.ReadInt64(&value), OkStatus());
123   EXPECT_EQ(value, 461);
124 }
125 
TEST(RawMethodUnion,InvokesServerStreaming)126 TEST(RawMethodUnion, InvokesServerStreaming) {
127   std::byte buffer[16];
128 
129   TestRequest::MemoryEncoder test_request(buffer);
130   test_request.WriteInteger(777)
131       .IgnoreError();  // TODO(pwbug/387): Handle Status properly
132   test_request.WriteStatusCode(2)
133       .IgnoreError();  // TODO(pwbug/387): Handle Status properly
134 
135   const Method& method =
136       std::get<2>(FakeGeneratedServiceImpl::kMethods).method();
137   ServerContextForTest<FakeGeneratedServiceImpl> context(method);
138 
139   rpc_lock().lock();
140   method.Invoke(context.get(), context.request(test_request));
141 
142   EXPECT_EQ(0u, context.output().total_packets());
143   EXPECT_EQ(777, context.service().last_request.integer);
144   EXPECT_EQ(2u, context.service().last_request.status_code);
145   EXPECT_TRUE(context.service().last_writer.active());
146   EXPECT_EQ(OkStatus(), context.service().last_writer.Finish());
147 }
148 
149 }  // namespace
150 }  // namespace pw::rpc::internal
151