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