xref: /aosp_15_r20/external/pigweed/pw_rpc/fuzz/engine_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 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/fuzz/engine.h"
16 
17 #include <chrono>
18 
19 #include "pw_containers/vector.h"
20 #include "pw_log/log.h"
21 #include "pw_rpc/benchmark.h"
22 #include "pw_rpc/internal/client_server_testing.h"
23 #include "pw_rpc/internal/client_server_testing_threaded.h"
24 #include "pw_rpc/internal/fake_channel_output.h"
25 #include "pw_thread/non_portable_test_thread_options.h"
26 #include "pw_unit_test/framework.h"
27 
28 namespace pw::rpc::fuzz {
29 namespace {
30 
31 using namespace std::literals::chrono_literals;
32 
33 // Maximum time, in milliseconds, that can elapse without a call completing or
34 // being dropped in some way..
35 const chrono::SystemClock::duration kTimeout = 5s;
36 
37 // These are fairly tight constraints in order to fit within the default
38 // `PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE`.
39 constexpr size_t kMaxPackets = 128;
40 constexpr size_t kMaxPayloadSize = 64;
41 
42 using BufferedChannelOutputBase =
43     internal::test::FakeChannelOutputBuffer<kMaxPackets, kMaxPayloadSize>;
44 
45 /// Channel output backed by a fixed buffer.
46 class BufferedChannelOutput : public BufferedChannelOutputBase {
47  public:
BufferedChannelOutput()48   BufferedChannelOutput() : BufferedChannelOutputBase() {}
49 };
50 
51 using FuzzerChannelOutputBase =
52     internal::WatchableChannelOutput<BufferedChannelOutput,
53                                      kMaxPayloadSize,
54                                      kMaxPackets,
55                                      kMaxPayloadSize>;
56 
57 /// Channel output that can be waited on by the server.
58 class FuzzerChannelOutput : public FuzzerChannelOutputBase {
59  public:
FuzzerChannelOutput(TestPacketProcessor && server_packet_processor=nullptr,TestPacketProcessor && client_packet_processor=nullptr)60   explicit FuzzerChannelOutput(
61       TestPacketProcessor&& server_packet_processor = nullptr,
62       TestPacketProcessor&& client_packet_processor = nullptr)
63       : FuzzerChannelOutputBase(std::move(server_packet_processor),
64                                 std::move(client_packet_processor)) {}
65 };
66 
67 using FuzzerContextBase =
68     internal::ClientServerTestContextThreaded<FuzzerChannelOutput,
69                                               kMaxPayloadSize,
70                                               kMaxPackets,
71                                               kMaxPayloadSize>;
72 class FuzzerContext : public FuzzerContextBase {
73  public:
74   static constexpr uint32_t kChannelId = 1;
75 
FuzzerContext(TestPacketProcessor && server_packet_processor=nullptr,TestPacketProcessor && client_packet_processor=nullptr)76   explicit FuzzerContext(
77       TestPacketProcessor&& server_packet_processor = nullptr,
78       TestPacketProcessor&& client_packet_processor = nullptr)
79       // TODO: b/290860904 - Replace TestOptionsThread0 with TestThreadContext.
80       : FuzzerContextBase(thread::test::TestOptionsThread0(),
81                           std::move(server_packet_processor),
82                           std::move(client_packet_processor)) {}
83 };
84 
85 class RpcFuzzTestingTest : public testing::Test {
86  protected:
SetUp()87   void SetUp() override { context_.server().RegisterService(service_); }
88 
Add(Action::Op op,size_t target,uint16_t value)89   void Add(Action::Op op, size_t target, uint16_t value) {
90     actions_.push_back(Action(op, target, value).Encode());
91   }
92 
Add(Action::Op op,size_t target,char val,size_t len)93   void Add(Action::Op op, size_t target, char val, size_t len) {
94     actions_.push_back(Action(op, target, val, len).Encode());
95   }
96 
NextThread()97   void NextThread() { actions_.push_back(0); }
98 
Run()99   void Run() {
100     Fuzzer fuzzer(context_.client(), FuzzerContext::kChannelId);
101     fuzzer.set_verbose(true);
102     fuzzer.set_timeout(kTimeout);
103     fuzzer.Run(actions_);
104   }
105 
TearDown()106   void TearDown() override { context_.server().UnregisterService(service_); }
107 
108  private:
109   FuzzerContext context_;
110   BenchmarkService service_;
111   Vector<uint32_t, Fuzzer::kMaxActions> actions_;
112 };
113 
114 // TODO: b/274437709 - Re-enable.
TEST_F(RpcFuzzTestingTest,DISABLED_SequentialRequests)115 TEST_F(RpcFuzzTestingTest, DISABLED_SequentialRequests) {
116   // Callback thread
117   Add(Action::kWriteStream, 1, 'B', 1);
118   Add(Action::kSkip, 0, 0);
119   Add(Action::kWriteStream, 2, 'B', 2);
120   Add(Action::kSkip, 0, 0);
121   Add(Action::kWriteStream, 3, 'B', 3);
122   Add(Action::kSkip, 0, 0);
123   NextThread();
124 
125   // Thread 1
126   Add(Action::kWriteStream, 0, 'A', 2);
127   Add(Action::kWait, 1, 0);
128   Add(Action::kWriteStream, 1, 'A', 4);
129   NextThread();
130 
131   // Thread 2
132   NextThread();
133   Add(Action::kWait, 2, 0);
134   Add(Action::kWriteStream, 2, 'A', 6);
135 
136   // Thread 3
137   NextThread();
138   Add(Action::kWait, 3, 0);
139 
140   Run();
141 }
142 
143 // TODO: b/274437709 - Re-enable.
TEST_F(RpcFuzzTestingTest,DISABLED_SimultaneousRequests)144 TEST_F(RpcFuzzTestingTest, DISABLED_SimultaneousRequests) {
145   // Callback thread
146   NextThread();
147 
148   // Thread 1
149   Add(Action::kWriteUnary, 1, 'A', 1);
150   Add(Action::kWait, 2, 0);
151   NextThread();
152 
153   // Thread 2
154   Add(Action::kWriteUnary, 2, 'B', 2);
155   Add(Action::kWait, 3, 0);
156   NextThread();
157 
158   // Thread 3
159   Add(Action::kWriteUnary, 3, 'C', 3);
160   Add(Action::kWait, 1, 0);
161   NextThread();
162 
163   Run();
164 }
165 
166 // TODO: b/274437709 - This test currently does not pass as it exhausts the fake
167 // channel. It will be re-enabled when the underlying stream is swapped for
168 // a pw_ring_buffer-based approach.
TEST_F(RpcFuzzTestingTest,DISABLED_CanceledRequests)169 TEST_F(RpcFuzzTestingTest, DISABLED_CanceledRequests) {
170   // Callback thread
171   NextThread();
172 
173   // Thread 1
174   for (size_t i = 0; i < 10; ++i) {
175     Add(Action::kWriteUnary, i % 3, 'A', i);
176   }
177   Add(Action::kWait, 0, 0);
178   Add(Action::kWait, 1, 0);
179   Add(Action::kWait, 2, 0);
180   NextThread();
181 
182   // Thread 2
183   for (size_t i = 0; i < 10; ++i) {
184     Add(Action::kCancel, i % 3, 0);
185   }
186   NextThread();
187 
188   // Thread 3
189   NextThread();
190 
191   Run();
192 }
193 
194 // TODO: b/274437709 - This test currently does not pass as it exhausts the fake
195 // channel. It will be re-enabled when the underlying stream is swapped for
196 // a pw_ring_buffer-based approach.
TEST_F(RpcFuzzTestingTest,DISABLED_AbandonedRequests)197 TEST_F(RpcFuzzTestingTest, DISABLED_AbandonedRequests) {
198   // Callback thread
199   NextThread();
200 
201   // Thread 1
202   for (size_t i = 0; i < 10; ++i) {
203     Add(Action::kWriteUnary, i % 3, 'A', i);
204   }
205   Add(Action::kWait, 0, 0);
206   Add(Action::kWait, 1, 0);
207   Add(Action::kWait, 2, 0);
208   NextThread();
209 
210   // Thread 2
211   for (size_t i = 0; i < 10; ++i) {
212     Add(Action::kAbandon, i % 3, 0);
213   }
214   NextThread();
215 
216   // Thread 3
217   NextThread();
218 
219   Run();
220 }
221 
222 // TODO: b/274437709 - This test currently does not pass as it exhausts the fake
223 // channel. It will be re-enabled when the underlying stream is swapped for
224 // a pw_ring_buffer-based approach.
TEST_F(RpcFuzzTestingTest,DISABLED_SwappedRequests)225 TEST_F(RpcFuzzTestingTest, DISABLED_SwappedRequests) {
226   Vector<uint32_t, Fuzzer::kMaxActions> actions;
227   // Callback thread
228   NextThread();
229   // Thread 1
230   for (size_t i = 0; i < 10; ++i) {
231     Add(Action::kWriteUnary, i % 3, 'A', i);
232   }
233   Add(Action::kWait, 0, 0);
234   Add(Action::kWait, 1, 0);
235   Add(Action::kWait, 2, 0);
236   NextThread();
237   // Thread 2
238   for (size_t i = 0; i < 100; ++i) {
239     auto j = i % 3;
240     Add(Action::kSwap, j, j + 1);
241   }
242   NextThread();
243   // Thread 3
244   NextThread();
245 
246   Run();
247 }
248 
249 // TODO: b/274437709 - This test currently does not pass as it exhausts the fake
250 // channel. It will be re-enabled when the underlying stream is swapped for
251 // a pw_ring_buffer-based approach.
TEST_F(RpcFuzzTestingTest,DISABLED_DestroyedRequests)252 TEST_F(RpcFuzzTestingTest, DISABLED_DestroyedRequests) {
253   // Callback thread
254   NextThread();
255 
256   // Thread 1
257   for (size_t i = 0; i < 100; ++i) {
258     Add(Action::kWriteUnary, i % 3, 'A', i);
259   }
260   Add(Action::kWait, 0, 0);
261   Add(Action::kWait, 1, 0);
262   Add(Action::kWait, 2, 0);
263   NextThread();
264 
265   // Thread 2
266   for (size_t i = 0; i < 100; ++i) {
267     Add(Action::kDestroy, i % 3, 0);
268   }
269   NextThread();
270 
271   // Thread 3
272   NextThread();
273 
274   Run();
275 }
276 
277 }  // namespace
278 }  // namespace pw::rpc::fuzz
279