blob: dc61666ba30490c2691435ab47c80c8ba2cabc31 [file] [log] [blame]
Jens Geyer54f392b2015-08-05 21:45:10 +02001/*
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
50G_BEGIN_DECLS
51
52GType 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
76struct _TestContainerServiceHandler {
77 TTestContainerServiceHandler parent_instance;
78
79 /* private */
80 GPtrArray *string_list;
81};
82typedef struct _TestContainerServiceHandler TestContainerServiceHandler;
83
84struct _TestContainerServiceHandlerClass {
85 TTestContainerServiceHandlerClass parent_class;
86};
87typedef struct _TestContainerServiceHandlerClass
88 TestContainerServiceHandlerClass;
89
90G_END_DECLS
91
92/* -------------------------------------------------------------------------- */
93
94G_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 */
99static 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 */
106static gboolean
107test_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 */
123static gboolean
124test_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
139static void
140test_container_service_handler_finalize (GObject *object) {
141 TestContainerServiceHandler *self = TEST_CONTAINER_SERVICE_HANDLER (object);
142
143 /* Destroy our internal containers */
144 g_ptr_array_unref (self->string_list);
145 self->string_list = NULL;
146
147 G_OBJECT_CLASS (test_container_service_handler_parent_class)->
148 finalize (object);
149}
150
151static void
152test_container_service_handler_init (TestContainerServiceHandler *self)
153{
154 /* Create our internal containers */
155 self->string_list = g_ptr_array_new_with_free_func (g_free);
156}
157
158static void
159test_container_service_handler_class_init (TestContainerServiceHandlerClass *klass)
160{
161 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
162 TTestContainerServiceHandlerClass *parent_class =
163 T_TEST_CONTAINER_SERVICE_HANDLER_CLASS (klass);
164
165 gobject_class->finalize = test_container_service_handler_finalize;
166
167 parent_class->receive_string_list =
168 test_container_service_handler_receive_string_list;
169 parent_class->return_string_list =
170 test_container_service_handler_return_string_list;
171}
172
173/* -------------------------------------------------------------------------- */
174
175/* Our test server, declared globally so we can access it within a signal
176 handler */
177ThriftServer *server = NULL;
178
179/* A signal handler used to detect when the child process (the test suite) has
180 exited so we know to shut down the server and terminate ourselves */
181static void
182sigchld_handler (int signal_number)
183{
184 THRIFT_UNUSED_VAR (signal_number);
185
186 /* The child process (the tests) has exited or been terminated; shut down the
187 server gracefully */
188 if (server != NULL)
189 thrift_server_stop (server);
190}
191
192static void
193test_containers_with_default_values (void)
194{
195 TTestContainersWithDefaultValues *default_values;
196 GPtrArray *string_list;
197
198 /* Fetch a new ContainersWithDefaultValues struct and its StringList member */
199 default_values = g_object_new (T_TEST_TYPE_CONTAINERS_WITH_DEFAULT_VALUES,
200 NULL);
201 g_object_get (default_values,
202 "StringList", &string_list,
203 NULL);
204
205 /* Make sure the list has been populated with its default values */
206 g_assert_cmpint (string_list->len, ==, 2);
207 g_assert_cmpstr (((gchar **)string_list->pdata)[0], ==, "Apache");
208 g_assert_cmpstr (((gchar **)string_list->pdata)[1], ==, "Thrift");
209
210 g_ptr_array_unref (string_list);
211 g_object_unref (default_values);
212}
213
214static void
215test_container_service_string_list (void)
216{
217 ThriftSocket *socket;
218 ThriftTransport *transport;
219 ThriftProtocol *protocol;
220
221 TTestContainerServiceIf *client;
222
223 GPtrArray *outgoing_string_list;
224 GPtrArray *incoming_string_list;
225
226 gchar *test_data[] = { "one", "two", "three" };
227 guint8 index;
228
229 GError *error = NULL;
230
231 /* Prepare our test data (our string list to send) */
232 outgoing_string_list = g_ptr_array_new ();
233 for (index = 0; index < 3; index += 1)
234 g_ptr_array_add (outgoing_string_list, &test_data[index]);
235
236 /* Create a client with which to access the server */
237 socket = g_object_new (THRIFT_TYPE_SOCKET,
238 "hostname", TEST_SERVER_HOSTNAME,
239 "port", TEST_SERVER_PORT,
240 NULL);
241 transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
242 "transport", socket,
243 NULL);
244 protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
245 "transport", transport,
246 NULL);
247
248 thrift_transport_open (transport, &error);
249 g_assert_no_error (error);
250
251 client = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_CLIENT,
252 "input_protocol", protocol,
253 "output_protocol", protocol,
254 NULL);
255
256 /* Send our data to the server and make sure we get the same data back on
257 retrieve */
258 g_assert
259 (t_test_container_service_client_receive_string_list (client,
260 outgoing_string_list,
261 &error) &&
262 error == NULL);
263
264 incoming_string_list = g_ptr_array_new ();
265 g_assert
266 (t_test_container_service_client_return_string_list (client,
267 &incoming_string_list,
268 &error) &&
269 error == NULL);
270
271 /* Make sure the two lists are equivalent */
272 g_assert_cmpint (incoming_string_list->len, ==, outgoing_string_list->len);
273 for (index = 0; index < incoming_string_list->len; index += 1)
274 g_assert_cmpstr (((gchar **)incoming_string_list->pdata)[index],
275 ==,
276 ((gchar **)outgoing_string_list->pdata)[index]);
277
278 /* Clean up and exit */
279 thrift_transport_close (transport, NULL);
280
281 g_ptr_array_unref (incoming_string_list);
282 g_ptr_array_unref (outgoing_string_list);
283
284 g_object_unref (client);
285 g_object_unref (protocol);
286 g_object_unref (transport);
287 g_object_unref (socket);
288}
289
290int
291main(int argc, char *argv[])
292{
293 pid_t pid;
294 int status;
295
296#if (!GLIB_CHECK_VERSION (2, 36, 0))
297 g_type_init ();
298#endif
299
300 /* Fork to run our test suite in a child process */
301 pid = fork ();
302 g_assert_cmpint (pid, >=, 0);
303
304 if (pid == 0) { /* The child process */
305 /* Wait a moment for the server to finish starting */
306 sleep (1);
307
308 g_test_init (&argc, &argv, NULL);
309
310 g_test_add_func
311 ("/testcontainertest/ContainerTest/Structs/ContainersWithDefaultValues",
312 test_containers_with_default_values);
313 g_test_add_func
314 ("/testcontainertest/ContainerTest/Services/ContainerService/StringList",
315 test_container_service_string_list);
316
317 /* Run the tests and make the result available to our parent process */
318 _exit (g_test_run ());
319 }
320 else {
321 TTestContainerServiceHandler *handler;
322 TTestContainerServiceProcessor *processor;
323
324 ThriftServerTransport *server_transport;
325 ThriftTransportFactory *transport_factory;
326 ThriftProtocolFactory *protocol_factory;
327
328 struct sigaction sigchld_action;
329
330 GError *error = NULL;
331 int exit_status = 1;
332
333 /* Trap the event of the child process terminating so we know to stop the
334 server and exit */
335 memset (&sigchld_action, 0, sizeof (sigchld_action));
336 sigchld_action.sa_handler = sigchld_handler;
337 sigchld_action.sa_flags = SA_RESETHAND;
338 sigaction (SIGCHLD, &sigchld_action, NULL);
339
340 /* Create our test server */
341 handler = g_object_new (TYPE_TEST_CONTAINER_SERVICE_HANDLER,
342 NULL);
343 processor = g_object_new (T_TEST_TYPE_CONTAINER_SERVICE_PROCESSOR,
344 "handler", handler,
345 NULL);
346 server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
347 "port", TEST_SERVER_PORT,
348 NULL);
349 transport_factory = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
350 NULL);
351 protocol_factory = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
352 NULL);
353
354 server = g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
355 "processor", processor,
356 "server_transport", server_transport,
357 "input_transport_factory", transport_factory,
358 "output_transport_factory", transport_factory,
359 "input_protocol_factory", protocol_factory,
360 "output_protocol_factory", protocol_factory,
361 NULL);
362
363 /* Start the server */
364 thrift_server_serve (server, &error);
365
366 /* Make sure the server stopped only because it was interrupted (by the
367 child process terminating) */
368 g_assert (g_error_matches (error,
369 THRIFT_SERVER_SOCKET_ERROR,
370 THRIFT_SERVER_SOCKET_ERROR_ACCEPT));
371
372 /* Free our resources */
373 g_object_unref (server);
374 g_object_unref (transport_factory);
375 g_object_unref (protocol_factory);
376 g_object_unref (server_transport);
377
378 g_object_unref (processor);
379 g_object_unref (handler);
380
381 /* Wait for the child process to complete and return its exit status */
382 g_assert (wait (&status) == pid);
383 if (WIFEXITED (status))
384 exit_status = WEXITSTATUS (status);
385
386 return exit_status;
387 }
388}