blob: e9e1dd9c50d366650a8c659a84fecff4aae77e05 [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 <gtest/gtest.h>
#include <lib/timekeeper/test_clock.h>
#include <wlan/mlme/timer.h>
#include <wlan/mlme/timer_manager.h>
#include "test_timer.h"
#include "test_utils.h"
namespace wlan {
namespace {
class MockedTimer : public Timer {
public:
MockedTimer() : Timer(0) {}
zx::time Now() const override { return clock.Now(); }
zx_status_t SetTimerImpl(zx::time d) override {
canceled = false;
deadline = d;
return next_set_timer;
}
zx_status_t CancelTimerImpl() override {
canceled = true;
return ZX_OK;
}
zx_status_t next_set_timer = ZX_OK;
bool canceled = false;
zx::time deadline = zx::time(0);
timekeeper::TestClock clock;
};
struct TimerManagerTest : public ::testing::Test {
TimerManagerTest() {
auto timer_box = fbl::make_unique<MockedTimer>();
timer = timer_box.get();
timer_manager = fbl::make_unique<TimerManager<std::string>>(std::move(timer_box));
}
timekeeper::TestClock* clock() { return &timer->clock; }
MockedTimer* timer;
fbl::unique_ptr<TimerManager<std::string>> timer_manager;
};
TEST_F(TimerManagerTest, HandleTimeout) {
TimeoutId one, two, three, four, five;
timer_manager->Schedule(zx::time(300), "three", &three);
timer_manager->Schedule(zx::time(100), "one", &one);
timer_manager->Schedule(zx::time(500), "five", &five);
timer_manager->Schedule(zx::time(200), "two", &two);
timer_manager->Schedule(zx::time(400), "four", &four);
EXPECT_EQ(5u, timer_manager->NumScheduled());
timer_manager->Cancel(two);
timer_manager->Cancel(four);
EXPECT_EQ(3u, timer_manager->NumScheduled());
clock()->Set(zx::time(350));
std::vector<std::string> events;
std::vector<TimeoutId> ids;
timer_manager->HandleTimeout([&](auto now, auto event, auto id) {
EXPECT_EQ(now, zx::time(350));
events.push_back(event);
ids.push_back(id);
});
// Only expect "one" and "three" to be reported since "two" has been canceled
// and all others are scheduled at a later time
EXPECT_EQ(events, std::vector<std::string>({"one", "three"}));
EXPECT_EQ(ids, std::vector<TimeoutId>({one, three}));
// Expect the timer to be set to "five" since "four" has been canceled
EXPECT_EQ(zx::time(500), timer->deadline);
EXPECT_EQ(1u, timer_manager->NumScheduled());
}
TEST_F(TimerManagerTest, CancelNearestEvent) {
TimeoutId foo, bar;
timer_manager->Schedule(zx::time(100), "foo", &foo);
timer_manager->Schedule(zx::time(200), "bar", &bar);
EXPECT_EQ(zx::time(100), timer->deadline);
EXPECT_EQ(2u, timer_manager->NumScheduled());
timer_manager->Cancel(foo);
// We don't expect Cancel() to reset the timer. Instead, the next HandleTimeout
// should simply ignore the canceled event.
EXPECT_EQ(zx::time(100), timer->deadline);
EXPECT_EQ(1u, timer_manager->NumScheduled());
clock()->Set(zx::time(150));
size_t num_handled = 0;
timer_manager->HandleTimeout([&](auto now, auto event, auto id) { num_handled++; });
EXPECT_EQ(0u, num_handled);
EXPECT_EQ(zx::time(200), timer->deadline);
EXPECT_EQ(1u, timer_manager->NumScheduled());
}
TEST_F(TimerManagerTest, HandleLastTimeout) {
timer_manager->Schedule(zx::time(100), "foo", nullptr);
EXPECT_EQ(zx::time(100), timer->deadline);
EXPECT_EQ(1u, timer_manager->NumScheduled());
timer->deadline = zx::time(0);
clock()->Set(zx::time(100));
std::vector<std::string> events;
timer_manager->HandleTimeout([&](auto now, auto event, auto id) { events.push_back(event); });
EXPECT_EQ(events, std::vector<std::string>({"foo"}));
// Make sure the timer has not been reset
EXPECT_EQ(timer->deadline, zx::time(0));
}
TEST_F(TimerManagerTest, SchedulingAtLaterTimeDoesNotResetTimer) {
timer_manager->Schedule(zx::time(300), "foo", nullptr);
EXPECT_EQ(zx::time(300), timer->deadline);
timer_manager->Schedule(zx::time(400), "bar", nullptr);
EXPECT_EQ(zx::time(300), timer->deadline);
}
TEST_F(TimerManagerTest, SchedulingAtEarlierTimeResetsTimer) {
timer_manager->Schedule(zx::time(400), "foo", nullptr);
EXPECT_EQ(zx::time(400), timer->deadline);
timer_manager->Schedule(zx::time(300), "bar", nullptr);
EXPECT_EQ(zx::time(300), timer->deadline);
}
TEST_F(TimerManagerTest, ScheduleAnotherTimeoutInCallback) {
timer_manager->Schedule(zx::time(200), "foo", nullptr);
clock()->Set(zx::time(200));
std::vector<std::string> events;
timer_manager->HandleTimeout([&](auto now, auto event, auto id) {
EXPECT_EQ(now, zx::time(200));
if (event == "foo") {
timer_manager->Schedule(zx::time(100), "bar", nullptr);
} else if (event == "bar") {
timer_manager->Schedule(zx::time(300), "baz", nullptr);
}
events.push_back(event);
});
// Expect "bar" to be processed immediately
EXPECT_EQ(events, std::vector<std::string>({"foo", "bar"}));
// The timer should be set to "baz"
EXPECT_EQ(zx::time(300), timer->deadline);
EXPECT_EQ(1u, timer_manager->NumScheduled());
}
TEST_F(TimerManagerTest, EventsWithSameDeadlineReportedInSchedulingOrder) {
constexpr size_t N = 20;
TimeoutId ids[N];
for (size_t i = 0; i < N; ++i) {
timer_manager->Schedule(zx::time(100), "", &ids[i]);
}
EXPECT_EQ(N, timer_manager->NumScheduled());
clock()->Set(zx::time(100));
std::vector<TimeoutId> reported_ids;
timer_manager->HandleTimeout(
[&](auto now, auto event, auto id) { reported_ids.push_back(id); });
EXPECT_RANGES_EQ(ids, reported_ids);
EXPECT_EQ(0u, timer_manager->NumScheduled());
}
TEST_F(TimerManagerTest, CancelAll) {
TimeoutId foo, bar;
timer_manager->Schedule(zx::time(100), "foo", &foo);
timer_manager->Schedule(zx::time(200), "bar", &bar);
EXPECT_FALSE(timer->canceled);
EXPECT_EQ(zx::time(100), timer->deadline);
EXPECT_EQ(2u, timer_manager->NumScheduled());
timer_manager->CancelAll();
EXPECT_TRUE(timer->canceled);
EXPECT_EQ(0u, timer_manager->NumScheduled());
}
} // namespace
} // namespace wlan