530 lines
18 KiB
C
530 lines
18 KiB
C
/*
|
|
* 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;
|
|
}
|
|
}
|