blob: 076e5d27e5a051f23d0fbb253a3a2e4899a7a52f [file] [log] [blame]
// Copyright 2017 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 <fidl/fuchsia.examples/cpp/wire.h>
#include <memory>
#include <ddktl/device.h>
#include <zxtest/zxtest.h>
// TODO(https://fxbug.dev/42128480): The negative compilation tests are broken
// #define TEST_WILL_NOT_COMPILE 1
namespace {
class TestNone : public ddk::Device<TestNone> {
public:
TestNone() : ddk::Device<TestNone>(nullptr) {}
void DdkRelease() {}
};
#define BEGIN_SUCCESS_CASE(name) \
class Test##name : public ddk::Device<Test##name, ddk::name> { \
public: \
Test##name() : ddk::Device<Test##name, ddk::name>(nullptr) {} \
void DdkRelease() {}
#define BEGIN_SUCCESS_DEPRECATED_CASE(name) \
class Test##name : public ddk::Device<Test##name, ddk_deprecated::name> { \
public: \
Test##name() : ddk::Device<Test##name, ddk_deprecated::name>(nullptr) {} \
void DdkRelease() {}
#define END_SUCCESS_CASE \
} \
;
BEGIN_SUCCESS_CASE(GetProtocolable)
zx_status_t DdkGetProtocol(uint32_t proto_id, void* protocol) { return ZX_OK; }
END_SUCCESS_CASE
BEGIN_SUCCESS_CASE(Initializable)
// As the txn does not contain a valid device pointer, the destructor won't throw an error
// if we don't reply.
void DdkInit(ddk::InitTxn txn) {}
END_SUCCESS_CASE
BEGIN_SUCCESS_CASE(Unbindable)
// As the txn does not contain a valid device pointer, the destructor won't throw an error
// if we don't reply.
void DdkUnbind(ddk::UnbindTxn txn) {}
END_SUCCESS_CASE
class TestMessageable;
using MessageableDevice =
ddk::Device<TestMessageable, ddk::Messageable<fuchsia_examples::Echo>::Mixin>;
class TestMessageable : public MessageableDevice {
public:
TestMessageable() : MessageableDevice(nullptr) {}
void DdkRelease() {}
void SendString(SendStringRequestView request, SendStringCompleter::Sync& completer) override {}
void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {}
};
BEGIN_SUCCESS_CASE(Suspendable)
// As the txn does not contain a valid device pointer, the destructor won't throw an error
// if we don't reply.
void DdkSuspend(ddk::SuspendTxn txn) {}
END_SUCCESS_CASE
BEGIN_SUCCESS_CASE(Resumable)
// As the txn does not contain a valid device pointer, the destructor won't throw an error
// if we don't reply.
void DdkResume(ddk::ResumeTxn txn) {}
END_SUCCESS_CASE
BEGIN_SUCCESS_CASE(Rxrpcable)
zx_status_t DdkRxrpc(zx_handle_t channel) { return ZX_OK; }
END_SUCCESS_CASE
template <typename T>
static void do_test() {
auto dev = std::make_unique<T>();
}
struct TestDispatch;
using TestDispatchType =
ddk::Device<TestDispatch, ddk::GetProtocolable, ddk::Initializable, ddk::Unbindable,
ddk::Suspendable, ddk::Resumable, ddk::Rxrpcable>;
struct TestDispatch : public TestDispatchType {
TestDispatch() : TestDispatchType(nullptr) {}
// Give access to the device ops for testing
const zx_protocol_device_t* GetDeviceOps() { return &ddk_device_proto_; }
zx_status_t DdkGetProtocol(uint32_t proto_id, void* protcool) {
get_protocol_called = true;
return ZX_OK;
}
void DdkInit(ddk::InitTxn txn) { init_called = true; }
void DdkUnbind(ddk::UnbindTxn txn) { unbind_called = true; }
void DdkRelease() { release_called = true; }
void DdkSuspend(ddk::SuspendTxn txn) { suspend_called = true; }
void DdkResume(ddk::ResumeTxn txn) { resume_called = true; }
zx_status_t DdkRxrpc(zx_handle_t channel) {
rxrpc_called = true;
return ZX_OK;
}
bool get_protocol_called = false;
bool init_called = false;
bool unbind_called = false;
bool release_called = false;
bool suspend_called = false;
bool resume_called = false;
bool rxrpc_called = false;
};
TEST(DdktlDevice, Dispatch) {
auto dev = std::make_unique<TestDispatch>();
// Since we're not adding the device to devmgr, we don't have a valid zx_device_t.
// TODO: use a devmgr API to add a test device, and use that instead
auto ctx = dev.get();
auto ops = dev->GetDeviceOps();
EXPECT_EQ(ZX_OK, ops->get_protocol(ctx, 0, nullptr), "");
ops->init(ctx);
ops->unbind(ctx);
ops->release(ctx);
ops->suspend(ctx, 2, false, 0);
ops->resume(ctx, DEV_POWER_STATE_D0);
EXPECT_EQ(ZX_OK, ops->rxrpc(ctx, 0), "");
EXPECT_TRUE(dev->get_protocol_called, "");
EXPECT_TRUE(dev->init_called, "");
EXPECT_TRUE(dev->unbind_called, "");
EXPECT_TRUE(dev->release_called, "");
EXPECT_TRUE(dev->suspend_called, "");
EXPECT_TRUE(dev->resume_called, "");
EXPECT_TRUE(dev->rxrpc_called, "");
}
#if TEST_WILL_NOT_COMPILE || 0
class TestNotReleasable : public ddk::Device<TestNotReleasable> {
public:
TestNotReleasable() : ddk::Device<TestNotReleasable>(nullptr) {}
};
#define DEFINE_FAIL_CASE(name) \
class TestNot##name : public ddk::Device<TestNot##name, ddk::name> { \
public: \
TestNot##name() : ddk::Device<TestNot##name, ddk::name>(nullptr) {} \
void DdkRelease() {} \
};
DEFINE_FAIL_CASE(GetProtocolable)
DEFINE_FAIL_CASE(Initializable)
DEFINE_FAIL_CASE(Unbindable)
DEFINE_FAIL_CASE(Suspendable)
DEFINE_FAIL_CASE(Resumable)
DEFINE_FAIL_CASE(Rxrpcable)
class TestBadOverride : public ddk::Device<TestBadOverride> {
public:
TestBadOverride() : ddk::Device<TestBadOverride>(nullptr) {}
void DdkRelease() {}
};
class TestHiddenOverride : public ddk::Device<TestHiddenOverride> {
public:
TestHiddenOverride() : ddk::Device<TestHiddenOverride>(nullptr) {}
private:
void DdkRelease() {}
};
class TestStaticOverride : public ddk::Device<TestStaticOverride> {
public:
TestStaticOverride() : ddk::Device<TestStaticOverride>(nullptr) {}
static void DdkRelease() {}
};
template <typename D>
struct A {
explicit A(zx_protocol_device_t* proto) {}
};
class TestNotAMixin : public ddk::Device<TestNotAMixin, A> {
public:
TestNotAMixin() : ddk::Device<TestNotAMixin, A>(nullptr) {}
void DdkRelease() {}
};
class TestNotAllMixins;
using TestNotAllMixinsType = ddk::Device<TestNotAllMixins, A>;
class TestNotAllMixins : public TestNotAllMixinsType {
public:
TestNotAllMixins() : TestNotAllMixinsType(nullptr) {}
void DdkRelease() {}
};
#endif
TEST(DdktlDevice, NoMixins) { do_test<TestNone>(); }
TEST(DdktlDevice, MixinGetProtocolable) { do_test<TestGetProtocolable>(); }
TEST(DdktlDevice, MixinInitializable) { do_test<TestInitializable>(); }
TEST(DdktlDevice, MixinUnbindable) { do_test<TestUnbindable>(); }
TEST(DdktlDevice, MixinSuspendable) { do_test<TestSuspendable>(); }
TEST(DdktlDevice, MixinResumable) { do_test<TestResumable>(); }
TEST(DdktlDevice, MixinRxrpcable) { do_test<TestRxrpcable>(); }
} // namespace
#if TEST_WILL_NOT_COMPILE || 0
TEST(DdktlDevice, FailNoGetProtocol) { do_test<TestNotGetProtocolable>(); }
TEST(DdktlDevice, FailNoInitialize) { do_test<TestNotInitializable>(); }
TEST(DdktlDevice, FailNoUnbind) { do_test<TestNotUnbindable>(); }
TEST(DdktlDevice, FailNoSuspende) { do_test<TestNotSuspendable>(); }
TEST(DdktlDevice, FailNoResume) { do_test<TestNotResumable>(); }
TEST(DdktlDevice, FailNoRxrpc) { do_test<TestNotRxrpcable>(); }
TEST(DdktlDevice, FailBadOverride) { do_test<TestBadOverride>(); }
TEST(DdktlDevice, FailHiddenOverride) { do_test<TestHiddenOverride>(); }
TEST(DdktlDevice, FailStaticOverride) { do_test<TestStaticOverride>(); }
TEST(DdktlDevice, FailNotAMixin) { do_test<TestNotAMixin>(); }
TEST(DdktlDevice, FailNotAllMixins) { do_test<TestNotAllMixins>(); }
#endif