• 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 "pw_allocator/testing.h"
19 #include "pw_assert/check.h"
20 #include "pw_compilation_testing/negative_compilation.h"
21 #include "pw_multibuf/allocator.h"
22 #include "pw_multibuf/allocator_async.h"
23 #include "pw_multibuf/simple_allocator.h"
24 #include "pw_preprocessor/compiler.h"
25 #include "pw_unit_test/framework.h"
26 
27 namespace {
28 
29 using ::pw::allocator::test::AllocatorForTest;
30 using ::pw::async2::Context;
31 using ::pw::async2::Dispatcher;
32 using ::pw::async2::Pending;
33 using ::pw::async2::Poll;
34 using ::pw::async2::Ready;
35 using ::pw::async2::Task;
36 using ::pw::async2::Waker;
37 using ::pw::channel::ByteChannel;
38 using ::pw::channel::DatagramWriter;
39 using ::pw::channel::kReadable;
40 using ::pw::channel::kReliable;
41 using ::pw::channel::kSeekable;
42 using ::pw::channel::kWritable;
43 using ::pw::multibuf::MultiBuf;
44 using ::pw::multibuf::MultiBufAllocationFuture;
45 using ::pw::multibuf::MultiBufAllocator;
46 using ::pw::multibuf::SimpleAllocator;
47 
48 static_assert(sizeof(::pw::channel::AnyChannel) == 2 * sizeof(void*));
49 
50 static_assert((kReliable < kReadable) && (kReadable < kWritable) &&
51               (kWritable < kSeekable));
52 
53 class ReliableByteReaderWriterStub
54     : public pw::channel::ByteChannelImpl<kReliable, kReadable, kWritable> {
55  private:
56   // Read functions
57 
DoPendRead(Context &)58   Poll<pw::Result<pw::multibuf::MultiBuf>> DoPendRead(Context&) override {
59     return Pending();
60   }
61 
62   // Write functions
63 
64   // Disable maybe-uninitialized: this check fails erroniously on Windows GCC.
65   PW_MODIFY_DIAGNOSTICS_PUSH();
66   PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
DoPendReadyToWrite(Context &)67   Poll<pw::Status> DoPendReadyToWrite(Context&) override { return Pending(); }
68   PW_MODIFY_DIAGNOSTICS_POP();
69 
DoPendAllocateWriteBuffer(Context &,size_t)70   Poll<std::optional<MultiBuf>> DoPendAllocateWriteBuffer(Context&,
71                                                           size_t) override {
72     return std::nullopt;
73   }
74 
DoStageWrite(pw::multibuf::MultiBuf &&)75   pw::Status DoStageWrite(pw::multibuf::MultiBuf&&) override {
76     return pw::Status::Unimplemented();
77   }
78 
DoPendWrite(Context &)79   Poll<pw::Status> DoPendWrite(Context&) override {
80     return Ready(pw::Status::Unimplemented());
81   }
82 
83   // Common functions
DoPendClose(Context &)84   Poll<pw::Status> DoPendClose(Context&) override { return pw::OkStatus(); }
85 };
86 
87 class ReadOnlyStub : public pw::channel::Implement<pw::channel::ByteReader> {
88  public:
89   constexpr ReadOnlyStub() = default;
90 
91  private:
92   // Read functions
DoPendRead(Context &)93   Poll<pw::Result<pw::multibuf::MultiBuf>> DoPendRead(Context&) override {
94     return Pending();
95   }
96 
DoPendClose(Context &)97   Poll<pw::Status> DoPendClose(Context&) override { return pw::OkStatus(); }
98 };
99 
100 class WriteOnlyStub : public pw::channel::Implement<pw::channel::ByteWriter> {
101  private:
102   // Write functions
103 
DoPendReadyToWrite(Context &)104   Poll<pw::Status> DoPendReadyToWrite(Context&) override { return Pending(); }
105 
DoPendAllocateWriteBuffer(Context &,size_t)106   Poll<std::optional<MultiBuf>> DoPendAllocateWriteBuffer(Context&,
107                                                           size_t) override {
108     return std::nullopt;
109   }
110 
DoStageWrite(pw::multibuf::MultiBuf &&)111   pw::Status DoStageWrite(pw::multibuf::MultiBuf&&) override {
112     return pw::Status::Unimplemented();
113   }
114 
DoPendWrite(Context &)115   Poll<pw::Status> DoPendWrite(Context&) override {
116     return Ready(pw::Status::Unimplemented());
117   }
118 
119   // Common functions
DoPendClose(Context &)120   Poll<pw::Status> DoPendClose(Context&) override { return pw::OkStatus(); }
121 };
122 
TEST(Channel,MethodsShortCircuitAfterCloseReturnsReady)123 TEST(Channel, MethodsShortCircuitAfterCloseReturnsReady) {
124   Dispatcher dispatcher;
125 
126   class : public Task {
127    public:
128     ReliableByteReaderWriterStub channel;
129 
130    private:
131     Poll<> DoPend(Context& cx) override {
132       EXPECT_TRUE(channel.is_read_open());
133       EXPECT_TRUE(channel.is_write_open());
134       EXPECT_EQ(Ready(pw::OkStatus()), channel.PendClose(cx));
135       EXPECT_FALSE(channel.is_read_open());
136       EXPECT_FALSE(channel.is_write_open());
137 
138       EXPECT_EQ(pw::Status::FailedPrecondition(),
139                 channel.PendRead(cx)->status());
140       EXPECT_EQ(Ready(pw::Status::FailedPrecondition()),
141                 channel.PendReadyToWrite(cx));
142       EXPECT_EQ(Ready(pw::Status::FailedPrecondition()), channel.PendWrite(cx));
143       EXPECT_EQ(Ready(pw::Status::FailedPrecondition()), channel.PendClose(cx));
144 
145       return Ready();
146     }
147   } test_task;
148   dispatcher.Post(test_task);
149 
150   EXPECT_TRUE(dispatcher.RunUntilStalled().IsReady());
151 }
152 
TEST(Channel,ReadOnlyChannelOnlyOpenForReads)153 TEST(Channel, ReadOnlyChannelOnlyOpenForReads) {
154   ReadOnlyStub read_only;
155 
156   EXPECT_TRUE(read_only.readable());
157   EXPECT_TRUE(read_only.is_read_open());
158   EXPECT_FALSE(read_only.is_write_open());
159 }
160 
TEST(Channel,WriteOnlyChannelOnlyOpenForWrites)161 TEST(Channel, WriteOnlyChannelOnlyOpenForWrites) {
162   WriteOnlyStub write_only;
163 
164   EXPECT_FALSE(write_only.readable());
165   EXPECT_FALSE(write_only.is_read_open());
166   EXPECT_TRUE(write_only.is_write_open());
167 }
168 
169 #if PW_NC_TEST(ChannelInvalidOrdering)
170 PW_NC_EXPECT("Properties must be specified in the following order");
Illegal(pw::channel::ByteChannel<kReadable,pw::channel::kReliable> & foo)171 bool Illegal(pw::channel::ByteChannel<kReadable, pw::channel::kReliable>& foo) {
172   return foo.is_read_open();
173 }
174 #elif PW_NC_TEST(ChannelImplInvalidOrdering)
175 PW_NC_EXPECT("Properties must be specified in the following order");
176 class BadChannel
177     : public pw::channel::ByteChannelImpl<kReadable, pw::channel::kReliable> {};
178 #elif PW_NC_TEST(ChannelNoProperties)
179 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
Illegal(pw::channel::ByteChannel<> & foo)180 bool Illegal(pw::channel::ByteChannel<>& foo) { return foo.is_read_open(); }
181 #elif PW_NC_TEST(ChannelImplNoProperties)
182 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
183 class NoChannel : public pw::channel::ByteChannelImpl<> {};
184 #elif PW_NC_TEST(ChannelNoReadOrWrite)
185 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
Illegal(pw::channel::ByteChannel<pw::channel::kReliable> & foo)186 bool Illegal(pw::channel::ByteChannel<pw::channel::kReliable>& foo) {
187   return foo.is_read_open();
188 }
189 #elif PW_NC_TEST(ChannelImplNoReadOrWrite)
190 PW_NC_EXPECT("At least one of kReadable or kWritable must be provided");
191 class BadChannel : public pw::channel::ByteChannelImpl<pw::channel::kReliable> {
192 };
193 #elif PW_NC_TEST(TooMany)
194 PW_NC_EXPECT("Too many properties given");
Illegal(pw::channel::ByteChannel<kReliable,kReliable,kReliable,kReadable,kWritable> & foo)195 bool Illegal(
196     pw::channel::
197         ByteChannel<kReliable, kReliable, kReliable, kReadable, kWritable>&
198             foo) {
199   return foo.is_read_open();
200 }
201 #elif PW_NC_TEST(Duplicates)
202 PW_NC_EXPECT("duplicates");
Illegal(pw::channel::ByteChannel<kReadable,kReadable> & foo)203 bool Illegal(pw::channel::ByteChannel<kReadable, kReadable>& foo) {
204   return foo.is_read_open();
205 }
206 #endif  // PW_NC_TEST
207 
208 class TestByteReader
209     : public pw::channel::ByteChannelImpl<kReliable, kReadable> {
210  public:
TestByteReader()211   TestByteReader() {}
212 
PushData(MultiBuf data)213   void PushData(MultiBuf data) {
214     bool was_empty = data_.empty();
215     data_.PushSuffix(std::move(data));
216     if (was_empty) {
217       std::move(read_waker_).Wake();
218     }
219   }
220 
221  private:
DoPendRead(Context & cx)222   Poll<pw::Result<MultiBuf>> DoPendRead(Context& cx) override {
223     if (data_.empty()) {
224       PW_ASYNC_STORE_WAKER(
225           cx, read_waker_, "TestByteReader is waiting for a call to PushData");
226       return Pending();
227     }
228     return std::move(data_);
229   }
230 
DoPendClose(Context &)231   Poll<pw::Status> DoPendClose(Context&) override {
232     return Ready(pw::OkStatus());
233   }
234 
235   Waker read_waker_;
236   MultiBuf data_;
237 };
238 
239 class TestDatagramWriter : public pw::channel::Implement<DatagramWriter> {
240  public:
TestDatagramWriter(MultiBufAllocator & alloc)241   TestDatagramWriter(MultiBufAllocator& alloc) : alloc_fut_(alloc) {}
242 
last_datagram() const243   const pw::multibuf::MultiBuf& last_datagram() const { return last_dgram_; }
244 
MakeReadyToWrite()245   void MakeReadyToWrite() {
246     PW_CHECK_INT_EQ(
247         state_,
248         kUnavailable,
249         "Can't make writable when write is pending or already writable");
250 
251     state_ = kReadyToWrite;
252     std::move(waker_).Wake();
253   }
254 
MakeReadyToFlush()255   void MakeReadyToFlush() {
256     PW_CHECK_INT_EQ(state_,
257                     kWritePending,
258                     "Can't make flushable unless a write is pending");
259 
260     state_ = kReadyToFlush;
261     std::move(waker_).Wake();
262   }
263 
264  private:
DoPendReadyToWrite(Context & cx)265   Poll<pw::Status> DoPendReadyToWrite(Context& cx) override {
266     if (state_ == kReadyToWrite) {
267       return Ready(pw::OkStatus());
268     }
269 
270     PW_ASYNC_STORE_WAKER(
271         cx,
272         waker_,
273         "TestDatagramWriter waiting for a call to MakeReadyToWrite");
274     return Pending();
275   }
276 
DoStageWrite(MultiBuf && buffer)277   pw::Status DoStageWrite(MultiBuf&& buffer) override {
278     if (state_ != kReadyToWrite) {
279       return pw::Status::Unavailable();
280     }
281 
282     state_ = kWritePending;
283     last_dgram_ = std::move(buffer);
284     return pw::OkStatus();
285   }
286 
DoPendAllocateWriteBuffer(Context & cx,size_t min_bytes)287   Poll<std::optional<MultiBuf>> DoPendAllocateWriteBuffer(
288       Context& cx, size_t min_bytes) override {
289     alloc_fut_.SetDesiredSize(min_bytes);
290     return alloc_fut_.Pend(cx);
291   }
292 
DoPendWrite(Context & cx)293   Poll<pw::Status> DoPendWrite(Context& cx) override {
294     if (state_ != kReadyToFlush) {
295       PW_ASYNC_STORE_WAKER(
296           cx, waker_, "TestDatagramWriter is waiting for its Channel to flush");
297       return Pending();
298     }
299     last_flush_ = last_write_;
300     return pw::OkStatus();
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   MultiBufAllocationFuture alloc_fut_;
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   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 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   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 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           Poll<std::optional<MultiBuf>> buffer =
397               channel_.PendAllocateWriteBuffer(cx, sizeof(kWriteData));
398           if (buffer.IsPending()) {
399             return Pending();
400           }
401           if (!buffer->has_value()) {
402             // Allocator should have enough space for `kWriteData`.
403             ADD_FAILURE();
404             return Ready();
405           }
406           pw::ConstByteSpan str(pw::as_bytes(pw::span(kWriteData)));
407           std::copy(str.begin(), str.end(), (**buffer).begin());
408           pw::Status write_status = channel_.StageWrite(std::move(**buffer));
409           PW_CHECK_OK(write_status);
410           state_ = kFlushPacket;
411           [[fallthrough]];
412         }
413         case kFlushPacket: {
414           auto result = channel_.PendWrite(cx);
415           if (result.IsPending()) {
416             return Pending();
417           }
418           test_executed += 1;
419           state_ = kWaitUntilReady;
420           return Ready();
421         }
422         default:
423           PW_CRASH("Illegal value");
424       }
425 
426       // This test is INCOMPLETE.
427 
428       test_executed += 1;
429       return Ready();
430     }
431 
432     enum { kWaitUntilReady, kFlushPacket } state_ = kWaitUntilReady;
433     DatagramWriter& channel_;
434   };
435 
436   SendWriteDataAndFlush test_task(write_channel.channel(), 24601);
437   dispatcher.Post(test_task);
438 
439   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
440   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
441 
442   write_channel.MakeReadyToWrite();
443 
444   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
445   EXPECT_EQ(dispatcher.RunUntilStalled(), Pending());
446 
447   write_channel.MakeReadyToFlush();
448 
449   EXPECT_EQ(dispatcher.RunUntilStalled(), Ready());
450   EXPECT_EQ(test_task.test_executed, 1);
451 
452   std::byte contents[64] = {};
453   const MultiBuf& dgram = write_channel.last_datagram();
454   std::copy(dgram.begin(), dgram.end(), contents);
455   EXPECT_STREQ(reinterpret_cast<const char*>(contents), kWriteData);
456 }
457 
TakesAChannel(const pw::channel::AnyChannel &)458 void TakesAChannel(const pw::channel::AnyChannel&) {}
459 
TakesAReadableByteChannel(const pw::channel::ByteChannel<kReadable> & channel)460 const pw::channel::ByteChannel<kReadable>& TakesAReadableByteChannel(
461     const pw::channel::ByteChannel<kReadable>& channel) {
462   return channel;
463 }
464 
TakesAWritableByteChannel(const pw::channel::ByteChannel<kWritable> &)465 void TakesAWritableByteChannel(const pw::channel::ByteChannel<kWritable>&) {}
466 
TEST(Channel,Conversions)467 TEST(Channel, Conversions) {
468   static constexpr size_t kArbitraryDataSize = 128;
469   static constexpr size_t kArbitraryMetaSize = 128;
470   std::array<std::byte, kArbitraryDataSize> data_area;
471   AllocatorForTest<kArbitraryMetaSize> meta_alloc;
472   SimpleAllocator simple_allocator(data_area, meta_alloc);
473 
474   const TestByteReader byte_channel;
475   const TestDatagramWriter datagram_channel(simple_allocator);
476 
477   TakesAReadableByteChannel(byte_channel.channel());
478 
479   TakesAReadableByteChannel(byte_channel.as<kReadable>());
480   TakesAReadableByteChannel(byte_channel.channel().as<kReadable>());
481 
482   TakesAReadableByteChannel(byte_channel.as<pw::channel::ByteReader>());
483   TakesAReadableByteChannel(
484       byte_channel.channel().as<pw::channel::ByteReader>());
485 
486   TakesAReadableByteChannel(
487       byte_channel.as<pw::channel::ByteChannel<kReliable, kReadable>>());
488   TakesAReadableByteChannel(
489       byte_channel.channel()
490           .as<pw::channel::ByteChannel<kReliable, kReadable>>());
491 
492   TakesAChannel(byte_channel);
493   TakesAChannel(byte_channel.as<pw::channel::AnyChannel>());
494 
495   TakesAWritableByteChannel(datagram_channel.IgnoreDatagramBoundaries());
496   TakesAWritableByteChannel(
497       datagram_channel.channel().IgnoreDatagramBoundaries());
498 
499   [[maybe_unused]] const pw::channel::AnyChannel& plain = byte_channel;
500 
501 #if PW_NC_TEST(CannotImplicitlyLoseWritability)
502   PW_NC_EXPECT("no matching function for call");
503   TakesAWritableByteChannel(byte_channel.channel());
504 #elif PW_NC_TEST(CannotExplicitlyLoseWritability)
505   PW_NC_EXPECT("Cannot use a non-writable channel as a writable channel");
506   TakesAWritableByteChannel(byte_channel.as<kWritable>());
507 #elif PW_NC_TEST(CannotIgnoreDatagramBoundariesOnByteChannel)
508   PW_NC_EXPECT("only be called to use a datagram channel to a byte channel");
509   std::ignore = byte_channel.IgnoreDatagramBoundaries();
510 #elif PW_NC_TEST(CannotIgnoreDatagramBoundariesOnByteChannelImpl)
511   PW_NC_EXPECT("only be called to use a datagram channel to a byte channel");
512   std::ignore = byte_channel.channel().IgnoreDatagramBoundaries();
513 #endif  // PW_NC_TEST
514 }
515 
516 #if PW_NC_TEST(CannotImplicitlyUseDatagramChannelAsByteChannel)
517 PW_NC_EXPECT("no matching function for call");
DatagramChannelNcTest(pw::channel::DatagramChannel<kReliable,kReadable> & dgram)518 void DatagramChannelNcTest(
519     pw::channel::DatagramChannel<kReliable, kReadable>& dgram) {
520   TakesAReadableByteChannel(dgram);
521 }
522 #elif PW_NC_TEST(CannotExplicitlyUseDatagramChannelAsByteChannel)
523 PW_NC_EXPECT("Datagram and byte channels are not interchangeable");
DatagramChannelNcTest(pw::channel::DatagramChannel<kReliable,kReadable> & dgram)524 void DatagramChannelNcTest(
525     pw::channel::DatagramChannel<kReliable, kReadable>& dgram) {
526   TakesAReadableByteChannel(dgram.as<pw::channel::ByteChannel<kReadable>>());
527 }
528 #endif  // PW_NC_TEST
529 
530 class Foo {
531  public:
Foo(pw::channel::ByteChannel<kReadable> &)532   Foo(pw::channel::ByteChannel<kReadable>&) {}
533   Foo(const Foo&) = default;
534 };
535 
536 // Define additional overloads to ensure the right overload is selected with the
537 // implicit conversion.
TakesAReadableByteChannel(const Foo &)538 [[maybe_unused]] void TakesAReadableByteChannel(const Foo&) {}
TakesAReadableByteChannel(int)539 [[maybe_unused]] void TakesAReadableByteChannel(int) {}
TakesAReadableByteChannel(const pw::channel::DatagramReaderWriter &)540 [[maybe_unused]] void TakesAReadableByteChannel(
541     const pw::channel::DatagramReaderWriter&) {}
542 
TEST(Channel,SelectsCorrectOverloadWhenRelyingOnImplicitConversion)543 TEST(Channel, SelectsCorrectOverloadWhenRelyingOnImplicitConversion) {
544   TestByteReader byte_channel;
545 
546   [[maybe_unused]] Foo selects_channel_ctor_not_copy_ctor(
547       byte_channel.channel());
548   EXPECT_EQ(&byte_channel.as<pw::channel::ByteChannel<kReadable>>(),
549             &TakesAReadableByteChannel(byte_channel.channel()));
550 }
551 
552 #if PW_NC_TEST(CannotCallUnsupportedWriteMethodsOnChannel)
553 PW_NC_EXPECT("PendReadyToWrite may only be called on writable channels");
Bad(Context & cx,pw::channel::DatagramReader & c)554 [[maybe_unused]] void Bad(Context& cx, pw::channel::DatagramReader& c) {
555   std::ignore = c.PendReadyToWrite(cx);
556 }
557 #elif PW_NC_TEST(CannotCallUnsupportedWriteMethodsOnChannelImpl)
558 PW_NC_EXPECT("PendReadyToWrite may only be called on writable channels");
Bad(Context & cx,pw::channel::ByteReaderImpl & c)559 [[maybe_unused]] void Bad(Context& cx, pw::channel::ByteReaderImpl& c) {
560   std::ignore = c.PendReadyToWrite(cx);
561 }
562 #elif PW_NC_TEST(CannotCallUnsupportedReadMethodsOnChannel)
563 PW_NC_EXPECT("PendRead may only be called on readable channels");
Bad(Context & cx,pw::channel::ByteWriter & c)564 [[maybe_unused]] void Bad(Context& cx, pw::channel::ByteWriter& c) {
565   std::ignore = c.PendRead(cx);
566 }
567 #elif PW_NC_TEST(CannotCallUnsupportedReadMethodsOnChannelImpl)
568 PW_NC_EXPECT("PendRead may only be called on readable channels");
Bad(Context & cx,pw::channel::DatagramWriterImpl & c)569 [[maybe_unused]] void Bad(Context& cx, pw::channel::DatagramWriterImpl& c) {
570   std::ignore = c.PendRead(cx);
571 }
572 #endif  // PW_NC_TEST
573 
574 }  // namespace
575