#include <gtest/gtest.h>
#include <hardware/hardware.h>
#include <chrono>
#include <mutex>
#include "queue_worker.h"
using android::QueueWorker;
#define UNUSED_ARG(x) (void)(x)
struct TestData {
TestData(int val) : value(val) {
}
virtual ~TestData() {
}
virtual void CheckValue(int prev_value) {
ASSERT_EQ(prev_value + 1, value);
}
int value;
};
struct TestQueueWorker : public QueueWorker<TestData> {
TestQueueWorker()
: QueueWorker("test-queueworker", HAL_PRIORITY_URGENT_DISPLAY), value(0) {
}
int Init() {
return InitWorker();
}
void ProcessWork(std::unique_ptr<TestData> data) {
std::lock_guard<std::mutex> blk(block);
data->CheckValue(value);
{
std::lock_guard<std::mutex> lk(lock);
value = data->value;
}
cond.notify_one();
}
void ProcessIdle() {
ASSERT_FALSE(idle());
}
std::mutex lock;
std::mutex block;
std::condition_variable cond;
int value;
};
struct QueueWorkerTest : public testing::Test {
static const int kTimeoutMs = 1000;
TestQueueWorker qw;
virtual void SetUp() {
qw.Init();
}
bool QueueValue(int val) {
std::unique_ptr<TestData> data(new TestData(val));
return !qw.QueueWork(std::move(data));
}
bool WaitFor(int val, int timeout_ms = kTimeoutMs) {
std::unique_lock<std::mutex> lk(qw.lock);
auto timeout = std::chrono::milliseconds(timeout_ms);
return qw.cond.wait_for(lk, timeout, [&] { return qw.value == val; });
}
};
struct IdleQueueWorkerTest : public QueueWorkerTest {
const int64_t kIdleTimeoutMs = 100;
virtual void SetUp() {
qw.set_idle_timeout(kIdleTimeoutMs);
qw.Init();
}
};
TEST_F(QueueWorkerTest, single_queue) {
// already isInitialized so should fail
ASSERT_NE(qw.Init(), 0);
ASSERT_EQ(qw.value, 0);
ASSERT_TRUE(QueueValue(1));
ASSERT_TRUE(WaitFor(1));
ASSERT_EQ(qw.value, 1);
ASSERT_FALSE(qw.IsWorkPending());
}
TEST_F(QueueWorkerTest, multiple_waits) {
for (int i = 1; i <= 100; i++) {
ASSERT_TRUE(QueueValue(i));
ASSERT_TRUE(WaitFor(i));
ASSERT_EQ(qw.value, i);
ASSERT_FALSE(qw.IsWorkPending());
}
}
TEST_F(QueueWorkerTest, multiple_queue) {
for (int i = 1; i <= 100; i++) {
ASSERT_TRUE(QueueValue(i));
}
ASSERT_TRUE(WaitFor(100));
ASSERT_EQ(qw.value, 100);
ASSERT_FALSE(qw.IsWorkPending());
}
TEST_F(QueueWorkerTest, blocking) {
// First wait for inital value to be setup
ASSERT_TRUE(QueueValue(1));
ASSERT_TRUE(WaitFor(1));
// Block processing and fill up the queue
std::unique_lock<std::mutex> lk(qw.block);
size_t expected_value = qw.max_queue_size() + 2;
for (size_t i = 2; i <= expected_value; i++) {
ASSERT_TRUE(QueueValue(i));
}
qw.set_queue_timeout(100);
// any additional queueing should fail
ASSERT_FALSE(QueueValue(expected_value + 1));
// make sure value is not changed while blocked
{
std::unique_lock<std::mutex> lock(qw.lock);
auto timeout = std::chrono::milliseconds(100);
ASSERT_FALSE(
qw.cond.wait_for(lock, timeout, [&] { return qw.value != 1; }));
}
ASSERT_EQ(qw.value, 1);
ASSERT_TRUE(qw.IsWorkPending());
// unblock and wait for value to be reached
lk.unlock();
ASSERT_TRUE(WaitFor(expected_value));
ASSERT_FALSE(qw.IsWorkPending());
}
TEST_F(QueueWorkerTest, exit_slow) {
struct SlowData : public TestData {
SlowData(int val) : TestData(val) {
}
void CheckValue(int prev_value) {
UNUSED_ARG(prev_value);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
std::unique_ptr<SlowData> data(new SlowData(1));
ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
data = std::unique_ptr<SlowData>(new SlowData(2));
ASSERT_EQ(qw.QueueWork(std::move(data)), 0);
qw.Exit();
ASSERT_FALSE(qw.initialized());
}
TEST_F(QueueWorkerTest, exit_empty) {
qw.Exit();
ASSERT_FALSE(qw.initialized());
}
TEST_F(QueueWorkerTest, queue_worker_noidling) {
ASSERT_TRUE(QueueValue(1));
ASSERT_TRUE(WaitFor(1));
ASSERT_FALSE(qw.idle());
auto timeout = std::chrono::milliseconds(200);
std::this_thread::sleep_for(timeout);
ASSERT_FALSE(qw.idle());
}
TEST_F(IdleQueueWorkerTest, queue_worker_idling) {
ASSERT_TRUE(QueueValue(1));
ASSERT_TRUE(WaitFor(1));
ASSERT_FALSE(qw.idle());
auto timeout = std::chrono::milliseconds(kIdleTimeoutMs + 10);
std::this_thread::sleep_for(timeout);
ASSERT_TRUE(qw.idle());
ASSERT_TRUE(QueueValue(2));
ASSERT_TRUE(WaitFor(2));
ASSERT_FALSE(qw.idle());
std::this_thread::sleep_for(3 * timeout);
ASSERT_TRUE(qw.idle());
ASSERT_TRUE(QueueValue(3));
ASSERT_TRUE(WaitFor(3));
for (int i = 4; i <= 100; i++) {
QueueValue(i);
}
ASSERT_FALSE(qw.idle());
qw.Exit();
ASSERT_FALSE(qw.initialized());
}