1 // Copyright 2022 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 #pragma once 15 16 #include "pw_async/dispatcher.h" 17 #include "pw_async_backend/fake_dispatcher.h" // nogncheck 18 19 namespace pw::async::test { 20 21 /// `FakeDispatcher` is a `Dispatcher` implementation for use in unit tests. 22 /// 23 /// Threading: `FakeDispatcher` is *NOT* thread-safe and, unlike other 24 /// `Dispatcher` implementations. This means that tasks must not be posted from 25 /// multiple threads at once, and tasks cannot be posted from other threads 26 /// while the dispatcher is executing. 27 /// 28 /// Time: `FakeDispatcher` uses simulated time. `RunUntil()` and `RunFor()` 29 /// advance time immediately, and `now()` returns the current simulated time. 30 /// 31 /// To support various `Task` backends, `FakeDispatcher` wraps a 32 /// `backend::NativeFakeDispatcher` that implements standard `FakeDispatcher` 33 /// behavior using `backend::NativeTask` objects. 34 class FakeDispatcher final : public Dispatcher { 35 public: FakeDispatcher()36 FakeDispatcher() : native_dispatcher_(*this) {} 37 38 /// Execute all runnable tasks and return without advancing simulated time. 39 /// Returns true iff any tasks were invoked during the run. RunUntilIdle()40 bool RunUntilIdle() { return native_dispatcher_.RunUntilIdle(); } 41 42 /// Run the dispatcher until Now() has reached `end_time`, executing all tasks 43 /// that come due before then. 44 /// Returns true iff any tasks were invoked during the run. RunUntil(chrono::SystemClock::time_point end_time)45 bool RunUntil(chrono::SystemClock::time_point end_time) { 46 return native_dispatcher_.RunUntil(end_time); 47 } 48 49 /// Run the Dispatcher until `duration` has elapsed, executing all tasks that 50 /// come due in that period. 51 /// Returns true iff any tasks were invoked during the run. RunFor(chrono::SystemClock::duration duration)52 bool RunFor(chrono::SystemClock::duration duration) { 53 return native_dispatcher_.RunFor(duration); 54 } 55 56 /// Stop processing tasks. After calling RequestStop(), the next time the 57 /// Dispatcher is run, all waiting Tasks will be dequeued and their 58 /// TaskFunctions called with a PW_STATUS_CANCELLED status. RequestStop()59 void RequestStop() { native_dispatcher_.RequestStop(); } 60 61 // Dispatcher overrides: Post(Task & task)62 void Post(Task& task) override { native_dispatcher_.Post(task); } PostAfter(Task & task,chrono::SystemClock::duration delay)63 void PostAfter(Task& task, chrono::SystemClock::duration delay) override { 64 native_dispatcher_.PostAfter(task, delay); 65 } PostAt(Task & task,chrono::SystemClock::time_point time)66 void PostAt(Task& task, chrono::SystemClock::time_point time) override { 67 native_dispatcher_.PostAt(task, time); 68 } Cancel(Task & task)69 bool Cancel(Task& task) override { return native_dispatcher_.Cancel(task); } 70 71 // VirtualSystemClock overrides: now()72 chrono::SystemClock::time_point now() override { 73 return native_dispatcher_.now(); 74 } 75 76 // Returns the inner NativeFakeDispatcher containing backend-specific 77 // state/logic. Only non-portable code should call these methods! native_type()78 backend::NativeFakeDispatcher& native_type() { return native_dispatcher_; } native_type()79 const backend::NativeFakeDispatcher& native_type() const { 80 return native_dispatcher_; 81 } 82 83 private: 84 backend::NativeFakeDispatcher native_dispatcher_; 85 }; 86 87 } // namespace pw::async::test 88