1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddktl/device.h>
6#include <fbl/alloc_checker.h>
7#include <fbl/unique_ptr.h>
8#include <unittest/unittest.h>
9
10//#define TEST_WILL_NOT_COMPILE 1
11
12namespace {
13
14class TestNone : public ddk::Device<TestNone> {
15  public:
16    TestNone() : ddk::Device<TestNone>(nullptr) {}
17
18    void DdkRelease() {}
19};
20
21#define BEGIN_SUCCESS_CASE(name) \
22class Test##name : public ddk::Device<Test##name, ddk::name> { \
23  public: \
24    Test##name() : ddk::Device<Test##name, ddk::name>(nullptr) {} \
25    void DdkRelease() {}
26
27#define END_SUCCESS_CASE };
28
29BEGIN_SUCCESS_CASE(GetProtocolable)
30    zx_status_t DdkGetProtocol(uint32_t proto_id, void* protocol) { return ZX_OK; }
31END_SUCCESS_CASE
32
33BEGIN_SUCCESS_CASE(Openable)
34    zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags) { return ZX_OK; }
35END_SUCCESS_CASE
36
37BEGIN_SUCCESS_CASE(OpenAtable)
38    zx_status_t DdkOpenAt(zx_device_t** dev_out, const char* path, uint32_t flags) {
39        return ZX_OK;
40    }
41END_SUCCESS_CASE
42
43BEGIN_SUCCESS_CASE(Closable)
44    zx_status_t DdkClose(uint32_t flags) { return ZX_OK; }
45END_SUCCESS_CASE
46
47BEGIN_SUCCESS_CASE(Unbindable)
48    void DdkUnbind() {}
49END_SUCCESS_CASE
50
51BEGIN_SUCCESS_CASE(Readable)
52    zx_status_t DdkRead(void* buf, size_t count, zx_off_t off, size_t* actual) { return ZX_OK; }
53END_SUCCESS_CASE
54
55BEGIN_SUCCESS_CASE(Writable)
56    zx_status_t DdkWrite(const void* buf, size_t count, zx_off_t off, size_t* actual) {
57        return ZX_OK;
58    }
59END_SUCCESS_CASE
60
61BEGIN_SUCCESS_CASE(GetSizable)
62    zx_off_t DdkGetSize() { return 0; }
63END_SUCCESS_CASE
64
65BEGIN_SUCCESS_CASE(Ioctlable)
66    zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
67                         size_t out_len, size_t* out_actual) {
68        return ZX_OK;
69    }
70END_SUCCESS_CASE
71
72BEGIN_SUCCESS_CASE(Messageable)
73    zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
74        return ZX_OK;
75    }
76END_SUCCESS_CASE
77
78BEGIN_SUCCESS_CASE(Suspendable)
79    zx_status_t DdkSuspend(uint32_t flags) { return ZX_OK; }
80END_SUCCESS_CASE
81
82BEGIN_SUCCESS_CASE(Resumable)
83    zx_status_t DdkResume(uint32_t flags) { return ZX_OK; }
84END_SUCCESS_CASE
85
86BEGIN_SUCCESS_CASE(Rxrpcable)
87    zx_status_t DdkRxrpc(zx_handle_t channel) { return ZX_OK; }
88END_SUCCESS_CASE
89
90template <typename T>
91static bool do_test() {
92    BEGIN_TEST;
93
94    fbl::AllocChecker ac;
95    auto dev = fbl::unique_ptr<T>(new (&ac) T);
96    ASSERT_TRUE(ac.check(), "");
97
98    END_TEST;
99}
100
101struct TestDispatch : public ddk::FullDevice<TestDispatch> {
102    TestDispatch() : ddk::FullDevice<TestDispatch>(nullptr) {}
103
104    // Give access to the device ops for testing
105    zx_protocol_device_t* GetDeviceOps() {
106        return &ddk_device_proto_;
107    }
108
109    zx_status_t DdkGetProtocol(uint32_t proto_id, void* protcool) {
110        get_protocol_called = true;
111        return ZX_OK;
112    }
113
114    zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags) {
115        open_called = true;
116        return ZX_OK;
117    }
118
119    zx_status_t DdkOpenAt(zx_device_t** dev_out, const char* path, uint32_t flags) {
120        open_at_called = true;
121        return ZX_OK;
122    }
123
124    zx_status_t DdkClose(uint32_t flags) {
125        close_called = true;
126        return ZX_OK;
127    }
128
129    void DdkUnbind() {
130        unbind_called = true;
131    }
132
133    void DdkRelease() {
134        release_called = true;
135    }
136
137    zx_status_t DdkRead(void* buf, size_t count, zx_off_t off, size_t* actual) {
138        read_called = true;
139        return ZX_OK;
140    }
141
142    zx_status_t DdkWrite(const void* buf, size_t count, zx_off_t off, size_t* actual) {
143        write_called = true;
144        return ZX_OK;
145    }
146
147    zx_off_t DdkGetSize() {
148        get_size_called = true;
149        return 0;
150    }
151
152    zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
153                         size_t out_len, size_t* out_actual) {
154        ioctl_called = true;
155        return ZX_OK;
156    }
157
158    zx_status_t DdkSuspend(uint32_t flags) {
159        suspend_called = true;
160        return ZX_OK;
161    }
162
163    zx_status_t DdkResume(uint32_t flags) {
164        resume_called = true;
165        return ZX_OK;
166    }
167
168    zx_status_t DdkRxrpc(zx_handle_t channel) {
169        rxrpc_called = true;
170        return ZX_OK;
171    }
172
173    bool get_protocol_called = false;
174    bool open_called = false;
175    bool open_at_called = false;
176    bool close_called = false;
177    bool unbind_called = false;
178    bool release_called = false;
179    bool read_called = false;
180    bool write_called = false;
181    bool get_size_called = false;
182    bool ioctl_called = false;
183    bool suspend_called = false;
184    bool resume_called = false;
185    bool rxrpc_called = false;
186};
187
188static bool test_dispatch() {
189    BEGIN_TEST;
190
191    fbl::AllocChecker ac;
192    auto dev = fbl::unique_ptr<TestDispatch>(new (&ac) TestDispatch);
193    ASSERT_TRUE(ac.check(), "");
194
195    // Since we're not adding the device to devmgr, we don't have a valid zx_device_t.
196    // TODO: use a devmgr API to add a test device, and use that instead
197    auto ctx = dev.get();
198    auto ops = dev->GetDeviceOps();
199    EXPECT_EQ(ZX_OK, ops->get_protocol(ctx, 0, nullptr), "");
200    EXPECT_EQ(ZX_OK, ops->open(ctx, nullptr, 0), "");
201    EXPECT_EQ(ZX_OK, ops->open_at(ctx, nullptr, "", 0), "");
202    EXPECT_EQ(ZX_OK, ops->close(ctx, 0), "");
203    ops->unbind(ctx);
204    ops->release(ctx);
205    EXPECT_EQ(ZX_OK, ops->read(ctx, nullptr, 0, 0, nullptr), "");
206    EXPECT_EQ(ZX_OK, ops->write(ctx, nullptr, 0, 0, nullptr), "");
207    EXPECT_EQ(0, ops->get_size(ctx), "");
208    EXPECT_EQ(ZX_OK, ops->ioctl(ctx, 0, nullptr, 0, nullptr, 0, nullptr), "");
209    EXPECT_EQ(ZX_OK, ops->suspend(ctx, 0), "");
210    EXPECT_EQ(ZX_OK, ops->resume(ctx, 0), "");
211    EXPECT_EQ(ZX_OK, ops->rxrpc(ctx, 0), "");
212
213    EXPECT_TRUE(dev->get_protocol_called, "");
214    EXPECT_TRUE(dev->open_called, "");
215    EXPECT_TRUE(dev->open_at_called, "");
216    EXPECT_TRUE(dev->close_called, "");
217    EXPECT_TRUE(dev->unbind_called, "");
218    EXPECT_TRUE(dev->release_called, "");
219    EXPECT_TRUE(dev->read_called, "");
220    EXPECT_TRUE(dev->write_called, "");
221    EXPECT_TRUE(dev->get_size_called, "");
222    EXPECT_TRUE(dev->ioctl_called, "");
223    EXPECT_TRUE(dev->suspend_called, "");
224    EXPECT_TRUE(dev->resume_called, "");
225    EXPECT_TRUE(dev->rxrpc_called, "");
226
227    END_TEST;
228}
229
230#if TEST_WILL_NOT_COMPILE || 0
231
232class TestNotReleasable : public ddk::Device<TestNotReleasable> {
233  public:
234    TestNotReleasable() : ddk::Device<TestNotReleasable>(nullptr) {}
235};
236
237#define DEFINE_FAIL_CASE(name) \
238class TestNot##name : public ddk::Device<TestNot##name, ddk::name> { \
239  public: \
240    TestNot##name() : ddk::Device<TestNot##name, ddk::name>(nullptr) {} \
241    void DdkRelease() {} \
242};
243
244DEFINE_FAIL_CASE(GetProtocolable)
245DEFINE_FAIL_CASE(Openable)
246DEFINE_FAIL_CASE(OpenAtable)
247DEFINE_FAIL_CASE(Closable)
248DEFINE_FAIL_CASE(Unbindable)
249DEFINE_FAIL_CASE(Readable)
250DEFINE_FAIL_CASE(Writable)
251DEFINE_FAIL_CASE(IotxnQueueable)
252DEFINE_FAIL_CASE(GetSizable)
253DEFINE_FAIL_CASE(Ioctlable)
254DEFINE_FAIL_CASE(Suspendable)
255DEFINE_FAIL_CASE(Resumable)
256DEFINE_FAIL_CASE(Rxrpcable)
257
258class TestBadOverride : public ddk::Device<TestBadOverride, ddk::Closable> {
259  public:
260    TestBadOverride() : ddk::Device<TestBadOverride, ddk::Closable>(nullptr) {}
261    void DdkRelease() {}
262
263    void DdkClose(uint32_t flags) {}
264};
265
266class TestHiddenOverride : public ddk::Device<TestHiddenOverride, ddk::Closable> {
267  public:
268    TestHiddenOverride()
269      : ddk::Device<TestHiddenOverride, ddk::Closable>(nullptr) {}
270    void DdkRelease() {}
271
272  private:
273    zx_status_t DdkClose(uint32_t flags) { return ZX_OK; }
274};
275
276class TestStaticOverride : public ddk::Device<TestStaticOverride, ddk::Closable> {
277  public:
278    TestStaticOverride()
279      : ddk::Device<TestStaticOverride, ddk::Closable>(nullptr) {}
280    void DdkRelease() {}
281
282    static zx_status_t DdkClose(uint32_t flags) { return ZX_OK; }
283};
284
285template <typename D>
286struct A {
287    explicit A(zx_protocol_device_t* proto) {}
288};
289
290class TestNotAMixin : public ddk::Device<TestNotAMixin, A> {
291  public:
292    TestNotAMixin() : ddk::Device<TestNotAMixin, A>(nullptr) {}
293    void DdkRelease() {}
294};
295
296class TestNotAllMixins;
297using TestNotAllMixinsType = ddk::Device<TestNotAllMixins,
298                                         ddk::Openable,
299                                         ddk::Closable,
300                                         A>;
301class TestNotAllMixins : public TestNotAllMixinsType {
302  public:
303    TestNotAllMixins() : TestNotAllMixinsType(nullptr) {}
304    void DdkRelease() {}
305    zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags) { return ZX_OK; }
306    zx_status_t DdkClose(uint32_t flags) { return ZX_OK; }
307};
308#endif
309
310}  // namespace
311
312BEGIN_TEST_CASE(ddktl_device)
313RUN_NAMED_TEST("No mixins", do_test<TestNone>);
314RUN_NAMED_TEST("ddk::GetProtocolable", do_test<TestGetProtocolable>);
315RUN_NAMED_TEST("ddk::Openable", do_test<TestOpenable>);
316RUN_NAMED_TEST("ddk::OpenAtable", do_test<TestOpenAtable>);
317RUN_NAMED_TEST("ddk::Closable", do_test<TestClosable>);
318RUN_NAMED_TEST("ddk::Unbindable", do_test<TestUnbindable>);
319RUN_NAMED_TEST("ddk::Readable", do_test<TestReadable>);
320RUN_NAMED_TEST("ddk::Writable", do_test<TestWritable>);
321RUN_NAMED_TEST("ddk::GetSizable", do_test<TestGetSizable>);
322RUN_NAMED_TEST("ddk::Ioctlable", do_test<TestIoctlable>);
323RUN_NAMED_TEST("ddk::Suspendable", do_test<TestSuspendable>);
324RUN_NAMED_TEST("ddk::Resumable", do_test<TestResumable>);
325RUN_NAMED_TEST("ddk::Rxrpcable", do_test<TestRxrpcable>);
326
327RUN_NAMED_TEST("Method dispatch test", test_dispatch);
328
329#if TEST_WILL_NOT_COMPILE || 0
330RUN_NAMED_TEST("FailNoDdkGetProtocol", do_test<TestNotGetProtocolable>);
331RUN_NAMED_TEST("FailNoDdkOpen", do_test<TestNotOpenable>);
332RUN_NAMED_TEST("FailNoDdkOpenAt", do_test<TestNotOpenAtable>);
333RUN_NAMED_TEST("FailNoDdkClose", do_test<TestNotClosable>);
334RUN_NAMED_TEST("FailNoDdkUnbind", do_test<TestNotUnbindable>);
335RUN_NAMED_TEST("FailNoDdkRelease", do_test<TestNotReleasable>);
336RUN_NAMED_TEST("FailNoDdkRead", do_test<TestNotReadable>);
337RUN_NAMED_TEST("FailNoDdkWrite", do_test<TestNotWritable>);
338RUN_NAMED_TEST("FailNoDdkIotxnQueue", do_test<TestNotIotxnQueueable>);
339RUN_NAMED_TEST("FailNoDdkGetSize", do_test<TestNotGetSizable>);
340RUN_NAMED_TEST("FailNoDdkIoctl", do_test<TestNotIoctlable>);
341RUN_NAMED_TEST("FailNoDdkSuspend", do_test<TestNotSuspendable>);
342RUN_NAMED_TEST("FailNoDdkResume", do_test<TestNotResumable>);
343RUN_NAMED_TEST("FailNoDdkRxrpc", do_test<TestNotRxrpcable>);
344RUN_NAMED_TEST("FailBadOverride", do_test<TestBadOverride>);
345RUN_NAMED_TEST("FailHiddenOverride", do_test<TestHiddenOverride>);
346RUN_NAMED_TEST("FailStaticOverride", do_test<TestStaticOverride>);
347RUN_NAMED_TEST("FailNotAMixin", do_test<TestNotAMixin>);
348RUN_NAMED_TEST("FailNotAllMixins", do_test<TestNotAllMixins>);
349#endif
350END_TEST_CASE(ddktl_device);
351
352test_case_element* test_case_ddktl_device = TEST_CASE_ELEMENT(ddktl_device);
353