blob: 9a37a6496f1f7d0141d46a21f03b940f0607c31b [file] [log] [blame]
// Copyright 2020 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 "src/ui/lib/escher/flib/fence_queue.h"
#include <lib/async-testing/test_loop.h>
#include <lib/syslog/cpp/macros.h>
#include <gtest/gtest.h>
#include "src/ui/lib/escher/test/flib/util.h"
namespace escher {
namespace test {
TEST(FenceQueueTest, QueueTaskWithNoFences) {
async::TestLoop loop;
bool task_complete = false;
auto task = [&task_complete] { task_complete = true; };
auto fence_queue = std::make_shared<FenceQueue>();
fence_queue->QueueTask(std::move(task), {});
EXPECT_FALSE(task_complete);
loop.RunUntilIdle();
EXPECT_TRUE(task_complete);
}
TEST(FenceQueueTest, QueueTaskWithFence) {
async::TestLoop loop;
bool task_complete = false;
auto task = [&task_complete] { task_complete = true; };
auto fence_queue = std::make_shared<FenceQueue>();
std::vector<zx::event> events;
zx::event fence;
zx::event::create(0, &fence);
events.emplace_back(CopyEvent(fence));
fence_queue->QueueTask(std::move(task), std::move(events));
EXPECT_FALSE(task_complete);
loop.RunUntilIdle();
EXPECT_FALSE(task_complete);
fence.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_TRUE(task_complete);
}
// Queues a task with multiple fences and checks that the task doesn't run until all fences have
// been signaled.
TEST(FenceQueueTest, QueueTaskWithMultipleFences) {
async::TestLoop loop;
bool task_complete = false;
auto task = [&task_complete] { task_complete = true; };
auto fence_queue = std::make_shared<FenceQueue>();
std::vector<zx::event> events;
zx::event fence1;
zx::event::create(0, &fence1);
events.emplace_back(CopyEvent(fence1));
zx::event fence2;
zx::event::create(0, &fence2);
events.emplace_back(CopyEvent(fence2));
fence_queue->QueueTask(std::move(task), std::move(events));
EXPECT_FALSE(task_complete);
loop.RunUntilIdle();
EXPECT_FALSE(task_complete);
// Signal fences out of order, since it shouldn't matter.
fence2.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_FALSE(task_complete);
fence1.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_TRUE(task_complete);
}
// Queues two tasks, then signals the fences in order and confirms that the updates are handled as
// their indiviual fences are signaled.
TEST(FenceQueueTest, QueueMultipleTasksWithFences_SignalledInOrder) {
async::TestLoop loop;
bool task1_complete = false;
bool task2_complete = false;
auto fence_queue = std::make_shared<FenceQueue>();
zx::event fence1;
{
std::vector<zx::event> events;
zx::event::create(0, &fence1);
events.emplace_back(CopyEvent(fence1));
fence_queue->QueueTask([&task1_complete] { task1_complete = true; }, std::move(events));
}
zx::event fence2;
{
std::vector<zx::event> events;
zx::event::create(0, &fence2);
events.emplace_back(CopyEvent(fence2));
fence_queue->QueueTask([&task2_complete] { task2_complete = true; }, std::move(events));
}
loop.RunUntilIdle();
EXPECT_FALSE(task1_complete);
EXPECT_FALSE(task2_complete);
fence1.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_TRUE(task1_complete);
EXPECT_FALSE(task2_complete);
fence2.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_TRUE(task1_complete);
EXPECT_TRUE(task2_complete);
}
// Queues two tasks, then signals their fences out order and confirms that no task is completed
// before the first task's fence is signaled.
TEST(FenceQueueTest, QueueMultipleTasksWithFences_SignalledOutOfOrder) {
async::TestLoop loop;
bool task1_complete = false;
bool task2_complete = false;
auto fence_queue = std::make_shared<FenceQueue>();
zx::event fence1;
{
std::vector<zx::event> events;
zx::event::create(0, &fence1);
events.emplace_back(CopyEvent(fence1));
fence_queue->QueueTask([&task1_complete] { task1_complete = true; }, std::move(events));
}
zx::event fence2;
{
std::vector<zx::event> events;
zx::event::create(0, &fence2);
events.emplace_back(CopyEvent(fence2));
fence_queue->QueueTask([&task2_complete] { task2_complete = true; }, std::move(events));
}
loop.RunUntilIdle();
EXPECT_FALSE(task1_complete);
EXPECT_FALSE(task2_complete);
// fence2 signalled, but task1 hasn't completed so should not run yet.
fence2.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_FALSE(task1_complete);
EXPECT_FALSE(task2_complete);
// All fences signalled, all tasks should run.
fence1.signal(0u, ZX_EVENT_SIGNALED);
loop.RunUntilIdle();
EXPECT_TRUE(task1_complete);
EXPECT_TRUE(task2_complete);
}
// Test that destroys the FenceQueue inside a task and confirms that it terminates gracefully.
TEST(FenceQueueTest, DestroyFenceQueueInTask) {
async::TestLoop loop;
bool task1_complete = false;
bool task2_complete = false;
auto fence_queue = std::make_shared<FenceQueue>();
// Keep only a weak reference outside the closure.
std::weak_ptr<FenceQueue> weak = fence_queue;
EXPECT_FALSE(weak.expired());
fence_queue->QueueTask(/*task*/
[&task1_complete, shared = std::move(fence_queue)]() mutable {
task1_complete = true;
shared.reset();
},
/*fences*/ {});
// Should not fire, since the queue should be destroyed before this.
weak.lock()->QueueTask(/*task*/ [&task2_complete] { task2_complete = true; }, /*fences*/ {});
loop.RunUntilIdle();
EXPECT_TRUE(task1_complete);
EXPECT_FALSE(task2_complete);
EXPECT_TRUE(weak.expired());
}
// Test that destroys the FenceQueue after a task should have been put on the looper and ensures it
// doesn't run.
TEST(FenceQueueTest, DestroyFenceQueueBeforeTask) {
async::TestLoop loop;
bool task_complete = false;
auto fence_queue = std::make_shared<FenceQueue>();
fence_queue->QueueTask(/*task*/ [&task_complete]() { task_complete = true; }, /*fences*/ {});
fence_queue.reset();
loop.RunUntilIdle();
EXPECT_FALSE(task_complete);
}
} // namespace test
} // namespace escher