1// Copyright 2018 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 <fuchsia/ldsvc/c/fidl.h>
6#include <string.h>
7#include <threads.h>
8#include <zircon/fidl.h>
9#include <zircon/syscalls.h>
10
11#include <unittest/unittest.h>
12
13static bool g_server_done = false;
14
15static zx_status_t ldsvc_Done(void* ctx) {
16    g_server_done = true;
17    return ZX_OK;
18}
19
20static zx_status_t ldsvc_LoadObject(void* ctx, const char* object_name_data, size_t object_name_size, fidl_txn_t* txn) {
21    size_t len = strlen("object name");
22    ASSERT_EQ(len, object_name_size, "");
23    EXPECT_EQ(0, memcmp(object_name_data, "object name", len), "");
24    zx_handle_t event = ZX_HANDLE_INVALID;
25    EXPECT_EQ(ZX_OK, zx_event_create(0, &event), "");
26    return fuchsia_ldsvc_LoaderLoadObject_reply(txn, 42, event);
27}
28
29static zx_status_t ldsvc_LoadScriptInterpreter(void* ctx, const char* interpreter_name_data, size_t interpreter_name_size, fidl_txn_t* txn) {
30    size_t len = strlen("script interpreter");
31    ASSERT_EQ(len, interpreter_name_size, "");
32    EXPECT_EQ(0, memcmp(interpreter_name_data, "script interpreter", len), "");
33    zx_handle_t event = ZX_HANDLE_INVALID;
34    EXPECT_EQ(ZX_OK, zx_event_create(0, &event), "");
35    return fuchsia_ldsvc_LoaderLoadScriptInterpreter_reply(txn, 43, event);
36}
37
38static zx_status_t ldsvc_Config(void* ctx, const char* config_data, size_t config_size, fidl_txn_t* txn) {
39    size_t len = strlen("my config");
40    ASSERT_EQ(len, config_size, "");
41    EXPECT_EQ(0, memcmp(config_data, "my config", len), "");
42    return fuchsia_ldsvc_LoaderConfig_reply(txn, 44);
43}
44
45static zx_status_t ldsvc_Clone(void* ctx, zx_handle_t loader, fidl_txn_t* txn) {
46    EXPECT_EQ(ZX_OK, zx_handle_close(loader), "");
47    return fuchsia_ldsvc_LoaderClone_reply(txn, 45);
48}
49
50static zx_status_t ldsvc_DebugPublishDataSink(void* ctx, const char* data_sink_data, size_t data_sink_size, zx_handle_t data, fidl_txn_t* txn) {
51    size_t len = strlen("my data sink");
52    ASSERT_EQ(len, data_sink_size, "");
53    EXPECT_EQ(0, memcmp(data_sink_data, "my data sink", len), "");
54    EXPECT_EQ(ZX_OK, zx_handle_close(data), "");
55    return fuchsia_ldsvc_LoaderDebugPublishDataSink_reply(txn, 46);
56}
57
58static zx_status_t ldsvc_DebugLoadConfig(void* ctx, const char* config_name_data, size_t config_name_size, fidl_txn_t* txn) {
59    size_t len = strlen("my debug config");
60    ASSERT_EQ(len, config_name_size, "");
61    EXPECT_EQ(0, memcmp(config_name_data, "my debug config", len), "");
62    zx_handle_t event = ZX_HANDLE_INVALID;
63    EXPECT_EQ(ZX_OK, zx_event_create(0, &event), "");
64    return fuchsia_ldsvc_LoaderDebugLoadConfig_reply(txn, 47, event);
65}
66
67static const fuchsia_ldsvc_Loader_ops_t kOps = {
68    .Done = ldsvc_Done,
69    .LoadObject = ldsvc_LoadObject,
70    .LoadScriptInterpreter = ldsvc_LoadScriptInterpreter,
71    .Config = ldsvc_Config,
72    .Clone = ldsvc_Clone,
73    .DebugPublishDataSink = ldsvc_DebugPublishDataSink,
74    .DebugLoadConfig = ldsvc_DebugLoadConfig,
75};
76
77typedef struct ldsvc_connection {
78    fidl_txn_t txn;
79    zx_handle_t channel;
80    zx_txid_t txid;
81    uint32_t reply_count;
82} ldsvc_connection_t;
83
84static zx_status_t ldsvc_server_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
85    ldsvc_connection_t* conn = (ldsvc_connection_t*)txn;
86    if (msg->num_bytes < sizeof(fidl_message_header_t))
87        return ZX_ERR_INVALID_ARGS;
88    fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
89    hdr->txid = conn->txid;
90    conn->txid = 0;
91    ++conn->reply_count;
92    return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
93                            msg->handles, msg->num_handles);
94}
95
96static int ldsvc_server(void* ctx) {
97    ldsvc_connection_t conn = {
98        .txn.reply = ldsvc_server_reply,
99        .channel = *(zx_handle_t*)ctx,
100        .reply_count = 0u,
101    };
102    zx_status_t status = ZX_OK;
103    g_server_done = false;
104
105    while (status == ZX_OK && !g_server_done) {
106        zx_signals_t observed;
107        status = zx_object_wait_one(
108            conn.channel, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
109            ZX_TIME_INFINITE, &observed);
110        if ((observed & ZX_CHANNEL_READABLE) != 0) {
111            ASSERT_EQ(ZX_OK, status, "");
112            char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
113            zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
114            fidl_msg_t msg = {
115                .bytes = bytes,
116                .handles = handles,
117                .num_bytes = 0u,
118                .num_handles = 0u,
119            };
120            status = zx_channel_read(conn.channel, 0, bytes, handles,
121                                     ZX_CHANNEL_MAX_MSG_BYTES,
122                                     ZX_CHANNEL_MAX_MSG_HANDLES,
123                                     &msg.num_bytes, &msg.num_handles);
124            ASSERT_EQ(ZX_OK, status, "");
125            ASSERT_GE(msg.num_bytes, sizeof(fidl_message_header_t), "");
126            fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
127            conn.txid = hdr->txid;
128            conn.reply_count = 0u;
129            status = fuchsia_ldsvc_Loader_dispatch(NULL, &conn.txn, &msg, &kOps);
130            ASSERT_EQ(ZX_OK, status, "");
131            if (!g_server_done)
132                ASSERT_EQ(1u, conn.reply_count, "");
133        } else {
134            break;
135        }
136    }
137
138    zx_handle_close(conn.channel);
139    return 0;
140}
141
142static bool loader_test(void) {
143    BEGIN_TEST;
144
145    zx_handle_t client, server;
146    zx_status_t status = zx_channel_create(0, &client, &server);
147    ASSERT_EQ(ZX_OK, status, "");
148
149    thrd_t thread;
150    int rv = thrd_create(&thread, ldsvc_server, &server);
151    ASSERT_EQ(thrd_success, rv, "");
152
153    {
154        const char* object_name = "object name";
155        zx_status_t rv = ZX_OK;
156        zx_handle_t object = ZX_HANDLE_INVALID;
157        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderLoadObject(client, object_name, strlen(object_name), &rv, &object), "");
158        ASSERT_EQ(42, rv, "");
159        ASSERT_EQ(ZX_OK, zx_handle_close(object), "");
160    }
161
162    {
163        const char* interpreter_name = "script interpreter";
164        zx_status_t rv = ZX_OK;
165        zx_handle_t object = ZX_HANDLE_INVALID;
166        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderLoadScriptInterpreter(client, interpreter_name, strlen(interpreter_name), &rv, &object), "");
167        ASSERT_EQ(43, rv, "");
168        ASSERT_EQ(ZX_OK, zx_handle_close(object), "");
169    }
170
171    {
172        const char* config = "my config";
173        zx_status_t rv = ZX_OK;
174        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderConfig(client, config, strlen(config), &rv), "");
175        ASSERT_EQ(44, rv, "");
176    }
177
178    {
179        zx_status_t rv = ZX_OK;
180        zx_handle_t h1, h2;
181        ASSERT_EQ(ZX_OK, zx_eventpair_create(0, &h1, &h2), "");
182        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderClone(client, h1, &rv), "");
183        ASSERT_EQ(45, rv, "");
184        ASSERT_EQ(ZX_ERR_PEER_CLOSED, zx_object_signal_peer(h2, 0, 0), "");
185        ASSERT_EQ(ZX_OK, zx_handle_close(h2), "");
186    }
187
188    {
189        const char* sink = "my data sink";
190        zx_status_t rv = ZX_OK;
191        zx_handle_t h1, h2;
192        ASSERT_EQ(ZX_OK, zx_eventpair_create(0, &h1, &h2), "");
193        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderDebugPublishDataSink(client, sink, strlen(sink), h1, &rv), "");
194        ASSERT_EQ(46, rv, "");
195        ASSERT_EQ(ZX_ERR_PEER_CLOSED, zx_object_signal_peer(h2, 0, 0), "");
196        ASSERT_EQ(ZX_OK, zx_handle_close(h2), "");
197    }
198
199    {
200        const char* config_name = "my debug config";
201        zx_status_t rv = ZX_OK;
202        zx_handle_t object = ZX_HANDLE_INVALID;
203        ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderDebugLoadConfig(client, config_name, strlen(config_name), &rv, &object), "");
204        ASSERT_EQ(47, rv, "");
205        ASSERT_EQ(ZX_OK, zx_handle_close(object), "");
206    }
207
208    ASSERT_EQ(ZX_OK, fuchsia_ldsvc_LoaderDone(client), "");
209    ASSERT_EQ(ZX_OK, zx_handle_close(client), "");
210
211    int result = 0;
212    rv = thrd_join(thread, &result);
213    ASSERT_EQ(thrd_success, rv, "");
214
215    END_TEST;
216}
217
218BEGIN_TEST_CASE(ldsvc_tests)
219RUN_NAMED_TEST("fuchsia.ldsvc.Loader test", loader_test)
220END_TEST_CASE(ldsvc_tests);
221