| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_async_fuchsia/dispatcher.h" |
| |
| #include <lib/async-testing/test_loop.h> |
| |
| #include "pw_async_fuchsia/util.h" |
| #include "pw_unit_test/framework.h" |
| |
| #define ASSERT_CANCELLED(status) ASSERT_EQ(Status::Cancelled(), status) |
| |
| using namespace std::chrono_literals; |
| |
| namespace pw::async_fuchsia { |
| |
| class DispatcherFuchsiaTest : public ::testing::Test { |
| public: |
| async_dispatcher_t* dispatcher() { return loop_.dispatcher(); } |
| void RunLoopUntilIdle() { loop_.RunUntilIdle(); } |
| void RunLoopFor(zx::duration duration) { loop_.RunFor(duration); } |
| |
| private: |
| ::async::TestLoop loop_; |
| }; |
| |
| TEST_F(DispatcherFuchsiaTest, TimeConversions) { |
| zx::time time{timespec{123, 456}}; |
| chrono::SystemClock::time_point tp = |
| pw::async_fuchsia::ZxTimeToTimepoint(time); |
| EXPECT_EQ(tp.time_since_epoch(), 123s + 456ns); |
| EXPECT_EQ(pw::async_fuchsia::TimepointToZxTime(tp), time); |
| } |
| |
| TEST_F(DispatcherFuchsiaTest, Basic) { |
| FuchsiaDispatcher fuchsia_dispatcher(dispatcher()); |
| |
| bool set = false; |
| async::Task task([&set](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| set = true; |
| }); |
| fuchsia_dispatcher.Post(task); |
| |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(set); |
| } |
| |
| TEST_F(DispatcherFuchsiaTest, DelayedTasks) { |
| FuchsiaDispatcher fuchsia_dispatcher(dispatcher()); |
| |
| int c = 0; |
| async::Task first([&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c = c * 10 + 1; |
| }); |
| async::Task second([&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c = c * 10 + 2; |
| }); |
| async::Task third([&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c = c * 10 + 3; |
| }); |
| |
| fuchsia_dispatcher.PostAfter(third, 20ms); |
| fuchsia_dispatcher.PostAfter(first, 5ms); |
| fuchsia_dispatcher.PostAfter(second, 10ms); |
| |
| RunLoopFor(zx::msec(25)); |
| EXPECT_EQ(c, 123); |
| } |
| |
| TEST_F(DispatcherFuchsiaTest, CancelTask) { |
| FuchsiaDispatcher fuchsia_dispatcher(dispatcher()); |
| |
| async::Task task([](async::Context& ctx, Status status) { FAIL(); }); |
| fuchsia_dispatcher.Post(task); |
| EXPECT_TRUE(fuchsia_dispatcher.Cancel(task)); |
| |
| RunLoopUntilIdle(); |
| } |
| |
| class DestructionChecker { |
| public: |
| explicit DestructionChecker(bool* flag) : flag_(flag) {} |
| DestructionChecker(DestructionChecker&& other) { |
| flag_ = other.flag_; |
| other.flag_ = nullptr; |
| } |
| ~DestructionChecker() { |
| if (flag_) { |
| *flag_ = true; |
| } |
| } |
| |
| private: |
| bool* flag_; |
| }; |
| |
| TEST_F(DispatcherFuchsiaTest, HeapAllocatedTasks) { |
| FuchsiaDispatcher fuchsia_dispatcher(dispatcher()); |
| |
| int c = 0; |
| for (int i = 0; i < 3; i++) { |
| Post(&fuchsia_dispatcher, [&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c++; |
| }); |
| } |
| |
| EXPECT_EQ(c, 0); |
| RunLoopUntilIdle(); |
| EXPECT_EQ(c, 3); |
| |
| // Test that the lambda is destroyed after being called. |
| bool flag = false; |
| Post(&fuchsia_dispatcher, |
| [checker = DestructionChecker(&flag)](async::Context& ctx, |
| Status status) {}); |
| EXPECT_FALSE(flag); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(flag); |
| } |
| |
| TEST_F(DispatcherFuchsiaTest, ChainedTasks) { |
| FuchsiaDispatcher fuchsia_dispatcher(dispatcher()); |
| |
| int c = 0; |
| |
| Post(&fuchsia_dispatcher, [&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c++; |
| Post(ctx.dispatcher, [&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c++; |
| Post(ctx.dispatcher, [&c](async::Context& ctx, Status status) { |
| PW_TEST_ASSERT_OK(status); |
| c++; |
| }); |
| }); |
| }); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(c, 3); |
| } |
| |
| } // namespace pw::async_fuchsia |