| // 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(fxbug.dev/51303): 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 |