• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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 #include "pw_channel/channel.h"
15 
16 #include <optional>
17 
18 #include "gtest/gtest.h"
19 #include "pw_allocator/testing.h"
20 #include "pw_assert/check.h"
21 #include "pw_compilation_testing/negative_compilation.h"
22 #include "pw_multibuf/allocator.h"
23 #include "pw_multibuf/simple_allocator.h"
24 #include "pw_preprocessor/compiler.h"
25 
26 namespace {
27 
28 using ::pw::allocator::test::AllocatorForTest;
29 using ::pw::async2::Context;
30 using ::pw::async2::Pending;
31 using ::pw::async2::Poll;
32 using ::pw::async2::Ready;
33 using ::pw::async2::Waker;
34 using ::pw::channel::ByteChannel;
35 using ::pw::channel::DatagramWriter;
36 using ::pw::channel::kReadable;
37 using ::pw::channel::kReliable;
38 using ::pw::channel::kSeekable;
39 using ::pw::channel::kWritable;
40 using ::pw::multibuf::MultiBuf;
41 using ::pw::multibuf::MultiBufAllocationFuture;
42 using ::pw::multibuf::MultiBufAllocator;
43 using ::pw::multibuf::SimpleAllocator;
44 
45 static_assert(sizeof(::pw::channel::AnyChannel) == 2 * sizeof(void*));
46 
47 static_assert((kReliable < kReadable) && (kReadable < kWritable) &&
48               (kWritable < kSeekable));
49 
50 class ReliableByteReaderWriterStub
51     : public pw::channel::ByteChannel<kReliable, kReadable, kWritable> {
52  private:
53   // Read functions
54 
DoPendRead(pw::async2::Context &)55   pw::async2::Poll<pw::Result<pw::multibuf::MultiBuf>> DoPendRead(
56       pw::async2::Context&) override {
57     return pw::async2::Pending();
58   }
59 
60   // Write functions
61 
62   // Disable maybe-uninitialized: this check fails erroniously on Windows GCC.
63   PW_MODIFY_DIAGNOSTICS_PUSH();
64   PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
DoPendReadyToWrite(pw::async2::Context &)65   pw::async2::Poll<pw::Status> DoPendReadyToWrite(
66       pw::async2::Context&) override {
67     return pw::async2::Pending();
68   }
69   PW_MODIFY_DIAGNOSTICS_POP();
70 
DoGetWriteAllocator()71   pw::multibuf::MultiBufAllocator& DoGetWriteAllocator() override {
72     // `DoPendReadyToWrite` never returns `Ready`, so this is not callable.
73     PW_CHECK(false);
74   }
75 
DoWrite(pw::multibuf::MultiBuf &&)76   pw::Result<pw::channel::WriteToken> DoWrite(
77       pw::multibuf::MultiBuf&&) override {
78     return pw::Status::Unimplemented();
79   }
80 
DoPendFlush(pw::async2::Context &)81   pw::async2::Poll<pw::Result<pw::channel::WriteToken>> DoPendFlush(
82       pw::async2::Context&) override {
83     return pw::async2::Ready(
84         pw::Result<pw::channel::WriteToken>(pw::Status::Unimplemented()));
85   }
86 
87   // Common functions
DoPendClose(pw::async2::Context &)88   pw::async2::Poll<pw::Status> DoPendClose(pw::async2::Context&) override {
89     return pw::OkStatus();
90   }
91 };
92 
93 class ReadOnlyStub : public pw::channel::ByteReader {
94  public:
95   constexpr ReadOnlyStub() = default;
96 
97  private:
98   // Read functions
DoPendRead(pw::async2::Context &)99   pw::async2::Poll<pw::Result<pw::multibuf::MultiBuf>> DoPendRead(
100       pw::async2::Context&) override {
101     return pw::async2::Pending();
102   }
103 
DoPendClose(pw::async2::Context &)104   pw::async2::Poll<pw::Status> DoPendClose(pw::async2::Context&) override {
105     return pw::OkStatus();
106   }
107 };
108 
109 class WriteOnlyStub : public pw::channel::ByteWriter {
110  private:
111   // Write functions
112 
DoPendReadyToWrite(pw::async2::Context &)113   pw::async2::Poll<pw::Status> DoPendReadyToWrite(
114       pw::async2::Context&) override {
115     return pw::async2::Pending();
116   }
117 
DoGetWriteAllocator()118   pw::multibuf::MultiBufAllocator& DoGetWriteAllocator() override {
119     PW_CHECK(false);
120   }
121 
DoWrite(pw::multibuf::MultiBuf &&)122   pw::Result<pw::channel::WriteToken> DoWrite(
123       pw::multibuf::MultiBuf&&) override {
124     return pw::Status::Unimplemented();
125   }
126 
DoPendFlush(pw::async2::Context &)127   pw::async2::Poll<pw::Result<pw::channel::WriteToken>> DoPendFlush(
128       pw::async2::Context&) override {
129     return pw::async2::Ready(
130         pw::Result<pw::channel::WriteToken>(pw::Status::Unimplemented()));
131   }
132 
133   // Common functions
DoPendClose(pw::async2::Context &)134   pw::async2::Poll<pw::Status> DoPendClose(pw::async2::Context&) override {
135     return pw::OkStatus();
136   }
137 };
138 
TEST(Channel,MethodsShortCircuitAfterCloseReturnsReady)139 TEST(Channel, MethodsShortCircuitAfterCloseReturnsReady) {
140   pw::async2::Dispatcher dispatcher;
141 
142   class : public pw::async2::Task {
143    public:
144     ReliableByteReaderWriterStub channel;
145 
146    private:
147     pw::async2::Poll<> DoPend(pw::async2::Context& cx) override {
148       EXPECT_TRUE(channel.is_read_open());
149       EXPECT_TRUE(channel.is_write_open());
150       EXPECT_EQ(pw::async2::Ready(pw::OkStatus()), channel.PendClose(cx));
151       EXPECT_FALSE(channel.is_read_open());
152       EXPECT_FALSE(channel.is_write_open());
153 
154       EXPECT_EQ(pw::Status::FailedPrecondition(),
155                 channel.PendRead(cx)->status());
156       EXPECT_EQ(Ready(pw::Status::FailedPrecondition()),
157                 channel.PendReadyToWrite(cx));
158       EXPECT_EQ(pw::Status::FailedPrecondition(),
159                 channel.PendFlush(cx)->status());
160       EXPECT_EQ(pw::async2::Ready(pw::Status::FailedPrecondition()),
161                 channel.PendClose(cx));
162 
163       return pw::async2::Ready();
164     }
165   } test_task;
166   dispatcher.Post(test_task);
167 
168   EXPECT_TRUE(dispatcher.RunUntilStalled().IsReady());
169 }
170 
TEST(Channel,ReadOnlyChannelOnlyOpenForReads)171 TEST(Channel, ReadOnlyChannelOnlyOpenForReads) {
172   ReadOnlyStub read_only;
173 
174   EXPECT_TRUE(read_only.readable());
175   EXPECT_TRUE(read_only.is_read_open());
176   EXPECT_FALSE(read_only.is_write_open());
177 }
178 
TEST(Channel,WriteOnlyChannelOnlyOpenForWrites)179 TEST(Channel, WriteOnlyChannelOnlyOpenForWrites) {
180   WriteOnlyStub write_only;
181 
182   EXPECT_FALSE(write_only.readable());
183   EXPECT_FALSE(write_only.is_read_open());
184   EXPECT_TRUE(write_only.is_write_open());
185 }
186 
187 #if PW_NC_TEST(InvalidOrdering)
188 PW_NC_EXPECT("Properties must be specified in the following order");
Illegal(pw::channel::ByteChannel<kReadable,pw::channel::kReliable> & foo)189 bool Illegal(pw::channel::ByteChannel<kReadable, pw::channel::kReliable>& foo) {
190   return foo.is_read_open();
191 }
192 #elif PW_NC_TEST(NoProperties)
193 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
Illegal(pw::channel::ByteChannel<> & foo)194 bool Illegal(pw::channel::ByteChannel<>& foo) { return foo.is_read_open(); }
195 #elif PW_NC_TEST(NoReadOrWrite)
196 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
Illegal(pw::channel::ByteChannel<pw::channel::kReliable> & foo)197 bool Illegal(pw::channel::ByteChannel<pw::channel::kReliable>& foo) {
198   return foo.is_read_open();
199 }
200 #elif PW_NC_TEST(TooMany)
201 PW_NC_EXPECT("Too many properties given");
Illegal(pw::channel::ByteChannel<kReliable,kReliable,kReliable,kReadable,kWritable> & foo)202 bool Illegal(
203     pw::channel::
204         ByteChannel<kReliable, kReliable, kReliable, kReadable, kWritable>&
205             foo) {
206   return foo.is_read_open();
207 }
208 #elif PW_NC_TEST(Duplicates)
209 PW_NC_EXPECT("duplicates");
Illegal(pw::channel::ByteChannel<kReadable,kReadable> & foo)210 bool Illegal(pw::channel::ByteChannel<kReadable, kReadable>& foo) {
211   return foo.is_read_open();
212 }
213 #endif  // PW_NC_TEST
214 
215 class TestByteReader : public ByteChannel<kReliable, kReadable> {
216  public:
TestByteReader()217   TestByteReader() {}
218 
PushData(MultiBuf data)219   void PushData(MultiBuf data) {
220     bool was_empty = data_.empty();
221     data_.PushSuffix(std::move(data));
222     if (was_empty) {
223       std::move(read_waker_).Wake();
224     }
225   }
226 
227  private:
DoPendRead(Context & cx)228   Poll<pw::Result<MultiBuf>> DoPendRead(Context& cx) override {
229     if (data_.empty()) {
230       read_waker_ = cx.GetWaker(pw::async2::WaitReason::Unspecified());
231       return Pending();
232     }
233     return std::move(data_);
234   }
235 
DoPendClose(Context &)236   Poll<pw::Status> DoPendClose(Context&) override {
237     return Ready(pw::OkStatus());
238   }
239 
240   Waker read_waker_;
241   MultiBuf data_;
242 };
243 
244 class TestDatagramWriter : public DatagramWriter {
245  public:
TestDatagramWriter(MultiBufAllocator & alloc)246   TestDatagramWriter(MultiBufAllocator& alloc) : alloc_(alloc) {}
247 
last_datagram() const248   const pw::multibuf::MultiBuf& last_datagram() const { return last_dgram_; }
249 
MakeReadyToWrite()250   void MakeReadyToWrite() {
251     PW_CHECK_INT_EQ(
252         state_,
253         kUnavailable,
254         "Can't make writable when write is pending or already writable");
255 
256     state_ = kReadyToWrite;
257     std::move(waker_).Wake();
258   }
259 
MakeReadyToFlush()260   void MakeReadyToFlush() {
261     PW_CHECK_INT_EQ(state_,
262                     kWritePending,
263                     "Can't make flushable unless a write is pending");
264 
265     state_ = kReadyToFlush;
266     std::move(waker_).Wake();
267   }
268 
269  private:
DoPendReadyToWrite(Context & cx)270   Poll<pw::Status> DoPendReadyToWrite(Context& cx) override {
271     if (state_ == kReadyToWrite) {
272       return Ready(pw::OkStatus());
273     }
274 
275     waker_ = cx.GetWaker(pw::async2::WaitReason::Unspecified());
276     return Pending();
277   }
278 
DoWrite(MultiBuf && buffer)279   pw::Result<pw::channel::WriteToken> DoWrite(MultiBuf&& buffer) override {
280     if (state_ != kReadyToWrite) {
281       return pw::Status::Unavailable();
282     }
283 
284     state_ = kWritePending;
285     last_dgram_ = std::move(buffer);
286     return CreateWriteToken(++last_write_);
287   }
288 
DoGetWriteAllocator()289   pw::multibuf::MultiBufAllocator& DoGetWriteAllocator() override {
290     return alloc_;
291   }
292 
DoPendFlush(Context & cx)293   Poll<pw::Result<pw::channel::WriteToken>> DoPendFlush(Context& cx) override {
294     if (state_ != kReadyToFlush) {
295       waker_ = cx.GetWaker(pw::async2::WaitReason::Unspecified());
296       return Pending();
297     }
298     last_flush_ = last_write_;
299     return Ready(
300         pw::Result<pw::channel::WriteToken>(CreateWriteToken(last_flush_)));
301   }
302 
DoPendClose(Context &)303   Poll<pw::Status> DoPendClose(Context&) override {
304     return Ready(pw::OkStatus());
305   }
306 
307   enum {
308     kUnavailable,
309     kReadyToWrite,
310     kWritePending,
311     kReadyToFlush,
312   } state_ = kUnavailable;
313   Waker waker_;
314   uint32_t last_write_ = 0;
315   uint32_t last_flush_ = 0;
316   MultiBuf last_dgram_;
317   MultiBufAllocator& alloc_;
318 };
319 
TEST(Channel,TestByteReader)320 TEST(Channel, TestByteReader) {
321   static constexpr char kReadData[] = "hello, world";
322   static constexpr size_t kReadDataSize = sizeof(kReadData);
323   static constexpr size_t kArbitraryMetaSize = 512;
324 
325   pw::async2::Dispatcher dispatcher;
326   std::array<std::byte, kReadDataSize> data_area;
327   AllocatorForTest<kArbitraryMetaSize> meta_alloc;
328   SimpleAllocator simple_allocator(data_area, meta_alloc);
329   std::optional<MultiBuf> read_buf_opt =
330       simple_allocator.Allocate(kReadDataSize);
331   ASSERT_TRUE(read_buf_opt.has_value());
332   MultiBuf& read_buf = *read_buf_opt;
333 
334   class : public pw::async2::Task {
335    public:
336     TestByteReader channel;
337     int test_executed = 0;
338 
339    private:
340     Poll<> DoPend(Context& cx) override {
341       auto result = channel.PendRead(cx);
342       if (!result.IsReady()) {
343         return Pending();
344       }
345 
346       auto actual_result = std::move(*result);
347       EXPECT_TRUE(actual_result.ok());
348 
349       std::byte contents[kReadDataSize] = {};
350 
351       EXPECT_EQ(actual_result->size(), sizeof(kReadData));
352       std::copy(actual_result->begin(), actual_result->end(), contents);
353       EXPECT_STREQ(reinterpret_cast<const char*>(contents), kReadData);
354 
355       test_executed += 1;
356       return Ready();
357     }
358   } test_task;
359 
360   dispatcher.Post(test_task);
361 
362   EXPECT_FALSE(dispatcher.RunUntilStalled().IsReady());
363 
364   auto kReadDataBytes = reinterpret_cast<const std::byte*>(kReadData);
365   std::copy(kReadDataBytes, kReadDataBytes + kReadDataSize, read_buf.begin());
366   test_task.channel.PushData(std::move(read_buf));
367   EXPECT_TRUE(dispatcher.RunUntilStalled().IsReady());
368 
369   EXPECT_EQ(test_task.test_executed, 1);
370 }
371 
TEST(Channel,TestDatagramWriter)372 TEST(Channel, TestDatagramWriter) {
373   pw::async2::Dispatcher dispatcher;
374   static constexpr size_t kArbitraryDataSize = 128;
375   static constexpr size_t kArbitraryMetaSize = 512;
376   std::array<std::byte, kArbitraryDataSize> data_area;
377   AllocatorForTest<kArbitraryMetaSize> meta_alloc;
378   SimpleAllocator simple_allocator(data_area, meta_alloc);
379   TestDatagramWriter write_channel(simple_allocator);
380 
381   static constexpr char kWriteData[] = "Hello there";
382 
383   class SendWriteDataAndFlush : public pw::async2::Task {
384    public:
385     explicit SendWriteDataAndFlush(DatagramWriter& channel, size_t)
386         : channel_(channel) {}
387     int test_executed = 0;
388 
389    private:
390     Poll<> DoPend(Context& cx) override {
391       switch (state_) {
392         case kWaitUntilReady: {
393           if (channel_.PendReadyToWrite(cx).IsPending()) {
394             return Pending();
395           }
396           if (!allocation_future_.has_value()) {
397             allocation_future_ =
398                 channel_.GetWriteAllocator().AllocateAsync(sizeof(kWriteData));
399           }
400           Poll<std::optional<MultiBuf>> buffer = allocation_future_->Pend(cx);
401           if (buffer.IsPending()) {
402             return Pending();
403           }
404           allocation_future_ = std::nullopt;
405           if (!buffer->has_value()) {
406             // Allocator should have enough space for `kWriteData`.
407             ADD_FAILURE();
408             return Ready();
409           }
410           pw::ConstByteSpan str(pw::as_bytes(pw::span(kWriteData)));
411           std::copy(str.begin(), str.end(), (**buffer).begin());
412           auto token = channel_.Write(std::move(**buffer));
413           PW_CHECK(token.ok());
414           write_token_ = *token;
415           state_ = kFlushPacket;
416           [[fallthrough]];
417         }
418         case kFlushPacket: {
419           auto result = channel_.PendFlush(cx);
420           if (result.IsPending() || **result < write_token_) {
421             return Pending();
422           }
423           test_executed += 1;
424           state_ = kWaitUntilReady;
425           return Ready();
426         }
427         default:
428           PW_CRASH("Illegal value");
429       }
430 
431       // This test is INCOMPLETE.
432 
433       test_executed += 1;
434       return Ready();
435     }
436 
437     enum { kWaitUntilReady, kFlushPacket } state_ = kWaitUntilReady;
438     std::optional<MultiBufAllocationFuture> allocation_future_;
439     DatagramWriter& channel_;
440     pw::channel::WriteToken write_token_;
441   };
442 
443   SendWriteDataAndFlush test_task(write_channel, 24601);
444   dispatcher.Post(test_task);
445 
446   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
447   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
448 
449   write_channel.MakeReadyToWrite();
450 
451   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
452   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
453 
454   write_channel.MakeReadyToFlush();
455 
456   EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
457   EXPECT_EQ(test_task.test_executed, 1);
458 
459   std::byte contents[64] = {};
460   const MultiBuf& dgram = write_channel.last_datagram();
461   std::copy(dgram.begin(), dgram.end(), contents);
462   EXPECT_STREQ(reinterpret_cast<const char*>(contents), kWriteData);
463 }
464 
TakesAChannel(const pw::channel::AnyChannel &)465 void TakesAChannel(const pw::channel::AnyChannel&) {}
466 
TakesAReadableByteChannel(const pw::channel::ByteChannel<kReadable> & channel)467 const pw::channel::ByteChannel<kReadable>& TakesAReadableByteChannel(
468     const pw::channel::ByteChannel<kReadable>& channel) {
469   return channel;
470 }
471 
TakesAWritableByteChannel(const pw::channel::ByteChannel<kWritable> &)472 void TakesAWritableByteChannel(const pw::channel::ByteChannel<kWritable>&) {}
473 
TEST(Channel,Conversions)474 TEST(Channel, Conversions) {
475   static constexpr size_t kArbitraryDataSize = 128;
476   static constexpr size_t kArbitraryMetaSize = 128;
477   std::array<std::byte, kArbitraryDataSize> data_area;
478   AllocatorForTest<kArbitraryMetaSize> meta_alloc;
479   SimpleAllocator simple_allocator(data_area, meta_alloc);
480 
481   const TestByteReader byte_channel;
482   const TestDatagramWriter datagram_channel(simple_allocator);
483 
484   TakesAReadableByteChannel(byte_channel);
485   TakesAReadableByteChannel(byte_channel.as<kReadable>());
486   TakesAReadableByteChannel(byte_channel.as<pw::channel::ByteReader>());
487   TakesAReadableByteChannel(
488       byte_channel.as<pw::channel::ByteChannel<kReliable, kReadable>>());
489   TakesAChannel(byte_channel);
490 
491   TakesAWritableByteChannel(datagram_channel.IgnoreDatagramBoundaries());
492 
493   [[maybe_unused]] const pw::channel::AnyChannel& plain = byte_channel;
494 
495 #if PW_NC_TEST(CannotImplicitlyLoseWritability)
496   PW_NC_EXPECT("no matching function for call");
497   TakesAWritableByteChannel(byte_channel);
498 #elif PW_NC_TEST(CannotExplicitlyLoseWritability)
499   PW_NC_EXPECT("Cannot use a non-writable channel as a writable channel");
500   TakesAWritableByteChannel(byte_channel.as<kWritable>());
501 #endif  // PW_NC_TEST
502 }
503 
504 #if PW_NC_TEST(CannotImplicitlyUseDatagramChannelAsByteChannel)
505 PW_NC_EXPECT("no matching function for call");
DatagramChannelNcTest(pw::channel::DatagramChannel<kReliable,kReadable> & dgram)506 void DatagramChannelNcTest(
507     pw::channel::DatagramChannel<kReliable, kReadable>& dgram) {
508   TakesAReadableByteChannel(dgram);
509 }
510 #elif PW_NC_TEST(CannotExplicitlyUseDatagramChannelAsByteChannel)
511 PW_NC_EXPECT("Datagram and byte channels are not interchangeable");
DatagramChannelNcTest(pw::channel::DatagramChannel<kReliable,kReadable> & dgram)512 void DatagramChannelNcTest(
513     pw::channel::DatagramChannel<kReliable, kReadable>& dgram) {
514   TakesAReadableByteChannel(dgram.as<pw::channel::ByteChannel<kReadable>>());
515 }
516 #endif  // PW_NC_TEST
517 
518 class Foo {
519  public:
Foo(pw::channel::ByteChannel<kReadable> &)520   Foo(pw::channel::ByteChannel<kReadable>&) {}
521   Foo(const Foo&) = default;
522 };
523 
524 // Define additional overloads to ensure the right overload is selected with the
525 // implicit conversion.
TakesAReadableByteChannel(const Foo &)526 [[maybe_unused]] void TakesAReadableByteChannel(const Foo&) {}
TakesAReadableByteChannel(int)527 [[maybe_unused]] void TakesAReadableByteChannel(int) {}
TakesAReadableByteChannel(const pw::channel::DatagramReaderWriter &)528 [[maybe_unused]] void TakesAReadableByteChannel(
529     const pw::channel::DatagramReaderWriter&) {}
530 
TEST(Channel,SelectsCorrectOverloadWhenRelyingOnImplicitConversion)531 TEST(Channel, SelectsCorrectOverloadWhenRelyingOnImplicitConversion) {
532   TestByteReader byte_channel;
533 
534   [[maybe_unused]] Foo selects_channel_ctor_not_copy_ctor(byte_channel);
535   EXPECT_EQ(&byte_channel.as<pw::channel::ByteChannel<kReadable>>(),
536             &TakesAReadableByteChannel(byte_channel));
537 }
538 
539 }  // namespace
540