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