#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()); }