ee96a8c729fd88f2dd5514151f0f880583b422a0
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_rpc / py / codegen_test.py
1 #!/usr/bin/env python3
2 # Copyright 2020 The Pigweed Authors
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 # use this file except in compliance with the License. You may obtain a copy of
6 # the License at
7 #
8 #     https://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
14 # the License.
15 """Tests the generated pw_rpc code."""
16
17 from pathlib import Path
18 import os
19 import subprocess
20 import tempfile
21 import unittest
22
23 TEST_PROTO_FILE = b"""\
24 syntax = "proto3";
25
26 package pw.rpc.test;
27
28 message TestRequest {
29   float integer = 1;
30 }
31
32 message TestResponse {
33   int32 value = 1;
34 }
35
36 message TestStreamResponse {
37   bytes chunk = 1;
38 }
39
40 message Empty {}
41
42 service TestService {
43   rpc TestRpc(TestRequest) returns (TestResponse) {}
44   rpc TestStreamRpc(Empty) returns (stream TestStreamResponse) {}
45 }
46 """
47
48 EXPECTED_NANOPB_CODE = """\
49 #pragma once
50
51 #include <array>
52 #include <cstddef>
53 #include <cstdint>
54 #include <type_traits>
55
56 #include "pw_rpc/internal/method.h"
57 #include "pw_rpc/server_context.h"
58 #include "pw_rpc/service.h"
59 #include "test.pb.h"
60
61 namespace pw::rpc::internal {
62
63 template <auto>
64 class ServiceMethodTraits;
65
66 }  // namespace pw::rpc::internal
67
68 namespace pw::rpc::test {
69 namespace generated {
70
71 template <typename Implementation>
72 class TestService : public ::pw::rpc::Service {
73  public:
74   using ServerContext = ::pw::rpc::ServerContext;
75   template <typename T>
76   using ServerWriter = ::pw::rpc::ServerWriter<T>;
77
78   constexpr TestService()
79       : ::pw::rpc::Service(kServiceId, kMethods) {}
80
81   TestService(const TestService&) = delete;
82   TestService& operator=(const TestService&) = delete;
83
84   static constexpr const char* name() { return "TestService"; }
85
86   // Used by ServiceMethodTraits to identify a base service.
87   constexpr void _PwRpcInternalGeneratedBase() const {}
88
89  private:
90   // Hash of "pw.rpc.test.TestService".
91   static constexpr uint32_t kServiceId = 0xcc0f6de0;
92
93   static ::pw::Status Invoke_TestRpc(
94       ::pw::rpc::internal::ServerCall& call,
95       const pw_rpc_test_TestRequest& request,
96       pw_rpc_test_TestResponse& response) {
97     return static_cast<Implementation&>(call.service())
98         .TestRpc(call.context(), request, response);
99   }
100
101   static void Invoke_TestStreamRpc(
102       ::pw::rpc::internal::ServerCall& call,
103       const pw_rpc_test_TestRequest& request,
104       ServerWriter<pw_rpc_test_TestStreamResponse>& writer) {
105     static_cast<Implementation&>(call.service())
106         .TestStreamRpc(call.context(), request, writer);
107   }
108
109   static constexpr std::array<::pw::rpc::internal::Method, 2> kMethods = {
110       ::pw::rpc::internal::Method::Unary<Invoke_TestRpc>(
111           0xbc924054,  // Hash of "TestRpc"
112           pw_rpc_test_TestRequest_fields,
113           pw_rpc_test_TestResponse_fields),
114       ::pw::rpc::internal::Method::ServerStreaming<Invoke_TestStreamRpc>(
115           0xd97a28fa,  // Hash of "TestStreamRpc"
116           pw_rpc_test_TestRequest_fields,
117           pw_rpc_test_TestStreamResponse_fields),
118   };
119
120   template <auto impl_method>
121   static constexpr const ::pw::rpc::internal::Method* MethodFor() {
122     if constexpr (std::is_same_v<decltype(impl_method), decltype(&Implementation::TestRpc)>) {
123       if constexpr (impl_method == &Implementation::TestRpc) {
124         return &std::get<0>(kMethods);
125       }
126     }
127     if constexpr (std::is_same_v<decltype(impl_method), decltype(&Implementation::TestStreamRpc)>) {
128       if constexpr (impl_method == &Implementation::TestStreamRpc) {
129         return &std::get<1>(kMethods);
130       }
131     }
132     return nullptr;
133   }
134
135   template <auto>
136   friend class ::pw::rpc::internal::ServiceMethodTraits;
137 };
138
139 }  // namespace generated
140 }  // namespace pw::rpc::test
141 """
142
143
144 class TestNanopbCodegen(unittest.TestCase):
145     """Test case for nanopb code generation."""
146     def setUp(self):
147         self._output_dir = tempfile.TemporaryDirectory()
148
149     def tearDown(self):
150         self._output_dir.cleanup()
151
152     def test_nanopb_codegen(self):
153         root = Path(os.getenv('PW_ROOT'))
154         proto_dir = root / 'pw_rpc' / 'pw_rpc_test_protos'
155         proto_file = proto_dir / 'test.proto'
156
157         venv_bin = 'Scripts' if os.name == 'nt' else 'bin'
158         plugin = root / '.python3-env' / venv_bin / 'pw_rpc_codegen'
159
160         command = (
161             'protoc',
162             f'-I{proto_dir}',
163             proto_file,
164             '--plugin',
165             f'protoc-gen-custom={plugin}',
166             '--custom_out',
167             self._output_dir.name,
168         )
169
170         subprocess.run(command)
171
172         generated_files = os.listdir(self._output_dir.name)
173         self.assertEqual(len(generated_files), 1)
174         self.assertEqual(generated_files[0], 'test.rpc.pb.h')
175
176         # Read the generated file, ignoring its preamble.
177         generated_code = Path(self._output_dir.name,
178                               generated_files[0]).read_text()
179         generated_code = generated_code[generated_code.index('#pragma'):]
180
181         self.assertEqual(generated_code, EXPECTED_NANOPB_CODE)