|  | /* | 
|  | * Licensed to the Apache Software Foundation (ASF) under one | 
|  | * or more contributor license agreements. See the NOTICE file | 
|  | * distributed with this work for additional information | 
|  | * regarding copyright ownership. The ASF licenses this file | 
|  | * to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance | 
|  | * with the License. You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, | 
|  | * software distributed under the License is distributed on an | 
|  | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | * KIND, either express or implied. See the License for the | 
|  | * specific language governing permissions and limitations | 
|  | * under the License. | 
|  | */ | 
|  |  | 
|  | #include "gen-c_glib/t_test_container_test_types.h" | 
|  | #include "gen-c_glib/t_test_container_service.h" | 
|  |  | 
|  | #include <thrift/c_glib/thrift.h> | 
|  | #include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h> | 
|  | #include <thrift/c_glib/protocol/thrift_binary_protocol.h> | 
|  | #include <thrift/c_glib/protocol/thrift_protocol_factory.h> | 
|  | #include <thrift/c_glib/server/thrift_server.h> | 
|  | #include <thrift/c_glib/server/thrift_simple_server.h> | 
|  | #include <thrift/c_glib/transport/thrift_buffered_transport_factory.h> | 
|  | #include <thrift/c_glib/transport/thrift_buffered_transport.h> | 
|  | #include <thrift/c_glib/transport/thrift_server_socket.h> | 
|  | #include <thrift/c_glib/transport/thrift_server_transport.h> | 
|  | #include <thrift/c_glib/transport/thrift_socket.h> | 
|  |  | 
|  | #include <glib-object.h> | 
|  | #include <glib.h> | 
|  |  | 
|  | #include <unistd.h> | 
|  | #include <signal.h> | 
|  | #include <string.h> | 
|  | #include <sys/wait.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #define TEST_SERVER_HOSTNAME "localhost" | 
|  | #define TEST_SERVER_PORT     9090 | 
|  |  | 
|  | /* -------------------------------------------------------------------------- | 
|  | The ContainerService handler we'll use for testing */ | 
|  |  | 
|  | G_BEGIN_DECLS | 
|  |  | 
|  | GType test_container_service_handler_get_type (void); | 
|  |  | 
|  | #define TYPE_TEST_CONTAINER_SERVICE_HANDLER \ | 
|  | (test_container_service_handler_get_type ()) | 
|  |  | 
|  | #define TEST_CONTAINER_SERVICE_HANDLER(obj)                             \ | 
|  | (G_TYPE_CHECK_INSTANCE_CAST ((obj),                                   \ | 
|  | TYPE_TEST_CONTAINER_SERVICE_HANDLER,     \ | 
|  | TestContainerServiceHandler)) | 
|  | #define TEST_CONTAINER_SERVICE_HANDLER_CLASS(c)                         \ | 
|  | (G_TYPE_CHECK_CLASS_CAST ((c),                                        \ | 
|  | TYPE_TEST_CONTAINER_SERVICE_HANDLER,        \ | 
|  | TestContainerServiceHandlerClass)) | 
|  | #define IS_TEST_CONTAINER_SERVICE_HANDLER(obj)                          \ | 
|  | (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                                   \ | 
|  | TYPE_TEST_CONTAINER_SERVICE_HANDLER)) | 
|  | #define IS_TEST_CONTAINER_SERVICE_HANDLER_CLASS(c)                      \ | 
|  | (G_TYPE_CHECK_CLASS_TYPE ((c),                                        \ | 
|  | TYPE_TEST_CONTAINER_SERVICE_HANDLER)) | 
|  | #define TEST_CONTAINER_SERVICE_HANDLER_GET_CLASS(obj)                   \ | 
|  | (G_TYPE_INSTANCE_GET_CLASS ((obj),                                    \ | 
|  | TYPE_TEST_CONTAINER_SERVICE_HANDLER,      \ | 
|  | TestContainerServiceHandlerClass)) | 
|  |  | 
|  | struct _TestContainerServiceHandler { | 
|  | TTestContainerServiceHandler parent_instance; | 
|  |  | 
|  | /* private */ | 
|  | GPtrArray *string_list; | 
|  | }; | 
|  | typedef struct _TestContainerServiceHandler TestContainerServiceHandler; | 
|  |  | 
|  | struct _TestContainerServiceHandlerClass { | 
|  | TTestContainerServiceHandlerClass parent_class; | 
|  | }; | 
|  | typedef struct _TestContainerServiceHandlerClass | 
|  | TestContainerServiceHandlerClass; | 
|  |  | 
|  | G_END_DECLS | 
|  |  | 
|  | /* -------------------------------------------------------------------------- */ | 
|  |  | 
|  | G_DEFINE_TYPE (TestContainerServiceHandler, | 
|  | test_container_service_handler, | 
|  | T_TEST_TYPE_CONTAINER_SERVICE_HANDLER) | 
|  |  | 
|  | /* A helper function used to append copies of strings to a string list */ | 
|  | static void append_string_to_ptr_array (gpointer element, gpointer ptr_array) | 
|  | { | 
|  | g_ptr_array_add ((GPtrArray *)ptr_array, g_strdup ((gchar *)element)); | 
|  | } | 
|  |  | 
|  | /* Accept a string list from the client and append its contents to our internal | 
|  | list */ | 
|  | static gboolean | 
|  | test_container_service_handler_receive_string_list (TTestContainerServiceIf *iface, | 
|  | const GPtrArray *stringList, | 
|  | GError **error) | 
|  | { | 
|  | TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface); | 
|  |  | 
|  | /* Append the client's strings to our own internal string list */ | 
|  | g_ptr_array_foreach ((GPtrArray *)stringList, | 
|  | append_string_to_ptr_array, | 
|  | self->string_list); | 
|  |  | 
|  | g_clear_error (error); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* Return the contents of our internal string list to the client */ | 
|  | static gboolean | 
|  | test_container_service_handler_return_string_list (TTestContainerServiceIf *iface, | 
|  | GPtrArray **_return, | 
|  | GError **error) | 
|  | { | 
|  | TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface); | 
|  |  | 
|  | /* Return (copies of) the strings contained in our list */ | 
|  | g_ptr_array_foreach (self->string_list, | 
|  | append_string_to_ptr_array, | 
|  | *_return); | 
|  |  | 
|  | g_clear_error (error); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | test_container_service_handler_return_list_string_list (TTestContainerServiceIf *iface, | 
|  | GPtrArray **_return, | 
|  | GError **error) | 
|  | { | 
|  | TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface); | 
|  | GPtrArray *nested_list; | 
|  |  | 
|  | /* Return a list containing our list of strings */ | 
|  | nested_list | 
|  | = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref); | 
|  | g_ptr_array_add (nested_list, self->string_list); | 
|  | g_ptr_array_ref (self->string_list); | 
|  |  | 
|  | g_ptr_array_add (*_return, nested_list); | 
|  |  | 
|  | g_clear_error (error); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static gboolean | 
|  | test_container_service_handler_return_typedefd_list_string_list (TTestContainerServiceIf *iface, | 
|  | TTestListStringList **_return, | 
|  | GError **error) | 
|  | { | 
|  | TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (iface); | 
|  | TTestStringList *nested_list; | 
|  |  | 
|  | /* Return a list containing our list of strings */ | 
|  | nested_list | 
|  | = g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref); | 
|  | g_ptr_array_add (nested_list, self->string_list); | 
|  | g_ptr_array_ref (self->string_list); | 
|  |  | 
|  | g_ptr_array_add (*_return, nested_list); | 
|  |  | 
|  | g_clear_error (error); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_handler_finalize (GObject *object) { | 
|  | TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (object); | 
|  |  | 
|  | /* Destroy our internal containers */ | 
|  | g_ptr_array_unref (self->string_list); | 
|  | self->string_list = NULL; | 
|  |  | 
|  | G_OBJECT_CLASS (test_container_service_handler_parent_class)-> | 
|  | finalize (object); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_handler_init (TestContainerServiceHandler *self) | 
|  | { | 
|  | /* Create our internal containers */ | 
|  | self->string_list = g_ptr_array_new_with_free_func (g_free); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_handler_class_init (TestContainerServiceHandlerClass *klass) | 
|  | { | 
|  | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); | 
|  | TTestContainerServiceHandlerClass *parent_class = | 
|  | T_TEST_CONTAINER_SERVICE_HANDLER_CLASS (klass); | 
|  |  | 
|  | gobject_class->finalize = test_container_service_handler_finalize; | 
|  |  | 
|  | parent_class->receive_string_list = | 
|  | test_container_service_handler_receive_string_list; | 
|  | parent_class->return_string_list = | 
|  | test_container_service_handler_return_string_list; | 
|  | parent_class->return_list_string_list = | 
|  | test_container_service_handler_return_list_string_list; | 
|  | parent_class->return_typedefd_list_string_list = | 
|  | test_container_service_handler_return_typedefd_list_string_list; | 
|  | } | 
|  |  | 
|  | /* -------------------------------------------------------------------------- */ | 
|  |  | 
|  | /* Our test server, declared globally so we can access it within a signal | 
|  | handler */ | 
|  | ThriftServer *server = NULL; | 
|  |  | 
|  | /* A signal handler used to detect when the child process (the test suite) has | 
|  | exited so we know to shut down the server and terminate ourselves */ | 
|  | static void | 
|  | sigchld_handler (int signal_number) | 
|  | { | 
|  | THRIFT_UNUSED_VAR (signal_number); | 
|  |  | 
|  | /* The child process (the tests) has exited or been terminated; shut down the | 
|  | server gracefully */ | 
|  | if (server != NULL) | 
|  | thrift_server_stop (server); | 
|  | } | 
|  |  | 
|  | /* A helper function that executes a test case against a newly constructed | 
|  | service client */ | 
|  | static void | 
|  | execute_with_service_client (void (*test_case)(TTestContainerServiceIf *, | 
|  | GError **)) | 
|  | { | 
|  | ThriftSocket *socket; | 
|  | ThriftTransport *transport; | 
|  | ThriftProtocol *protocol; | 
|  |  | 
|  | TTestContainerServiceIf *client; | 
|  |  | 
|  | GError *error = NULL; | 
|  |  | 
|  | /* Create a client with which to access the server */ | 
|  | socket    = g_object_new (THRIFT_TYPE_SOCKET, | 
|  | "hostname", TEST_SERVER_HOSTNAME, | 
|  | "port",     TEST_SERVER_PORT, | 
|  | NULL); | 
|  | transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT, | 
|  | "transport", socket, | 
|  | NULL); | 
|  | protocol  = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, | 
|  | "transport", transport, | 
|  | NULL); | 
|  |  | 
|  | thrift_transport_open (transport, &error); | 
|  | g_assert_no_error (error); | 
|  |  | 
|  | client = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_CLIENT, | 
|  | "input_protocol",  protocol, | 
|  | "output_protocol", protocol, | 
|  | NULL); | 
|  |  | 
|  | /* Execute the test against this client */ | 
|  | (*test_case)(client, &error); | 
|  | g_assert_no_error (error); | 
|  |  | 
|  | /* Clean up and exit */ | 
|  | thrift_transport_close (transport, NULL); | 
|  |  | 
|  | g_object_unref (client); | 
|  | g_object_unref (protocol); | 
|  | g_object_unref (transport); | 
|  | g_object_unref (socket); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_containers_with_default_values (void) | 
|  | { | 
|  | TTestContainersWithDefaultValues *default_values; | 
|  | GPtrArray *string_list; | 
|  |  | 
|  | /* Fetch a new ContainersWithDefaultValues struct and its StringList member */ | 
|  | default_values = g_object_new (T_TEST_TYPE_CONTAINERS_WITH_DEFAULT_VALUES, | 
|  | NULL); | 
|  | g_object_get (default_values, | 
|  | "StringList", &string_list, | 
|  | NULL); | 
|  |  | 
|  | /* Make sure the list has been populated with its default values */ | 
|  | g_assert_cmpint (string_list->len, ==, 2); | 
|  | g_assert_cmpstr (((gchar **)string_list->pdata)[0], ==, "Apache"); | 
|  | g_assert_cmpstr (((gchar **)string_list->pdata)[1], ==, "Thrift"); | 
|  |  | 
|  | g_ptr_array_unref (string_list); | 
|  | g_object_unref (default_values); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_string_list_inner (TTestContainerServiceIf *client, | 
|  | GError **error) | 
|  | { | 
|  | gchar *test_data[] = { "one", "two", "three" }; | 
|  |  | 
|  | GPtrArray *outgoing_string_list; | 
|  | GPtrArray *incoming_string_list; | 
|  | guint index; | 
|  |  | 
|  | g_clear_error (error); | 
|  |  | 
|  | /* Prepare our test data (our string list to send) */ | 
|  | outgoing_string_list = g_ptr_array_new (); | 
|  | for (index = 0; index < 3; index += 1) | 
|  | g_ptr_array_add (outgoing_string_list, &test_data[index]); | 
|  |  | 
|  | /* Send our data to the server and make sure we get the same data back on | 
|  | retrieve */ | 
|  | g_assert | 
|  | (t_test_container_service_client_receive_string_list (client, | 
|  | outgoing_string_list, | 
|  | error) && | 
|  | *error == NULL); | 
|  |  | 
|  | incoming_string_list = g_ptr_array_new (); | 
|  | g_assert | 
|  | (t_test_container_service_client_return_string_list (client, | 
|  | &incoming_string_list, | 
|  | error) && | 
|  | *error == NULL); | 
|  |  | 
|  | /* Make sure the two lists are equivalent */ | 
|  | g_assert_cmpint (incoming_string_list->len, ==, outgoing_string_list->len); | 
|  | for (index = 0; index < incoming_string_list->len; index += 1) | 
|  | g_assert_cmpstr (((gchar **)incoming_string_list->pdata)[index], | 
|  | ==, | 
|  | ((gchar **)outgoing_string_list->pdata)[index]); | 
|  |  | 
|  | /* Clean up and exit */ | 
|  | g_ptr_array_unref (incoming_string_list); | 
|  | g_ptr_array_unref (outgoing_string_list); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_string_list (void) | 
|  | { | 
|  | execute_with_service_client (test_container_service_string_list_inner); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_list_string_list_inner (TTestContainerServiceIf *client, | 
|  | GError **error) | 
|  | { | 
|  | GPtrArray *incoming_list; | 
|  | GPtrArray *nested_list; | 
|  |  | 
|  | g_clear_error (error); | 
|  |  | 
|  | /* Receive a list of string lists from the server */ | 
|  | incoming_list = | 
|  | g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref); | 
|  | g_assert | 
|  | (t_test_container_service_client_return_list_string_list (client, | 
|  | &incoming_list, | 
|  | error) && | 
|  | *error == NULL); | 
|  |  | 
|  | /* Make sure the list and its contents are valid */ | 
|  | g_assert_cmpint (incoming_list->len, >, 0); | 
|  |  | 
|  | nested_list = (GPtrArray *)g_ptr_array_index (incoming_list, 0); | 
|  | g_assert (nested_list != NULL); | 
|  | g_assert_cmpint (nested_list->len, >=, 0); | 
|  |  | 
|  | /* Clean up and exit */ | 
|  | g_ptr_array_unref (incoming_list); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_list_string_list (void) | 
|  | { | 
|  | execute_with_service_client (test_container_service_list_string_list_inner); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_typedefd_list_string_list_inner (TTestContainerServiceIf *client, | 
|  | GError **error) | 
|  | { | 
|  | TTestListStringList *incoming_list; | 
|  | TTestStringList *nested_list; | 
|  |  | 
|  | g_clear_error (error); | 
|  |  | 
|  | /* Receive a list of string lists from the server */ | 
|  | incoming_list = | 
|  | g_ptr_array_new_with_free_func ((GDestroyNotify)g_ptr_array_unref); | 
|  | g_assert | 
|  | (t_test_container_service_client_return_list_string_list (client, | 
|  | &incoming_list, | 
|  | error) && | 
|  | *error == NULL); | 
|  |  | 
|  | /* Make sure the list and its contents are valid */ | 
|  | g_assert_cmpint (incoming_list->len, >, 0); | 
|  |  | 
|  | nested_list = (TTestStringList *)g_ptr_array_index (incoming_list, 0); | 
|  | g_assert (nested_list != NULL); | 
|  | g_assert_cmpint (nested_list->len, >=, 0); | 
|  |  | 
|  | /* Clean up and exit */ | 
|  | g_ptr_array_unref (incoming_list); | 
|  | } | 
|  |  | 
|  | static void | 
|  | test_container_service_typedefd_list_string_list (void) | 
|  | { | 
|  | execute_with_service_client | 
|  | (test_container_service_typedefd_list_string_list_inner); | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | pid_t pid; | 
|  | int status; | 
|  |  | 
|  | #if (!GLIB_CHECK_VERSION (2, 36, 0)) | 
|  | g_type_init (); | 
|  | #endif | 
|  |  | 
|  | /* Fork to run our test suite in a child process */ | 
|  | pid = fork (); | 
|  | g_assert_cmpint (pid, >=, 0); | 
|  |  | 
|  | if (pid == 0) {    /* The child process */ | 
|  | /* Wait a moment for the server to finish starting */ | 
|  | sleep (1); | 
|  |  | 
|  | g_test_init (&argc, &argv, NULL); | 
|  |  | 
|  | g_test_add_func | 
|  | ("/testcontainertest/ContainerTest/Structs/ContainersWithDefaultValues", | 
|  | test_containers_with_default_values); | 
|  | g_test_add_func | 
|  | ("/testcontainertest/ContainerTest/Services/ContainerService/StringList", | 
|  | test_container_service_string_list); | 
|  | g_test_add_func | 
|  | ("/testcontainertest/ContainerTest/Services/ContainerService/ListStringList", | 
|  | test_container_service_list_string_list); | 
|  | g_test_add_func | 
|  | ("/testcontainertest/ContainerTest/Services/ContainerService/TypedefdListStringList", | 
|  | test_container_service_typedefd_list_string_list); | 
|  |  | 
|  | /* Run the tests and make the result available to our parent process */ | 
|  | _exit (g_test_run ()); | 
|  | } | 
|  | else { | 
|  | TTestContainerServiceHandler *handler; | 
|  | TTestContainerServiceProcessor *processor; | 
|  |  | 
|  | ThriftServerTransport *server_transport; | 
|  | ThriftTransportFactory *transport_factory; | 
|  | ThriftProtocolFactory *protocol_factory; | 
|  |  | 
|  | struct sigaction sigchld_action; | 
|  |  | 
|  | GError *error = NULL; | 
|  | int exit_status = 1; | 
|  |  | 
|  | /* Trap the event of the child process terminating so we know to stop the | 
|  | server and exit */ | 
|  | memset (&sigchld_action, 0, sizeof (sigchld_action)); | 
|  | sigchld_action.sa_handler = sigchld_handler; | 
|  | sigchld_action.sa_flags = SA_RESETHAND; | 
|  | sigaction (SIGCHLD, &sigchld_action, NULL); | 
|  |  | 
|  | /* Create our test server */ | 
|  | handler = g_object_new (TYPE_TEST_CONTAINER_SERVICE_HANDLER, | 
|  | NULL); | 
|  | processor = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_PROCESSOR, | 
|  | "handler", handler, | 
|  | NULL); | 
|  | server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET, | 
|  | "port", TEST_SERVER_PORT, | 
|  | NULL); | 
|  | transport_factory = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY, | 
|  | NULL); | 
|  | protocol_factory = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY, | 
|  | NULL); | 
|  |  | 
|  | server = g_object_new (THRIFT_TYPE_SIMPLE_SERVER, | 
|  | "processor",                processor, | 
|  | "server_transport",         server_transport, | 
|  | "input_transport_factory",  transport_factory, | 
|  | "output_transport_factory", transport_factory, | 
|  | "input_protocol_factory",   protocol_factory, | 
|  | "output_protocol_factory",  protocol_factory, | 
|  | NULL); | 
|  |  | 
|  | /* Start the server */ | 
|  | thrift_server_serve (server, &error); | 
|  |  | 
|  | /* Make sure the server stopped only because it was interrupted (by the | 
|  | child process terminating) */ | 
|  | g_assert(!error || g_error_matches(error, | 
|  | THRIFT_SERVER_SOCKET_ERROR, | 
|  | THRIFT_SERVER_SOCKET_ERROR_ACCEPT)); | 
|  |  | 
|  | /* Free our resources */ | 
|  | g_clear_object (&server); | 
|  | g_clear_object (&protocol_factory); | 
|  | g_clear_object (&transport_factory); | 
|  | g_clear_object (&server_transport); | 
|  | g_clear_object (&processor); | 
|  | g_clear_object (&handler); | 
|  |  | 
|  | /* Wait for the child process to complete and return its exit status */ | 
|  | g_assert (wait (&status) == pid); | 
|  | if (WIFEXITED (status)) | 
|  | exit_status = WEXITSTATUS (status); | 
|  |  | 
|  | return exit_status; | 
|  | } | 
|  | } |