/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } }