blob: c593461dbd3d60178ac211df0f3f11b33ddabe34 [file] [log] [blame] [edit]
// Copyright 2024 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-loop/loop.h>
#include <lib/async/cpp/irq.h>
#include <lib/async/cpp/task.h>
#include <lib/sync/cpp/completion.h>
#include <lib/zx/interrupt.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <gtest/gtest.h>
#include "src/devices/testing/mock-ddk/mock-device.h"
#include "src/graphics/display/lib/driver-framework-migration-utils/dispatcher/loop-backed-dispatcher-factory.h"
#include "src/lib/testing/predicates/status.h"
namespace display {
namespace {
TEST(LoopBackedDispatcher, DispatchAsyncTasks) {
std::shared_ptr<MockDevice> mock_root = MockDevice::FakeRootParent();
LoopBackedDispatcherFactory factory(mock_root.get());
zx::result<std::unique_ptr<Dispatcher>> create_dispatcher_result =
factory.Create("test_dispatcher", /*scheduler_role=*/{});
ASSERT_OK(create_dispatcher_result.status_value());
std::unique_ptr<Dispatcher> dispatcher = std::move(create_dispatcher_result).value();
libsync::Completion task_completion;
async::PostTask(dispatcher->async_dispatcher(), [&task_completion] { task_completion.Signal(); });
task_completion.Wait();
ASSERT_TRUE(task_completion.signaled());
}
TEST(LoopBackedDispatcher, DispatchIrq) {
std::shared_ptr<MockDevice> mock_root = MockDevice::FakeRootParent();
LoopBackedDispatcherFactory factory(mock_root.get());
zx::result<std::unique_ptr<Dispatcher>> create_dispatcher_result =
factory.Create("irq_dispatcher", /*scheduler_role=*/{});
ASSERT_OK(create_dispatcher_result.status_value());
std::unique_ptr<Dispatcher> dispatcher = std::move(create_dispatcher_result).value();
zx::interrupt irq;
zx_status_t status = zx::interrupt::create(zx::resource{}, 0, ZX_INTERRUPT_VIRTUAL, &irq);
ASSERT_OK(status);
zx::time_boot latest_handled_irq_timestamp;
libsync::Completion irq_handler_invoked;
libsync::Completion irq_handler_canceled;
async::Irq irq_handler(
irq.get(), /*trigger=*/ZX_SIGNAL_NONE,
[&irq_handler_invoked, &irq_handler_canceled, &latest_handled_irq_timestamp](
async_dispatcher_t* dispatcher, async::Irq* irq, zx_status_t status,
const zx_packet_interrupt_t* interrupt) {
ASSERT_TRUE(status == ZX_OK || status == ZX_ERR_CANCELED)
<< "Invalid async Irq wait status: " << zx_status_get_string(status);
if (status == ZX_ERR_CANCELED) {
irq_handler_canceled.Signal();
return;
}
latest_handled_irq_timestamp = zx::time_boot(interrupt->timestamp);
irq_handler_invoked.Signal();
// Acknowledges the interrupt so that it can be triggered again.
zx::unowned_interrupt(irq->object())->ack();
});
irq_handler.Begin(dispatcher->async_dispatcher());
status = irq_handler_invoked.Wait(zx::sec(1));
EXPECT_STATUS(status, ZX_ERR_TIMED_OUT);
// Manually trigger the virtual interrupt.
static constexpr zx::time_boot kIrqTimestamp1 = zx::time_boot(0x12345678);
status = irq.trigger(0u, kIrqTimestamp1);
ASSERT_OK(status);
// The interrupt handler is invoked when the interrupt is triggered.
irq_handler_invoked.Wait();
EXPECT_EQ(latest_handled_irq_timestamp, kIrqTimestamp1);
// Manually trigger the virtual interrupt again.
irq_handler_invoked.Reset();
static constexpr zx::time_boot kIrqTimestamp2 = zx::time_boot(0x23456789);
status = irq.trigger(0u, kIrqTimestamp2);
ASSERT_OK(status);
// The interrupt handler can be invoked again when the interrupt is triggered
// again.
irq_handler_invoked.Wait();
EXPECT_EQ(latest_handled_irq_timestamp, kIrqTimestamp2);
dispatcher.reset();
irq_handler_canceled.Wait();
}
} // namespace
} // namespace display