1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include "gen-c_glib/t_test_container_test_types.h"
21 #include "gen-c_glib/t_test_container_service.h"
22
23 #include <thrift/c_glib/thrift.h>
24 #include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
25 #include <thrift/c_glib/protocol/thrift_binary_protocol.h>
26 #include <thrift/c_glib/protocol/thrift_protocol_factory.h>
27 #include <thrift/c_glib/server/thrift_server.h>
28 #include <thrift/c_glib/server/thrift_simple_server.h>
29 #include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
30 #include <thrift/c_glib/transport/thrift_buffered_transport.h>
31 #include <thrift/c_glib/transport/thrift_server_socket.h>
32 #include <thrift/c_glib/transport/thrift_server_transport.h>
33 #include <thrift/c_glib/transport/thrift_socket.h>
34
35 #include <glib-object.h>
36 #include <glib.h>
37
38 #include <unistd.h>
39 #include <signal.h>
40 #include <string.h>
41 #include <sys/wait.h>
42 #include <sys/types.h>
43
44 #define TEST_SERVER_HOSTNAME "localhost"
45 #define TEST_SERVER_PORT 9090
46
47 /* --------------------------------------------------------------------------
48 The ContainerService handler we'll use for testing */
49
50 G_BEGIN_DECLS
51
52 GType test_container_service_handler_get_type (void);
53
54 #define TYPE_TEST_CONTAINER_SERVICE_HANDLER \
55 (test_container_service_handler_get_type ())
56
57 #define TEST_CONTAINER_SERVICE_HANDLER(obj) \
58 (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
59 TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
60 TestContainerServiceHandler))
61 #define TEST_CONTAINER_SERVICE_HANDLER_CLASS(c) \
62 (G_TYPE_CHECK_CLASS_CAST ((c), \
63 TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
64 TestContainerServiceHandlerClass))
65 #define IS_TEST_CONTAINER_SERVICE_HANDLER(obj) \
66 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
67 TYPE_TEST_CONTAINER_SERVICE_HANDLER))
68 #define IS_TEST_CONTAINER_SERVICE_HANDLER_CLASS(c) \
69 (G_TYPE_CHECK_CLASS_TYPE ((c), \
70 TYPE_TEST_CONTAINER_SERVICE_HANDLER))
71 #define TEST_CONTAINER_SERVICE_HANDLER_GET_CLASS(obj) \
72 (G_TYPE_INSTANCE_GET_CLASS ((obj), \
73 TYPE_TEST_CONTAINER_SERVICE_HANDLER, \
74 TestContainerServiceHandlerClass))
75
76 struct _TestContainerServiceHandler {
77 TTestContainerServiceHandler parent_instance;
78
79 /* private */
80 GPtrArray *string_list;
81 };
82 typedef struct _TestContainerServiceHandler TestContainerServiceHandler;
83
84 struct _TestContainerServiceHandlerClass {
85 TTestContainerServiceHandlerClass parent_class;
86 };
87 typedef struct _TestContainerServiceHandlerClass
88 TestContainerServiceHandlerClass;
89
90 G_END_DECLS
91
92 /* -------------------------------------------------------------------------- */
93
G_DEFINE_TYPE(TestContainerServiceHandler,test_container_service_handler,T_TEST_TYPE_CONTAINER_SERVICE_HANDLER)94 G_DEFINE_TYPE (TestContainerServiceHandler,
95 test_container_service_handler,
96 T_TEST_TYPE_CONTAINER_SERVICE_HANDLER)
97
98 /* A helper function used to append copies of strings to a string list */
99 static void append_string_to_ptr_array (gpointer element, gpointer ptr_array)
100 {
101 g_ptr_array_add ((GPtrArray *)ptr_array, g_strdup ((gchar *)element));
102 }
103
104 /* Accept a string list from the client and append its contents to our internal
105 list */
106 static gboolean
test_container_service_handler_receive_string_list(TTestContainerServiceIf * iface,const GPtrArray * stringList,GError ** error)107 test_container_service_handler_receive_string_list (TTestContainerServiceIf *iface,
108 const GPtrArray *stringList,
109 GError **error)
110 {
111 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
112
113 /* Append the client's strings to our own internal string list */
114 g_ptr_array_foreach ((GPtrArray *)stringList,
115 append_string_to_ptr_array,
116 self->string_list);
117
118 g_clear_error (error);
119 return TRUE;
120 }
121
122 /* Return the contents of our internal string list to the client */
123 static gboolean
test_container_service_handler_return_string_list(TTestContainerServiceIf * iface,GPtrArray ** _return,GError ** error)124 test_container_service_handler_return_string_list (TTestContainerServiceIf *iface,
125 GPtrArray **_return,
126 GError **error)
127 {
128 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
129
130 /* Return (copies of) the strings contained in our list */
131 g_ptr_array_foreach (self->string_list,
132 append_string_to_ptr_array,
133 *_return);
134
135 g_clear_error (error);
136 return TRUE;
137 }
138
139 static gboolean
test_container_service_handler_return_list_string_list(TTestContainerServiceIf * iface,GPtrArray ** _return,GError ** error)140 test_container_service_handler_return_list_string_list (TTestContainerServiceIf *iface,
141 GPtrArray **_return,
142 GError **error)
143 {
144 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
145 GPtrArray *nested_list;
146
147 /* Return a list containing our list of strings */
148 nested_list
149 = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
150 g_ptr_array_add (nested_list, self->string_list);
151 g_ptr_array_ref (self->string_list);
152
153 g_ptr_array_add (*_return, nested_list);
154
155 g_clear_error (error);
156 return TRUE;
157 }
158
159 static gboolean
test_container_service_handler_return_typedefd_list_string_list(TTestContainerServiceIf * iface,TTestListStringList ** _return,GError ** error)160 test_container_service_handler_return_typedefd_list_string_list (TTestContainerServiceIf *iface,
161 TTestListStringList **_return,
162 GError **error)
163 {
164 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface);
165 TTestStringList *nested_list;
166
167 /* Return a list containing our list of strings */
168 nested_list
169 = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
170 g_ptr_array_add (nested_list, self->string_list);
171 g_ptr_array_ref (self->string_list);
172
173 g_ptr_array_add (*_return, nested_list);
174
175 g_clear_error (error);
176 return TRUE;
177 }
178
179 static void
test_container_service_handler_finalize(GObject * object)180 test_container_service_handler_finalize (GObject *object) {
181 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (object);
182
183 /* Destroy our internal containers */
184 g_ptr_array_unref (self->string_list);
185 self->string_list = NULL;
186
187 G_OBJECT_CLASS (test_container_service_handler_parent_class)->
188 finalize (object);
189 }
190
191 static void
test_container_service_handler_init(TestContainerServiceHandler * self)192 test_container_service_handler_init (TestContainerServiceHandler *self)
193 {
194 /* Create our internal containers */
195 self->string_list = g_ptr_array_new_with_free_func (g_free);
196 }
197
198 static void
test_container_service_handler_class_init(TestContainerServiceHandlerClass * klass)199 test_container_service_handler_class_init (TestContainerServiceHandlerClass *klass)
200 {
201 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
202 TTestContainerServiceHandlerClass *parent_class =
203 T_TEST_CONTAINER_SERVICE_HANDLER_CLASS (klass);
204
205 gobject_class->finalize = test_container_service_handler_finalize;
206
207 parent_class->receive_string_list =
208 test_container_service_handler_receive_string_list;
209 parent_class->return_string_list =
210 test_container_service_handler_return_string_list;
211 parent_class->return_list_string_list =
212 test_container_service_handler_return_list_string_list;
213 parent_class->return_typedefd_list_string_list =
214 test_container_service_handler_return_typedefd_list_string_list;
215 }
216
217 /* -------------------------------------------------------------------------- */
218
219 /* Our test server, declared globally so we can access it within a signal
220 handler */
221 ThriftServer *server = NULL;
222
223 /* A signal handler used to detect when the child process (the test suite) has
224 exited so we know to shut down the server and terminate ourselves */
225 static void
sigchld_handler(int signal_number)226 sigchld_handler (int signal_number)
227 {
228 THRIFT_UNUSED_VAR (signal_number);
229
230 /* The child process (the tests) has exited or been terminated; shut down the
231 server gracefully */
232 if (server != NULL)
233 thrift_server_stop (server);
234 }
235
236 /* A helper function that executes a test case against a newly constructed
237 service client */
238 static void
execute_with_service_client(void (* test_case)(TTestContainerServiceIf *,GError **))239 execute_with_service_client (void (*test_case)(TTestContainerServiceIf *,
240 GError **))
241 {
242 ThriftSocket *socket;
243 ThriftTransport *transport;
244 ThriftProtocol *protocol;
245
246 TTestContainerServiceIf *client;
247
248 GError *error = NULL;
249
250 /* Create a client with which to access the server */
251 socket = g_object_new (THRIFT_TYPE_SOCKET,
252 "hostname", TEST_SERVER_HOSTNAME,
253 "port", TEST_SERVER_PORT,
254 NULL);
255 transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
256 "transport", socket,
257 NULL);
258 protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
259 "transport", transport,
260 NULL);
261
262 thrift_transport_open (transport, &error);
263 g_assert_no_error (error);
264
265 client = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_CLIENT,
266 "input_protocol", protocol,
267 "output_protocol", protocol,
268 NULL);
269
270 /* Execute the test against this client */
271 (*test_case)(client, &error);
272 g_assert_no_error (error);
273
274 /* Clean up and exit */
275 thrift_transport_close (transport, NULL);
276
277 g_object_unref (client);
278 g_object_unref (protocol);
279 g_object_unref (transport);
280 g_object_unref (socket);
281 }
282
283 static void
test_containers_with_default_values(void)284 test_containers_with_default_values (void)
285 {
286 TTestContainersWithDefaultValues *default_values;
287 GPtrArray *string_list;
288
289 /* Fetch a new ContainersWithDefaultValues struct and its StringList member */
290 default_values = g_object_new (T_TEST_TYPE_CONTAINERS_WITH_DEFAULT_VALUES,
291 NULL);
292 g_object_get (default_values,
293 "StringList", &string_list,
294 NULL);
295
296 /* Make sure the list has been populated with its default values */
297 g_assert_cmpint (string_list->len, ==, 2);
298 g_assert_cmpstr (((gchar **)string_list->pdata)[0], ==, "Apache");
299 g_assert_cmpstr (((gchar **)string_list->pdata)[1], ==, "Thrift");
300
301 g_ptr_array_unref (string_list);
302 g_object_unref (default_values);
303 }
304
305 static void
test_container_service_string_list_inner(TTestContainerServiceIf * client,GError ** error)306 test_container_service_string_list_inner (TTestContainerServiceIf *client,
307 GError **error)
308 {
309 gchar *test_data[] = { "one", "two", "three" };
310
311 GPtrArray *outgoing_string_list;
312 GPtrArray *incoming_string_list;
313 guint index;
314
315 g_clear_error (error);
316
317 /* Prepare our test data (our string list to send) */
318 outgoing_string_list = g_ptr_array_new ();
319 for (index = 0; index < 3; index += 1)
320 g_ptr_array_add (outgoing_string_list, &test_data[index]);
321
322 /* Send our data to the server and make sure we get the same data back on
323 retrieve */
324 g_assert
325 (t_test_container_service_client_receive_string_list (client,
326 outgoing_string_list,
327 error) &&
328 *error == NULL);
329
330 incoming_string_list = g_ptr_array_new ();
331 g_assert
332 (t_test_container_service_client_return_string_list (client,
333 &incoming_string_list,
334 error) &&
335 *error == NULL);
336
337 /* Make sure the two lists are equivalent */
338 g_assert_cmpint (incoming_string_list->len, ==, outgoing_string_list->len);
339 for (index = 0; index < incoming_string_list->len; index += 1)
340 g_assert_cmpstr (((gchar **)incoming_string_list->pdata)[index],
341 ==,
342 ((gchar **)outgoing_string_list->pdata)[index]);
343
344 /* Clean up and exit */
345 g_ptr_array_unref (incoming_string_list);
346 g_ptr_array_unref (outgoing_string_list);
347 }
348
349 static void
test_container_service_string_list(void)350 test_container_service_string_list (void)
351 {
352 execute_with_service_client (test_container_service_string_list_inner);
353 }
354
355 static void
test_container_service_list_string_list_inner(TTestContainerServiceIf * client,GError ** error)356 test_container_service_list_string_list_inner (TTestContainerServiceIf *client,
357 GError **error)
358 {
359 GPtrArray *incoming_list;
360 GPtrArray *nested_list;
361
362 g_clear_error (error);
363
364 /* Receive a list of string lists from the server */
365 incoming_list =
366 g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
367 g_assert
368 (t_test_container_service_client_return_list_string_list (client,
369 &incoming_list,
370 error) &&
371 *error == NULL);
372
373 /* Make sure the list and its contents are valid */
374 g_assert_cmpint (incoming_list->len, >, 0);
375
376 nested_list = (GPtrArray *)g_ptr_array_index (incoming_list, 0);
377 g_assert (nested_list != NULL);
378 g_assert_cmpint (nested_list->len, >=, 0);
379
380 /* Clean up and exit */
381 g_ptr_array_unref (incoming_list);
382 }
383
384 static void
test_container_service_list_string_list(void)385 test_container_service_list_string_list (void)
386 {
387 execute_with_service_client (test_container_service_list_string_list_inner);
388 }
389
390 static void
test_container_service_typedefd_list_string_list_inner(TTestContainerServiceIf * client,GError ** error)391 test_container_service_typedefd_list_string_list_inner (TTestContainerServiceIf *client,
392 GError **error)
393 {
394 TTestListStringList *incoming_list;
395 TTestStringList *nested_list;
396
397 g_clear_error (error);
398
399 /* Receive a list of string lists from the server */
400 incoming_list =
401 g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref);
402 g_assert
403 (t_test_container_service_client_return_list_string_list (client,
404 &incoming_list,
405 error) &&
406 *error == NULL);
407
408 /* Make sure the list and its contents are valid */
409 g_assert_cmpint (incoming_list->len, >, 0);
410
411 nested_list = (TTestStringList *)g_ptr_array_index (incoming_list, 0);
412 g_assert (nested_list != NULL);
413 g_assert_cmpint (nested_list->len, >=, 0);
414
415 /* Clean up and exit */
416 g_ptr_array_unref (incoming_list);
417 }
418
419 static void
test_container_service_typedefd_list_string_list(void)420 test_container_service_typedefd_list_string_list (void)
421 {
422 execute_with_service_client
423 (test_container_service_typedefd_list_string_list_inner);
424 }
425
426 int
main(int argc,char * argv[])427 main(int argc, char *argv[])
428 {
429 pid_t pid;
430 int status;
431
432 #if (!GLIB_CHECK_VERSION (2, 36, 0))
433 g_type_init ();
434 #endif
435
436 /* Fork to run our test suite in a child process */
437 pid = fork ();
438 g_assert_cmpint (pid, >=, 0);
439
440 if (pid == 0) { /* The child process */
441 /* Wait a moment for the server to finish starting */
442 sleep (1);
443
444 g_test_init (&argc, &argv, NULL);
445
446 g_test_add_func
447 ("/testcontainertest/ContainerTest/Structs/ContainersWithDefaultValues",
448 test_containers_with_default_values);
449 g_test_add_func
450 ("/testcontainertest/ContainerTest/Services/ContainerService/StringList",
451 test_container_service_string_list);
452 g_test_add_func
453 ("/testcontainertest/ContainerTest/Services/ContainerService/ListStringList",
454 test_container_service_list_string_list);
455 g_test_add_func
456 ("/testcontainertest/ContainerTest/Services/ContainerService/TypedefdListStringList",
457 test_container_service_typedefd_list_string_list);
458
459 /* Run the tests and make the result available to our parent process */
460 _exit (g_test_run ());
461 }
462 else {
463 TTestContainerServiceHandler *handler;
464 TTestContainerServiceProcessor *processor;
465
466 ThriftServerTransport *server_transport;
467 ThriftTransportFactory *transport_factory;
468 ThriftProtocolFactory *protocol_factory;
469
470 struct sigaction sigchld_action;
471
472 GError *error = NULL;
473 int exit_status = 1;
474
475 /* Trap the event of the child process terminating so we know to stop the
476 server and exit */
477 memset (&sigchld_action, 0, sizeof (sigchld_action));
478 sigchld_action.sa_handler = sigchld_handler;
479 sigchld_action.sa_flags = SA_RESETHAND;
480 sigaction (SIGCHLD, &sigchld_action, NULL);
481
482 /* Create our test server */
483 handler = g_object_new (TYPE_TEST_CONTAINER_SERVICE_HANDLER,
484 NULL);
485 processor = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_PROCESSOR,
486 "handler", handler,
487 NULL);
488 server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
489 "port", TEST_SERVER_PORT,
490 NULL);
491 transport_factory = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
492 NULL);
493 protocol_factory = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
494 NULL);
495
496 server = g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
497 "processor", processor,
498 "server_transport", server_transport,
499 "input_transport_factory", transport_factory,
500 "output_transport_factory", transport_factory,
501 "input_protocol_factory", protocol_factory,
502 "output_protocol_factory", protocol_factory,
503 NULL);
504
505 /* Start the server */
506 thrift_server_serve (server, &error);
507
508 /* Make sure the server stopped only because it was interrupted (by the
509 child process terminating) */
510 g_assert(!error || g_error_matches(error,
511 THRIFT_SERVER_SOCKET_ERROR,
512 THRIFT_SERVER_SOCKET_ERROR_ACCEPT));
513
514 /* Free our resources */
515 g_clear_object (&server);
516 g_clear_object (&protocol_factory);
517 g_clear_object (&transport_factory);
518 g_clear_object (&server_transport);
519 g_clear_object (&processor);
520 g_clear_object (&handler);
521
522 /* Wait for the child process to complete and return its exit status */
523 g_assert (wait (&status) == pid);
524 if (WIFEXITED (status))
525 exit_status = WEXITSTATUS (status);
526
527 return exit_status;
528 }
529 }
530