tizen 2.3.1 release
[framework/connectivity/bluez.git] / unit / test-mgmt.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdbool.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31
32 #include <glib.h>
33
34 #include "lib/bluetooth.h"
35 #include "lib/mgmt.h"
36
37 #include "src/shared/mgmt.h"
38
39 struct context {
40         GMainLoop *main_loop;
41         int fd;
42         struct mgmt *mgmt_client;
43         guint server_source;
44         GList *handler_list;
45 };
46
47 enum action {
48         ACTION_PASSED,
49         ACTION_IGNORE,
50         ACTION_RESPOND,
51 };
52
53 struct handler {
54         const void *cmd_data;
55         uint16_t cmd_size;
56         const void *rsp_data;
57         uint16_t rsp_size;
58         uint8_t rsp_status;
59         bool match_prefix;
60         enum action action;
61 };
62
63 static void mgmt_debug(const char *str, void *user_data)
64 {
65         const char *prefix = user_data;
66
67         g_print("%s%s\n", prefix, str);
68 }
69
70 static void context_quit(struct context *context)
71 {
72         g_main_loop_quit(context->main_loop);
73 }
74
75 static void check_actions(struct context *context, int fd,
76                                         const void *data, uint16_t size)
77 {
78         GList *list;
79
80         for (list = g_list_first(context->handler_list); list;
81                                                 list = g_list_next(list)) {
82                 struct handler *handler = list->data;
83                 int ret;
84
85                 if (handler->match_prefix) {
86                         if (size < handler->cmd_size)
87                                 continue;
88                 } else {
89                         if (size != handler->cmd_size)
90                                 continue;
91                 }
92
93                 if (memcmp(data, handler->cmd_data, handler->cmd_size))
94                         continue;
95
96                 switch (handler->action) {
97                 case ACTION_PASSED:
98                         context_quit(context);
99                         return;
100                 case ACTION_RESPOND:
101                         ret = write(fd, handler->rsp_data, handler->rsp_size);
102                         g_assert(ret >= 0);
103                         return;
104                 case ACTION_IGNORE:
105                         return;
106                 }
107         }
108
109         g_test_message("Command not handled\n");
110         g_assert_not_reached();
111 }
112
113 static gboolean server_handler(GIOChannel *channel, GIOCondition cond,
114                                                         gpointer user_data)
115 {
116         struct context *context = user_data;
117         unsigned char buf[512];
118         ssize_t result;
119         int fd;
120
121         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
122                 return FALSE;
123
124         fd = g_io_channel_unix_get_fd(channel);
125
126         result = read(fd, buf, sizeof(buf));
127         if (result < 0)
128                 return FALSE;
129
130         check_actions(context, fd, buf, result);
131
132         return TRUE;
133 }
134
135 static struct context *create_context(void)
136 {
137         struct context *context = g_new0(struct context, 1);
138         GIOChannel *channel;
139         int err, sv[2];
140
141         context->main_loop = g_main_loop_new(NULL, FALSE);
142         g_assert(context->main_loop);
143
144         err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
145         g_assert(err == 0);
146
147         context->fd = sv[0];
148         channel = g_io_channel_unix_new(sv[0]);
149
150         g_io_channel_set_close_on_unref(channel, TRUE);
151         g_io_channel_set_encoding(channel, NULL, NULL);
152         g_io_channel_set_buffered(channel, FALSE);
153
154         context->server_source = g_io_add_watch(channel,
155                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
156                                 server_handler, context);
157         g_assert(context->server_source > 0);
158
159         g_io_channel_unref(channel);
160
161         context->mgmt_client = mgmt_new(sv[1]);
162         g_assert(context->mgmt_client);
163
164         if (g_test_verbose() == TRUE)
165                 mgmt_set_debug(context->mgmt_client,
166                                         mgmt_debug, "mgmt: ", NULL);
167
168         mgmt_set_close_on_unref(context->mgmt_client, true);
169
170         return context;
171 }
172
173 static void execute_context(struct context *context)
174 {
175         g_main_loop_run(context->main_loop);
176
177         g_list_free_full(context->handler_list, g_free);
178
179         g_source_remove(context->server_source);
180
181         mgmt_unref(context->mgmt_client);
182
183         g_main_loop_unref(context->main_loop);
184
185         g_free(context);
186 }
187
188 static void add_action(struct context *context,
189                                 const void *cmd_data, uint16_t cmd_size,
190                                 const void *rsp_data, uint16_t rsp_size,
191                                 uint8_t rsp_status, bool match_prefix,
192                                 enum action action)
193 {
194         struct handler *handler = g_new0(struct handler, 1);
195
196         handler->cmd_data = cmd_data;
197         handler->cmd_size = cmd_size;
198         handler->rsp_data = rsp_data;
199         handler->rsp_size = rsp_size;
200         handler->rsp_status = rsp_status;
201         handler->match_prefix = match_prefix;
202         handler->action = action;
203
204         context->handler_list = g_list_append(context->handler_list, handler);
205 }
206
207 struct command_test_data {
208         uint16_t opcode;
209         uint16_t index;
210         uint16_t length;
211         const void *param;
212         const void *cmd_data;
213         uint16_t cmd_size;
214         const void *rsp_data;
215         uint16_t rsp_size;
216         uint8_t rsp_status;
217 };
218
219 static const unsigned char read_version_command[] =
220                                 { 0x01, 0x00, 0xff, 0xff, 0x00, 0x00 };
221 static const unsigned char read_version_response[] =
222                                 { 0x01, 0x00, 0xff, 0xff, 0x06, 0x00,
223                                 0x01, 0x00, 0x00, 0x01, 0x06, 0x00 };
224
225 static const struct command_test_data command_test_1 = {
226         .opcode = MGMT_OP_READ_VERSION,
227         .index = MGMT_INDEX_NONE,
228         .cmd_data = read_version_command,
229         .cmd_size = sizeof(read_version_command),
230         .rsp_data = read_version_response,
231         .rsp_size = sizeof(read_version_response),
232         .rsp_status = MGMT_STATUS_SUCCESS,
233 };
234
235 static const unsigned char read_info_command[] =
236                                 { 0x04, 0x00, 0x00, 0x02, 0x00, 0x00 };
237
238 static const struct command_test_data command_test_2 = {
239         .opcode = MGMT_OP_READ_INFO,
240         .index = 512,
241         .cmd_data = read_info_command,
242         .cmd_size = sizeof(read_info_command),
243 };
244
245 static const unsigned char invalid_index_response[] =
246                                 { 0x02, 0x00, 0xff, 0xff, 0x03, 0x00,
247                                 0x01, 0x00, 0x11 };
248
249 static const struct command_test_data command_test_3 = {
250         .opcode = MGMT_OP_READ_VERSION,
251         .index = MGMT_INDEX_NONE,
252         .cmd_data = read_version_command,
253         .cmd_size = sizeof(read_version_command),
254         .rsp_data = invalid_index_response,
255         .rsp_size = sizeof(invalid_index_response),
256         .rsp_status = MGMT_STATUS_INVALID_INDEX,
257 };
258
259 static const unsigned char event_index_added[] =
260                                 { 0x04, 0x00, 0x01, 0x00, 0x00, 0x00 };
261
262 static const struct command_test_data event_test_1 = {
263         .opcode = MGMT_EV_INDEX_ADDED,
264         .index = MGMT_INDEX_NONE,
265         .cmd_data = event_index_added,
266         .cmd_size = sizeof(event_index_added),
267 };
268
269 static void test_command(gconstpointer data)
270 {
271         const struct command_test_data *test = data;
272         struct context *context = create_context();
273
274         add_action(context, test->cmd_data, test->cmd_size,
275                         test->rsp_data, test->rsp_size, test->rsp_status,
276                         false, ACTION_PASSED);
277
278         mgmt_send(context->mgmt_client, test->opcode, test->index,
279                                         test->length, test->param,
280                                                 NULL ,NULL, NULL);
281
282         execute_context(context);
283 }
284
285 static void response_cb(uint8_t status, uint16_t length, const void *param,
286                                                         void *user_data)
287 {
288         struct context *context = user_data;
289         struct handler *handler = context->handler_list->data;
290
291         g_assert_cmpint(status, ==, handler->rsp_status);
292
293         context_quit(context);
294 }
295
296 static void test_response(gconstpointer data)
297 {
298         const struct command_test_data *test = data;
299         struct context *context = create_context();
300
301         add_action(context, test->cmd_data, test->cmd_size,
302                         test->rsp_data, test->rsp_size, test->rsp_status,
303                         false, ACTION_RESPOND);
304
305         mgmt_send(context->mgmt_client, test->opcode, test->index,
306                                         test->length, test->param,
307                                         response_cb, context, NULL);
308
309         execute_context(context);
310 }
311
312 static void event_cb(uint16_t index, uint16_t length, const void *param,
313                                                         void *user_data)
314 {
315         struct context *context = user_data;
316
317         if (g_test_verbose())
318                 printf("Event received\n");
319
320         context_quit(context);
321 }
322
323 static void test_event(gconstpointer data)
324 {
325         const struct command_test_data *test = data;
326         struct context *context = create_context();
327
328         mgmt_register(context->mgmt_client, test->opcode, test->index,
329                                                 event_cb, context, NULL);
330
331         g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
332                                                                 test->cmd_size);
333
334         execute_context(context);
335 }
336
337 static void test_event2(gconstpointer data)
338 {
339         const struct command_test_data *test = data;
340         struct context *context = create_context();
341
342         mgmt_register(context->mgmt_client, test->opcode, test->index,
343                                                 event_cb, context, NULL);
344         mgmt_register(context->mgmt_client, test->opcode, test->index,
345                                                 event_cb, context, NULL);
346
347         g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
348                                                                 test->cmd_size);
349
350         execute_context(context);
351 }
352
353 static void unregister_all_cb(uint16_t index, uint16_t length,
354                                         const void *param, void *user_data)
355 {
356         struct context *context = user_data;
357
358         mgmt_unregister_all(context->mgmt_client);
359
360         context_quit(context);
361 }
362
363 static void test_unregister_all(gconstpointer data)
364 {
365         const struct command_test_data *test = data;
366         struct context *context = create_context();
367
368         mgmt_register(context->mgmt_client, test->opcode, test->index,
369                                         unregister_all_cb, context, NULL);
370         mgmt_register(context->mgmt_client, test->opcode, test->index,
371                                                 event_cb, context, NULL);
372
373         g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
374                                                                 test->cmd_size);
375
376         execute_context(context);
377 }
378
379 static void unregister_index_cb(uint16_t index, uint16_t length,
380                                         const void *param, void *user_data)
381 {
382         struct context *context = user_data;
383
384         mgmt_unregister_index(context->mgmt_client, index);
385
386         context_quit(context);
387 }
388
389 static void destroy_cb(uint16_t index, uint16_t length, const void *param,
390                                                         void *user_data)
391 {
392         struct context *context = user_data;
393
394         mgmt_unref(context->mgmt_client);
395         context->mgmt_client = NULL;
396
397         context_quit(context);
398 }
399
400 static void test_unregister_index(gconstpointer data)
401 {
402         const struct command_test_data *test = data;
403         struct context *context = create_context();
404
405         mgmt_register(context->mgmt_client, test->opcode, test->index,
406                                         unregister_index_cb, context, NULL);
407         mgmt_register(context->mgmt_client, test->opcode, test->index,
408                                                 event_cb, context, NULL);
409
410         g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
411                                                                 test->cmd_size);
412
413         execute_context(context);
414 }
415
416 static void test_destroy(gconstpointer data)
417 {
418         const struct command_test_data *test = data;
419         struct context *context = create_context();
420
421         mgmt_register(context->mgmt_client, test->opcode, test->index,
422                                         destroy_cb, context, NULL);
423         mgmt_register(context->mgmt_client, test->opcode, test->index,
424                                                 event_cb, context, NULL);
425
426         g_assert_cmpint(write(context->fd, test->cmd_data, test->cmd_size), ==,
427                                                                 test->cmd_size);
428
429         execute_context(context);
430 }
431
432 int main(int argc, char *argv[])
433 {
434         g_test_init(&argc, &argv, NULL);
435
436         g_test_add_data_func("/mgmt/command/1", &command_test_1, test_command);
437         g_test_add_data_func("/mgmt/command/2", &command_test_2, test_command);
438
439         g_test_add_data_func("/mgmt/response/1", &command_test_1,
440                                                                 test_response);
441         g_test_add_data_func("/mgmt/response/2", &command_test_3,
442                                                                 test_response);
443
444         g_test_add_data_func("/mgmt/event/1", &event_test_1, test_event);
445         g_test_add_data_func("/mgmt/event/2", &event_test_1, test_event2);
446
447         g_test_add_data_func("/mgmt/unregister/1", &event_test_1,
448                                                         test_unregister_all);
449         g_test_add_data_func("/mgmt/unregister/2", &event_test_1,
450                                                         test_unregister_index);
451
452         g_test_add_data_func("/mgmt/destroy/1", &event_test_1, test_destroy);
453
454         return g_test_run();
455 }