blob: 434c25ae72778759886917f4317d377cf5db93c8 [file] [log] [blame]
Benny Prijono0a749f12005-10-31 21:02:30 +00001/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c 4 10/29/05 10:23p Bennylp $
2 */
3/*
4 * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c $
5 *
6 * 4 10/29/05 10:23p Bennylp
7 * Fixed no-memory exception.
8 *
9 * 3 10/29/05 11:51a Bennylp
10 * Version 0.3-pre2.
11 *
12 * 2 10/14/05 12:26a Bennylp
13 * Finished error code framework, some fixes in ioqueue, etc. Pretty
14 * major.
15 *
16 */
17#include "test.h"
18
19/**
20 * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
21 *
22 * This file provides implementation to test the
23 * functionality of the I/O queue when TCP socket is used.
24 *
25 *
26 * This file is <b>pjlib-test/ioq_tcp.c</b>
27 *
28 * \include pjlib-test/ioq_tcp.c
29 */
30
31
32#if INCLUDE_TCP_IOQUEUE_TEST
33
34#include <pjlib.h>
35
36#if PJ_HAS_TCP
37
38#define THIS_FILE "test_tcp"
39#define PORT 50000
40#define NON_EXISTANT_PORT 50123
41#define LOOP 100
42#define BUF_MIN_SIZE 32
43#define BUF_MAX_SIZE 2048
44#define SOCK_INACTIVE_MIN (4-2)
45#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
46#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
47
48static pj_ssize_t callback_read_size,
49 callback_write_size,
50 callback_accept_status,
51 callback_connect_status;
52static pj_ioqueue_key_t*callback_read_key,
53 *callback_write_key,
54 *callback_accept_key,
55 *callback_connect_key;
56
57static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
58{
59 callback_read_key = key;
60 callback_read_size = bytes_read;
61}
62
63static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written)
64{
65 callback_write_key = key;
66 callback_write_size = bytes_written;
67}
68
69static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock,
70 int status)
71{
72 PJ_UNUSED_ARG(sock);
73
74 callback_accept_key = key;
75 callback_accept_status = status;
76}
77
78static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
79{
80 callback_connect_key = key;
81 callback_connect_status = status;
82}
83
84static pj_ioqueue_callback test_cb =
85{
86 &on_ioqueue_read,
87 &on_ioqueue_write,
88 &on_ioqueue_accept,
89 &on_ioqueue_connect,
90};
91
92static int send_recv_test(pj_ioqueue_t *ioque,
93 pj_ioqueue_key_t *skey,
94 pj_ioqueue_key_t *ckey,
95 void *send_buf,
96 void *recv_buf,
97 pj_ssize_t bufsize,
98 pj_timestamp *t_elapsed)
99{
100 int rc;
101 pj_ssize_t bytes;
102 pj_timestamp t1, t2;
103 int pending_op = 0;
104
105 // Start reading on the server side.
106 rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize);
107 if (rc != 0 && rc != PJ_EPENDING) {
108 return -100;
109 }
110
111 ++pending_op;
112
113 // Randomize send buffer.
114 pj_create_random_string((char*)send_buf, bufsize);
115
116 // Starts send on the client side.
117 bytes = pj_ioqueue_write(ioque, ckey, send_buf, bufsize);
118 if (bytes != bufsize && bytes != PJ_EPENDING) {
119 return -120;
120 }
121 if (bytes == PJ_EPENDING) {
122 ++pending_op;
123 }
124
125 // Begin time.
126 pj_get_timestamp(&t1);
127
128 // Reset indicators
129 callback_read_size = callback_write_size = 0;
130 callback_read_key = callback_write_key = NULL;
131
132 // Poll the queue until we've got completion event in the server side.
133 rc = 0;
134 while (pending_op > 0) {
135 rc = pj_ioqueue_poll(ioque, NULL);
136 if (rc > 0) {
137 if (callback_read_size) {
138 if (callback_read_size != bufsize) {
139 return -160;
140 }
141 if (callback_read_key != skey)
142 return -161;
143 }
144 if (callback_write_size) {
145 if (callback_write_key != ckey)
146 return -162;
147 }
148 pending_op -= rc;
149 }
150 if (rc < 0) {
151 return -170;
152 }
153 }
154
155 // End time.
156 pj_get_timestamp(&t2);
157 t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
158
159 if (rc < 0) {
160 return -150;
161 }
162
163 // Compare recv buffer with send buffer.
164 if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
165 return -180;
166 }
167
168 // Success
169 return 0;
170}
171
172
173/*
174 * Compliance test for success scenario.
175 */
176static int compliance_test_0(void)
177{
178 pj_sock_t ssock=-1, csock0=-1, csock1=-1;
179 pj_sockaddr_in addr, client_addr, rmt_addr;
180 int client_addr_len;
181 pj_pool_t *pool = NULL;
182 char *send_buf, *recv_buf;
183 pj_ioqueue_t *ioque = NULL;
184 pj_ioqueue_key_t *skey, *ckey0, *ckey1;
185 int bufsize = BUF_MIN_SIZE;
186 pj_ssize_t status = -1;
187 int pending_op = 0;
188 pj_timestamp t_elapsed;
189 pj_str_t s;
190 pj_status_t rc;
191
192 // Create pool.
193 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
194
195 // Allocate buffers for send and receive.
196 send_buf = (char*)pj_pool_alloc(pool, bufsize);
197 recv_buf = (char*)pj_pool_alloc(pool, bufsize);
198
199 // Create server socket and client socket for connecting
200 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock);
201 if (rc != PJ_SUCCESS) {
202 app_perror("...error creating socket", rc);
203 status=-1; goto on_error;
204 }
205
206 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
207 if (rc != PJ_SUCCESS) {
208 app_perror("...error creating socket", rc);
209 status=-1; goto on_error;
210 }
211
212 // Bind server socket.
213 memset(&addr, 0, sizeof(addr));
214 addr.sin_family = PJ_AF_INET;
215 addr.sin_port = pj_htons(PORT);
216 if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
217 app_perror("...bind error", rc);
218 status=-10; goto on_error;
219 }
220
221 // Create I/O Queue.
222 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque);
223 if (rc != PJ_SUCCESS) {
224 app_perror("...ERROR in pj_ioqueue_create()", rc);
225 status=-20; goto on_error;
226 }
227
228 // Register server socket and client socket.
229 rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
230 if (rc == PJ_SUCCESS)
231 rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
232 &ckey1);
233 else
234 ckey1 = NULL;
235 if (rc != PJ_SUCCESS) {
236 app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
237 status=-23; goto on_error;
238 }
239
240 // Server socket listen().
241 if (pj_sock_listen(ssock, 5)) {
242 app_perror("...ERROR in pj_sock_listen()", rc);
243 status=-25; goto on_error;
244 }
245
246 // Server socket accept()
247 client_addr_len = sizeof(pj_sockaddr_in);
248 status = pj_ioqueue_accept(ioque, skey, &csock0, &client_addr, &rmt_addr, &client_addr_len);
249 if (status != PJ_EPENDING) {
250 app_perror("...ERROR in pj_ioqueue_accept()", rc);
251 status=-30; goto on_error;
252 }
253 if (status==PJ_EPENDING) {
254 ++pending_op;
255 }
256
257 // Initialize remote address.
258 memset(&addr, 0, sizeof(addr));
259 addr.sin_family = PJ_AF_INET;
260 addr.sin_port = pj_htons(PORT);
261 addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
262
263 // Client socket connect()
264 status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
265 if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
266 app_perror("...ERROR in pj_ioqueue_connect()", rc);
267 status=-40; goto on_error;
268 }
269 if (status==PJ_EPENDING) {
270 ++pending_op;
271 }
272
273 // Poll until connected
274 callback_read_size = callback_write_size = 0;
275 callback_accept_status = callback_connect_status = -2;
276
277 callback_read_key = callback_write_key =
278 callback_accept_key = callback_connect_key = NULL;
279
280 while (pending_op) {
281 pj_time_val timeout = {1, 0};
282
283 status=pj_ioqueue_poll(ioque, &timeout);
284 if (status > 0) {
285 if (callback_accept_status != -2) {
286 if (callback_accept_status != 0) {
287 status=-41; goto on_error;
288 }
289 if (callback_accept_key != skey) {
290 status=-41; goto on_error;
291 }
292 }
293
294 if (callback_connect_status != -2) {
295 if (callback_connect_status != 0) {
296 status=-50; goto on_error;
297 }
298 if (callback_connect_key != ckey1) {
299 status=-51; goto on_error;
300 }
301 }
302
303 pending_op -= status;
304
305 if (pending_op == 0) {
306 status = 0;
307 }
308 }
309 }
310
311 // Check accepted socket.
312 if (csock0 == PJ_INVALID_SOCKET) {
313 status = -69;
314 app_perror("...accept() error", pj_get_os_error());
315 goto on_error;
316 }
317
318 // Register newly accepted socket.
319 rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
320 &test_cb, &ckey0);
321 if (rc != PJ_SUCCESS) {
322 app_perror("...ERROR in pj_ioqueue_register_sock", rc);
323 status = -70;
324 goto on_error;
325 }
326
327 // Test send and receive.
328 t_elapsed.u32.lo = 0;
329 status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed);
330 if (status != 0) {
331 goto on_error;
332 }
333
334 // Success
335 status = 0;
336
337on_error:
338 if (ssock != PJ_INVALID_SOCKET)
339 pj_sock_close(ssock);
340 if (csock1 != PJ_INVALID_SOCKET)
341 pj_sock_close(csock1);
342 if (csock0 != PJ_INVALID_SOCKET)
343 pj_sock_close(csock0);
344 if (ioque != NULL)
345 pj_ioqueue_destroy(ioque);
346 pj_pool_release(pool);
347 return status;
348
349}
350
351/*
352 * Compliance test for failed scenario.
353 * In this case, the client connects to a non-existant service.
354 */
355static int compliance_test_1(void)
356{
357 pj_sock_t csock1=-1;
358 pj_sockaddr_in addr;
359 pj_pool_t *pool = NULL;
360 pj_ioqueue_t *ioque = NULL;
361 pj_ioqueue_key_t *ckey1;
362 pj_ssize_t status = -1;
363 int pending_op = 0;
364 pj_str_t s;
365 pj_status_t rc;
366
367 // Create pool.
368 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
369
370 // Create I/O Queue.
371 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque);
372 if (!ioque) {
373 status=-20; goto on_error;
374 }
375
376 // Create client socket
377 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
378 if (rc != PJ_SUCCESS) {
379 app_perror("...ERROR in pj_sock_socket()", rc);
380 status=-1; goto on_error;
381 }
382
383 // Register client socket.
384 rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
385 &test_cb, &ckey1);
386 if (rc != PJ_SUCCESS) {
387 app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
388 status=-23; goto on_error;
389 }
390
391 // Initialize remote address.
392 memset(&addr, 0, sizeof(addr));
393 addr.sin_family = PJ_AF_INET;
394 addr.sin_port = pj_htons(NON_EXISTANT_PORT);
395 addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
396
397 // Client socket connect()
398 status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
399 if (status==PJ_SUCCESS) {
400 // unexpectedly success!
401 status = -30;
402 goto on_error;
403 }
404 if (status != PJ_EPENDING) {
405 // success
406 } else {
407 ++pending_op;
408 }
409
410 callback_connect_status = -2;
411 callback_connect_key = NULL;
412
413 // Poll until we've got result
414 while (pending_op) {
415 pj_time_val timeout = {1, 0};
416
417 status=pj_ioqueue_poll(ioque, &timeout);
418 if (status > 0) {
419 if (callback_connect_key==ckey1) {
420 if (callback_connect_status == 0) {
421 // unexpectedly connected!
422 status = -50;
423 goto on_error;
424 }
425 }
426
427 pending_op -= status;
428 if (pending_op == 0) {
429 status = 0;
430 }
431 }
432 }
433
434 // Success
435 status = 0;
436
437on_error:
438 if (csock1 != PJ_INVALID_SOCKET)
439 pj_sock_close(csock1);
440 if (ioque != NULL)
441 pj_ioqueue_destroy(ioque);
442 pj_pool_release(pool);
443 return status;
444}
445
446int tcp_ioqueue_test()
447{
448 int status;
449
450 PJ_LOG(3, (THIS_FILE, "..compliance test 0 (success scenario)"));
451 if ((status=compliance_test_0()) != 0) {
452 PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
453 return status;
454 }
455 PJ_LOG(3, (THIS_FILE, "..compliance test 1 (failed scenario)"));
456 if ((status=compliance_test_1()) != 0) {
457 PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
458 return status;
459 }
460
461 return 0;
462}
463
464#endif /* PJ_HAS_TCP */
465
466
467#else
468/* To prevent warning about "translation unit is empty"
469 * when this test is disabled.
470 */
471int dummy_uiq_tcp;
472#endif /* INCLUDE_TCP_IOQUEUE_TEST */
473
474