xref: /aosp_15_r20/external/pigweed/pw_uart/blocking_adapter_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_uart/blocking_adapter.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <array>
18*61c4878aSAndroid Build Coastguard Worker #include <memory>
19*61c4878aSAndroid Build Coastguard Worker #include <mutex>
20*61c4878aSAndroid Build Coastguard Worker #include <optional>
21*61c4878aSAndroid Build Coastguard Worker #include <utility>
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/array.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/lock_annotations.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/mutex.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_sync/timed_thread_notification.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/test_thread_context.h"
30*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
31*61c4878aSAndroid Build Coastguard Worker #include "pw_work_queue/work_queue.h"
32*61c4878aSAndroid Build Coastguard Worker 
33*61c4878aSAndroid Build Coastguard Worker // Waits for something critical for test execution.
34*61c4878aSAndroid Build Coastguard Worker // We use PW_CHECK to ensure we crash on timeout instead of hanging forever.
35*61c4878aSAndroid Build Coastguard Worker // This is a macro so the crash points to the invocation site.
36*61c4878aSAndroid Build Coastguard Worker #define ASSERT_WAIT(waitable) PW_CHECK(waitable.try_acquire_for(1000ms))
37*61c4878aSAndroid Build Coastguard Worker 
38*61c4878aSAndroid Build Coastguard Worker namespace pw::uart {
39*61c4878aSAndroid Build Coastguard Worker namespace {
40*61c4878aSAndroid Build Coastguard Worker 
41*61c4878aSAndroid Build Coastguard Worker using namespace std::chrono_literals;
42*61c4878aSAndroid Build Coastguard Worker 
43*61c4878aSAndroid Build Coastguard Worker // A mock UartNonBlocking for testing the blocking adapter.
44*61c4878aSAndroid Build Coastguard Worker class UartNonBlockingMock : public UartNonBlocking {
45*61c4878aSAndroid Build Coastguard Worker  public:
enabled() const46*61c4878aSAndroid Build Coastguard Worker   bool enabled() const { return enabled_; }
47*61c4878aSAndroid Build Coastguard Worker 
WaitAndCompleteRead(Status status,ConstByteSpan data)48*61c4878aSAndroid Build Coastguard Worker   void WaitAndCompleteRead(Status status, ConstByteSpan data) {
49*61c4878aSAndroid Build Coastguard Worker     // Wait for a read to start.
50*61c4878aSAndroid Build Coastguard Worker     ASSERT_WAIT(read_started_);
51*61c4878aSAndroid Build Coastguard Worker 
52*61c4878aSAndroid Build Coastguard Worker     std::optional<ReadTransaction> read = ConsumeCurrentRead();
53*61c4878aSAndroid Build Coastguard Worker     PW_CHECK(read.has_value());
54*61c4878aSAndroid Build Coastguard Worker 
55*61c4878aSAndroid Build Coastguard Worker     // Copy data into rx buffer;
56*61c4878aSAndroid Build Coastguard Worker     PW_CHECK_UINT_GE(read->rx_buffer.size(), data.size());
57*61c4878aSAndroid Build Coastguard Worker     std::copy(data.begin(), data.end(), read->rx_buffer.begin());
58*61c4878aSAndroid Build Coastguard Worker 
59*61c4878aSAndroid Build Coastguard Worker     read->Complete(status, data.size());
60*61c4878aSAndroid Build Coastguard Worker   }
61*61c4878aSAndroid Build Coastguard Worker 
WaitForWrite()62*61c4878aSAndroid Build Coastguard Worker   ConstByteSpan WaitForWrite() PW_LOCKS_EXCLUDED(mutex_) {
63*61c4878aSAndroid Build Coastguard Worker     // Wait for a write to start.
64*61c4878aSAndroid Build Coastguard Worker     ASSERT_WAIT(write_started_);
65*61c4878aSAndroid Build Coastguard Worker 
66*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(mutex_);
67*61c4878aSAndroid Build Coastguard Worker     PW_CHECK(current_write_.has_value());
68*61c4878aSAndroid Build Coastguard Worker     return current_write_->tx_buffer;
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker 
CompleteWrite(StatusWithSize status_size)71*61c4878aSAndroid Build Coastguard Worker   void CompleteWrite(StatusWithSize status_size) {
72*61c4878aSAndroid Build Coastguard Worker     std::optional<WriteTransaction> write = ConsumeCurrentWrite();
73*61c4878aSAndroid Build Coastguard Worker     PW_CHECK(write.has_value());
74*61c4878aSAndroid Build Coastguard Worker     write->Complete(status_size);
75*61c4878aSAndroid Build Coastguard Worker   }
76*61c4878aSAndroid Build Coastguard Worker 
WaitAndCompleteFlush(Status status)77*61c4878aSAndroid Build Coastguard Worker   void WaitAndCompleteFlush(Status status) {
78*61c4878aSAndroid Build Coastguard Worker     // Wait for a flush to start.
79*61c4878aSAndroid Build Coastguard Worker     ASSERT_WAIT(flush_started_);
80*61c4878aSAndroid Build Coastguard Worker 
81*61c4878aSAndroid Build Coastguard Worker     std::optional<FlushTransaction> flush = ConsumeCurrentFlush();
82*61c4878aSAndroid Build Coastguard Worker     PW_CHECK(flush.has_value());
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker     flush->Complete(status);
85*61c4878aSAndroid Build Coastguard Worker   }
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker  private:
88*61c4878aSAndroid Build Coastguard Worker   sync::Mutex mutex_;
89*61c4878aSAndroid Build Coastguard Worker   bool enabled_ = false;
90*61c4878aSAndroid Build Coastguard Worker 
91*61c4878aSAndroid Build Coastguard Worker   //
92*61c4878aSAndroid Build Coastguard Worker   // UartNonBlocking impl.
93*61c4878aSAndroid Build Coastguard Worker   //
DoEnable(bool enabled)94*61c4878aSAndroid Build Coastguard Worker   Status DoEnable(bool enabled) override {
95*61c4878aSAndroid Build Coastguard Worker     enabled_ = enabled;
96*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
97*61c4878aSAndroid Build Coastguard Worker   }
98*61c4878aSAndroid Build Coastguard Worker 
DoSetBaudRate(uint32_t)99*61c4878aSAndroid Build Coastguard Worker   Status DoSetBaudRate(uint32_t) override { return OkStatus(); }
DoConservativeReadAvailable()100*61c4878aSAndroid Build Coastguard Worker   size_t DoConservativeReadAvailable() override { return 0; }
DoClearPendingReceiveBytes()101*61c4878aSAndroid Build Coastguard Worker   Status DoClearPendingReceiveBytes() override { return OkStatus(); }
102*61c4878aSAndroid Build Coastguard Worker 
103*61c4878aSAndroid Build Coastguard Worker   // Read
104*61c4878aSAndroid Build Coastguard Worker   struct ReadTransaction {
105*61c4878aSAndroid Build Coastguard Worker     ByteSpan rx_buffer;
106*61c4878aSAndroid Build Coastguard Worker     size_t min_bytes;
107*61c4878aSAndroid Build Coastguard Worker     Function<void(Status, ConstByteSpan buffer)> callback;
108*61c4878aSAndroid Build Coastguard Worker 
Completepw::uart::__anonea452bc80111::UartNonBlockingMock::ReadTransaction109*61c4878aSAndroid Build Coastguard Worker     void Complete(Status status, size_t num_bytes) {
110*61c4878aSAndroid Build Coastguard Worker       callback(status, rx_buffer.first(num_bytes));
111*61c4878aSAndroid Build Coastguard Worker     }
112*61c4878aSAndroid Build Coastguard Worker   };
113*61c4878aSAndroid Build Coastguard Worker   std::optional<ReadTransaction> current_read_ PW_GUARDED_BY(mutex_);
114*61c4878aSAndroid Build Coastguard Worker   sync::TimedThreadNotification read_started_;
115*61c4878aSAndroid Build Coastguard Worker 
ConsumeCurrentRead()116*61c4878aSAndroid Build Coastguard Worker   std::optional<ReadTransaction> ConsumeCurrentRead()
117*61c4878aSAndroid Build Coastguard Worker       PW_LOCKS_EXCLUDED(mutex_) {
118*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(mutex_);
119*61c4878aSAndroid Build Coastguard Worker     return std::exchange(current_read_, std::nullopt);
120*61c4878aSAndroid Build Coastguard Worker   }
121*61c4878aSAndroid Build Coastguard Worker 
DoRead(ByteSpan rx_buffer,size_t min_bytes,Function<void (Status,ConstByteSpan buffer)> && callback)122*61c4878aSAndroid Build Coastguard Worker   Status DoRead(ByteSpan rx_buffer,
123*61c4878aSAndroid Build Coastguard Worker                 size_t min_bytes,
124*61c4878aSAndroid Build Coastguard Worker                 Function<void(Status, ConstByteSpan buffer)>&& callback)
125*61c4878aSAndroid Build Coastguard Worker       override PW_LOCKS_EXCLUDED(mutex_) {
126*61c4878aSAndroid Build Coastguard Worker     {
127*61c4878aSAndroid Build Coastguard Worker       std::lock_guard lock(mutex_);
128*61c4878aSAndroid Build Coastguard Worker 
129*61c4878aSAndroid Build Coastguard Worker       if (current_read_) {
130*61c4878aSAndroid Build Coastguard Worker         return Status::Unavailable();
131*61c4878aSAndroid Build Coastguard Worker       }
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker       current_read_.emplace(ReadTransaction{
134*61c4878aSAndroid Build Coastguard Worker           .rx_buffer = rx_buffer,
135*61c4878aSAndroid Build Coastguard Worker           .min_bytes = min_bytes,
136*61c4878aSAndroid Build Coastguard Worker           .callback = std::move(callback),
137*61c4878aSAndroid Build Coastguard Worker       });
138*61c4878aSAndroid Build Coastguard Worker     }
139*61c4878aSAndroid Build Coastguard Worker 
140*61c4878aSAndroid Build Coastguard Worker     read_started_.release();
141*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
142*61c4878aSAndroid Build Coastguard Worker   }
143*61c4878aSAndroid Build Coastguard Worker 
DoCancelRead()144*61c4878aSAndroid Build Coastguard Worker   bool DoCancelRead() override {
145*61c4878aSAndroid Build Coastguard Worker     std::optional<ReadTransaction> read = ConsumeCurrentRead();
146*61c4878aSAndroid Build Coastguard Worker     if (!read.has_value()) {
147*61c4878aSAndroid Build Coastguard Worker       return false;
148*61c4878aSAndroid Build Coastguard Worker     }
149*61c4878aSAndroid Build Coastguard Worker     read->Complete(Status::Cancelled(), 0);
150*61c4878aSAndroid Build Coastguard Worker     return true;
151*61c4878aSAndroid Build Coastguard Worker   }
152*61c4878aSAndroid Build Coastguard Worker 
153*61c4878aSAndroid Build Coastguard Worker   // Write
154*61c4878aSAndroid Build Coastguard Worker   struct WriteTransaction {
155*61c4878aSAndroid Build Coastguard Worker     ConstByteSpan tx_buffer;
156*61c4878aSAndroid Build Coastguard Worker     Function<void(StatusWithSize)> callback;
157*61c4878aSAndroid Build Coastguard Worker 
Completepw::uart::__anonea452bc80111::UartNonBlockingMock::WriteTransaction158*61c4878aSAndroid Build Coastguard Worker     void Complete(StatusWithSize status_size) { callback(status_size); }
159*61c4878aSAndroid Build Coastguard Worker   };
160*61c4878aSAndroid Build Coastguard Worker   std::optional<WriteTransaction> current_write_ PW_GUARDED_BY(mutex_);
161*61c4878aSAndroid Build Coastguard Worker   sync::TimedThreadNotification write_started_;
162*61c4878aSAndroid Build Coastguard Worker 
ConsumeCurrentWrite()163*61c4878aSAndroid Build Coastguard Worker   std::optional<WriteTransaction> ConsumeCurrentWrite()
164*61c4878aSAndroid Build Coastguard Worker       PW_LOCKS_EXCLUDED(mutex_) {
165*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(mutex_);
166*61c4878aSAndroid Build Coastguard Worker     return std::exchange(current_write_, std::nullopt);
167*61c4878aSAndroid Build Coastguard Worker   }
168*61c4878aSAndroid Build Coastguard Worker 
DoWrite(ConstByteSpan tx_buffer,Function<void (StatusWithSize status)> && callback)169*61c4878aSAndroid Build Coastguard Worker   Status DoWrite(ConstByteSpan tx_buffer,
170*61c4878aSAndroid Build Coastguard Worker                  Function<void(StatusWithSize status)>&& callback) override
171*61c4878aSAndroid Build Coastguard Worker       PW_LOCKS_EXCLUDED(mutex_) {
172*61c4878aSAndroid Build Coastguard Worker     {
173*61c4878aSAndroid Build Coastguard Worker       std::lock_guard lock(mutex_);
174*61c4878aSAndroid Build Coastguard Worker 
175*61c4878aSAndroid Build Coastguard Worker       if (current_write_) {
176*61c4878aSAndroid Build Coastguard Worker         return Status::Unavailable();
177*61c4878aSAndroid Build Coastguard Worker       }
178*61c4878aSAndroid Build Coastguard Worker 
179*61c4878aSAndroid Build Coastguard Worker       current_write_.emplace(WriteTransaction{
180*61c4878aSAndroid Build Coastguard Worker           .tx_buffer = tx_buffer,
181*61c4878aSAndroid Build Coastguard Worker           .callback = std::move(callback),
182*61c4878aSAndroid Build Coastguard Worker       });
183*61c4878aSAndroid Build Coastguard Worker     }
184*61c4878aSAndroid Build Coastguard Worker 
185*61c4878aSAndroid Build Coastguard Worker     write_started_.release();
186*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
187*61c4878aSAndroid Build Coastguard Worker   }
188*61c4878aSAndroid Build Coastguard Worker 
DoCancelWrite()189*61c4878aSAndroid Build Coastguard Worker   bool DoCancelWrite() override {
190*61c4878aSAndroid Build Coastguard Worker     std::optional<WriteTransaction> write = ConsumeCurrentWrite();
191*61c4878aSAndroid Build Coastguard Worker     if (!write.has_value()) {
192*61c4878aSAndroid Build Coastguard Worker       return false;
193*61c4878aSAndroid Build Coastguard Worker     }
194*61c4878aSAndroid Build Coastguard Worker     write->Complete(StatusWithSize::Cancelled(0));
195*61c4878aSAndroid Build Coastguard Worker     return true;
196*61c4878aSAndroid Build Coastguard Worker   }
197*61c4878aSAndroid Build Coastguard Worker 
198*61c4878aSAndroid Build Coastguard Worker   // Flush
199*61c4878aSAndroid Build Coastguard Worker   struct FlushTransaction {
200*61c4878aSAndroid Build Coastguard Worker     Function<void(Status)> callback;
201*61c4878aSAndroid Build Coastguard Worker 
Completepw::uart::__anonea452bc80111::UartNonBlockingMock::FlushTransaction202*61c4878aSAndroid Build Coastguard Worker     void Complete(Status status) { callback(status); }
203*61c4878aSAndroid Build Coastguard Worker   };
204*61c4878aSAndroid Build Coastguard Worker   std::optional<FlushTransaction> current_flush_ PW_GUARDED_BY(mutex_);
205*61c4878aSAndroid Build Coastguard Worker   sync::TimedThreadNotification flush_started_;
206*61c4878aSAndroid Build Coastguard Worker 
ConsumeCurrentFlush()207*61c4878aSAndroid Build Coastguard Worker   std::optional<FlushTransaction> ConsumeCurrentFlush()
208*61c4878aSAndroid Build Coastguard Worker       PW_LOCKS_EXCLUDED(mutex_) {
209*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(mutex_);
210*61c4878aSAndroid Build Coastguard Worker     return std::exchange(current_flush_, std::nullopt);
211*61c4878aSAndroid Build Coastguard Worker   }
212*61c4878aSAndroid Build Coastguard Worker 
DoFlushOutput(Function<void (Status status)> && callback)213*61c4878aSAndroid Build Coastguard Worker   Status DoFlushOutput(Function<void(Status status)>&& callback) override
214*61c4878aSAndroid Build Coastguard Worker       PW_LOCKS_EXCLUDED(mutex_) {
215*61c4878aSAndroid Build Coastguard Worker     {
216*61c4878aSAndroid Build Coastguard Worker       std::lock_guard lock(mutex_);
217*61c4878aSAndroid Build Coastguard Worker 
218*61c4878aSAndroid Build Coastguard Worker       if (current_flush_) {
219*61c4878aSAndroid Build Coastguard Worker         return Status::Unavailable();
220*61c4878aSAndroid Build Coastguard Worker       }
221*61c4878aSAndroid Build Coastguard Worker 
222*61c4878aSAndroid Build Coastguard Worker       current_flush_.emplace(FlushTransaction{
223*61c4878aSAndroid Build Coastguard Worker           .callback = std::move(callback),
224*61c4878aSAndroid Build Coastguard Worker       });
225*61c4878aSAndroid Build Coastguard Worker     }
226*61c4878aSAndroid Build Coastguard Worker 
227*61c4878aSAndroid Build Coastguard Worker     flush_started_.release();
228*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
229*61c4878aSAndroid Build Coastguard Worker   }
230*61c4878aSAndroid Build Coastguard Worker 
DoCancelFlushOutput()231*61c4878aSAndroid Build Coastguard Worker   bool DoCancelFlushOutput() override {
232*61c4878aSAndroid Build Coastguard Worker     std::optional<FlushTransaction> flush = ConsumeCurrentFlush();
233*61c4878aSAndroid Build Coastguard Worker     if (!flush.has_value()) {
234*61c4878aSAndroid Build Coastguard Worker       return false;
235*61c4878aSAndroid Build Coastguard Worker     }
236*61c4878aSAndroid Build Coastguard Worker     flush->Complete(Status::Cancelled());
237*61c4878aSAndroid Build Coastguard Worker     return true;
238*61c4878aSAndroid Build Coastguard Worker   }
239*61c4878aSAndroid Build Coastguard Worker };
240*61c4878aSAndroid Build Coastguard Worker 
241*61c4878aSAndroid Build Coastguard Worker // Test fixture
242*61c4878aSAndroid Build Coastguard Worker class BlockingAdapterTest : public ::testing::Test {
243*61c4878aSAndroid Build Coastguard Worker  protected:
BlockingAdapterTest()244*61c4878aSAndroid Build Coastguard Worker   BlockingAdapterTest() : adapter(underlying) {}
245*61c4878aSAndroid Build Coastguard Worker 
246*61c4878aSAndroid Build Coastguard Worker   UartNonBlockingMock underlying;
247*61c4878aSAndroid Build Coastguard Worker   UartBlockingAdapter adapter;
248*61c4878aSAndroid Build Coastguard Worker 
249*61c4878aSAndroid Build Coastguard Worker   work_queue::WorkQueueWithBuffer<2> work_queue;
250*61c4878aSAndroid Build Coastguard Worker 
251*61c4878aSAndroid Build Coastguard Worker   // State used by tests.
252*61c4878aSAndroid Build Coastguard Worker   // Ideally these would be locals, but that would require capturing more than
253*61c4878aSAndroid Build Coastguard Worker   // one pointer worth of data, exceeding PW_FUNCTION_INLINE_CALLABLE_SIZE.
254*61c4878aSAndroid Build Coastguard Worker   sync::TimedThreadNotification blocking_action_complete;
255*61c4878aSAndroid Build Coastguard Worker   static constexpr auto kReadBufferSize = 16;
256*61c4878aSAndroid Build Coastguard Worker   std::array<std::byte, kReadBufferSize> read_buffer;
257*61c4878aSAndroid Build Coastguard Worker   StatusWithSize read_result;
258*61c4878aSAndroid Build Coastguard Worker   Status write_result;
259*61c4878aSAndroid Build Coastguard Worker 
SetUp()260*61c4878aSAndroid Build Coastguard Worker   void SetUp() override { StartWorkQueueThread(); }
261*61c4878aSAndroid Build Coastguard Worker 
TearDown()262*61c4878aSAndroid Build Coastguard Worker   void TearDown() override { StopWorkQueueThread(); }
263*61c4878aSAndroid Build Coastguard Worker 
StartWorkQueueThread()264*61c4878aSAndroid Build Coastguard Worker   void StartWorkQueueThread() {
265*61c4878aSAndroid Build Coastguard Worker     PW_CHECK(!work_queue_thread_, "WorkQueue thread already started");
266*61c4878aSAndroid Build Coastguard Worker     work_queue_thread_context_ =
267*61c4878aSAndroid Build Coastguard Worker         std::make_unique<thread::test::TestThreadContext>();
268*61c4878aSAndroid Build Coastguard Worker     work_queue_thread_.emplace(work_queue_thread_context_->options(),
269*61c4878aSAndroid Build Coastguard Worker                                work_queue);
270*61c4878aSAndroid Build Coastguard Worker   }
271*61c4878aSAndroid Build Coastguard Worker 
StopWorkQueueThread()272*61c4878aSAndroid Build Coastguard Worker   void StopWorkQueueThread() {
273*61c4878aSAndroid Build Coastguard Worker     if (work_queue_thread_) {
274*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Stopping work queue...");
275*61c4878aSAndroid Build Coastguard Worker       work_queue.RequestStop();
276*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
277*61c4878aSAndroid Build Coastguard Worker       work_queue_thread_->join();
278*61c4878aSAndroid Build Coastguard Worker #else
279*61c4878aSAndroid Build Coastguard Worker       work_queue_thread_->detach();
280*61c4878aSAndroid Build Coastguard Worker #endif
281*61c4878aSAndroid Build Coastguard Worker       // Once stopped, the WorkQueue cannot be started again (stop_requested_
282*61c4878aSAndroid Build Coastguard Worker       // latches), so we don't set work_queue_thread_ to std::nullopt here.
283*61c4878aSAndroid Build Coastguard Worker       // work_queue_thread_ = std::nullopt;
284*61c4878aSAndroid Build Coastguard Worker     }
285*61c4878aSAndroid Build Coastguard Worker   }
286*61c4878aSAndroid Build Coastguard Worker 
287*61c4878aSAndroid Build Coastguard Worker  private:
288*61c4878aSAndroid Build Coastguard Worker   std::unique_ptr<thread::test::TestThreadContext> work_queue_thread_context_;
289*61c4878aSAndroid Build Coastguard Worker   std::optional<thread::Thread> work_queue_thread_;
290*61c4878aSAndroid Build Coastguard Worker };
291*61c4878aSAndroid Build Coastguard Worker 
292*61c4878aSAndroid Build Coastguard Worker //
293*61c4878aSAndroid Build Coastguard Worker // Enable
294*61c4878aSAndroid Build Coastguard Worker //
295*61c4878aSAndroid Build Coastguard Worker 
TEST_F(BlockingAdapterTest,EnableWorks)296*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, EnableWorks) {
297*61c4878aSAndroid Build Coastguard Worker   // Start out disabled
298*61c4878aSAndroid Build Coastguard Worker   ASSERT_FALSE(underlying.enabled());
299*61c4878aSAndroid Build Coastguard Worker 
300*61c4878aSAndroid Build Coastguard Worker   // Can enable
301*61c4878aSAndroid Build Coastguard Worker   PW_TEST_EXPECT_OK(adapter.Enable());
302*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(underlying.enabled());
303*61c4878aSAndroid Build Coastguard Worker }
304*61c4878aSAndroid Build Coastguard Worker 
TEST_F(BlockingAdapterTest,DisableWorks)305*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, DisableWorks) {
306*61c4878aSAndroid Build Coastguard Worker   // Start out enabled
307*61c4878aSAndroid Build Coastguard Worker   PW_TEST_ASSERT_OK(underlying.Enable());
308*61c4878aSAndroid Build Coastguard Worker   ASSERT_TRUE(underlying.enabled());
309*61c4878aSAndroid Build Coastguard Worker 
310*61c4878aSAndroid Build Coastguard Worker   // Can disable
311*61c4878aSAndroid Build Coastguard Worker   PW_TEST_EXPECT_OK(adapter.Disable());
312*61c4878aSAndroid Build Coastguard Worker   EXPECT_FALSE(underlying.enabled());
313*61c4878aSAndroid Build Coastguard Worker }
314*61c4878aSAndroid Build Coastguard Worker 
315*61c4878aSAndroid Build Coastguard Worker //
316*61c4878aSAndroid Build Coastguard Worker // Read
317*61c4878aSAndroid Build Coastguard Worker //
318*61c4878aSAndroid Build Coastguard Worker 
TEST_F(BlockingAdapterTest,ReadWorks)319*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, ReadWorks) {
320*61c4878aSAndroid Build Coastguard Worker   // Call blocking ReadExactly on the work queue.
321*61c4878aSAndroid Build Coastguard Worker   work_queue.CheckPushWork([this]() {
322*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Calling adapter.ReadExactly()...");
323*61c4878aSAndroid Build Coastguard Worker     read_result = adapter.ReadExactly(read_buffer);
324*61c4878aSAndroid Build Coastguard Worker     blocking_action_complete.release();
325*61c4878aSAndroid Build Coastguard Worker   });
326*61c4878aSAndroid Build Coastguard Worker 
327*61c4878aSAndroid Build Coastguard Worker   constexpr auto kRxData = bytes::Array<0x12, 0x34, 0x56>();
328*61c4878aSAndroid Build Coastguard Worker   static_assert(kRxData.size() <= kReadBufferSize);
329*61c4878aSAndroid Build Coastguard Worker 
330*61c4878aSAndroid Build Coastguard Worker   underlying.WaitAndCompleteRead(OkStatus(), kRxData);
331*61c4878aSAndroid Build Coastguard Worker 
332*61c4878aSAndroid Build Coastguard Worker   // Wait for the read to complete.
333*61c4878aSAndroid Build Coastguard Worker   ASSERT_WAIT(blocking_action_complete);
334*61c4878aSAndroid Build Coastguard Worker 
335*61c4878aSAndroid Build Coastguard Worker   PW_TEST_EXPECT_OK(read_result.status());
336*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(read_result.size(), kRxData.size());
337*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(std::equal(kRxData.begin(), kRxData.end(), read_buffer.begin()));
338*61c4878aSAndroid Build Coastguard Worker }
339*61c4878aSAndroid Build Coastguard Worker 
TEST_F(BlockingAdapterTest,ReadHandlesTimeouts)340*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, ReadHandlesTimeouts) {
341*61c4878aSAndroid Build Coastguard Worker   // Call blocking TryReadExactlyFor on the work queue.
342*61c4878aSAndroid Build Coastguard Worker   work_queue.CheckPushWork([this]() {
343*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Calling adapter.TryReadExactlyFor()...");
344*61c4878aSAndroid Build Coastguard Worker     read_result = adapter.TryReadExactlyFor(read_buffer, 100ms);
345*61c4878aSAndroid Build Coastguard Worker     blocking_action_complete.release();
346*61c4878aSAndroid Build Coastguard Worker   });
347*61c4878aSAndroid Build Coastguard Worker 
348*61c4878aSAndroid Build Coastguard Worker   // Don't complete the transaction; let it time out.
349*61c4878aSAndroid Build Coastguard Worker 
350*61c4878aSAndroid Build Coastguard Worker   // Wait for the read to complete.
351*61c4878aSAndroid Build Coastguard Worker   ASSERT_WAIT(blocking_action_complete);
352*61c4878aSAndroid Build Coastguard Worker 
353*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(read_result.status(), Status::DeadlineExceeded());
354*61c4878aSAndroid Build Coastguard Worker }
355*61c4878aSAndroid Build Coastguard Worker 
356*61c4878aSAndroid Build Coastguard Worker //
357*61c4878aSAndroid Build Coastguard Worker // Write
358*61c4878aSAndroid Build Coastguard Worker //
TEST_F(BlockingAdapterTest,WriteWorks)359*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, WriteWorks) {
360*61c4878aSAndroid Build Coastguard Worker   static constexpr auto kTxData = bytes::Array<0x12, 0x34, 0x56>();
361*61c4878aSAndroid Build Coastguard Worker 
362*61c4878aSAndroid Build Coastguard Worker   // Call blocking Write on the work queue.
363*61c4878aSAndroid Build Coastguard Worker   work_queue.CheckPushWork([this]() {
364*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Calling adapter.Write()...");
365*61c4878aSAndroid Build Coastguard Worker     write_result = adapter.Write(kTxData);
366*61c4878aSAndroid Build Coastguard Worker     blocking_action_complete.release();
367*61c4878aSAndroid Build Coastguard Worker   });
368*61c4878aSAndroid Build Coastguard Worker 
369*61c4878aSAndroid Build Coastguard Worker   ConstByteSpan tx_buffer = underlying.WaitForWrite();
370*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(tx_buffer.size(), kTxData.size());
371*61c4878aSAndroid Build Coastguard Worker   EXPECT_TRUE(std::equal(tx_buffer.begin(), tx_buffer.end(), kTxData.begin()));
372*61c4878aSAndroid Build Coastguard Worker 
373*61c4878aSAndroid Build Coastguard Worker   underlying.CompleteWrite(StatusWithSize(tx_buffer.size()));
374*61c4878aSAndroid Build Coastguard Worker 
375*61c4878aSAndroid Build Coastguard Worker   // Wait for the write to complete.
376*61c4878aSAndroid Build Coastguard Worker   ASSERT_WAIT(blocking_action_complete);
377*61c4878aSAndroid Build Coastguard Worker   PW_TEST_EXPECT_OK(write_result);
378*61c4878aSAndroid Build Coastguard Worker }
379*61c4878aSAndroid Build Coastguard Worker 
TEST_F(BlockingAdapterTest,WriteHandlesTimeouts)380*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, WriteHandlesTimeouts) {
381*61c4878aSAndroid Build Coastguard Worker   static constexpr auto kTxData = bytes::Array<0x12, 0x34, 0x56>();
382*61c4878aSAndroid Build Coastguard Worker 
383*61c4878aSAndroid Build Coastguard Worker   // Call blocking TryWriteFor on the work queue.
384*61c4878aSAndroid Build Coastguard Worker   work_queue.CheckPushWork([this]() {
385*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Calling adapter.TryWriteFor()...");
386*61c4878aSAndroid Build Coastguard Worker     write_result = adapter.TryWriteFor(kTxData, 100ms).status();
387*61c4878aSAndroid Build Coastguard Worker     blocking_action_complete.release();
388*61c4878aSAndroid Build Coastguard Worker   });
389*61c4878aSAndroid Build Coastguard Worker 
390*61c4878aSAndroid Build Coastguard Worker   // Don't complete the transaction; let it time out.
391*61c4878aSAndroid Build Coastguard Worker 
392*61c4878aSAndroid Build Coastguard Worker   // Wait for the write to complete.
393*61c4878aSAndroid Build Coastguard Worker   ASSERT_WAIT(blocking_action_complete);
394*61c4878aSAndroid Build Coastguard Worker   EXPECT_EQ(write_result, Status::DeadlineExceeded());
395*61c4878aSAndroid Build Coastguard Worker }
396*61c4878aSAndroid Build Coastguard Worker 
397*61c4878aSAndroid Build Coastguard Worker //
398*61c4878aSAndroid Build Coastguard Worker // FlushOutput
399*61c4878aSAndroid Build Coastguard Worker //
TEST_F(BlockingAdapterTest,FlushOutputWorks)400*61c4878aSAndroid Build Coastguard Worker TEST_F(BlockingAdapterTest, FlushOutputWorks) {
401*61c4878aSAndroid Build Coastguard Worker   // Call blocking FlushOutput on the work queue.
402*61c4878aSAndroid Build Coastguard Worker   work_queue.CheckPushWork([this]() {
403*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Calling adapter.FlushOutput()...");
404*61c4878aSAndroid Build Coastguard Worker     write_result = adapter.FlushOutput();
405*61c4878aSAndroid Build Coastguard Worker     blocking_action_complete.release();
406*61c4878aSAndroid Build Coastguard Worker   });
407*61c4878aSAndroid Build Coastguard Worker 
408*61c4878aSAndroid Build Coastguard Worker   underlying.WaitAndCompleteFlush(OkStatus());
409*61c4878aSAndroid Build Coastguard Worker 
410*61c4878aSAndroid Build Coastguard Worker   // Wait for the flush to complete.
411*61c4878aSAndroid Build Coastguard Worker   ASSERT_WAIT(blocking_action_complete);
412*61c4878aSAndroid Build Coastguard Worker   PW_TEST_EXPECT_OK(write_result);
413*61c4878aSAndroid Build Coastguard Worker }
414*61c4878aSAndroid Build Coastguard Worker 
415*61c4878aSAndroid Build Coastguard Worker // FlushOutput does not provide a variant with timeout.
416*61c4878aSAndroid Build Coastguard Worker 
417*61c4878aSAndroid Build Coastguard Worker }  // namespace
418*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::uart
419