blob: e7d471fc8fc7b119a227fcd3e12a3b3740e54581 [file] [log] [blame]
// 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