blob: 44531f899bb06d70702a1f9c2ba829f81944e183 [file] [log] [blame]
// Copyright 2019 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/task.h>
#include <lib/async/time.h>
#include <zircon/time.h>
#include <functional>
#include <memory>
#include <optional>
#include <gtest/gtest.h>
#include "src/connectivity/wlan/drivers/testing/lib/sim-env/sim-env.h"
#include "src/connectivity/wlan/drivers/testing/lib/sim-env/sim-sta-ifc.h"
// zx::time() gives us an absolute time of zero
#define ABSOLUTE_TIME(delay) (zx::time() + (delay))
namespace wlan::testing {
namespace {
constexpr zx::duration kSimulatedClockDuration = zx::sec(10);
} // namespace
class EventTest : public ::testing::Test, public simulation::StationIfc {
public:
EventTest() : env_(std::make_unique<simulation::Environment>()), completed_(false) {
env_->AddStation(this);
}
~EventTest() {
if (env_) {
env_->RemoveStation(this);
}
}
// StationIfc methods
void Rx(std::shared_ptr<const simulation::SimFrame> frame,
std::shared_ptr<const simulation::WlanRxInfo> info) override {}
// Testing callbacks
void SingleEventCallback();
void PeriodicEventCallback();
void SequentialEventCallback();
void DuplicateTimeEventCallback(uint64_t value);
void NotificationReceived(uint64_t value);
std::unique_ptr<simulation::Environment> env_;
bool completed_;
};
/*** Uneventful test ***/
// Verify that we can run through a simulation with no events
TEST_F(EventTest, Uneventful) {
env_->Run(kSimulatedClockDuration);
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(zx::usec(0)));
}
/*** SingleEvent test ***/
void EventTest::SingleEventCallback() { completed_ = true; }
// Test with a single event
TEST_F(EventTest, SingleEvent) {
constexpr zx::duration delay = zx::msec(100);
EXPECT_EQ(env_->ScheduleNotification(std::bind(&EventTest::SingleEventCallback, this), delay),
ZX_OK);
env_->Run(kSimulatedClockDuration);
EXPECT_EQ(completed_, true);
// Time should stop at the last event
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(delay));
}
/*** PeriodicEvents test ***/
constexpr uint32_t kNumPeriodicEvents = 47;
constexpr zx::duration kEventPeriod = zx::msec(78);
void EventTest::PeriodicEventCallback() {
static int32_t remaining_events = kNumPeriodicEvents;
remaining_events--;
if (remaining_events == 0) {
completed_ = true;
} else {
EXPECT_GT(remaining_events, 0);
EXPECT_EQ(env_->ScheduleNotification(std::bind(&EventTest::PeriodicEventCallback, this),
kEventPeriod),
ZX_OK);
}
}
// Test with periodic events
TEST_F(EventTest, PeriodicEvents) {
EXPECT_EQ(
env_->ScheduleNotification(std::bind(&EventTest::PeriodicEventCallback, this), kEventPeriod),
ZX_OK);
env_->Run(kSimulatedClockDuration);
EXPECT_EQ(completed_, true);
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(kEventPeriod * kNumPeriodicEvents));
}
/*** InvalidTime test ***/
// Attempt to add event before current time
TEST_F(EventTest, InvalidTime) {
constexpr zx::duration delay = zx::msec(-100);
EXPECT_EQ(env_->ScheduleNotification(std::bind(&EventTest::SingleEventCallback, this), delay),
ZX_ERR_INVALID_ARGS);
env_->Run(kSimulatedClockDuration);
EXPECT_EQ(completed_, false);
// Time should not have advanced
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(zx::usec(0)));
}
/*** SequentialEvents test ***/
constexpr int32_t kSequentialTestSecs = 99;
void EventTest::SequentialEventCallback() {
static int32_t remaining_events = kSequentialTestSecs + 1;
// Invoked at time 0, then every second thereafter
static uint32_t expected_time = 0;
remaining_events--;
if (remaining_events == 0) {
completed_ = true;
} else {
EXPECT_GT(remaining_events, 0);
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(zx::sec(expected_time)));
}
expected_time++;
}
// Verify that events can be added in any order and are processed sequentially
TEST_F(EventTest, SequentialEvents) {
// Add the even-second events
for (int32_t i = 0; i <= kSequentialTestSecs; i += 2) {
zx::duration delay = zx::sec(i);
EXPECT_EQ(
env_->ScheduleNotification(std::bind(&EventTest::SequentialEventCallback, this), delay),
ZX_OK);
}
// Add the odd-second events
for (int32_t i = (kSequentialTestSecs % 2) ? kSequentialTestSecs : (kSequentialTestSecs - 1);
i > 0; i -= 2) {
zx::duration delay = zx::sec(i);
EXPECT_EQ(
env_->ScheduleNotification(std::bind(&EventTest::SequentialEventCallback, this), delay),
ZX_OK);
}
env_->Run(zx::sec(kSequentialTestSecs));
EXPECT_EQ(completed_, true);
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(zx::sec(kSequentialTestSecs)));
}
/*** DuplicateEvents test ***/
constexpr uint32_t kDuplicateEventCount = 10;
constexpr zx::duration kDuplicateEventsTime = zx::msec(120);
void EventTest::DuplicateTimeEventCallback(uint64_t value) {
static int64_t remaining_events = kDuplicateEventCount;
static uint64_t expected_value = 0;
remaining_events--;
if (remaining_events == 0) {
completed_ = true;
} else {
EXPECT_GT(remaining_events, 0);
// Verify that events are processed in order
EXPECT_EQ(expected_value, value);
}
expected_value++;
}
// Test multiple events at same time. Events should be processed in the order they were added.
TEST_F(EventTest, DuplicateEvents) {
for (uint32_t i = 0; i < kDuplicateEventCount; i++) {
EXPECT_EQ(env_->ScheduleNotification(std::bind(&EventTest::DuplicateTimeEventCallback, this, i),
kDuplicateEventsTime),
ZX_OK);
}
env_->Run(kDuplicateEventsTime);
EXPECT_EQ(completed_, true);
EXPECT_EQ(env_->GetTime(), ABSOLUTE_TIME(kDuplicateEventsTime));
}
// Test cancelling events. Start with events scheduled at 1, 2, 3, 4, and 5 seconds into
// simulation.
//
// Timeline:
// 1s: Event fires, do nothing
// 2s: Cancel event at time 4s (ZX_OK)
// Test calls to cancel with invalid args
// 3s: Test calls to cancel with invalid args
// 5s: Event fires, do nothing
constexpr size_t kNotificationsCount = 5;
static struct CancelNotificationState {
bool notifications_seen[kNotificationsCount];
uint64_t ids[kNotificationsCount];
} cancel_state;
void EventTest::NotificationReceived(uint64_t value) {
ASSERT_LE(value, kNotificationsCount);
cancel_state.notifications_seen[value] = true;
if (value == 1) { // 2s into the test
// Try to cancel an already-passed event
EXPECT_EQ(ZX_ERR_NOT_FOUND, env_->CancelNotification(cancel_state.ids[0]));
// Try to cancel our current event
EXPECT_EQ(ZX_ERR_NOT_FOUND, env_->CancelNotification(cancel_state.ids[1]));
// Try to cancel a future event with incorrect ID
uint64_t fake_id = 0x6b46616b654964;
bool match_found;
do {
match_found = false;
for (size_t i = 0; i < kNotificationsCount; i++) {
if (fake_id == cancel_state.ids[i]) {
match_found = true;
fake_id++;
break;
}
}
} while (match_found);
EXPECT_EQ(ZX_ERR_NOT_FOUND, env_->CancelNotification(fake_id));
// Cancel a future event (hopefully successfully)
EXPECT_EQ(ZX_OK, env_->CancelNotification(cancel_state.ids[3]));
}
if (value == 2) { // 3s into the test
// Try cancelling a previously-cancelled event
EXPECT_EQ(ZX_ERR_NOT_FOUND, env_->CancelNotification(cancel_state.ids[3]));
}
}
TEST_F(EventTest, CancelEvents) {
// Create and initialize notification objects
for (uint64_t i = 0; i < kNotificationsCount; i++) {
cancel_state.notifications_seen[i] = false;
EXPECT_EQ(ZX_OK,
env_->ScheduleNotification(std::bind(&EventTest::NotificationReceived, this, i),
zx::sec(i + 1), &cancel_state.ids[i]));
}
env_->Run(kSimulatedClockDuration);
// Verify notification events seen (except the one at time 4, which should have been cancelled)
for (uint64_t i = 0; i < kNotificationsCount; i++) {
EXPECT_EQ(cancel_state.notifications_seen[i], (i == 3) ? false : true);
}
}
// Test the event system, used through the async dispatcher API.
TEST_F(EventTest, AsyncDispatcher) {
// Set up a couple of tasks.
struct Task : public async_task_t {
explicit Task(zx_time_t deadline) {
handler = [](async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
static_cast<Task*>(task)->status.emplace(status);
};
this->deadline = deadline;
}
std::optional<zx_status_t> status;
};
auto task1 = Task(ZX_HOUR(10));
auto task2 = Task(ZX_HOUR(20));
auto task3 = Task(ZX_HOUR(30));
auto task4 = Task(ZX_HOUR(40));
// Post the tasks in unsorted order, and then run the first one.
EXPECT_EQ(ZX_OK, async_post_task(env_->GetDispatcher(), &task3));
EXPECT_EQ(ZX_OK, async_post_task(env_->GetDispatcher(), &task1));
EXPECT_EQ(ZX_OK, async_post_task(env_->GetDispatcher(), &task4));
EXPECT_EQ(ZX_OK, async_post_task(env_->GetDispatcher(), &task2));
env_->Run(zx::hour(15));
// Cancel the second task, then run up to the third one.
EXPECT_EQ(ZX_ERR_NOT_FOUND, async_cancel_task(env_->GetDispatcher(), &task1));
EXPECT_EQ(ZX_OK, async_cancel_task(env_->GetDispatcher(), &task2));
env_->Run(zx::hour(20));
// Task 1 and task 3 both ran.
EXPECT_TRUE(task1.status.has_value());
EXPECT_TRUE(task3.status.has_value());
EXPECT_EQ(ZX_OK, task1.status.value());
EXPECT_EQ(ZX_OK, task3.status.value());
// Task 2 was cancelled, task 4 hasn't fired yet.
EXPECT_FALSE(task2.status.has_value());
EXPECT_FALSE(task4.status.has_value());
// Shut down the environment, and task 4 will get a shutdown notification.
env_.reset();
EXPECT_TRUE(task4.status.has_value());
EXPECT_EQ(ZX_ERR_CANCELED, task4.status.value());
}
} // namespace wlan::testing