1 #include <stdio.h>
2 #include <string.h>
3 
4 #include <glib.h>
5 #include <glib-object.h>
6 
7 #define DFT_DOMAIN g_quark_from_string("TEST")
8 #include <searpc.h>
9 
10 #include "searpc-server.h"
11 #include "searpc-client.h"
12 #include "searpc-named-pipe-transport.h"
13 #include "clar.h"
14 
15 #if !defined(WIN32)
16 static const char *pipe_path = "/tmp/.searpc-test";
17 #else
18 static const char *pipe_path = "\\\\.\\pipe\\libsearpc-test";
19 #endif
20 
21 /* sample class */
22 
23 #define MAMAN_TYPE_BAR                  (maman_bar_get_type ())
24 #define MAMAN_BAR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
25 #define MAMAN_IS_BAR(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
26 #define MAMAN_BAR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
27 #define MAMAN_IS_BAR_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
28 #define MAMAN_BAR_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
29 
30 #define NAMED_PIPE_SERVER_THREAD_POOL_SIZE 50
31 
32 typedef struct _MamanBar        MamanBar;
33 typedef struct _MamanBarClass   MamanBarClass;
34 
35 struct _MamanBar
36 {
37     GObject parent_instance;
38 
39     gchar *name;
40     int    papa_number;
41 };
42 
43 struct _MamanBarClass
44 {
45     GObjectClass parent_class;
46 };
47 
48 G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
49 
50 enum
51 {
52   PROP_0,
53   PROP_MAMAN_NAME,
54   PROP_PAPA_NUMBER
55 };
56 
57 static void
maman_bar_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)58 maman_bar_set_property (GObject      *object,
59                         guint         property_id,
60                         const GValue *value,
61                         GParamSpec   *pspec)
62 {
63     MamanBar *self = MAMAN_BAR (object);
64 
65     switch (property_id) {
66     case PROP_MAMAN_NAME:
67         g_free (self->name);
68         self->name = g_value_dup_string (value);
69         break;
70 
71     case PROP_PAPA_NUMBER:
72         self->papa_number = g_value_get_uchar (value);
73         break;
74 
75     default:
76         /* We don't have any other property... */
77         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
78         break;
79     }
80 }
81 
82 static void
maman_bar_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)83 maman_bar_get_property (GObject    *object,
84                         guint       property_id,
85                         GValue     *value,
86                         GParamSpec *pspec)
87 {
88     MamanBar *self = MAMAN_BAR (object);
89 
90     switch (property_id) {
91     case PROP_MAMAN_NAME:
92         g_value_set_string (value, self->name);
93         break;
94 
95     case PROP_PAPA_NUMBER:
96         g_value_set_uchar (value, self->papa_number);
97         break;
98 
99     default:
100         /* We don't have any other property... */
101         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
102         break;
103     }
104 }
105 
106 static void
maman_bar_finalize(GObject * gobject)107 maman_bar_finalize (GObject *gobject)
108 {
109   MamanBar *self = MAMAN_BAR (gobject);
110 
111   g_free (self->name);
112 
113   /* Chain up to the parent class */
114   G_OBJECT_CLASS (maman_bar_parent_class)->finalize (gobject);
115 }
116 
117 static void
maman_bar_class_init(MamanBarClass * klass)118 maman_bar_class_init (MamanBarClass *klass)
119 {
120     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
121     GParamSpec *pspec;
122 
123     gobject_class->set_property = maman_bar_set_property;
124     gobject_class->get_property = maman_bar_get_property;
125     gobject_class->finalize = maman_bar_finalize;
126 
127     pspec = g_param_spec_string ("name",
128                                  "Maman name",
129                                  "Set maman's name",
130                                  "no-name-set" /* default value */,
131                                  G_PARAM_READWRITE);
132     g_object_class_install_property (gobject_class,
133                                      PROP_MAMAN_NAME,
134                                      pspec);
135 
136     pspec = g_param_spec_uchar ("papa-number",
137                                 "Number of current Papa",
138                                 "Set/Get papa's number",
139                                 0  /* minimum value */,
140                                 10 /* maximum value */,
141                                 2  /* default value */,
142                                 G_PARAM_READWRITE);
143     g_object_class_install_property (gobject_class,
144                                      PROP_PAPA_NUMBER,
145                                      pspec);
146 }
147 
148 static void
maman_bar_init(MamanBar * self)149 maman_bar_init (MamanBar *self)
150 {
151 
152 }
153 
154 /* sample client */
155 static SearpcClient *client;
156 /* sample client with named pipe as transport */
157 static SearpcClient *client_with_pipe_transport;
158 
159 char *
sample_send(void * arg,const gchar * fcall_str,size_t fcall_len,size_t * ret_len)160 sample_send(void *arg, const gchar *fcall_str,
161             size_t fcall_len, size_t *ret_len)
162 {
163     cl_assert_ (strcmp(arg, "test") == 0, arg);
164 
165     char *ret;
166     /* directly call in memory, instead of send via network */
167     gchar *temp = g_strdup(fcall_str);
168     ret = searpc_server_call_function ("test", temp, fcall_len, ret_len);
169     g_free (temp);
170     return ret;
171 }
172 
173 int
sample_async_send(void * arg,gchar * fcall_str,size_t fcall_len,void * rpc_priv)174 sample_async_send (void *arg, gchar *fcall_str,
175                    size_t fcall_len, void *rpc_priv)
176 {
177     cl_assert (strcmp(arg, "test_async") == 0);
178 
179     char *ret;
180     size_t ret_len;
181     gchar *temp = g_strdup(fcall_str);
182 
183     ret = searpc_server_call_function ("test", temp, fcall_len, &ret_len);
184     g_free (temp);
185 
186     searpc_client_generic_callback (ret, ret_len, rpc_priv, NULL);
187 
188     g_free (ret);
189     return 0;
190 }
191 
192 gchar *
get_substring(const gchar * orig_str,int sub_len,GError ** error)193 get_substring (const gchar *orig_str, int sub_len, GError **error)
194 {
195     if (sub_len > strlen(orig_str)) {
196         g_set_error (error, DFT_DOMAIN, 100,
197                      "Substring length larger than the length of origin string");
198         return NULL;
199     }
200     gchar *ret = g_malloc0(sub_len+1);
201     memcpy(ret, orig_str, sub_len);
202     ret[sub_len] = '\0';
203     return ret;
204 }
205 
206 static SearpcClient *
do_create_client_with_pipe_transport()207 do_create_client_with_pipe_transport()
208 {
209     SearpcNamedPipeClient *pipe_client = searpc_create_named_pipe_client(pipe_path);
210     cl_must_pass_(searpc_named_pipe_client_connect(pipe_client), "named pipe client failed to connect");
211     return searpc_client_with_named_pipe_transport(pipe_client, "test");
212 }
213 
214 
215 void
test_searpc__simple_call(void)216 test_searpc__simple_call (void)
217 {
218     gchar* result;
219     GError *error = NULL;
220 
221     result = searpc_client_call__string (client, "get_substring", &error,
222                                          2, "string", "hello", "int", 2);
223     cl_assert (error == NULL);
224     cl_assert (strcmp(result, "he") == 0);
225     g_free (result);
226 
227     /* error should return */
228     result = NULL;
229     result = searpc_client_call__string (client, "get_substring", &error,
230                                          2, "string", "hello", "int", 10);
231     cl_assert (error->message);
232     g_free (result);
233     g_error_free(error);
234 }
235 
236 void
test_searpc__invalid_call(void)237 test_searpc__invalid_call (void)
238 {
239     gchar* result;
240     GError *error = NULL;
241 
242     result = searpc_client_call__string (client, "nonexist_func", &error,
243                                          2, "string", "hello", "int", 2);
244     cl_assert (error != NULL);
245     g_free (result);
246     g_error_free (error);
247 }
248 
249 GObject *
get_maman_bar(const char * name,GError ** error)250 get_maman_bar(const char *name, GError **error)
251 {
252     return g_object_new(MAMAN_TYPE_BAR, "name", name, NULL);
253 }
254 
255 void
test_searpc__object_call(void)256 test_searpc__object_call (void)
257 {
258     GObject *result;
259     GError *error = NULL;
260 
261     result = searpc_client_call__object (client, "get_maman_bar",
262                                          MAMAN_TYPE_BAR, &error,
263                                          1, "string", "kitty");
264     cl_assert (error == NULL);
265     g_object_unref (result);
266 }
267 
268 GList *
get_maman_bar_list(const char * name,int num,GError ** error)269 get_maman_bar_list (const char *name, int num, GError **error)
270 {
271     char buf[256];
272     GList *ret = 0;
273     int i;
274     GObject *obj;
275 
276     if (num < 0) {
277         g_set_error (error, DFT_DOMAIN, 100, "num must be positive.");
278         return NULL;
279     }
280     if (num > 1000) {
281         g_set_error (error, DFT_DOMAIN, 100, "num must no larger than 1000.");
282         return NULL;
283     }
284 
285     for (i = 0; i < num; i++) {
286         sprintf (buf, "%s%d", name, i);
287         obj = g_object_new(MAMAN_TYPE_BAR, "name", buf, NULL);
288         ret = g_list_prepend (ret, obj);
289     }
290     ret = g_list_reverse (ret);
291     return ret;
292 }
293 
294 
295 void
test_searpc__objlist_call(void)296 test_searpc__objlist_call (void)
297 {
298     GList *result, *ptr;
299     GError *error = NULL;
300 
301     result = searpc_client_call__objlist (client, "get_maman_bar_list",
302                                           MAMAN_TYPE_BAR, &error,
303                                           2, "string", "kitty", "int", 10);
304     cl_assert (error == NULL);
305     for (ptr = result; ptr; ptr = ptr->next)
306         g_object_unref (ptr->data);
307     g_list_free (result);
308 
309     result =  searpc_client_call__objlist (client, "get_maman_bar_list",
310                                            MAMAN_TYPE_BAR, &error,
311                                            2, "string", "kitty", "int", 0);
312     cl_assert (error == NULL);
313     for (ptr = result; ptr; ptr = ptr->next)
314         g_object_unref (ptr->data);
315     g_list_free (result);
316 }
317 
318 json_t *
simple_json_rpc(const char * name,int num,GError ** error)319 simple_json_rpc (const char *name, int num, GError **error)
320 {
321     json_t * ret = json_object();
322     json_object_set_new (ret, name, json_integer (num));
323     return ret;
324 }
325 
326 void
test_searpc__json_return_type(void)327 test_searpc__json_return_type (void)
328 {
329     json_t *result;
330     GError *error = NULL;
331 
332     result = searpc_client_call__json (client, "simple_json_rpc",
333                                        &error, 2,
334                                        "string", "year",
335                                        "int", 2016);
336     cl_assert (error == NULL);
337     cl_assert (json_integer_value(json_object_get(result, "year")) == 2016);
338     json_decref(result);
339 }
340 
341 json_t *
count_json_kvs(const json_t * obj,GError ** error)342 count_json_kvs (const json_t *obj, GError **error)
343 {
344     int count = 0;
345 
346     json_t *member;
347     member = json_object_iter ((json_t*)obj);
348     while (member != NULL) {
349         member = json_object_iter_next ((json_t*)obj, member);
350         count++;
351     }
352 
353     json_t * ret = json_object();
354     json_object_set_new (ret, "number_of_kvs", json_integer (count));
355     return ret;
356 }
357 
358 void
test_searpc__json_param_type(void)359 test_searpc__json_param_type (void)
360 {
361     json_t *result;
362     GError *error = NULL;
363 
364     json_t *param = json_object();
365     json_object_set_new (param, "a", json_integer (1));
366     json_object_set_new (param, "b", json_integer (2));
367 
368     result = searpc_client_call__json (client, "count_json_kvs",
369                                        &error, 1,
370                                        "json", param);
371 
372     cl_assert_ (error == NULL, error ? error->message : "");
373     int count = json_integer_value(json_object_get((json_t*)result, "number_of_kvs"));
374     char *msg = json_dumps(result, JSON_INDENT(2));
375     cl_assert_(count == 2, msg);
376     free (msg);
377     json_decref(param);
378     json_decref(result);
379 }
380 
381 
simple_callback(void * result,void * user_data,GError * error)382 void simple_callback (void *result, void *user_data, GError *error)
383 {
384     char *res = (char *)result;
385 
386     cl_assert (strcmp(res, "he") == 0);
387 }
388 
simple_callback_error(void * result,void * user_data,GError * error)389 void simple_callback_error (void *result, void *user_data, GError *error)
390 {
391     cl_assert (result == NULL);
392     cl_assert (error != NULL);
393     g_error_free (error);
394 }
395 
396 void
test_searpc__simple_call_async(void)397 test_searpc__simple_call_async (void)
398 {
399     searpc_client_async_call__string (client, "get_substring",
400                                       simple_callback, NULL,
401                                       2, "string", "hello", "int", 2);
402 
403     searpc_client_async_call__string (client, "get_substring",
404                                       simple_callback_error, NULL,
405                                       2, "string", "hello", "int", 10);
406 }
407 
async_callback_json(void * result,void * user_data,GError * error)408 void async_callback_json (void *result, void *user_data, GError *error)
409 {
410     cl_assert(json_integer_value(json_object_get((json_t*)result, "hello")) == 10);
411 }
412 
413 void
test_searpc__simple_call_async_json(void)414 test_searpc__simple_call_async_json (void)
415 {
416     searpc_client_async_call__json (client, "simple_json_rpc",
417                                     async_callback_json, NULL,
418                                     2, "string", "hello", "int", 10);
419 }
420 
421 void
test_searpc__pipe_simple_call(void)422 test_searpc__pipe_simple_call (void)
423 {
424     gchar* result;
425     GError *error = NULL;
426 
427     result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
428                                          2, "string", "hello", "int", 2);
429     cl_assert_ (error == NULL, error ? error->message : "");
430     cl_assert (strcmp(result, "he") == 0);
431     g_free (result);
432 
433     /* error should return */
434     result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
435                                          2, "string", "hello", "int", 10);
436     cl_assert (error->message);
437     g_free (result);
438 }
439 
440 void
test_searpc__pipe_large_request(void)441 test_searpc__pipe_large_request (void)
442 {
443     gchar* result;
444     GError *error = NULL;
445 
446     // 10MB
447     int size = 10 * 1024 * 1024;
448     GString *large_string = g_string_sized_new(size);
449     while (large_string->len < size) {
450         g_string_append(large_string, "aaaa");
451     }
452 
453     // Large request
454     result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
455                                          2, "string", large_string->str, "int", 2);
456     cl_assert_ (error == NULL, error ? error->message : "");
457     cl_assert_ (strcmp(result, "aa") == 0, result);
458     g_free (result);
459 
460     // Large request & Large response
461     result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
462                                          2, "string", large_string->str, "int", size - 2);
463     cl_assert_ (error == NULL, error ? error->message : "");
464     // cl_assert (strcmp(result, "aa") == 0);
465     g_free (result);
466 
467     g_string_free (large_string, TRUE);
468 }
469 
do_pipe_connect_and_request(void * arg)470 static void * do_pipe_connect_and_request(void *arg)
471 {
472     SearpcClient *client = do_create_client_with_pipe_transport();
473 
474     // 100KB
475     int size = 100 * 1024;
476     GString *large_string = g_string_sized_new(size);
477     while (large_string->len < size) {
478         g_string_append(large_string, "aaaa");
479     }
480 
481     gchar* result;
482     GError *error = NULL;
483     result = searpc_client_call__string (client, "get_substring", &error,
484                                          2, "string", large_string->str, "int", 2);
485     cl_assert_ (error == NULL, error ? error->message : "");
486     cl_assert_ (strcmp(result, "aa") == 0, result);
487     g_free (result);
488 
489     g_string_free (large_string, TRUE);
490     searpc_free_client_with_pipe_transport(client);
491 
492     return NULL;
493 }
494 
495 // Simulate the situation that the server can handle multiple clients connecting
496 // at the same time.
497 void
test_searpc__pipe_concurrent_clients(void)498 test_searpc__pipe_concurrent_clients (void)
499 {
500     // M concurrent clients, and run the test for N times.
501     int m_clients = 5;
502     int n_times = 20;
503 
504     int i;
505     for (i = 0; i < n_times; i++) {
506         g_usleep(100000);
507         pthread_t *threads = g_new0(pthread_t, m_clients);
508 
509         int j;
510         for (j = 0; j < m_clients; j++) {
511             pthread_create(&threads[j], NULL, do_pipe_connect_and_request, NULL);
512         }
513 
514         void *ret;
515         for (j = 0; j < m_clients; j++) {
516             pthread_join(threads[j], &ret);
517         }
518         g_free (threads);
519     }
520 }
521 
522 
523 #include "searpc-signature.h"
524 #include "searpc-marshal.h"
525 
526 void
test_searpc__initialize(void)527 test_searpc__initialize (void)
528 {
529     searpc_server_init (register_marshals);
530     searpc_create_service ("test");
531     searpc_server_register_function ("test", get_substring, "get_substring",
532                                      searpc_signature_string__string_int());
533     searpc_server_register_function ("test", get_maman_bar, "get_maman_bar",
534                                      searpc_signature_object__string());
535     searpc_server_register_function ("test", get_maman_bar_list, "get_maman_bar_list",
536                                      searpc_signature_objlist__string_int());
537     searpc_server_register_function ("test", simple_json_rpc, "simple_json_rpc",
538                                      searpc_signature_json__string_int());
539     searpc_server_register_function ("test", count_json_kvs, "count_json_kvs",
540                                      searpc_signature_json__json());
541 
542     /* sample client */
543     client = searpc_client_new();
544     client->send = sample_send;
545     client->arg = "test";
546 
547     client->async_send = sample_async_send;
548     client->async_arg = "test_async";
549 
550     SearpcNamedPipeServer *pipe_server = searpc_create_named_pipe_server_with_threadpool(pipe_path, NAMED_PIPE_SERVER_THREAD_POOL_SIZE);
551     cl_must_pass_(searpc_named_pipe_server_start(pipe_server), "named pipe server failed to start");
552 #if defined(WIN32)
553     // Wait for the server thread to start
554     Sleep(1000);
555 #endif
556 
557     client_with_pipe_transport = do_create_client_with_pipe_transport();
558 }
559 
560 void
test_searpc__cleanup(void)561 test_searpc__cleanup (void)
562 {
563     searpc_free_client_with_pipe_transport(client_with_pipe_transport);
564 
565     /* free memory for memory debug with valgrind */
566     searpc_server_final();
567 }
568