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