blob: 55a09d1f089969fedda69ec8cdb347efd6b2be32 [file] [log] [blame]
// 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 |task| to update |var| to point to |value| when it fires.
void InitVariableUpdateTask(async::TaskClosure* task, int* var, int value) {
task->set_handler([var, value]() {
*var = value;
});
}
// 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_t*, async::Wait*,
zx_status_t status, const zx_packet_signal_t*) {
if (status == ZX_OK) {
*var = value;
}
});
}
bool get_default_test() {
BEGIN_TEST;
EXPECT_NULL(async_get_default());
async::TestLoop* loop = new async::TestLoop();
EXPECT_EQ(loop->async(), async_get_default());
delete loop;
EXPECT_NULL(async_get_default());
END_TEST;
}
bool fake_clock_test() {
async::TestLoop loop;
BEGIN_TEST;
EXPECT_EQ(zx::time(0).get(), loop.Now().get());
EXPECT_EQ(zx::time(0).get(), async_now(loop.async()));
loop.AdvanceTimeBy(zx::sec(1));
EXPECT_EQ(zx::sec(1).get(), loop.Now().get());
loop.AdvanceTimeBy(zx::sec(2));
EXPECT_EQ(zx::sec(3).get(), loop.Now().get());
END_TEST;
}
bool simple_task_posting_test() {
async::TestLoop loop;
async_t* async = loop.async();
int var = 0;
BEGIN_TEST;
// |taskA|: updates |var| to 1 with a deadline of t = 2.
async::TaskClosure taskA;
InitVariableUpdateTask(&taskA, &var, 1);
EXPECT_EQ(ZX_OK, taskA.PostForTime(async, zx::time(0) + zx::sec(2)));
loop.RunUntilIdle();
// t = 1: nothing should happen, as |taskA| has a deadline of 1.
EXPECT_EQ(0, var);
// t = 1: nothing should happen, as |taskA| has a deadline of 2.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
EXPECT_EQ(0, var);
// t = 2: |taskA| should have updated |var| to 1.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
EXPECT_EQ(1, var);
END_TEST;
}
bool task_with_same_deadlines_test() {
async::TestLoop loop;
async_t* async = loop.async();
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;
InitVariableUpdateTask(&taskA, &var, 1);
EXPECT_EQ(ZX_OK, taskA.PostForTime(async, zx::time(0) + zx::sec(3)));
async::TaskClosure taskB;
InitVariableUpdateTask(&taskB, &var, 2);
EXPECT_EQ(ZX_OK, taskB.PostForTime(async, zx::time(0) + zx::sec(3)));
// t = 3: Since |taskB| was posted after |taskA|, it's handler was called
// after |taskA|'s.'
loop.AdvanceTimeBy(zx::sec(3));
loop.RunUntilIdle();
EXPECT_EQ(2, var);
END_TEST;
}
// Test tasks that post tasks.
bool compounded_task_posting_test() {
async::TestLoop loop;
async_t* async = loop.async();
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;
InitVariableUpdateTask(&taskB, &var, 2);
async::TaskClosure taskA;
taskA.set_handler([&async, &taskB, &var]() {
var = 1;
EXPECT_EQ(ZX_OK, taskB.PostForTime(async, zx::time(0) + zx::sec(2)));
});
EXPECT_EQ(ZX_OK, taskA.PostForTime(async, zx::time(1)));
EXPECT_EQ(0, var);
// t = 1: |taskA| should have updated |var| to 1.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
EXPECT_EQ(1, var);
// t = 2: |taskB| should have updated |var| to 2.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
EXPECT_EQ(2, var);
END_TEST;
}
bool task_canceling_test() {
async::TestLoop loop;
async_t* async = loop.async();
int var = 0;
async::TaskClosure taskA;
InitVariableUpdateTask(&taskA, &var, 2);
async::TaskClosure taskB;
InitVariableUpdateTask(&taskB, &var, 1);
async::TaskClosure taskC;
InitVariableUpdateTask(&taskC, &var, 3);
BEGIN_TEST;
EXPECT_EQ(ZX_OK, taskA.PostForTime(async, zx::time(0) + zx::sec(1)));
EXPECT_EQ(ZX_OK, taskB.PostForTime(async, zx::time(0) + zx::sec(2)));
EXPECT_EQ(ZX_OK, taskC.PostForTime(async, zx::time(0) + zx::sec(3)));
loop.AdvanceTimeBy(zx::sec(2));
EXPECT_EQ(ZX_OK, taskB.Cancel());
loop.RunUntilIdle();
// t = 2; both |taskA| and |taskB| would be due, but since |taskB| was cancelled
// only |taskA|'s handler was called: |var| should be 2.
// unchanged.
EXPECT_EQ(2, var);
EXPECT_EQ(ZX_OK, taskC.Cancel());
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
// t = 3: |taskC| was cancelled, so |var| should remain at 2.
EXPECT_EQ(2, var);
END_TEST;
}
bool simple_wait_posting_test() {
async::TestLoop loop;
async_t* async = loop.async();
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(async));
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));
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));
loop.RunUntilIdle();
EXPECT_EQ(1, var);
END_TEST;
}
// Test waits that trigger waits.
bool compounded_wait_posting_test() {
async::TestLoop loop;
async_t* async = loop.async();
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_t* async, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t*) {
if (status == ZX_OK) {
var = 1;
EXPECT_EQ(ZX_OK, waitB.Begin(async));
}
});
EXPECT_EQ(ZX_OK, waitA.Begin(async));
EXPECT_EQ(0, var);
// |waitA| should trigger.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_1));
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));
loop.RunUntilIdle();
EXPECT_EQ(2, var);
END_TEST;
}
bool wait_canceling_test() {
async::TestLoop loop;
async_t* async = loop.async();
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(async));
EXPECT_EQ(ZX_OK, waitB.Begin(async));
EXPECT_EQ(ZX_OK, waitC.Begin(async));
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));
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());
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_t* async = loop.async();
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;
InitVariableUpdateTask(&taskC, &var, 3);
async::Wait waitB;
waitB.set_object(event.get());
waitB.set_trigger(ZX_USER_SIGNAL_0);
waitB.set_handler([&taskC, &var](async_t* async, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t*) {
if (status == ZX_OK) {
var = 2;
EXPECT_EQ(ZX_OK, taskC.PostForTime(async, zx::time(0) + zx::sec(2)));
}
});
async::TaskClosure taskA;
taskA.set_handler([&async, &waitB, &var]() {
var = 1;
EXPECT_EQ(ZX_OK, waitB.Begin(async));
});
EXPECT_EQ(ZX_OK, taskA.PostForTime(async, zx::time(1)));
EXPECT_EQ(0, var);
// t = 1: |taskA| should have updated |var| to 1.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
EXPECT_EQ(1, var);
// By now, |waitB| should have begun; on signal, |var| should be updated to 2.
EXPECT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
loop.RunUntilIdle();
EXPECT_EQ(2, var);
// t = 3: |taskC| should have updated |var| to 2.
loop.AdvanceTimeBy(zx::sec(1));
loop.RunUntilIdle();
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(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)