blob: 3b9d4926194e2273df37cd5d6d60013248f087e2 [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 <stdio.h>
#include <lib/async-testutils/dispatcher_stub.h>
#include <lib/async/cpp/exception.h>
#include <unittest/unittest.h>
namespace {
const zx_handle_t dummy_task = static_cast<zx_handle_t>(1);
const uint32_t dummy_options = 99;
const zx_koid_t dummy_pid = 23;
const zx_koid_t dummy_tid = 42;
const zx_port_packet_t dummy_exception{
.key = 0,
.type = 0,
.status = ZX_OK,
.exception = {
.pid = dummy_pid,
.tid = dummy_tid,
.reserved0 = 0u,
.reserved1 = 0u},
};
class MockDispatcher : public async::DispatcherStub {
public:
enum class Op {
NONE,
BIND,
UNBIND,
};
zx_status_t BindExceptionPort(async_exception_t* exception) override {
printf("%s: called\n", __func__);
last_op = Op::BIND;
last_exception = exception;
return next_status;
}
zx_status_t UnbindExceptionPort(async_exception_t* exception) override {
printf("%s: called\n", __func__);
last_op = Op::UNBIND;
last_exception = exception;
return next_status;
}
Op last_op = Op::NONE;
async_exception_t* last_exception = nullptr;
zx_status_t next_status = ZX_OK;
};
class Harness {
public:
Harness() { Reset(); }
void Reset() {
handler_ran = false;
last_exception = nullptr;
last_status = ZX_ERR_INTERNAL;
last_report = nullptr;
}
void Handler(async_dispatcher_t* dispatcher, async::ExceptionBase* exception,
zx_status_t status, const zx_port_packet_t* report) {
handler_ran = true;
last_exception = exception;
last_status = status;
last_report = report;
}
virtual async::ExceptionBase& exception() = 0;
bool handler_ran;
async::ExceptionBase* last_exception;
zx_status_t last_status;
const zx_port_packet_t* last_report;
};
class LambdaHarness : public Harness {
public:
LambdaHarness(zx_handle_t task = ZX_HANDLE_INVALID,
uint32_t options = 0)
: exception_{task, options,
[this](async_dispatcher_t* dispatcher, async::Exception* exception,
zx_status_t status, const zx_port_packet_t* report) {
Handler(dispatcher, exception, status, report);
}} {}
async::ExceptionBase& exception() override { return exception_; }
private:
async::Exception exception_;
};
class MethodHarness : public Harness {
public:
MethodHarness(zx_handle_t task = ZX_HANDLE_INVALID,
uint32_t options = 0)
: exception_{this, task, options} {}
async::ExceptionBase& exception() override { return exception_; }
private:
async::ExceptionMethod<Harness, &Harness::Handler> exception_;
};
template <typename Harness>
bool exception_is_bound_test() {
BEGIN_TEST;
MockDispatcher dispatcher;
Harness harness;
EXPECT_FALSE(harness.exception().is_bound());
EXPECT_EQ(harness.exception().Bind(&dispatcher), ZX_OK);
EXPECT_TRUE(harness.exception().is_bound());
EXPECT_EQ(harness.exception().Unbind(), ZX_OK);
EXPECT_FALSE(harness.exception().is_bound());
END_TEST;
}
template <typename Harness>
bool exception_bind_test() {
BEGIN_TEST;
MockDispatcher dispatcher;
{
Harness harness(dummy_task, dummy_options);
EXPECT_FALSE(harness.exception().is_bound());
dispatcher.next_status = ZX_OK;
EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
EXPECT_TRUE(harness.exception().is_bound());
EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
EXPECT_EQ(dummy_task, dispatcher.last_exception->task);
EXPECT_EQ(dummy_options, dispatcher.last_exception->options);
EXPECT_FALSE(harness.handler_ran);
harness.Reset();
dispatcher.last_op = MockDispatcher::Op::NONE;
EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, harness.exception().Bind(&dispatcher));
EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
EXPECT_FALSE(harness.handler_ran);
}
EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
{
Harness harness(dummy_task, dummy_options);
EXPECT_FALSE(harness.exception().is_bound());
dispatcher.next_status = ZX_ERR_BAD_STATE;
EXPECT_EQ(ZX_ERR_BAD_STATE, harness.exception().Bind(&dispatcher));
EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
EXPECT_FALSE(harness.exception().is_bound());
EXPECT_FALSE(harness.handler_ran);
}
EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
END_TEST;
}
template <typename Harness>
bool exception_unbind_test() {
BEGIN_TEST;
MockDispatcher dispatcher;
{
Harness harness(dummy_task, dummy_options);
EXPECT_FALSE(harness.exception().is_bound());
EXPECT_EQ(ZX_ERR_NOT_FOUND, harness.exception().Unbind());
EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
EXPECT_FALSE(harness.exception().is_bound());
EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
EXPECT_TRUE(harness.exception().is_bound());
EXPECT_EQ(ZX_OK, harness.exception().Unbind());
EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
EXPECT_FALSE(harness.exception().is_bound());
dispatcher.last_op = MockDispatcher::Op::NONE;
EXPECT_EQ(ZX_ERR_NOT_FOUND, harness.exception().Unbind());
EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
EXPECT_FALSE(harness.exception().is_bound());
}
EXPECT_EQ(MockDispatcher::Op::NONE, dispatcher.last_op);
END_TEST;
}
template <typename Harness>
bool exception_run_handler_test() {
BEGIN_TEST;
MockDispatcher dispatcher;
{
Harness harness(dummy_task, dummy_options);
EXPECT_FALSE(harness.exception().is_bound());
EXPECT_EQ(ZX_OK, harness.exception().Bind(&dispatcher));
EXPECT_EQ(MockDispatcher::Op::BIND, dispatcher.last_op);
EXPECT_TRUE(harness.exception().is_bound());
harness.Reset();
dispatcher.last_exception->handler(&dispatcher, dispatcher.last_exception, ZX_OK, &dummy_exception);
EXPECT_TRUE(harness.handler_ran);
EXPECT_EQ(&harness.exception(), harness.last_exception);
EXPECT_EQ(ZX_OK, harness.last_status);
EXPECT_EQ(&dummy_exception, harness.last_report);
EXPECT_TRUE(harness.exception().is_bound());
}
EXPECT_EQ(MockDispatcher::Op::UNBIND, dispatcher.last_op);
END_TEST;
}
bool unsupported_bind_test() {
BEGIN_TEST;
async::DispatcherStub dispatcher;
async_exception_t exception{};
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
async_bind_exception_port(&dispatcher, &exception),
"valid args");
END_TEST;
}
bool unsupported_unbind_test() {
BEGIN_TEST;
async::DispatcherStub dispatcher;
async_exception_t exception{};
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
async_unbind_exception_port(&dispatcher, &exception),
"valid args");
END_TEST;
}
bool unsupported_resume_test() {
BEGIN_TEST;
async::DispatcherStub dispatcher;
async_exception_t exception{};
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED,
async_resume_from_exception(&dispatcher, &exception,
ZX_HANDLE_INVALID, 0u),
"valid args");
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(exception_tests)
RUN_TEST((exception_is_bound_test<LambdaHarness>))
RUN_TEST((exception_is_bound_test<MethodHarness>))
RUN_TEST((exception_bind_test<LambdaHarness>))
RUN_TEST((exception_bind_test<MethodHarness>))
RUN_TEST((exception_unbind_test<LambdaHarness>))
RUN_TEST((exception_unbind_test<MethodHarness>))
RUN_TEST((exception_run_handler_test<LambdaHarness>))
RUN_TEST((exception_run_handler_test<MethodHarness>))
RUN_TEST(unsupported_bind_test)
RUN_TEST(unsupported_unbind_test)
RUN_TEST(unsupported_resume_test)
END_TEST_CASE(exception_tests)