blob: c6755e49600dfe90a9c5f3fdf03de84feaecc368 [file] [log] [blame] [edit]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async-testutils/test_loop.h>
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/time.h>
#include <lib/async/cpp/wait.h>
#include <lib/async/default.h>
#include <lib/zx/event.h>
#include <lib/zx/time.h>
#include <unittest/unittest.h>
#include <zircon/syscalls.h>
namespace {
// Initializes |wait| to updates |var| to point to |value| once |ZX_USER_SIGNAL_0| is
// seen.
void InitVariableUpdateWait(async::Wait* wait, int* var, int value, zx::event* event) {
wait->set_object(event->get());
wait->set_trigger(ZX_USER_SIGNAL_0);
wait->set_handler([var, value](async_dispatcher_t*, async::Wait*, zx_status_t,
const zx_packet_signal_t*) {
*var = value;
});
}
bool get_default_test() {
BEGIN_TEST;
EXPECT_NULL(async_get_default_dispatcher());
async::TestLoop* loop = new async::TestLoop();
EXPECT_EQ(loop->dispatcher(), async_get_default_dispatcher());
delete loop;
EXPECT_NULL(async_get_default_dispatcher());
END_TEST;
}
bool fake_clock_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
BEGIN_TEST;
EXPECT_EQ(zx::sec(0).get(), loop.Now().get());
EXPECT_EQ(zx::sec(0).get(), async_now(dispatcher));
EXPECT_FALSE(loop.RunUntilIdle());
// Loop was idle already, so current time should remain the same.
EXPECT_EQ(zx::sec(0).get(), loop.Now().get());
EXPECT_EQ(zx::sec(0).get(), async_now(dispatcher));
loop.AdvanceTimeBy(zx::sec(1));
EXPECT_EQ(zx::sec(1).get(), loop.Now().get());
EXPECT_EQ(zx::sec(1).get(), async_now(dispatcher));
loop.AdvanceTimeTo(zx::time(0) + zx::sec(3));
EXPECT_EQ(zx::sec(3).get(), loop.Now().get());
EXPECT_EQ(zx::sec(3).get(), async_now(dispatcher));
EXPECT_FALSE(loop.RunFor(zx::sec(7)));
EXPECT_EQ(zx::sec(10).get(), loop.Now().get());
EXPECT_EQ(zx::sec(10).get(), async_now(dispatcher));
EXPECT_FALSE(loop.RunUntil(zx::time(0) + zx::sec(12)));
EXPECT_EQ(zx::sec(12).get(), loop.Now().get());
EXPECT_EQ(zx::sec(12).get(), async_now(dispatcher));
// t = 12, so nothing should happen in trying to reset the clock to t = 10.
loop.AdvanceTimeTo(zx::time(0) + zx::sec(10));
EXPECT_EQ(zx::sec(12).get(), loop.Now().get());
EXPECT_EQ(zx::sec(12).get(), async_now(dispatcher));
END_TEST;
}
bool simple_task_posting_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
BEGIN_TEST;
// |taskA|: updates |var| to 1 with a deadline of t = 2.
async::TaskClosure task([&var] { var = 1; });
EXPECT_EQ(ZX_OK, task.PostForTime(dispatcher, zx::time(0) + zx::sec(2)));
// t = 1: nothing should happen, as |taskA| has a deadline of 1.
EXPECT_FALSE(loop.RunFor(zx::sec(1)));
EXPECT_EQ(0, var);
// t = 2: |taskA| should have updated |var| to 1.
loop.AdvanceTimeBy(zx::sec(1));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(1, var);
END_TEST;
}
bool task_with_same_deadlines_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
BEGIN_TEST;
// |taskA|: updates |var| to 1 with a deadline of t = 3.
// |taskB|: updates |var| to 2 with a deadline of t = 3.
async::TaskClosure taskA([&var] { var = 1; });
EXPECT_EQ(ZX_OK, taskA.PostDelayed(dispatcher, zx::sec(3)));
async::TaskClosure taskB([&var] { var = 2; });
EXPECT_EQ(ZX_OK, taskB.PostDelayed(dispatcher, zx::sec(3)));
// t = 3: Since |taskB| was posted after |taskA|, it's handler was called
// after |taskA|'s.'
EXPECT_TRUE(loop.RunFor(zx::sec(3)));
EXPECT_EQ(2, var);
END_TEST;
}
// Test tasks that post tasks.
bool compounded_task_posting_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
BEGIN_TEST;
// |taskA|: updates |var| to 1 and posts |taskB| at t = 1.
// |taskB|: updates |var| to 2 at t = 3.
async::TaskClosure taskB([&var] { var = 2; });
async::TaskClosure taskA([&var, &taskB, dispatcher] {
var = 1;
EXPECT_EQ(ZX_OK, taskB.PostForTime(dispatcher, zx::time(0) + zx::sec(2)));
});
EXPECT_EQ(ZX_OK, taskA.PostForTime(dispatcher, zx::time(0) + zx::sec(1)));
EXPECT_EQ(0, var);
// t = 1: |taskA| should have updated |var| to 1.
EXPECT_TRUE(loop.RunFor(zx::sec(1)));
EXPECT_EQ(1, var);
// t = 2: |taskB| should have updated |var| to 2.
loop.AdvanceTimeTo(zx::time(0) + zx::sec(2));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(2, var);
END_TEST;
}
bool correct_time_while_dispatching_tasks_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
BEGIN_TEST;
// Post |taskA| for t = 1, set the time to t = 2, and post |taskB| also to
// t = 1. During dispatching of both task, the current time to t = 2.
async::TaskClosure taskA([dispatcher] {
EXPECT_EQ(zx::sec(2).get(), async_now(dispatcher));
});
async::TaskClosure taskB([dispatcher] {
EXPECT_EQ(zx::sec(2).get(), async_now(dispatcher));
});
taskA.PostForTime(dispatcher, zx::time(0) + zx::sec(1));
loop.AdvanceTimeTo(zx::time(0) + zx::sec(2));
taskB.PostForTime(dispatcher, zx::time(0) + zx::sec(1));
EXPECT_TRUE(loop.RunUntilIdle());
// Post |taskC| and |taskD| for t = 4, 5, and then run until t = 6. Both
// tasks should have current time equal to deadline on dispatch.
async::TaskClosure taskC([dispatcher] {
EXPECT_EQ(zx::sec(4).get(), async_now(dispatcher));
});
async::TaskClosure taskD([dispatcher] {
EXPECT_EQ(zx::sec(5).get(), async_now(dispatcher));
});
taskC.PostForTime(dispatcher, zx::time(0) + zx::sec(4));
taskD.PostForTime(dispatcher, zx::time(0) + zx::sec(5));
EXPECT_TRUE(loop.RunUntil(zx::time(0) + zx::sec(6)));
END_TEST;
}
bool task_canceling_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
async::TaskClosure taskA([&var] { var = 2; });
async::TaskClosure taskB([&var] { var = 1; });
async::TaskClosure taskC([&var] { var = 3; });
BEGIN_TEST;
EXPECT_EQ(ZX_OK, taskA.PostDelayed(dispatcher, zx::sec(1)));
EXPECT_EQ(ZX_OK, taskB.PostDelayed(dispatcher, zx::sec(2)));
EXPECT_EQ(ZX_OK, taskC.PostDelayed(dispatcher, zx::sec(3)));
EXPECT_EQ(ZX_OK, taskB.Cancel());
// t = 2; both |taskA| and |taskB| would be due, but since |taskB| was cancelled
// only |taskA|'s handler was called: |var| should be 2.
EXPECT_TRUE(loop.RunFor(zx::sec(2)));
EXPECT_EQ(2, var);
EXPECT_EQ(ZX_OK, taskC.Cancel());
// t = 3: |taskC| was cancelled, so |var| should remain at 2.
EXPECT_FALSE(loop.RunFor(zx::sec(1)));
EXPECT_EQ(2, var);
END_TEST;
}
bool simple_wait_posting_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
zx::event event;
async::Wait wait;
BEGIN_TEST;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &event));
InitVariableUpdateWait(&wait, &var, 1, &event);
EXPECT_EQ(ZX_OK, wait.Begin(dispatcher));
EXPECT_FALSE(loop.RunUntilIdle());
EXPECT_EQ(0, var);
// |wait| will only be triggered by |ZX_USER_SIGNAL_1|.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_1));
EXPECT_FALSE(loop.RunUntilIdle());
EXPECT_EQ(0, var);
// With the correct signal, |var| should be updated to 1.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(1, var);
END_TEST;
}
// Test waits that trigger waits.
bool compounded_wait_posting_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
zx::event event;
BEGIN_TEST;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &event));
// |waitA|: updates |var| to 1 and begins |waitB| on |ZX_USER_SIGNAL_1|.
// |waitB|: updates |var| to 2 on |ZX_USER_SIGNAL_0|.
async::Wait waitB;
InitVariableUpdateWait(&waitB, &var, 2, &event);
async::Wait waitA;
waitA.set_object(event.get());
waitA.set_trigger(ZX_USER_SIGNAL_1);
waitA.set_handler([&waitB, &var](async_dispatcher_t* dispatcher, async::Wait*, zx_status_t,
const zx_packet_signal_t*) {
var = 1;
EXPECT_EQ(ZX_OK, waitB.Begin(dispatcher));
});
EXPECT_EQ(ZX_OK, waitA.Begin(dispatcher));
EXPECT_EQ(0, var);
// |waitA| should trigger.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_1));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(1, var);
// |waitB| should have begun by now and trigger.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(2, var);
END_TEST;
}
bool wait_canceling_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
zx::event eventA;
zx::event eventB;
zx::event eventC;
async::Wait waitA;
async::Wait waitB;
async::Wait waitC;
BEGIN_TEST;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &eventA));
EXPECT_EQ(ZX_OK, zx::event::create(0u, &eventB));
EXPECT_EQ(ZX_OK, zx::event::create(0u, &eventC));
InitVariableUpdateWait(&waitA, &var, 1, &eventA);
InitVariableUpdateWait(&waitB, &var, 2, &eventB);
InitVariableUpdateWait(&waitC, &var, 3, &eventC);
EXPECT_EQ(ZX_OK, waitA.Begin(dispatcher));
EXPECT_EQ(ZX_OK, waitB.Begin(dispatcher));
EXPECT_EQ(ZX_OK, waitC.Begin(dispatcher));
EXPECT_EQ(ZX_OK, waitB.Cancel());
// Have |eventA| and |eventB| fire: |waitB| was canceled, so only |waitA|'s
// shoud have been called: |var| should be 1.
EXPECT_EQ(ZX_OK, eventA.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_EQ(ZX_OK, eventB.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_TRUE(loop.RunUntilIdle());
EXPECT_EQ(1, var);
// If |eventC| fires before the canceling of |waitC|, |waitC|'s handler
// should still not be called.
EXPECT_EQ(ZX_OK, eventC.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_EQ(ZX_OK, waitC.Cancel());
EXPECT_FALSE(loop.RunUntilIdle());
EXPECT_EQ(1, var);
END_TEST;
}
// Test a task that begins a wait to post a task.
bool mixed_task_and_wait_posting_test() {
async::TestLoop loop;
async_dispatcher_t* dispatcher = loop.dispatcher();
int var = 0;
zx::event event;
BEGIN_TEST;
EXPECT_EQ(ZX_OK, zx::event::create(0u, &event));
// |taskA|: update var to 1 and begin |waitB| with a deadline of t = 1;
// |waitB|: update var to 2 and post |taskC| on seeing |ZX_USER_SIGNAL_0|;
// |taskC|: update var to 3 with a deadline of t = 3;
async::TaskClosure taskC([&var] {
EXPECT_EQ(2, var);
var = 3;
});
async::Wait waitB;
waitB.set_object(event.get());
waitB.set_trigger(ZX_USER_SIGNAL_0);
waitB.set_handler([&taskC, &var](async_dispatcher_t* dispatcher, async::Wait*, zx_status_t,
const zx_packet_signal_t*) {
var = 2;
EXPECT_EQ(ZX_OK, taskC.PostForTime(dispatcher, zx::time(0) + zx::sec(3)));
});
async::TaskClosure taskA([&waitB, &var, dispatcher] {
var = 1;
EXPECT_EQ(ZX_OK, waitB.Begin(dispatcher));
});
EXPECT_EQ(ZX_OK, taskA.PostForTime(dispatcher, zx::time(0) + zx::sec(1)));
EXPECT_EQ(0, var);
// t = 1: |taskA| should have updated |var| to 1.
EXPECT_TRUE(loop.RunUntil(zx::time(0) + zx::sec(1)));
EXPECT_EQ(1, var);
// By now, |waitB| should have begun; on signal, |var| should be updated
// first to 2, and then to 3 by |taskC|.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
EXPECT_TRUE(loop.RunUntil(zx::time(0) + zx::sec(3)));
EXPECT_EQ(3, var);
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(test_loop_tests)
RUN_TEST(get_default_test)
RUN_TEST(fake_clock_test)
RUN_TEST(simple_task_posting_test)
RUN_TEST(task_with_same_deadlines_test)
RUN_TEST(compounded_task_posting_test)
RUN_TEST(correct_time_while_dispatching_tasks_test)
RUN_TEST(task_canceling_test)
RUN_TEST(simple_wait_posting_test)
RUN_TEST(compounded_wait_posting_test)
RUN_TEST(wait_canceling_test)
RUN_TEST(mixed_task_and_wait_posting_test)
END_TEST_CASE(test_loop_tests)