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