summaryrefslogtreecommitdiff
path: root/pjlib/src/pjlib-test
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src/pjlib-test')
-rw-r--r--pjlib/src/pjlib-test/activesock.c521
-rw-r--r--pjlib/src/pjlib-test/atomic.c109
-rw-r--r--pjlib/src/pjlib-test/echo_clt.c269
-rw-r--r--pjlib/src/pjlib-test/errno.c171
-rw-r--r--pjlib/src/pjlib-test/exception.c282
-rw-r--r--pjlib/src/pjlib-test/exception_wrap.cpp24
-rw-r--r--pjlib/src/pjlib-test/fifobuf.c117
-rw-r--r--pjlib/src/pjlib-test/file.c225
-rw-r--r--pjlib/src/pjlib-test/hash_test.c163
-rw-r--r--pjlib/src/pjlib-test/ioq_perf.c572
-rw-r--r--pjlib/src/pjlib-test/ioq_tcp.c972
-rw-r--r--pjlib/src/pjlib-test/ioq_udp.c951
-rw-r--r--pjlib/src/pjlib-test/ioq_unreg.c387
-rw-r--r--pjlib/src/pjlib-test/list.c226
-rw-r--r--pjlib/src/pjlib-test/main.c107
-rw-r--r--pjlib/src/pjlib-test/main_mod.c40
-rw-r--r--pjlib/src/pjlib-test/main_rtems.c326
-rw-r--r--pjlib/src/pjlib-test/main_symbian.cpp133
-rw-r--r--pjlib/src/pjlib-test/main_win32.c211
-rw-r--r--pjlib/src/pjlib-test/mutex.c234
-rw-r--r--pjlib/src/pjlib-test/os.c116
-rw-r--r--pjlib/src/pjlib-test/pjlib_test_reg.rss12
-rw-r--r--pjlib/src/pjlib-test/pool.c321
-rw-r--r--pjlib/src/pjlib-test/pool_perf.c194
-rw-r--r--pjlib/src/pjlib-test/pool_wrap.cpp24
-rw-r--r--pjlib/src/pjlib-test/rand.c54
-rw-r--r--pjlib/src/pjlib-test/rbtree.c168
-rw-r--r--pjlib/src/pjlib-test/rtems_network_config.h149
-rw-r--r--pjlib/src/pjlib-test/select.c221
-rw-r--r--pjlib/src/pjlib-test/sleep.c224
-rw-r--r--pjlib/src/pjlib-test/sock.c877
-rw-r--r--pjlib/src/pjlib-test/sock_perf.c190
-rw-r--r--pjlib/src/pjlib-test/ssl_sock.c1424
-rw-r--r--pjlib/src/pjlib-test/string.c438
-rw-r--r--pjlib/src/pjlib-test/test.c234
-rw-r--r--pjlib/src/pjlib-test/test.h129
-rw-r--r--pjlib/src/pjlib-test/test_wrap.cpp24
-rw-r--r--pjlib/src/pjlib-test/thread.c336
-rw-r--r--pjlib/src/pjlib-test/timer.c196
-rw-r--r--pjlib/src/pjlib-test/timestamp.c235
-rw-r--r--pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c214
-rw-r--r--pjlib/src/pjlib-test/udp_echo_srv_sync.c167
-rw-r--r--pjlib/src/pjlib-test/util.c140
43 files changed, 12127 insertions, 0 deletions
diff --git a/pjlib/src/pjlib-test/activesock.c b/pjlib/src/pjlib-test/activesock.c
new file mode 100644
index 0000000..399cc29
--- /dev/null
+++ b/pjlib/src/pjlib-test/activesock.c
@@ -0,0 +1,521 @@
+/* $Id: activesock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+/**
+ * \page page_pjlib_activesock_test Test: Active Socket
+ *
+ * This file is <b>pjlib-test/activesock.c</b>
+ *
+ * \include pjlib-test/activesock.c
+ */
+
+#if INCLUDE_ACTIVESOCK_TEST
+
+#define THIS_FILE "activesock.c"
+
+
+/*******************************************************************
+ * Simple UDP echo server.
+ */
+struct udp_echo_srv
+{
+ pj_activesock_t *asock;
+ pj_bool_t echo_enabled;
+ pj_uint16_t port;
+ pj_ioqueue_op_key_t send_key;
+ pj_status_t status;
+ unsigned rx_cnt;
+ unsigned rx_err_cnt, tx_err_cnt;
+};
+
+static void udp_echo_err(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(3,(THIS_FILE, " error: %s: %s", title, errmsg));
+}
+
+static pj_bool_t udp_echo_srv_on_data_recvfrom(pj_activesock_t *asock,
+ void *data,
+ pj_size_t size,
+ const pj_sockaddr_t *src_addr,
+ int addr_len,
+ pj_status_t status)
+{
+ struct udp_echo_srv *srv;
+ pj_ssize_t sent;
+
+
+ srv = (struct udp_echo_srv*) pj_activesock_get_user_data(asock);
+
+ if (status != PJ_SUCCESS) {
+ srv->status = status;
+ srv->rx_err_cnt++;
+ udp_echo_err("recvfrom() callback", status);
+ return PJ_TRUE;
+ }
+
+ srv->rx_cnt++;
+
+ /* Send back if echo is enabled */
+ if (srv->echo_enabled) {
+ sent = size;
+ srv->status = pj_activesock_sendto(asock, &srv->send_key, data,
+ &sent, 0,
+ src_addr, addr_len);
+ if (srv->status != PJ_SUCCESS) {
+ srv->tx_err_cnt++;
+ udp_echo_err("sendto()", status);
+ }
+ }
+
+ return PJ_TRUE;
+}
+
+
+static pj_status_t udp_echo_srv_create(pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_bool_t enable_echo,
+ struct udp_echo_srv **p_srv)
+{
+ struct udp_echo_srv *srv;
+ pj_sock_t sock_fd = PJ_INVALID_SOCKET;
+ pj_sockaddr addr;
+ int addr_len;
+ pj_activesock_cb activesock_cb;
+ pj_status_t status;
+
+ srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv);
+ srv->echo_enabled = enable_echo;
+
+ pj_sockaddr_in_init(&addr.ipv4, NULL, 0);
+ addr_len = sizeof(addr);
+
+ pj_bzero(&activesock_cb, sizeof(activesock_cb));
+ activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom;
+
+ status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb,
+ srv, &srv->asock, &addr);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock_fd);
+ udp_echo_err("pj_activesock_create()", status);
+ return status;
+ }
+
+ srv->port = pj_ntohs(addr.ipv4.sin_port);
+
+ pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
+
+ status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0);
+ if (status != PJ_SUCCESS) {
+ pj_activesock_close(srv->asock);
+ udp_echo_err("pj_activesock_start_recvfrom()", status);
+ return status;
+ }
+
+
+ *p_srv = srv;
+ return PJ_SUCCESS;
+}
+
+static void udp_echo_srv_destroy(struct udp_echo_srv *srv)
+{
+ pj_activesock_close(srv->asock);
+}
+
+/*******************************************************************
+ * UDP ping pong test (send packet back and forth between two UDP echo
+ * servers.
+ */
+static int udp_ping_pong_test(void)
+{
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_pool_t *pool = NULL;
+ struct udp_echo_srv *srv1=NULL, *srv2=NULL;
+ pj_bool_t need_send = PJ_TRUE;
+ unsigned data = 0;
+ int count, ret;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "pingpong", 512, 512, NULL);
+ if (!pool)
+ return -10;
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ ret = -20;
+ udp_echo_err("pj_ioqueue_create()", status);
+ goto on_return;
+ }
+
+ status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1);
+ if (status != PJ_SUCCESS) {
+ ret = -30;
+ goto on_return;
+ }
+
+ status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2);
+ if (status != PJ_SUCCESS) {
+ ret = -40;
+ goto on_return;
+ }
+
+ /* initiate the first send */
+ for (count=0; count<1000; ++count) {
+ unsigned last_rx1, last_rx2;
+ unsigned i;
+
+ if (need_send) {
+ pj_str_t loopback;
+ pj_sockaddr_in addr;
+ pj_ssize_t sent;
+
+ ++data;
+
+ sent = sizeof(data);
+ loopback = pj_str("127.0.0.1");
+ pj_sockaddr_in_init(&addr, &loopback, srv2->port);
+ status = pj_activesock_sendto(srv1->asock, &srv1->send_key,
+ &data, &sent, 0,
+ &addr, sizeof(addr));
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ ret = -50;
+ udp_echo_err("sendto()", status);
+ goto on_return;
+ }
+
+ need_send = PJ_FALSE;
+ }
+
+ last_rx1 = srv1->rx_cnt;
+ last_rx2 = srv2->rx_cnt;
+
+ for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
+ pj_time_val delay = {0, 10};
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 100);
+#else
+ pj_ioqueue_poll(ioqueue, &delay);
+#endif
+ }
+
+ if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 ||
+ srv2->rx_err_cnt+srv2->tx_err_cnt != 0)
+ {
+ /* Got error */
+ ret = -60;
+ goto on_return;
+ }
+
+ if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) {
+ /* Packet lost */
+ ret = -70;
+ udp_echo_err("packets have been lost", PJ_ETIMEDOUT);
+ goto on_return;
+ }
+ }
+
+ ret = 0;
+
+on_return:
+ if (srv2)
+ udp_echo_srv_destroy(srv2);
+ if (srv1)
+ udp_echo_srv_destroy(srv1);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return ret;
+}
+
+
+
+#define SIGNATURE 0xdeadbeef
+struct tcp_pkt
+{
+ pj_uint32_t signature;
+ pj_uint32_t seq;
+ char fill[513];
+};
+
+struct tcp_state
+{
+ pj_bool_t err;
+ pj_bool_t sent;
+ pj_uint32_t next_recv_seq;
+ pj_uint8_t pkt[600];
+};
+
+struct send_key
+{
+ pj_ioqueue_op_key_t op_key;
+};
+
+
+static pj_bool_t tcp_on_data_read(pj_activesock_t *asock,
+ void *data,
+ pj_size_t size,
+ pj_status_t status,
+ pj_size_t *remainder)
+{
+ struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock);
+ char *next = (char*) data;
+
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ PJ_LOG(1,("", " err: status=%d", status));
+ st->err = PJ_TRUE;
+ return PJ_FALSE;
+ }
+
+ while (size >= sizeof(struct tcp_pkt)) {
+ struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next;
+
+ if (tcp_pkt->signature != SIGNATURE) {
+ PJ_LOG(1,("", " err: invalid signature at seq=%d",
+ st->next_recv_seq));
+ st->err = PJ_TRUE;
+ return PJ_FALSE;
+ }
+ if (tcp_pkt->seq != st->next_recv_seq) {
+ PJ_LOG(1,("", " err: wrong sequence"));
+ st->err = PJ_TRUE;
+ return PJ_FALSE;
+ }
+
+ st->next_recv_seq++;
+ next += sizeof(struct tcp_pkt);
+ size -= sizeof(struct tcp_pkt);
+ }
+
+ if (size) {
+ pj_memmove(data, next, size);
+ *remainder = size;
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t sent)
+{
+ struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock);
+
+ PJ_UNUSED_ARG(op_key);
+
+ st->sent = 1;
+
+ if (sent < 1) {
+ st->err = PJ_TRUE;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+static int tcp_perf_test(void)
+{
+ enum { COUNT=10000 };
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
+ pj_activesock_t *asock1 = NULL, *asock2 = NULL;
+ pj_activesock_cb cb;
+ struct tcp_state *state1, *state2;
+ unsigned i;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
+
+ status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1,
+ &sock2);
+ if (status != PJ_SUCCESS) {
+ status = -100;
+ goto on_return;
+ }
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ status = -110;
+ goto on_return;
+ }
+
+ pj_bzero(&cb, sizeof(cb));
+ cb.on_data_read = &tcp_on_data_read;
+ cb.on_data_sent = &tcp_on_data_sent;
+
+ state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
+ status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
+ &cb, state1, &asock1);
+ if (status != PJ_SUCCESS) {
+ status = -120;
+ goto on_return;
+ }
+
+ state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
+ status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
+ &cb, state2, &asock2);
+ if (status != PJ_SUCCESS) {
+ status = -130;
+ goto on_return;
+ }
+
+ status = pj_activesock_start_read(asock1, pool, 1000, 0);
+ if (status != PJ_SUCCESS) {
+ status = -140;
+ goto on_return;
+ }
+
+ /* Send packet as quickly as possible */
+ for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
+ struct tcp_pkt *pkt;
+ struct send_key send_key[2], *op_key;
+ pj_ssize_t len;
+
+ pkt = (struct tcp_pkt*)state2->pkt;
+ pkt->signature = SIGNATURE;
+ pkt->seq = i;
+ pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
+
+ op_key = &send_key[i%2];
+ pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
+
+ state2->sent = PJ_FALSE;
+ len = sizeof(*pkt);
+ status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
+ if (status == PJ_EPENDING) {
+ do {
+#if PJ_SYMBIAN
+ pj_symbianos_poll(-1, -1);
+#else
+ pj_ioqueue_poll(ioqueue, NULL);
+#endif
+ } while (!state2->sent);
+ } else {
+#if PJ_SYMBIAN
+ /* The Symbian socket always returns PJ_SUCCESS for TCP send,
+ * eventhough the remote end hasn't received the data yet.
+ * If we continue sending, eventually send() will block,
+ * possibly because the send buffer is full. So we need to
+ * poll the ioqueue periodically, to let receiver gets the
+ * data.
+ */
+ pj_symbianos_poll(-1, 0);
+#endif
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,("", " err: send status=%d", status));
+ status = -180;
+ break;
+ } else if (status == PJ_SUCCESS) {
+ if (len != sizeof(*pkt)) {
+ PJ_LOG(1,("", " err: shouldn't report partial sent"));
+ status = -190;
+ break;
+ }
+ }
+ }
+
+#ifndef PJ_SYMBIAN
+ for (;;) {
+ pj_time_val timeout = {0, 10};
+ if (pj_ioqueue_poll(ioqueue, &timeout) < 1)
+ break;
+ }
+#endif
+
+ }
+
+ /* Wait until everything has been sent/received */
+ if (state1->next_recv_seq < COUNT) {
+#ifdef PJ_SYMBIAN
+ while (pj_symbianos_poll(-1, 1000) == PJ_TRUE)
+ ;
+#else
+ pj_time_val delay = {0, 100};
+ while (pj_ioqueue_poll(ioqueue, &delay) > 0)
+ ;
+#endif
+ }
+
+ if (status == PJ_EPENDING)
+ status = PJ_SUCCESS;
+
+ if (status != 0)
+ goto on_return;
+
+ if (state1->err) {
+ status = -183;
+ goto on_return;
+ }
+ if (state2->err) {
+ status = -186;
+ goto on_return;
+ }
+ if (state1->next_recv_seq != COUNT) {
+ PJ_LOG(3,("", " err: only %u packets received, expecting %u",
+ state1->next_recv_seq, COUNT));
+ status = -195;
+ goto on_return;
+ }
+
+on_return:
+ if (asock2)
+ pj_activesock_close(asock2);
+ if (asock1)
+ pj_activesock_close(asock1);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+
+int activesock_test(void)
+{
+ int ret;
+
+ PJ_LOG(3,("", "..udp ping/pong test"));
+ ret = udp_ping_pong_test();
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..tcp perf test"));
+ ret = tcp_perf_test();
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+#else /* INCLUDE_ACTIVESOCK_TEST */
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_active_sock_test;
+#endif /* INCLUDE_ACTIVESOCK_TEST */
+
diff --git a/pjlib/src/pjlib-test/atomic.c b/pjlib/src/pjlib-test/atomic.c
new file mode 100644
index 0000000..f9cddf4
--- /dev/null
+++ b/pjlib/src/pjlib-test/atomic.c
@@ -0,0 +1,109 @@
+/* $Id: atomic.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+/**
+ * \page page_pjlib_atomic_test Test: Atomic Variable
+ *
+ * This file provides implementation of \b atomic_test(). It tests the
+ * functionality of the atomic variable API.
+ *
+ * \section atomic_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_atomic_create()
+ * - pj_atomic_get()
+ * - pj_atomic_inc()
+ * - pj_atomic_dec()
+ * - pj_atomic_set()
+ * - pj_atomic_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/atomic.c</b>
+ *
+ * \include pjlib-test/atomic.c
+ */
+
+
+#if INCLUDE_ATOMIC_TEST
+
+int atomic_test(void)
+{
+ pj_pool_t *pool;
+ pj_atomic_t *atomic_var;
+ pj_status_t rc;
+
+ pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
+ if (!pool)
+ return -10;
+
+ /* create() */
+ rc = pj_atomic_create(pool, 111, &atomic_var);
+ if (rc != 0) {
+ return -20;
+ }
+
+ /* get: check the value. */
+ if (pj_atomic_get(atomic_var) != 111)
+ return -30;
+
+ /* increment. */
+ pj_atomic_inc(atomic_var);
+ if (pj_atomic_get(atomic_var) != 112)
+ return -40;
+
+ /* decrement. */
+ pj_atomic_dec(atomic_var);
+ if (pj_atomic_get(atomic_var) != 111)
+ return -50;
+
+ /* set */
+ pj_atomic_set(atomic_var, 211);
+ if (pj_atomic_get(atomic_var) != 211)
+ return -60;
+
+ /* add */
+ pj_atomic_add(atomic_var, 10);
+ if (pj_atomic_get(atomic_var) != 221)
+ return -60;
+
+ /* check the value again. */
+ if (pj_atomic_get(atomic_var) != 221)
+ return -70;
+
+ /* destroy */
+ rc = pj_atomic_destroy(atomic_var);
+ if (rc != 0)
+ return -80;
+
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_atomic_test;
+#endif /* INCLUDE_ATOMIC_TEST */
+
diff --git a/pjlib/src/pjlib-test/echo_clt.c b/pjlib/src/pjlib-test/echo_clt.c
new file mode 100644
index 0000000..0ce27dc
--- /dev/null
+++ b/pjlib/src/pjlib-test/echo_clt.c
@@ -0,0 +1,269 @@
+/* $Id: echo_clt.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_ECHO_CLIENT
+
+enum { BUF_SIZE = 512 };
+
+struct client
+{
+ int sock_type;
+ const char *server;
+ int port;
+};
+
+static pj_atomic_t *totalBytes;
+static pj_atomic_t *timeout_counter;
+static pj_atomic_t *invalid_counter;
+
+#define MSEC_PRINT_DURATION 1000
+
+static int wait_socket(pj_sock_t sock, unsigned msec_timeout)
+{
+ pj_fd_set_t fdset;
+ pj_time_val timeout;
+
+ timeout.sec = 0;
+ timeout.msec = msec_timeout;
+ pj_time_val_normalize(&timeout);
+
+ PJ_FD_ZERO(&fdset);
+ PJ_FD_SET(sock, &fdset);
+
+ return pj_sock_select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
+}
+
+static int echo_client_thread(void *arg)
+{
+ pj_sock_t sock;
+ char send_buf[BUF_SIZE];
+ char recv_buf[BUF_SIZE];
+ pj_sockaddr_in addr;
+ pj_str_t s;
+ pj_status_t rc;
+ pj_uint32_t buffer_id;
+ pj_uint32_t buffer_counter;
+ struct client *client = arg;
+ pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS;
+ unsigned counter = 0;
+
+ rc = app_socket(pj_AF_INET(), client->sock_type, 0, -1, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create socket", rc);
+ return -10;
+ }
+
+ rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server),
+ (pj_uint16_t)client->port);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to resolve server", rc);
+ return -15;
+ }
+
+ rc = pj_sock_connect(sock, &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS) {
+ app_perror("...connect() error", rc);
+ pj_sock_close(sock);
+ return -20;
+ }
+
+ PJ_LOG(3,("", "...socket connected to %s:%d",
+ pj_inet_ntoa(addr.sin_addr),
+ pj_ntohs(addr.sin_port)));
+
+ pj_memset(send_buf, 'A', BUF_SIZE);
+ send_buf[BUF_SIZE-1]='\0';
+
+ /* Give other thread chance to initialize themselves! */
+ pj_thread_sleep(200);
+
+ //PJ_LOG(3,("", "...thread %p running", pj_thread_this()));
+
+ buffer_id = (pj_uint32_t) pj_thread_this();
+ buffer_counter = 0;
+
+ *(pj_uint32_t*)send_buf = buffer_id;
+
+ for (;;) {
+ int rc;
+ pj_ssize_t bytes;
+
+ ++counter;
+
+ //while (wait_socket(sock,0) > 0)
+ // ;
+
+ /* Send a packet. */
+ bytes = BUF_SIZE;
+ *(pj_uint32_t*)(send_buf+4) = ++buffer_counter;
+ rc = pj_sock_send(sock, send_buf, &bytes, 0);
+ if (rc != PJ_SUCCESS || bytes != BUF_SIZE) {
+ if (rc != last_send_err) {
+ app_perror("...send() error", rc);
+ PJ_LOG(3,("", "...ignoring subsequent error.."));
+ last_send_err = rc;
+ pj_thread_sleep(100);
+ }
+ continue;
+ }
+
+ rc = wait_socket(sock, 500);
+ if (rc == 0) {
+ PJ_LOG(3,("", "...timeout"));
+ bytes = 0;
+ pj_atomic_inc(timeout_counter);
+ } else if (rc < 0) {
+ rc = pj_get_netos_error();
+ app_perror("...select() error", rc);
+ break;
+ } else {
+ /* Receive back the original packet. */
+ bytes = 0;
+ do {
+ pj_ssize_t received = BUF_SIZE - bytes;
+ rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0);
+ if (rc != PJ_SUCCESS || received == 0) {
+ if (rc != last_recv_err) {
+ app_perror("...recv() error", rc);
+ PJ_LOG(3,("", "...ignoring subsequent error.."));
+ last_recv_err = rc;
+ pj_thread_sleep(100);
+ }
+ bytes = 0;
+ received = 0;
+ break;
+ }
+ bytes += received;
+ } while (bytes != BUF_SIZE && bytes != 0);
+ }
+
+ if (bytes == 0)
+ continue;
+
+ if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) {
+ recv_buf[BUF_SIZE-1] = '\0';
+ PJ_LOG(3,("", "...error: buffer %u has changed!\n"
+ "send_buf=%s\n"
+ "recv_buf=%s\n",
+ counter, send_buf, recv_buf));
+ pj_atomic_inc(invalid_counter);
+ }
+
+ /* Accumulate total received. */
+ pj_atomic_add(totalBytes, bytes);
+ }
+
+ pj_sock_close(sock);
+ return 0;
+}
+
+int echo_client(int sock_type, const char *server, int port)
+{
+ pj_pool_t *pool;
+ pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS];
+ pj_status_t rc;
+ struct client client;
+ int i;
+ pj_atomic_value_t last_received;
+ pj_timestamp last_report;
+
+ client.sock_type = sock_type;
+ client.server = server;
+ client.port = port;
+
+ pool = pj_pool_create( mem, NULL, 4000, 4000, NULL );
+
+ rc = pj_atomic_create(pool, 0, &totalBytes);
+ if (rc != PJ_SUCCESS) {
+ PJ_LOG(3,("", "...error: unable to create atomic variable", rc));
+ return -30;
+ }
+ rc = pj_atomic_create(pool, 0, &invalid_counter);
+ rc = pj_atomic_create(pool, 0, &timeout_counter);
+
+ PJ_LOG(3,("", "Echo client started"));
+ PJ_LOG(3,("", " Destination: %s:%d",
+ ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT));
+ PJ_LOG(3,("", " Press Ctrl-C to exit"));
+
+ for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+ rc = pj_thread_create( pool, NULL, &echo_client_thread, &client,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -10;
+ }
+ }
+
+ last_received = 0;
+ pj_get_timestamp(&last_report);
+
+ for (;;) {
+ pj_timestamp now;
+ unsigned long received, cur_received;
+ unsigned msec;
+ pj_highprec_t bw;
+ pj_time_val elapsed;
+ unsigned bw32;
+ pj_uint32_t timeout, invalid;
+
+ pj_thread_sleep(1000);
+
+ pj_get_timestamp(&now);
+ elapsed = pj_elapsed_time(&last_report, &now);
+ msec = PJ_TIME_VAL_MSEC(elapsed);
+
+ received = pj_atomic_get(totalBytes);
+ cur_received = received - last_received;
+
+ bw = cur_received;
+ pj_highprec_mul(bw, 1000);
+ pj_highprec_div(bw, msec);
+
+ bw32 = (unsigned)bw;
+
+ last_report = now;
+ last_received = received;
+
+ timeout = pj_atomic_get(timeout_counter);
+ invalid = pj_atomic_get(invalid_counter);
+
+ PJ_LOG(3,("",
+ "...%d threads, total bandwidth: %d KB/s, "
+ "timeout=%d, invalid=%d",
+ ECHO_CLIENT_MAX_THREADS, bw32/1000,
+ timeout, invalid));
+ }
+
+ for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+ pj_thread_join( thread[i] );
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+
+#else
+int dummy_echo_client;
+#endif /* INCLUDE_ECHO_CLIENT */
diff --git a/pjlib/src/pjlib-test/errno.c b/pjlib/src/pjlib-test/errno.c
new file mode 100644
index 0000000..c06ad5b
--- /dev/null
+++ b/pjlib/src/pjlib-test/errno.c
@@ -0,0 +1,171 @@
+/* $Id: errno.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/string.h>
+
+#if INCLUDE_ERRNO_TEST
+
+#define THIS_FILE "errno"
+
+#if defined(PJ_WIN32) && PJ_WIN32 != 0
+# include <windows.h>
+#endif
+
+#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
+# include <errno.h>
+#endif
+
+static void trim_newlines(char *s)
+{
+ while (*s) {
+ if (*s == '\r' || *s == '\n')
+ *s = ' ';
+ ++s;
+ }
+}
+
+int my_strncasecmp(const char *s1, const char *s2, int max_len)
+{
+ while (*s1 && *s2 && max_len > 0) {
+ if (pj_tolower(*s1) != pj_tolower(*s2))
+ return -1;
+ ++s1;
+ ++s2;
+ --max_len;
+ }
+ return 0;
+}
+
+const char *my_stristr(const char *whole, const char *part)
+{
+ int part_len = strlen(part);
+ while (*whole) {
+ if (my_strncasecmp(whole, part, part_len) == 0)
+ return whole;
+ ++whole;
+ }
+ return NULL;
+}
+
+int errno_test(void)
+{
+ enum { CUT = 6 };
+ pj_status_t rc = 0;
+ char errbuf[256];
+
+ PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully"));
+
+ PJ_UNUSED_ARG(rc);
+
+ /*
+ * Windows platform error.
+ */
+# ifdef ERROR_INVALID_DATA
+ rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+#ifndef PJ_WIN32_WINCE
+ return -20;
+#endif
+ }
+
+ /* Cut version. */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf));
+# endif
+
+ /*
+ * Unix errors
+ */
+# if defined(EINVAL) && !defined(PJ_SYMBIAN)
+ rc = PJ_STATUS_FROM_OS(EINVAL);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+ return -30;
+ }
+
+ /* Cut */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf));
+# endif
+
+ /*
+ * Windows WSA errors
+ */
+# ifdef WSAEINVAL
+ rc = PJ_STATUS_FROM_OS(WSAEINVAL);
+ pj_set_os_error(rc);
+
+ /* Whole */
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ trim_newlines(errbuf);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf));
+ if (my_stristr(errbuf, "invalid") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"invalid\" string in the msg"));
+ return -40;
+ }
+
+ /* Cut */
+ pj_strerror(rc, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf));
+# endif
+
+ pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf));
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf));
+ if (my_stristr(errbuf, "BUG") == NULL) {
+ PJ_LOG(3, (THIS_FILE,
+ "...error: expecting \"BUG\" string in the msg"));
+ return -20;
+ }
+
+ pj_strerror(PJ_EBUG, errbuf, CUT);
+ PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'",
+ CUT, errbuf));
+
+ /* Perror */
+ pj_perror(3, THIS_FILE, PJ_SUCCESS, "...testing %s", "pj_perror");
+ PJ_PERROR(3,(THIS_FILE, PJ_SUCCESS, "...testing %s", "PJ_PERROR"));
+
+ return 0;
+}
+
+
+#endif /* INCLUDE_ERRNO_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/exception.c b/pjlib/src/pjlib-test/exception.c
new file mode 100644
index 0000000..4d4b1cd
--- /dev/null
+++ b/pjlib/src/pjlib-test/exception.c
@@ -0,0 +1,282 @@
+/* $Id: exception.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_exception_test Test: Exception Handling
+ *
+ * This file provides implementation of \b exception_test(). It tests the
+ * functionality of the exception handling API.
+ *
+ * @note This test use static ID not acquired through proper registration.
+ * This is not recommended, since it may create ID collissions.
+ *
+ * \section exception_test_sec Scope of the Test
+ *
+ * Some scenarios tested:
+ * - no exception situation
+ * - basic TRY/CATCH
+ * - multiple exception handlers
+ * - default handlers
+ *
+ *
+ * This file is <b>pjlib-test/exception.c</b>
+ *
+ * \include pjlib-test/exception.c
+ */
+
+
+#if INCLUDE_EXCEPTION_TEST
+
+#include <pjlib.h>
+
+#define ID_1 1
+#define ID_2 2
+
+static int throw_id_1(void)
+{
+ PJ_THROW( ID_1 );
+ PJ_UNREACHED(return -1;)
+}
+
+static int throw_id_2(void)
+{
+ PJ_THROW( ID_2 );
+ PJ_UNREACHED(return -1;)
+}
+
+static int try_catch_test(void)
+{
+ PJ_USE_EXCEPTION;
+ int rc = -200;
+
+ PJ_TRY {
+ PJ_THROW(ID_1);
+ }
+ PJ_CATCH_ANY {
+ rc = 0;
+ }
+ PJ_END;
+ return rc;
+}
+
+static int throw_in_handler(void)
+{
+ PJ_USE_EXCEPTION;
+ int rc = 0;
+
+ PJ_TRY {
+ PJ_THROW(ID_1);
+ }
+ PJ_CATCH_ANY {
+ if (PJ_GET_EXCEPTION() != ID_1)
+ rc = -300;
+ else
+ PJ_THROW(ID_2);
+ }
+ PJ_END;
+ return rc;
+}
+
+static int return_in_handler(void)
+{
+ PJ_USE_EXCEPTION;
+
+ PJ_TRY {
+ PJ_THROW(ID_1);
+ }
+ PJ_CATCH_ANY {
+ return 0;
+ }
+ PJ_END;
+ return -400;
+}
+
+
+static int test(void)
+{
+ int rc = 0;
+ PJ_USE_EXCEPTION;
+
+ /*
+ * No exception situation.
+ */
+ PJ_TRY {
+ rc = rc;
+ }
+ PJ_CATCH_ANY {
+ rc = -3;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+
+ /*
+ * Basic TRY/CATCH
+ */
+ PJ_TRY {
+ rc = throw_id_1();
+
+ // should not reach here.
+ rc = -10;
+ }
+ PJ_CATCH_ANY {
+ int id = PJ_GET_EXCEPTION();
+ if (id != ID_1) {
+ PJ_LOG(3,("", "...error: got unexpected exception %d (%s)",
+ id, pj_exception_id_name(id)));
+ if (!rc) rc = -20;
+ }
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Multiple exceptions handlers
+ */
+ PJ_TRY {
+ rc = throw_id_2();
+ // should not reach here.
+ rc = -25;
+ }
+ PJ_CATCH_ANY {
+ switch (PJ_GET_EXCEPTION()) {
+ case ID_1:
+ if (!rc) rc = -30; break;
+ case ID_2:
+ if (!rc) rc = 0; break;
+ default:
+ if (!rc) rc = -40;
+ break;
+ }
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Test default handler.
+ */
+ PJ_TRY {
+ rc = throw_id_1();
+ // should not reach here
+ rc = -50;
+ }
+ PJ_CATCH_ANY {
+ switch (PJ_GET_EXCEPTION()) {
+ case ID_1:
+ if (!rc) rc = 0;
+ break;
+ default:
+ if (!rc) rc = -60;
+ break;
+ }
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Nested handlers
+ */
+ PJ_TRY {
+ rc = try_catch_test();
+ }
+ PJ_CATCH_ANY {
+ rc = -70;
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * Throwing exception inside handler
+ */
+ rc = -80;
+ PJ_TRY {
+ int rc2;
+ rc2 = throw_in_handler();
+ if (rc2)
+ rc = rc2;
+ }
+ PJ_CATCH_ANY {
+ if (PJ_GET_EXCEPTION() == ID_2) {
+ rc = 0;
+ } else {
+ rc = -90;
+ }
+ }
+ PJ_END;
+
+ if (rc != 0)
+ return rc;
+
+
+ /*
+ * Return from handler. Returning from the function inside a handler
+ * should be okay (though returning from the function inside the
+ * PJ_TRY block IS NOT OKAY!!). We want to test to see if handler
+ * is cleaned up properly, but not sure how to do this.
+ */
+ PJ_TRY {
+ int rc2;
+ rc2 = return_in_handler();
+ if (rc2)
+ rc = rc2;
+ }
+ PJ_CATCH_ANY {
+ rc = -100;
+ }
+ PJ_END;
+
+
+ return 0;
+}
+
+int exception_test(void)
+{
+ int i, rc;
+ enum { LOOP = 10 };
+
+ for (i=0; i<LOOP; ++i) {
+ if ((rc=test()) != 0) {
+ PJ_LOG(3,("", "...failed at i=%d (rc=%d)", i, rc));
+ return rc;
+ }
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_exception_test;
+#endif /* INCLUDE_EXCEPTION_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/exception_wrap.cpp b/pjlib/src/pjlib-test/exception_wrap.cpp
new file mode 100644
index 0000000..9c5b31b
--- /dev/null
+++ b/pjlib/src/pjlib-test/exception_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: exception_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "exception.c"
diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c
new file mode 100644
index 0000000..89c3ee2
--- /dev/null
+++ b/pjlib/src/pjlib-test/fifobuf.c
@@ -0,0 +1,117 @@
+/* $Id: fifobuf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_fifobuf_test;
+
+#if INCLUDE_FIFOBUF_TEST
+
+#include <pjlib.h>
+
+int fifobuf_test()
+{
+ enum { SIZE = 1024, MAX_ENTRIES = 128,
+ MIN_SIZE = 4, MAX_SIZE = 64,
+ LOOP=10000 };
+ pj_pool_t *pool;
+ pj_fifobuf_t fifo;
+ unsigned available = SIZE;
+ void *entries[MAX_ENTRIES];
+ void *buffer;
+ int i;
+
+ pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL);
+ if (!pool)
+ return -10;
+
+ buffer = pj_pool_alloc(pool, SIZE);
+ if (!buffer)
+ return -20;
+
+ pj_fifobuf_init (&fifo, buffer, SIZE);
+
+ // Test 1
+ for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+ int size;
+ int c, f;
+ c = i%2;
+ f = (i+1)%2;
+ do {
+ size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[c] = pj_fifobuf_alloc (&fifo, size);
+ } while (entries[c] == 0);
+ if ( i!=0) {
+ pj_fifobuf_free(&fifo, entries[f]);
+ }
+ }
+ if (entries[(i+1)%2])
+ pj_fifobuf_free(&fifo, entries[(i+1)%2]);
+
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -1;
+ }
+
+ // Test 2
+ entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE);
+ if (!entries[0]) return -1;
+ for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+ int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[1] = pj_fifobuf_alloc (&fifo, size);
+ if (entries[1])
+ pj_fifobuf_unalloc(&fifo, entries[1]);
+ }
+ pj_fifobuf_unalloc(&fifo, entries[0]);
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -2;
+ }
+
+ // Test 3
+ for (i=0; i<LOOP; ++i) {
+ int count, j;
+ for (count=0; available>=MIN_SIZE+4 && count < MAX_ENTRIES;) {
+ int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+ entries[count] = pj_fifobuf_alloc (&fifo, size);
+ if (entries[count]) {
+ available -= (size+4);
+ ++count;
+ }
+ }
+ for (j=0; j<count; ++j) {
+ pj_fifobuf_free (&fifo, entries[j]);
+ }
+ available = SIZE;
+ }
+
+ if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+ pj_assert(0);
+ return -3;
+ }
+ pj_pool_release(pool);
+ return 0;
+}
+
+#endif /* INCLUDE_FIFOBUF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/file.c b/pjlib/src/pjlib-test/file.c
new file mode 100644
index 0000000..df086e5
--- /dev/null
+++ b/pjlib/src/pjlib-test/file.c
@@ -0,0 +1,225 @@
+/* $Id: file.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_FILE_TEST
+
+#define FILENAME "testfil1.txt"
+#define NEWNAME "testfil2.txt"
+#define INCLUDE_FILE_TIME_TEST 0
+
+static char buffer[11] = {'H', 'e', 'l', 'l', 'o', ' ',
+ 'W', 'o', 'r', 'l', 'd' };
+
+static int file_test_internal(void)
+{
+ enum { FILE_MAX_AGE = 1000 };
+ pj_oshandle_t fd = 0;
+ pj_status_t status;
+ char readbuf[sizeof(buffer)+16];
+ pj_file_stat stat;
+ pj_time_val start_time;
+ pj_ssize_t size;
+ pj_off_t pos;
+
+ PJ_LOG(3,("", "..file io test.."));
+
+ /* Get time. */
+ pj_gettimeofday(&start_time);
+
+ /* Delete original file if exists. */
+ if (pj_file_exists(FILENAME))
+ pj_file_delete(FILENAME);
+
+ /*
+ * Write data to the file.
+ */
+ status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_open() error", status);
+ return -10;
+ }
+
+ size = sizeof(buffer);
+ status = pj_file_write(fd, buffer, &size);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_write() error", status);
+ pj_file_close(fd);
+ return -20;
+ }
+ if (size != sizeof(buffer))
+ return -25;
+
+ status = pj_file_close(fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_close() error", status);
+ return -30;
+ }
+
+ /* Check the file existance and size. */
+ if (!pj_file_exists(FILENAME))
+ return -40;
+
+ if (pj_file_size(FILENAME) != sizeof(buffer))
+ return -50;
+
+ /* Get file stat. */
+ status = pj_file_getstat(FILENAME, &stat);
+ if (status != PJ_SUCCESS)
+ return -60;
+
+ /* Check stat size. */
+ if (stat.size != sizeof(buffer))
+ return -70;
+
+#if INCLUDE_FILE_TIME_TEST
+ /* Check file creation time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.ctime, start_time))
+ return -80;
+ /* Check file creation time is not much later. */
+ PJ_TIME_VAL_SUB(stat.ctime, start_time);
+ if (stat.ctime.sec > FILE_MAX_AGE)
+ return -90;
+
+ /* Check file modification time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.mtime, start_time))
+ return -80;
+ /* Check file modification time is not much later. */
+ PJ_TIME_VAL_SUB(stat.mtime, start_time);
+ if (stat.mtime.sec > FILE_MAX_AGE)
+ return -90;
+
+ /* Check file access time >= start_time. */
+ if (!PJ_TIME_VAL_GTE(stat.atime, start_time))
+ return -80;
+ /* Check file access time is not much later. */
+ PJ_TIME_VAL_SUB(stat.atime, start_time);
+ if (stat.atime.sec > FILE_MAX_AGE)
+ return -90;
+#endif
+
+ /*
+ * Re-open the file and read data.
+ */
+ status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_open() error", status);
+ return -100;
+ }
+
+ size = 0;
+ while (size < (pj_ssize_t)sizeof(readbuf)) {
+ pj_ssize_t read;
+ read = 1;
+ status = pj_file_read(fd, &readbuf[size], &read);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,("", "...error reading file after %d bytes (error follows)",
+ size));
+ app_perror("...error", status);
+ return -110;
+ }
+ if (read == 0) {
+ // EOF
+ break;
+ }
+ size += read;
+ }
+
+ if (size != sizeof(buffer))
+ return -120;
+
+ /*
+ if (!pj_file_eof(fd, PJ_O_RDONLY))
+ return -130;
+ */
+
+ if (pj_memcmp(readbuf, buffer, size) != 0)
+ return -140;
+
+ /* Seek test. */
+ status = pj_file_setpos(fd, 4, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_setpos() error", status);
+ return -141;
+ }
+
+ /* getpos test. */
+ status = pj_file_getpos(fd, &pos);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_getpos() error", status);
+ return -142;
+ }
+ if (pos != 4)
+ return -143;
+
+ status = pj_file_close(fd);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_close() error", status);
+ return -150;
+ }
+
+ /*
+ * Rename test.
+ */
+ status = pj_file_move(FILENAME, NEWNAME);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_move() error", status);
+ return -160;
+ }
+
+ if (pj_file_exists(FILENAME))
+ return -170;
+ if (!pj_file_exists(NEWNAME))
+ return -180;
+
+ if (pj_file_size(NEWNAME) != sizeof(buffer))
+ return -190;
+
+ /* Delete test. */
+ status = pj_file_delete(NEWNAME);
+ if (status != PJ_SUCCESS) {
+ app_perror("...file_delete() error", status);
+ return -200;
+ }
+
+ if (pj_file_exists(NEWNAME))
+ return -210;
+
+ PJ_LOG(3,("", "...success"));
+ return PJ_SUCCESS;
+}
+
+
+int file_test(void)
+{
+ int rc = file_test_internal();
+
+ /* Delete test file if exists. */
+ if (pj_file_exists(FILENAME))
+ pj_file_delete(FILENAME);
+
+ return rc;
+}
+
+#else
+int dummy_file_test;
+#endif
+
diff --git a/pjlib/src/pjlib-test/hash_test.c b/pjlib/src/pjlib-test/hash_test.c
new file mode 100644
index 0000000..088107d
--- /dev/null
+++ b/pjlib/src/pjlib-test/hash_test.c
@@ -0,0 +1,163 @@
+/* $Id: hash_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/hash.h>
+#include <pj/rand.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include "test.h"
+
+#if INCLUDE_HASH_TEST
+
+#define HASH_COUNT 31
+
+static int hash_test_with_key(pj_pool_t *pool, unsigned char key)
+{
+ pj_hash_table_t *ht;
+ unsigned value = 0x12345;
+ pj_hash_iterator_t it_buf, *it;
+ unsigned *entry;
+
+ ht = pj_hash_create(pool, HASH_COUNT);
+ if (!ht)
+ return -10;
+
+ pj_hash_set(pool, ht, &key, sizeof(key), 0, &value);
+
+ entry = (unsigned*) pj_hash_get(ht, &key, sizeof(key), NULL);
+ if (!entry)
+ return -20;
+
+ if (*entry != value)
+ return -30;
+
+ if (pj_hash_count(ht) != 1)
+ return -30;
+
+ it = pj_hash_first(ht, &it_buf);
+ if (it == NULL)
+ return -40;
+
+ entry = (unsigned*) pj_hash_this(ht, it);
+ if (!entry)
+ return -50;
+
+ if (*entry != value)
+ return -60;
+
+ it = pj_hash_next(ht, it);
+ if (it != NULL)
+ return -70;
+
+ /* Erase item */
+
+ pj_hash_set(NULL, ht, &key, sizeof(key), 0, NULL);
+
+ if (pj_hash_get(ht, &key, sizeof(key), NULL) != NULL)
+ return -80;
+
+ if (pj_hash_count(ht) != 0)
+ return -90;
+
+ it = pj_hash_first(ht, &it_buf);
+ if (it != NULL)
+ return -100;
+
+ return 0;
+}
+
+
+static int hash_collision_test(pj_pool_t *pool)
+{
+ enum {
+ COUNT = HASH_COUNT * 4
+ };
+ pj_hash_table_t *ht;
+ pj_hash_iterator_t it_buf, *it;
+ unsigned char *values;
+ unsigned i;
+
+ ht = pj_hash_create(pool, HASH_COUNT);
+ if (!ht)
+ return -200;
+
+ values = (unsigned char*) pj_pool_alloc(pool, COUNT);
+
+ for (i=0; i<COUNT; ++i) {
+ values[i] = (unsigned char)i;
+ pj_hash_set(pool, ht, &i, sizeof(i), 0, &values[i]);
+ }
+
+ if (pj_hash_count(ht) != COUNT)
+ return -210;
+
+ for (i=0; i<COUNT; ++i) {
+ unsigned char *entry;
+ entry = (unsigned char*) pj_hash_get(ht, &i, sizeof(i), NULL);
+ if (!entry)
+ return -220;
+ if (*entry != values[i])
+ return -230;
+ }
+
+ i = 0;
+ it = pj_hash_first(ht, &it_buf);
+ while (it) {
+ ++i;
+ it = pj_hash_next(ht, it);
+ }
+
+ if (i != COUNT)
+ return -240;
+
+ return 0;
+}
+
+
+/*
+ * Hash table test.
+ */
+int hash_test(void)
+{
+ pj_pool_t *pool = pj_pool_create(mem, "hash", 512, 512, NULL);
+ int rc;
+ unsigned i;
+
+ /* Test to fill in each row in the table */
+ for (i=0; i<=HASH_COUNT; ++i) {
+ rc = hash_test_with_key(pool, (unsigned char)i);
+ if (rc != 0) {
+ pj_pool_release(pool);
+ return rc;
+ }
+ }
+
+ /* Collision test */
+ rc = hash_collision_test(pool);
+ if (rc != 0) {
+ pj_pool_release(pool);
+ return rc;
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+#endif /* INCLUDE_HASH_TEST */
+
diff --git a/pjlib/src/pjlib-test/ioq_perf.c b/pjlib/src/pjlib-test/ioq_perf.c
new file mode 100644
index 0000000..776be61
--- /dev/null
+++ b/pjlib/src/pjlib-test/ioq_perf.c
@@ -0,0 +1,572 @@
+/* $Id: ioq_perf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+/**
+ * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance
+ *
+ * Test the performance of the I/O queue, using typical producer
+ * consumer test. The test should examine the effect of using multiple
+ * threads on the performance.
+ *
+ * This file is <b>pjlib-test/ioq_perf.c</b>
+ *
+ * \include pjlib-test/ioq_perf.c
+ */
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+
+#ifdef _MSC_VER
+# pragma warning ( disable: 4204) // non-constant aggregate initializer
+#endif
+
+#define THIS_FILE "ioq_perf"
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+
+static pj_bool_t thread_quit_flag;
+static pj_status_t last_error;
+static unsigned last_error_counter;
+
+/* Descriptor for each producer/consumer pair. */
+typedef struct test_item
+{
+ pj_sock_t server_fd,
+ client_fd;
+ pj_ioqueue_t *ioqueue;
+ pj_ioqueue_key_t *server_key,
+ *client_key;
+ pj_ioqueue_op_key_t recv_op,
+ send_op;
+ int has_pending_send;
+ pj_size_t buffer_size;
+ char *outgoing_buffer;
+ char *incoming_buffer;
+ pj_size_t bytes_sent,
+ bytes_recv;
+} test_item;
+
+/* Callback when data has been read.
+ * Increment item->bytes_recv and ready to read the next data.
+ */
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ test_item *item = (test_item*)pj_ioqueue_get_user_data(key);
+ pj_status_t rc;
+ int data_is_available = 1;
+
+ //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read));
+
+ do {
+ if (thread_quit_flag)
+ return;
+
+ if (bytes_read < 0) {
+ pj_status_t rc = -bytes_read;
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ if (rc != last_error) {
+ //last_error = rc;
+ pj_strerror(rc, errmsg, sizeof(errmsg));
+ PJ_LOG(3,(THIS_FILE,"...error: read error, bytes_read=%d (%s)",
+ bytes_read, errmsg));
+ PJ_LOG(3,(THIS_FILE,
+ ".....additional info: total read=%u, total sent=%u",
+ item->bytes_recv, item->bytes_sent));
+ } else {
+ last_error_counter++;
+ }
+ bytes_read = 0;
+
+ } else if (bytes_read == 0) {
+ PJ_LOG(3,(THIS_FILE, "...socket has closed!"));
+ }
+
+ item->bytes_recv += bytes_read;
+
+ /* To assure that the test quits, even if main thread
+ * doesn't have time to run.
+ */
+ if (item->bytes_recv > item->buffer_size * 10000)
+ thread_quit_flag = 1;
+
+ bytes_read = item->buffer_size;
+ rc = pj_ioqueue_recv( key, op_key,
+ item->incoming_buffer, &bytes_read, 0 );
+
+ if (rc == PJ_SUCCESS) {
+ data_is_available = 1;
+ } else if (rc == PJ_EPENDING) {
+ data_is_available = 0;
+ } else {
+ data_is_available = 0;
+ if (rc != last_error) {
+ last_error = rc;
+ app_perror("...error: read error(1)", rc);
+ } else {
+ last_error_counter++;
+ }
+ }
+
+ if (!item->has_pending_send) {
+ pj_ssize_t sent = item->buffer_size;
+ rc = pj_ioqueue_send(item->client_key, &item->send_op,
+ item->outgoing_buffer, &sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: write error", rc);
+ }
+
+ item->has_pending_send = (rc==PJ_EPENDING);
+ }
+
+ } while (data_is_available);
+}
+
+/* Callback when data has been written.
+ * Increment item->bytes_sent and write the next data.
+ */
+static void on_write_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+{
+ test_item *item = (test_item*) pj_ioqueue_get_user_data(key);
+
+ //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent));
+
+ if (thread_quit_flag)
+ return;
+
+ item->has_pending_send = 0;
+ item->bytes_sent += bytes_sent;
+
+ if (bytes_sent <= 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d",
+ bytes_sent));
+ }
+ else {
+ pj_status_t rc;
+
+ bytes_sent = item->buffer_size;
+ rc = pj_ioqueue_send( item->client_key, op_key,
+ item->outgoing_buffer, &bytes_sent, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: write error", rc);
+ }
+
+ item->has_pending_send = (rc==PJ_EPENDING);
+ }
+}
+
+struct thread_arg
+{
+ int id;
+ pj_ioqueue_t *ioqueue;
+ unsigned counter;
+};
+
+/* The worker thread. */
+static int worker_thread(void *p)
+{
+ struct thread_arg *arg = (struct thread_arg*) p;
+ const pj_time_val timeout = {0, 100};
+ int rc;
+
+ while (!thread_quit_flag) {
+
+ ++arg->counter;
+ rc = pj_ioqueue_poll(arg->ioqueue, &timeout);
+ //TRACE_((THIS_FILE, " thread: poll returned rc=%d", rc));
+ if (rc < 0) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(-rc, errmsg, sizeof(errmsg));
+ PJ_LOG(3, (THIS_FILE,
+ "...error in pj_ioqueue_poll() in thread %d "
+ "after %d loop: %s [pj_status_t=%d]",
+ arg->id, arg->counter, errmsg, -rc));
+ //return -1;
+ }
+ }
+ return 0;
+}
+
+/* Calculate the bandwidth for the specific test configuration.
+ * The test is simple:
+ * - create sockpair_cnt number of producer-consumer socket pair.
+ * - create thread_cnt number of worker threads.
+ * - each producer will send buffer_size bytes data as fast and
+ * as soon as it can.
+ * - each consumer will read buffer_size bytes of data as fast
+ * as it could.
+ * - measure the total bytes received by all consumers during a
+ * period of time.
+ */
+static int perform_test(pj_bool_t allow_concur,
+ int sock_type, const char *type_name,
+ unsigned thread_cnt, unsigned sockpair_cnt,
+ pj_size_t buffer_size,
+ pj_size_t *p_bandwidth)
+{
+ enum { MSEC_DURATION = 5000 };
+ pj_pool_t *pool;
+ test_item *items;
+ pj_thread_t **thread;
+ pj_ioqueue_t *ioqueue;
+ pj_status_t rc;
+ pj_ioqueue_callback ioqueue_callback;
+ pj_uint32_t total_elapsed_usec, total_received;
+ pj_highprec_t bandwidth;
+ pj_timestamp start, stop;
+ unsigned i;
+
+ TRACE_((THIS_FILE, " starting test.."));
+
+ ioqueue_callback.on_read_complete = &on_read_complete;
+ ioqueue_callback.on_write_complete = &on_write_complete;
+
+ thread_quit_flag = 0;
+
+ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+ if (!pool)
+ return -10;
+
+ items = (test_item*) pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item));
+ thread = (pj_thread_t**)
+ pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*));
+
+ TRACE_((THIS_FILE, " creating ioqueue.."));
+ rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create ioqueue", rc);
+ return -15;
+ }
+
+ rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_ioqueue_set_default_concurrency()", rc);
+ return -16;
+ }
+
+ /* Initialize each producer-consumer pair. */
+ for (i=0; i<sockpair_cnt; ++i) {
+ pj_ssize_t bytes;
+
+ items[i].ioqueue = ioqueue;
+ items[i].buffer_size = buffer_size;
+ items[i].outgoing_buffer = (char*) pj_pool_alloc(pool, buffer_size);
+ items[i].incoming_buffer = (char*) pj_pool_alloc(pool, buffer_size);
+ items[i].bytes_recv = items[i].bytes_sent = 0;
+
+ /* randomize outgoing buffer. */
+ pj_create_random_string(items[i].outgoing_buffer, buffer_size);
+
+ /* Create socket pair. */
+ TRACE_((THIS_FILE, " calling socketpair.."));
+ rc = app_socketpair(pj_AF_INET(), sock_type, 0,
+ &items[i].server_fd, &items[i].client_fd);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket pair", rc);
+ return -20;
+ }
+
+ /* Register server socket to ioqueue. */
+ TRACE_((THIS_FILE, " register(1).."));
+ rc = pj_ioqueue_register_sock(pool, ioqueue,
+ items[i].server_fd,
+ &items[i], &ioqueue_callback,
+ &items[i].server_key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: registering server socket to ioqueue", rc);
+ return -60;
+ }
+
+ /* Register client socket to ioqueue. */
+ TRACE_((THIS_FILE, " register(2).."));
+ rc = pj_ioqueue_register_sock(pool, ioqueue,
+ items[i].client_fd,
+ &items[i], &ioqueue_callback,
+ &items[i].client_key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: registering server socket to ioqueue", rc);
+ return -70;
+ }
+
+ /* Start reading. */
+ TRACE_((THIS_FILE, " pj_ioqueue_recv.."));
+ bytes = items[i].buffer_size;
+ rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op,
+ items[i].incoming_buffer, &bytes,
+ 0);
+ if (rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_recv", rc);
+ return -73;
+ }
+
+ /* Start writing. */
+ TRACE_((THIS_FILE, " pj_ioqueue_write.."));
+ bytes = items[i].buffer_size;
+ rc = pj_ioqueue_send(items[i].client_key, &items[i].send_op,
+ items[i].outgoing_buffer, &bytes, 0);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_write", rc);
+ return -76;
+ }
+
+ items[i].has_pending_send = (rc==PJ_EPENDING);
+ }
+
+ /* Create the threads. */
+ for (i=0; i<thread_cnt; ++i) {
+ struct thread_arg *arg;
+
+ arg = (struct thread_arg*) pj_pool_zalloc(pool, sizeof(*arg));
+ arg->id = i;
+ arg->ioqueue = ioqueue;
+ arg->counter = 0;
+
+ rc = pj_thread_create( pool, NULL,
+ &worker_thread,
+ arg,
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ PJ_THREAD_SUSPENDED, &thread[i] );
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -80;
+ }
+ }
+
+ /* Mark start time. */
+ rc = pj_get_timestamp(&start);
+ if (rc != PJ_SUCCESS)
+ return -90;
+
+ /* Start the thread. */
+ TRACE_((THIS_FILE, " resuming all threads.."));
+ for (i=0; i<thread_cnt; ++i) {
+ rc = pj_thread_resume(thread[i]);
+ if (rc != 0)
+ return -100;
+ }
+
+ /* Wait for MSEC_DURATION seconds.
+ * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually,
+ * but unfortunately it doesn't work when system doesn't employ
+ * timeslicing for threads.
+ */
+ TRACE_((THIS_FILE, " wait for few seconds.."));
+ do {
+ pj_thread_sleep(1);
+
+ /* Mark end time. */
+ rc = pj_get_timestamp(&stop);
+
+ if (thread_quit_flag) {
+ TRACE_((THIS_FILE, " transfer limit reached.."));
+ break;
+ }
+
+ if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) {
+ TRACE_((THIS_FILE, " time limit reached.."));
+ break;
+ }
+
+ } while (1);
+
+ /* Terminate all threads. */
+ TRACE_((THIS_FILE, " terminating all threads.."));
+ thread_quit_flag = 1;
+
+ for (i=0; i<thread_cnt; ++i) {
+ TRACE_((THIS_FILE, " join thread %d..", i));
+ pj_thread_join(thread[i]);
+ }
+
+ /* Close all sockets. */
+ TRACE_((THIS_FILE, " closing all sockets.."));
+ for (i=0; i<sockpair_cnt; ++i) {
+ pj_ioqueue_unregister(items[i].server_key);
+ pj_ioqueue_unregister(items[i].client_key);
+ }
+
+ /* Destroy threads */
+ for (i=0; i<thread_cnt; ++i) {
+ pj_thread_destroy(thread[i]);
+ }
+
+ /* Destroy ioqueue. */
+ TRACE_((THIS_FILE, " destroying ioqueue.."));
+ pj_ioqueue_destroy(ioqueue);
+
+ /* Calculate actual time in usec. */
+ total_elapsed_usec = pj_elapsed_usec(&start, &stop);
+
+ /* Calculate total bytes received. */
+ total_received = 0;
+ for (i=0; i<sockpair_cnt; ++i) {
+ total_received = items[i].bytes_recv;
+ }
+
+ /* bandwidth = total_received*1000/total_elapsed_usec */
+ bandwidth = total_received;
+ pj_highprec_mul(bandwidth, 1000);
+ pj_highprec_div(bandwidth, total_elapsed_usec);
+
+ *p_bandwidth = (pj_uint32_t)bandwidth;
+
+ PJ_LOG(3,(THIS_FILE, " %.4s %2d %2d %8d KB/s",
+ type_name, thread_cnt, sockpair_cnt,
+ *p_bandwidth));
+
+ /* Done. */
+ pj_pool_release(pool);
+
+ TRACE_((THIS_FILE, " done.."));
+ return 0;
+}
+
+static int ioqueue_perf_test_imp(pj_bool_t allow_concur)
+{
+ enum { BUF_SIZE = 512 };
+ int i, rc;
+ struct {
+ int type;
+ const char *type_name;
+ int thread_cnt;
+ int sockpair_cnt;
+ } test_param[] =
+ {
+ { pj_SOCK_DGRAM(), "udp", 1, 1},
+ { pj_SOCK_DGRAM(), "udp", 1, 2},
+ { pj_SOCK_DGRAM(), "udp", 1, 4},
+ { pj_SOCK_DGRAM(), "udp", 1, 8},
+ { pj_SOCK_DGRAM(), "udp", 2, 1},
+ { pj_SOCK_DGRAM(), "udp", 2, 2},
+ { pj_SOCK_DGRAM(), "udp", 2, 4},
+ { pj_SOCK_DGRAM(), "udp", 2, 8},
+ { pj_SOCK_DGRAM(), "udp", 4, 1},
+ { pj_SOCK_DGRAM(), "udp", 4, 2},
+ { pj_SOCK_DGRAM(), "udp", 4, 4},
+ { pj_SOCK_DGRAM(), "udp", 4, 8},
+ { pj_SOCK_DGRAM(), "udp", 4, 16},
+ { pj_SOCK_STREAM(), "tcp", 1, 1},
+ { pj_SOCK_STREAM(), "tcp", 1, 2},
+ { pj_SOCK_STREAM(), "tcp", 1, 4},
+ { pj_SOCK_STREAM(), "tcp", 1, 8},
+ { pj_SOCK_STREAM(), "tcp", 2, 1},
+ { pj_SOCK_STREAM(), "tcp", 2, 2},
+ { pj_SOCK_STREAM(), "tcp", 2, 4},
+ { pj_SOCK_STREAM(), "tcp", 2, 8},
+ { pj_SOCK_STREAM(), "tcp", 4, 1},
+ { pj_SOCK_STREAM(), "tcp", 4, 2},
+ { pj_SOCK_STREAM(), "tcp", 4, 4},
+ { pj_SOCK_STREAM(), "tcp", 4, 8},
+ { pj_SOCK_STREAM(), "tcp", 4, 16},
+/*
+ { pj_SOCK_DGRAM(), "udp", 32, 1},
+ { pj_SOCK_DGRAM(), "udp", 32, 1},
+ { pj_SOCK_DGRAM(), "udp", 32, 1},
+ { pj_SOCK_DGRAM(), "udp", 32, 1},
+ { pj_SOCK_DGRAM(), "udp", 1, 32},
+ { pj_SOCK_DGRAM(), "udp", 1, 32},
+ { pj_SOCK_DGRAM(), "udp", 1, 32},
+ { pj_SOCK_DGRAM(), "udp", 1, 32},
+ { pj_SOCK_STREAM(), "tcp", 32, 1},
+ { pj_SOCK_STREAM(), "tcp", 32, 1},
+ { pj_SOCK_STREAM(), "tcp", 32, 1},
+ { pj_SOCK_STREAM(), "tcp", 32, 1},
+ { pj_SOCK_STREAM(), "tcp", 1, 32},
+ { pj_SOCK_STREAM(), "tcp", 1, 32},
+ { pj_SOCK_STREAM(), "tcp", 1, 32},
+ { pj_SOCK_STREAM(), "tcp", 1, 32},
+*/
+ };
+ pj_size_t best_bandwidth;
+ int best_index = 0;
+
+ PJ_LOG(3,(THIS_FILE, " Benchmarking %s ioqueue:", pj_ioqueue_name()));
+ PJ_LOG(3,(THIS_FILE, " Testing with concurency=%d", allow_concur));
+ PJ_LOG(3,(THIS_FILE, " ======================================="));
+ PJ_LOG(3,(THIS_FILE, " Type Threads Skt.Pairs Bandwidth"));
+ PJ_LOG(3,(THIS_FILE, " ======================================="));
+
+ best_bandwidth = 0;
+ for (i=0; i<(int)(sizeof(test_param)/sizeof(test_param[0])); ++i) {
+ pj_size_t bandwidth;
+
+ rc = perform_test(allow_concur,
+ test_param[i].type,
+ test_param[i].type_name,
+ test_param[i].thread_cnt,
+ test_param[i].sockpair_cnt,
+ BUF_SIZE,
+ &bandwidth);
+ if (rc != 0)
+ return rc;
+
+ if (bandwidth > best_bandwidth)
+ best_bandwidth = bandwidth, best_index = i;
+
+ /* Give it a rest before next test, to allow system to close the
+ * sockets properly.
+ */
+ pj_thread_sleep(500);
+ }
+
+ PJ_LOG(3,(THIS_FILE,
+ " Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s",
+ test_param[best_index].type_name,
+ test_param[best_index].thread_cnt,
+ test_param[best_index].sockpair_cnt,
+ best_bandwidth));
+ PJ_LOG(3,(THIS_FILE, " (Note: packet size=%d, total errors=%u)",
+ BUF_SIZE, last_error_counter));
+ return 0;
+}
+
+/*
+ * main test entry.
+ */
+int ioqueue_perf_test(void)
+{
+ int rc;
+
+ rc = ioqueue_perf_test_imp(PJ_TRUE);
+ if (rc != 0)
+ return rc;
+
+ rc = ioqueue_perf_test_imp(PJ_FALSE);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_perf_test;
+#endif /* INCLUDE_IOQUEUE_PERF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c
new file mode 100644
index 0000000..8ecbc0f
--- /dev/null
+++ b/pjlib/src/pjlib-test/ioq_tcp.c
@@ -0,0 +1,972 @@
+/* $Id: ioq_tcp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when TCP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_tcp.c</b>
+ *
+ * \include pjlib-test/ioq_tcp.c
+ */
+
+
+#if INCLUDE_TCP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#if PJ_HAS_TCP
+
+#define THIS_FILE "test_tcp"
+#define NON_EXISTANT_PORT 50123
+#define LOOP 100
+#define BUF_MIN_SIZE 32
+#define BUF_MAX_SIZE 2048
+#define SOCK_INACTIVE_MIN (4-2)
+#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+static pj_ssize_t callback_read_size,
+ callback_write_size,
+ callback_accept_status,
+ callback_connect_status;
+static unsigned callback_call_count;
+static pj_ioqueue_key_t *callback_read_key,
+ *callback_write_key,
+ *callback_accept_key,
+ *callback_connect_key;
+static pj_ioqueue_op_key_t *callback_read_op,
+ *callback_write_op,
+ *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ callback_read_key = key;
+ callback_read_op = op_key;
+ callback_read_size = bytes_read;
+ callback_call_count++;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_written)
+{
+ callback_write_key = key;
+ callback_write_op = op_key;
+ callback_write_size = bytes_written;
+ callback_call_count++;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t sock,
+ int status)
+{
+ if (sock == PJ_INVALID_SOCKET) {
+
+ if (status != PJ_SUCCESS) {
+ /* Ignore. Could be blocking error */
+ app_perror(".....warning: received error in on_ioqueue_accept() callback",
+ status);
+ } else {
+ callback_accept_status = -61;
+ PJ_LOG(3,("", "..... on_ioqueue_accept() callback was given "
+ "invalid socket and status is %d", status));
+ }
+ } else {
+ pj_sockaddr addr;
+ int client_addr_len;
+
+ client_addr_len = sizeof(addr);
+ status = pj_sock_getsockname(sock, &addr, &client_addr_len);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_getsockname()", status);
+ }
+
+ callback_accept_key = key;
+ callback_accept_op = op_key;
+ callback_accept_status = status;
+ callback_call_count++;
+ }
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+ callback_connect_key = key;
+ callback_connect_status = status;
+ callback_call_count++;
+}
+
+static pj_ioqueue_callback test_cb =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect,
+};
+
+static int send_recv_test(pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *skey,
+ pj_ioqueue_key_t *ckey,
+ void *send_buf,
+ void *recv_buf,
+ pj_ssize_t bufsize,
+ pj_timestamp *t_elapsed)
+{
+ pj_status_t status;
+ pj_ssize_t bytes;
+ pj_time_val timeout;
+ pj_timestamp t1, t2;
+ int pending_op = 0;
+ pj_ioqueue_op_key_t read_op, write_op;
+
+ // Start reading on the server side.
+ bytes = bufsize;
+ status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...pj_ioqueue_recv error", status);
+ return -100;
+ }
+
+ if (status == PJ_EPENDING)
+ ++pending_op;
+ else {
+ /* Does not expect to return error or immediate data. */
+ return -115;
+ }
+
+ // Randomize send buffer.
+ pj_create_random_string((char*)send_buf, bufsize);
+
+ // Starts send on the client side.
+ bytes = bufsize;
+ status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0);
+ if (status != PJ_SUCCESS && bytes != PJ_EPENDING) {
+ return -120;
+ }
+ if (status == PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Begin time.
+ pj_get_timestamp(&t1);
+
+ // Reset indicators
+ callback_read_size = callback_write_size = 0;
+ callback_read_key = callback_write_key = NULL;
+ callback_read_op = callback_write_op = NULL;
+
+ // Poll the queue until we've got completion event in the server side.
+ status = 0;
+ while (pending_op > 0) {
+ timeout.sec = 1; timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+ PJ_UNUSED_ARG(ioque);
+ status = pj_symbianos_poll(-1, 1000);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status > 0) {
+ if (callback_read_size) {
+ if (callback_read_size != bufsize)
+ return -160;
+ if (callback_read_key != skey)
+ return -161;
+ if (callback_read_op != &read_op)
+ return -162;
+ }
+ if (callback_write_size) {
+ if (callback_write_key != ckey)
+ return -163;
+ if (callback_write_op != &write_op)
+ return -164;
+ }
+ pending_op -= status;
+ }
+ if (status == 0) {
+ PJ_LOG(3,("", "...error: timed out"));
+ }
+ if (status < 0) {
+ return -170;
+ }
+ }
+
+ // Pending op is zero.
+ // Subsequent poll should yield zero too.
+ timeout.sec = timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+ status = pj_symbianos_poll(-1, 1);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status != 0)
+ return -173;
+
+ // End time.
+ pj_get_timestamp(&t2);
+ t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ // Compare recv buffer with send buffer.
+ if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+ return -180;
+ }
+
+ // Success
+ return 0;
+}
+
+
+/*
+ * Compliance test for success scenario.
+ */
+static int compliance_test_0(pj_bool_t allow_concur)
+{
+ pj_sock_t ssock=-1, csock0=-1, csock1=-1;
+ pj_sockaddr_in addr, client_addr, rmt_addr;
+ int client_addr_len;
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey=NULL, *ckey0=NULL, *ckey1=NULL;
+ pj_ioqueue_op_key_t accept_op;
+ int bufsize = BUF_MIN_SIZE;
+ pj_ssize_t status = -1;
+ int pending_op = 0;
+ pj_timestamp t_elapsed;
+ pj_str_t s;
+ pj_status_t rc;
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Create server socket and client socket for connecting
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-1; goto on_error;
+ }
+
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-1; goto on_error;
+ }
+
+ // Bind server socket.
+ pj_sockaddr_in_init(&addr, 0, 0);
+ if ((rc=pj_sock_bind(ssock, &addr, sizeof(addr))) != 0 ) {
+ app_perror("...bind error", rc);
+ status=-10; goto on_error;
+ }
+
+ // Get server address.
+ client_addr_len = sizeof(addr);
+ rc = pj_sock_getsockname(ssock, &addr, &client_addr_len);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_getsockname()", rc);
+ status=-15; goto on_error;
+ }
+ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_create()", rc);
+ status=-20; goto on_error;
+ }
+
+ // Concurrency
+ rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc);
+ status=-21; goto on_error;
+ }
+
+ // Register server socket and client socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
+ if (rc == PJ_SUCCESS)
+ rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
+ &ckey1);
+ else
+ ckey1 = NULL;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+ status=-23; goto on_error;
+ }
+
+ // Server socket listen().
+ if (pj_sock_listen(ssock, 5)) {
+ app_perror("...ERROR in pj_sock_listen()", rc);
+ status=-25; goto on_error;
+ }
+
+ // Server socket accept()
+ client_addr_len = sizeof(pj_sockaddr_in);
+ status = pj_ioqueue_accept(skey, &accept_op, &csock0,
+ &client_addr, &rmt_addr, &client_addr_len);
+ if (status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_accept()", rc);
+ status=-30; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_connect()", rc);
+ status=-40; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Poll until connected
+ callback_read_size = callback_write_size = 0;
+ callback_accept_status = callback_connect_status = -2;
+ callback_call_count = 0;
+
+ callback_read_key = callback_write_key =
+ callback_accept_key = callback_connect_key = NULL;
+ callback_accept_op = callback_read_op = callback_write_op = NULL;
+
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+ callback_call_count = 0;
+ pj_symbianos_poll(-1, 1000);
+ status = callback_call_count;
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status > 0) {
+ if (callback_accept_status != -2) {
+ if (callback_accept_status != 0) {
+ status=-41; goto on_error;
+ }
+ if (callback_accept_key != skey) {
+ status=-42; goto on_error;
+ }
+ if (callback_accept_op != &accept_op) {
+ status=-43; goto on_error;
+ }
+ callback_accept_status = -2;
+ }
+
+ if (callback_connect_status != -2) {
+ if (callback_connect_status != 0) {
+ status=-50; goto on_error;
+ }
+ if (callback_connect_key != ckey1) {
+ status=-51; goto on_error;
+ }
+ callback_connect_status = -2;
+ }
+
+ if (status > pending_op) {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: pj_ioqueue_poll() returned %d "
+ "(only expecting %d)",
+ status, pending_op));
+ return -52;
+ }
+ pending_op -= status;
+
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+ status = pj_symbianos_poll(-1, 1000);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status != 0) {
+ status=-60; goto on_error;
+ }
+ }
+
+ // Check accepted socket.
+ if (csock0 == PJ_INVALID_SOCKET) {
+ status = -69;
+ app_perror("...accept() error", pj_get_os_error());
+ goto on_error;
+ }
+
+ // Register newly accepted socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
+ &test_cb, &ckey0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+ status = -70;
+ goto on_error;
+ }
+
+ // Test send and receive.
+ t_elapsed.u32.lo = 0;
+ status = send_recv_test(ioque, ckey0, ckey1, send_buf,
+ recv_buf, bufsize, &t_elapsed);
+ if (status != 0) {
+ goto on_error;
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (skey != NULL)
+ pj_ioqueue_unregister(skey);
+ else if (ssock != PJ_INVALID_SOCKET)
+ pj_sock_close(ssock);
+
+ if (ckey1 != NULL)
+ pj_ioqueue_unregister(ckey1);
+ else if (csock1 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock1);
+
+ if (ckey0 != NULL)
+ pj_ioqueue_unregister(ckey0);
+ else if (csock0 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock0);
+
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+/*
+ * Compliance test for failed scenario.
+ * In this case, the client connects to a non-existant service.
+ */
+static int compliance_test_1(pj_bool_t allow_concur)
+{
+ pj_sock_t csock1=PJ_INVALID_SOCKET;
+ pj_sockaddr_in addr;
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *ckey1 = NULL;
+ pj_ssize_t status = -1;
+ int pending_op = 0;
+ pj_str_t s;
+ pj_status_t rc;
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (!ioque) {
+ status=-20; goto on_error;
+ }
+
+ // Concurrency
+ rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ status=-21; goto on_error;
+ }
+
+ // Create client socket
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_socket()", rc);
+ status=-1; goto on_error;
+ }
+
+ // Register client socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
+ &test_cb, &ckey1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+ status=-23; goto on_error;
+ }
+
+ // Initialize remote address.
+ pj_sockaddr_in_init(&addr, pj_cstr(&s, "127.0.0.1"), NON_EXISTANT_PORT);
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+ if (status==PJ_SUCCESS) {
+ // unexpectedly success!
+ status = -30;
+ goto on_error;
+ }
+ if (status != PJ_EPENDING) {
+ // success
+ } else {
+ ++pending_op;
+ }
+
+ callback_connect_status = -2;
+ callback_connect_key = NULL;
+
+ // Poll until we've got result
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+ callback_call_count = 0;
+ pj_symbianos_poll(-1, 1000);
+ status = callback_call_count;
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status > 0) {
+ if (callback_connect_key==ckey1) {
+ if (callback_connect_status == 0) {
+ // unexpectedly connected!
+ status = -50;
+ goto on_error;
+ }
+ }
+
+ if (status > pending_op) {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: pj_ioqueue_poll() returned %d "
+ "(only expecting %d)",
+ status, pending_op));
+ return -552;
+ }
+
+ pending_op -= status;
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+ status = pj_symbianos_poll(-1, 1000);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status != 0) {
+ status=-60; goto on_error;
+ }
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (ckey1 != NULL)
+ pj_ioqueue_unregister(ckey1);
+ else if (csock1 != PJ_INVALID_SOCKET)
+ pj_sock_close(csock1);
+
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+}
+
+
+/*
+ * Repeated connect/accept on the same listener socket.
+ */
+static int compliance_test_2(pj_bool_t allow_concur)
+{
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
+ enum { MAX_PAIR = 1, TEST_LOOP = 2 };
+#else
+ enum { MAX_PAIR = 4, TEST_LOOP = 2 };
+#endif
+
+ struct listener
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ pj_sockaddr_in addr;
+ int addr_len;
+ } listener;
+
+ struct server
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ pj_sockaddr_in local_addr;
+ pj_sockaddr_in rem_addr;
+ int rem_addr_len;
+ pj_ioqueue_op_key_t accept_op;
+ } server[MAX_PAIR];
+
+ struct client
+ {
+ pj_sock_t sock;
+ pj_ioqueue_key_t *key;
+ } client[MAX_PAIR];
+
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ int i, bufsize = BUF_MIN_SIZE;
+ pj_ssize_t status;
+ int test_loop, pending_op = 0;
+ pj_timestamp t_elapsed;
+ pj_str_t s;
+ pj_status_t rc;
+
+ listener.sock = PJ_INVALID_SOCKET;
+ listener.key = NULL;
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ server[i].sock = PJ_INVALID_SOCKET;
+ server[i].key = NULL;
+ }
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ client[i].sock = PJ_INVALID_SOCKET;
+ client[i].key = NULL;
+ }
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_create()", rc);
+ return -10;
+ }
+
+
+ // Concurrency
+ rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc);
+ return -11;
+ }
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Create listener socket
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener.sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-20; goto on_error;
+ }
+
+ // Bind listener socket.
+ pj_sockaddr_in_init(&listener.addr, 0, 0);
+ if ((rc=pj_sock_bind(listener.sock, &listener.addr, sizeof(listener.addr))) != 0 ) {
+ app_perror("...bind error", rc);
+ status=-30; goto on_error;
+ }
+
+ // Get listener address.
+ listener.addr_len = sizeof(listener.addr);
+ rc = pj_sock_getsockname(listener.sock, &listener.addr, &listener.addr_len);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_getsockname()", rc);
+ status=-40; goto on_error;
+ }
+ listener.addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+
+ // Register listener socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, listener.sock, NULL, &test_cb,
+ &listener.key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR", rc);
+ status=-50; goto on_error;
+ }
+
+
+ // Listener socket listen().
+ if (pj_sock_listen(listener.sock, 5)) {
+ app_perror("...ERROR in pj_sock_listen()", rc);
+ status=-60; goto on_error;
+ }
+
+
+ for (test_loop=0; test_loop < TEST_LOOP; ++test_loop) {
+ // Client connect and server accept.
+ for (i=0; i<MAX_PAIR; ++i) {
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &client[i].sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating socket", rc);
+ status=-70; goto on_error;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioque, client[i].sock, NULL,
+ &test_cb, &client[i].key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error ", rc);
+ status=-80; goto on_error;
+ }
+
+ // Server socket accept()
+ pj_ioqueue_op_key_init(&server[i].accept_op,
+ sizeof(server[i].accept_op));
+ server[i].rem_addr_len = sizeof(pj_sockaddr_in);
+ status = pj_ioqueue_accept(listener.key, &server[i].accept_op,
+ &server[i].sock, &server[i].local_addr,
+ &server[i].rem_addr,
+ &server[i].rem_addr_len);
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_accept()", rc);
+ status=-90; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+
+ // Client socket connect()
+ status = pj_ioqueue_connect(client[i].key, &listener.addr,
+ sizeof(listener.addr));
+ if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR in pj_ioqueue_connect()", rc);
+ status=-100; goto on_error;
+ }
+ if (status==PJ_EPENDING) {
+ ++pending_op;
+ }
+
+ // Poll until connection of this pair established
+ while (pending_op) {
+ pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+ status = pj_symbianos_poll(-1, 1000);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status > 0) {
+ if (status > pending_op) {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: pj_ioqueue_poll() returned %d "
+ "(only expecting %d)",
+ status, pending_op));
+ return -110;
+ }
+ pending_op -= status;
+
+ if (pending_op == 0) {
+ status = 0;
+ }
+ }
+ }
+ }
+
+ // There's no pending operation.
+ // When we poll the ioqueue, there must not be events.
+ if (pending_op == 0) {
+ pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+ status = pj_symbianos_poll(-1, 1000);
+#else
+ status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ if (status != 0) {
+ status=-120; goto on_error;
+ }
+ }
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ // Check server socket.
+ if (server[i].sock == PJ_INVALID_SOCKET) {
+ status = -130;
+ app_perror("...accept() error", pj_get_os_error());
+ goto on_error;
+ }
+
+ // Check addresses
+ if (server[i].local_addr.sin_family != pj_AF_INET() ||
+ server[i].local_addr.sin_addr.s_addr == 0 ||
+ server[i].local_addr.sin_port == 0)
+ {
+ app_perror("...ERROR address not set", rc);
+ status = -140;
+ goto on_error;
+ }
+
+ if (server[i].rem_addr.sin_family != pj_AF_INET() ||
+ server[i].rem_addr.sin_addr.s_addr == 0 ||
+ server[i].rem_addr.sin_port == 0)
+ {
+ app_perror("...ERROR address not set", rc);
+ status = -150;
+ goto on_error;
+ }
+
+
+ // Register newly accepted socket.
+ rc = pj_ioqueue_register_sock(pool, ioque, server[i].sock, NULL,
+ &test_cb, &server[i].key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+ status = -160;
+ goto on_error;
+ }
+
+ // Test send and receive.
+ t_elapsed.u32.lo = 0;
+ status = send_recv_test(ioque, server[i].key, client[i].key,
+ send_buf, recv_buf, bufsize, &t_elapsed);
+ if (status != 0) {
+ goto on_error;
+ }
+ }
+
+ // Success
+ status = 0;
+
+ for (i=0; i<MAX_PAIR; ++i) {
+ if (server[i].key != NULL) {
+ pj_ioqueue_unregister(server[i].key);
+ server[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (server[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(server[i].sock);
+ server[i].sock = PJ_INVALID_SOCKET;
+ }
+
+ if (client[i].key != NULL) {
+ pj_ioqueue_unregister(client[i].key);
+ client[i].key = NULL;
+ client[i].sock = PJ_INVALID_SOCKET;
+ } else if (client[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(client[i].sock);
+ client[i].sock = PJ_INVALID_SOCKET;
+ }
+ }
+ }
+
+ status = 0;
+
+on_error:
+ for (i=0; i<MAX_PAIR; ++i) {
+ if (server[i].key != NULL) {
+ pj_ioqueue_unregister(server[i].key);
+ server[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (server[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(server[i].sock);
+ server[i].sock = PJ_INVALID_SOCKET;
+ }
+
+ if (client[i].key != NULL) {
+ pj_ioqueue_unregister(client[i].key);
+ client[i].key = NULL;
+ server[i].sock = PJ_INVALID_SOCKET;
+ } else if (client[i].sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(client[i].sock);
+ client[i].sock = PJ_INVALID_SOCKET;
+ }
+ }
+
+ if (listener.key) {
+ pj_ioqueue_unregister(listener.key);
+ listener.key = NULL;
+ } else if (listener.sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(listener.sock);
+ listener.sock = PJ_INVALID_SOCKET;
+ }
+
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+
+static int tcp_ioqueue_test_impl(pj_bool_t allow_concur)
+{
+ int status;
+
+ PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 0 (success scenario)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_0(allow_concur)) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 1 (failed scenario)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_1(allow_concur)) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "..%s compliance test 2 (repeated accept)",
+ pj_ioqueue_name()));
+ if ((status=compliance_test_2(allow_concur)) != 0) {
+ PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+ return status;
+ }
+
+ return 0;
+}
+
+int tcp_ioqueue_test()
+{
+ int rc;
+
+ rc = tcp_ioqueue_test_impl(PJ_TRUE);
+ if (rc != 0)
+ return rc;
+
+ rc = tcp_ioqueue_test_impl(PJ_FALSE);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+#endif /* PJ_HAS_TCP */
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_tcp;
+#endif /* INCLUDE_TCP_IOQUEUE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c
new file mode 100644
index 0000000..6928641
--- /dev/null
+++ b/pjlib/src/pjlib-test/ioq_udp.c
@@ -0,0 +1,951 @@
+/* $Id: ioq_udp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when UDP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_udp.c</b>
+ *
+ * \include pjlib-test/ioq_udp.c
+ */
+
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#include <pj/compat/socket.h>
+
+#define THIS_FILE "test_udp"
+#define PORT 51233
+#define LOOP 2
+///#define LOOP 2
+#define BUF_MIN_SIZE 32
+#define BUF_MAX_SIZE 2048
+#define SOCK_INACTIVE_MIN (1)
+#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+#undef TRACE_
+#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg))
+
+#if 0
+# define TRACE__(args) PJ_LOG(3,args)
+#else
+# define TRACE__(args)
+#endif
+
+
+static pj_ssize_t callback_read_size,
+ callback_write_size,
+ callback_accept_status,
+ callback_connect_status;
+static pj_ioqueue_key_t *callback_read_key,
+ *callback_write_key,
+ *callback_accept_key,
+ *callback_connect_key;
+static pj_ioqueue_op_key_t *callback_read_op,
+ *callback_write_op,
+ *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ callback_read_key = key;
+ callback_read_op = op_key;
+ callback_read_size = bytes_read;
+ TRACE__((THIS_FILE, " callback_read_key = %p, bytes=%d",
+ key, bytes_read));
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_written)
+{
+ callback_write_key = key;
+ callback_write_op = op_key;
+ callback_write_size = bytes_written;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_sock_t sock, int status)
+{
+ PJ_UNUSED_ARG(sock);
+ callback_accept_key = key;
+ callback_accept_op = op_key;
+ callback_accept_status = status;
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+ callback_connect_key = key;
+ callback_connect_status = status;
+}
+
+static pj_ioqueue_callback test_cb =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect,
+};
+
+#ifdef PJ_WIN32
+# define S_ADDR S_un.S_addr
+#else
+# define S_ADDR s_addr
+#endif
+
+/*
+ * compliance_test()
+ * To test that the basic IOQueue functionality works. It will just exchange
+ * data between two sockets.
+ */
+static int compliance_test(pj_bool_t allow_concur)
+{
+ pj_sock_t ssock=-1, csock=-1;
+ pj_sockaddr_in addr, dst_addr;
+ int addrlen;
+ pj_pool_t *pool = NULL;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey = NULL, *ckey = NULL;
+ pj_ioqueue_op_key_t read_op, write_op;
+ int bufsize = BUF_MIN_SIZE;
+ pj_ssize_t bytes, status = -1;
+ pj_str_t temp;
+ pj_bool_t send_pending, recv_pending;
+ pj_status_t rc;
+
+ pj_set_os_error(PJ_SUCCESS);
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Allocate sockets for sending and receiving.
+ TRACE_("creating sockets...");
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock);
+ if (rc==PJ_SUCCESS)
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock);
+ else
+ csock = PJ_INVALID_SOCKET;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_sock_socket()", rc);
+ status=-1; goto on_error;
+ }
+
+ // Bind server socket.
+ TRACE_("bind socket...");
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = pj_AF_INET();
+ addr.sin_port = pj_htons(PORT);
+ if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+ status=-10; goto on_error;
+ }
+
+ // Create I/O Queue.
+ TRACE_("create ioqueue...");
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ status=-20; goto on_error;
+ }
+
+ // Set concurrency
+ TRACE_("set concurrency...");
+ rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ status=-21; goto on_error;
+ }
+
+ // Register server and client socket.
+ // We put this after inactivity socket, hopefully this can represent the
+ // worst waiting time.
+ TRACE_("registering first sockets...");
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
+ &test_cb, &skey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(10): ioqueue_register error", rc);
+ status=-25; goto on_error;
+ }
+ TRACE_("registering second sockets...");
+ rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL,
+ &test_cb, &ckey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(11): ioqueue_register error", rc);
+ status=-26; goto on_error;
+ }
+
+ // Randomize send_buf.
+ pj_create_random_string(send_buf, bufsize);
+
+ // Register reading from ioqueue.
+ TRACE_("start recvfrom...");
+ pj_bzero(&addr, sizeof(addr));
+ addrlen = sizeof(addr);
+ bytes = bufsize;
+ rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0,
+ &addr, &addrlen);
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_recvfrom", rc);
+ status=-28; goto on_error;
+ } else if (rc == PJ_EPENDING) {
+ recv_pending = 1;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: recvfrom returned pending"));
+ } else {
+ PJ_LOG(3, (THIS_FILE,
+ "......error: recvfrom returned immediate ok!"));
+ status=-29; goto on_error;
+ }
+
+ // Set destination address to send the packet.
+ TRACE_("set destination address...");
+ temp = pj_str("127.0.0.1");
+ if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) {
+ app_perror("...error: unable to resolve 127.0.0.1", rc);
+ status=-290; goto on_error;
+ }
+
+ // Write must return the number of bytes.
+ TRACE_("start sendto...");
+ bytes = bufsize;
+ rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &dst_addr,
+ sizeof(dst_addr));
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_sendto", rc);
+ status=-30; goto on_error;
+ } else if (rc == PJ_EPENDING) {
+ send_pending = 1;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: sendto returned pending"));
+ } else {
+ send_pending = 0;
+ PJ_LOG(3, (THIS_FILE,
+ "......ok: sendto returned immediate success"));
+ }
+
+ // reset callback variables.
+ callback_read_size = callback_write_size = 0;
+ callback_accept_status = callback_connect_status = -2;
+ callback_read_key = callback_write_key =
+ callback_accept_key = callback_connect_key = NULL;
+ callback_read_op = callback_write_op = NULL;
+
+ // Poll if pending.
+ while (send_pending || recv_pending) {
+ int rc;
+ pj_time_val timeout = { 5, 0 };
+
+ TRACE_("poll...");
+#ifdef PJ_SYMBIAN
+ rc = pj_symbianos_poll(-1, 5000);
+#else
+ rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+
+ if (rc == 0) {
+ PJ_LOG(1,(THIS_FILE, "...ERROR: timed out..."));
+ status=-45; goto on_error;
+ } else if (rc < 0) {
+ app_perror("...ERROR in ioqueue_poll()", -rc);
+ status=-50; goto on_error;
+ }
+
+ if (callback_read_key != NULL) {
+ if (callback_read_size != bufsize) {
+ status=-61; goto on_error;
+ }
+ if (callback_read_key != skey) {
+ status=-65; goto on_error;
+ }
+ if (callback_read_op != &read_op) {
+ status=-66; goto on_error;
+ }
+
+ if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+ status=-67; goto on_error;
+ }
+ if (addrlen != sizeof(pj_sockaddr_in)) {
+ status=-68; goto on_error;
+ }
+ if (addr.sin_family != pj_AF_INET()) {
+ status=-69; goto on_error;
+ }
+
+
+ recv_pending = 0;
+ }
+
+ if (callback_write_key != NULL) {
+ if (callback_write_size != bufsize) {
+ status=-73; goto on_error;
+ }
+ if (callback_write_key != ckey) {
+ status=-75; goto on_error;
+ }
+ if (callback_write_op != &write_op) {
+ status=-76; goto on_error;
+ }
+
+ send_pending = 0;
+ }
+ }
+
+ // Success
+ status = 0;
+
+on_error:
+ if (skey)
+ pj_ioqueue_unregister(skey);
+ else if (ssock != -1)
+ pj_sock_close(ssock);
+
+ if (ckey)
+ pj_ioqueue_unregister(ckey);
+ else if (csock != -1)
+ pj_sock_close(csock);
+
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release(pool);
+ return status;
+
+}
+
+
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ unsigned *p_packet_cnt = (unsigned*) pj_ioqueue_get_user_data(key);
+
+ PJ_UNUSED_ARG(op_key);
+ PJ_UNUSED_ARG(bytes_read);
+
+ (*p_packet_cnt)++;
+}
+
+/*
+ * unregister_test()
+ * Check if callback is still called after socket has been unregistered or
+ * closed.
+ */
+static int unregister_test(pj_bool_t allow_concur)
+{
+ enum { RPORT = 50000, SPORT = 50001 };
+ pj_pool_t *pool;
+ pj_ioqueue_t *ioqueue;
+ pj_sock_t ssock;
+ pj_sock_t rsock;
+ int addrlen;
+ pj_sockaddr_in addr;
+ pj_ioqueue_key_t *key;
+ pj_ioqueue_op_key_t opkey;
+ pj_ioqueue_callback cb;
+ unsigned packet_cnt;
+ char sendbuf[10], recvbuf[10];
+ pj_ssize_t bytes;
+ pj_time_val timeout;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "test", 4000, 4000, NULL);
+ if (!pool) {
+ app_perror("Unable to create pool", PJ_ENOMEM);
+ return -100;
+ }
+
+ status = pj_ioqueue_create(pool, 16, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error creating ioqueue", status);
+ return -110;
+ }
+
+ // Set concurrency
+ TRACE_("set concurrency...");
+ status = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+ if (status != PJ_SUCCESS) {
+ return -112;
+ }
+
+ /* Create sender socket */
+ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error initializing socket", status);
+ return -120;
+ }
+
+ /* Create receiver socket. */
+ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error initializing socket", status);
+ return -130;
+ }
+
+ /* Register rsock to ioqueue. */
+ pj_bzero(&cb, sizeof(cb));
+ cb.on_read_complete = &on_read_complete;
+ packet_cnt = 0;
+ status = pj_ioqueue_register_sock(pool, ioqueue, rsock, &packet_cnt,
+ &cb, &key);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error registering to ioqueue", status);
+ return -140;
+ }
+
+ /* Init operation key. */
+ pj_ioqueue_op_key_init(&opkey, sizeof(opkey));
+
+ /* Start reading. */
+ bytes = sizeof(recvbuf);
+ status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
+ if (status != PJ_EPENDING) {
+ app_perror("Expecting PJ_EPENDING, but got this", status);
+ return -150;
+ }
+
+ /* Init destination address. */
+ addrlen = sizeof(addr);
+ status = pj_sock_getsockname(rsock, &addr, &addrlen);
+ if (status != PJ_SUCCESS) {
+ app_perror("getsockname error", status);
+ return -160;
+ }
+
+ /* Override address with 127.0.0.1, since getsockname will return
+ * zero in the address field.
+ */
+ addr.sin_addr = pj_inet_addr2("127.0.0.1");
+
+ /* Init buffer to send */
+ pj_ansi_strcpy(sendbuf, "Hello0123");
+
+ /* Send one packet. */
+ bytes = sizeof(sendbuf);
+ status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
+ &addr, sizeof(addr));
+
+ if (status != PJ_SUCCESS) {
+ app_perror("sendto error", status);
+ return -170;
+ }
+
+ /* Check if packet is received. */
+ timeout.sec = 1; timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_ioqueue_poll(ioqueue, &timeout);
+#endif
+
+ if (packet_cnt != 1) {
+ return -180;
+ }
+
+ /* Just to make sure things are settled.. */
+ pj_thread_sleep(100);
+
+ /* Start reading again. */
+ bytes = sizeof(recvbuf);
+ status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
+ if (status != PJ_EPENDING) {
+ app_perror("Expecting PJ_EPENDING, but got this", status);
+ return -190;
+ }
+
+ /* Reset packet counter */
+ packet_cnt = 0;
+
+ /* Send one packet. */
+ bytes = sizeof(sendbuf);
+ status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
+ &addr, sizeof(addr));
+
+ if (status != PJ_SUCCESS) {
+ app_perror("sendto error", status);
+ return -200;
+ }
+
+ /* Now unregister and close socket. */
+ pj_ioqueue_unregister(key);
+
+ /* Poll ioqueue. */
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ timeout.sec = 1; timeout.msec = 0;
+ pj_ioqueue_poll(ioqueue, &timeout);
+#endif
+
+ /* Must NOT receive any packets after socket is closed! */
+ if (packet_cnt > 0) {
+ PJ_LOG(3,(THIS_FILE, "....errror: not expecting to receive packet "
+ "after socket has been closed"));
+ return -210;
+ }
+
+ /* Success */
+ pj_sock_close(ssock);
+ pj_ioqueue_destroy(ioqueue);
+
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+
+/*
+ * Testing with many handles.
+ * This will just test registering PJ_IOQUEUE_MAX_HANDLES count
+ * of sockets to the ioqueue.
+ */
+static int many_handles_test(pj_bool_t allow_concur)
+{
+ enum { MAX = PJ_IOQUEUE_MAX_HANDLES };
+ pj_pool_t *pool;
+ pj_ioqueue_t *ioqueue;
+ pj_sock_t *sock;
+ pj_ioqueue_key_t **key;
+ pj_status_t rc;
+ int count, i; /* must be signed */
+
+ PJ_LOG(3,(THIS_FILE,"...testing with so many handles"));
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ key = (pj_ioqueue_key_t**)
+ pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*));
+ sock = (pj_sock_t*) pj_pool_alloc(pool, MAX*sizeof(pj_sock_t));
+
+ /* Create IOQueue */
+ rc = pj_ioqueue_create(pool, MAX, &ioqueue);
+ if (rc != PJ_SUCCESS || ioqueue == NULL) {
+ app_perror("...error in pj_ioqueue_create", rc);
+ return -10;
+ }
+
+ // Set concurrency
+ rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ return -11;
+ }
+
+ /* Register as many sockets. */
+ for (count=0; count<MAX; ++count) {
+ sock[count] = PJ_INVALID_SOCKET;
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[count]);
+ if (rc != PJ_SUCCESS || sock[count] == PJ_INVALID_SOCKET) {
+ PJ_LOG(3,(THIS_FILE, "....unable to create %d-th socket, rc=%d",
+ count, rc));
+ break;
+ }
+ key[count] = NULL;
+ rc = pj_ioqueue_register_sock(pool, ioqueue, sock[count],
+ NULL, &test_cb, &key[count]);
+ if (rc != PJ_SUCCESS || key[count] == NULL) {
+ PJ_LOG(3,(THIS_FILE, "....unable to register %d-th socket, rc=%d",
+ count, rc));
+ return -30;
+ }
+ }
+
+ /* Test complete. */
+
+ /* Now deregister and close all handles. */
+
+ /* NOTE for RTEMS:
+ * It seems that the order of close(sock) is pretty important here.
+ * If we close the sockets with the same order as when they were created,
+ * RTEMS doesn't seem to reuse the sockets, thus next socket created
+ * will have descriptor higher than the last socket created.
+ * If we close the sockets in the reverse order, then the descriptor will
+ * get reused.
+ * This used to cause problem with select ioqueue, since the ioqueue
+ * always gives FD_SETSIZE for the first select() argument. This ioqueue
+ * behavior can be changed with setting PJ_SELECT_NEEDS_NFDS macro.
+ */
+ for (i=count-1; i>=0; --i) {
+ ///for (i=0; i<count; ++i) {
+ rc = pj_ioqueue_unregister(key[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_ioqueue_unregister", rc);
+ }
+ }
+
+ rc = pj_ioqueue_destroy(ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_ioqueue_destroy", rc);
+ }
+
+ pj_pool_release(pool);
+
+ PJ_LOG(3,(THIS_FILE,"....many_handles_test() ok"));
+
+ return 0;
+}
+
+/*
+ * Multi-operation test.
+ */
+
+/*
+ * Benchmarking IOQueue
+ */
+static int bench_test(pj_bool_t allow_concur, int bufsize,
+ int inactive_sock_count)
+{
+ pj_sock_t ssock=-1, csock=-1;
+ pj_sockaddr_in addr;
+ pj_pool_t *pool = NULL;
+ pj_sock_t *inactive_sock=NULL;
+ pj_ioqueue_op_key_t *inactive_read_op;
+ char *send_buf, *recv_buf;
+ pj_ioqueue_t *ioque = NULL;
+ pj_ioqueue_key_t *skey, *ckey, *keys[SOCK_INACTIVE_MAX+2];
+ pj_timestamp t1, t2, t_elapsed;
+ int rc=0, i; /* i must be signed */
+ pj_str_t temp;
+ char errbuf[PJ_ERR_MSG_SIZE];
+
+ TRACE__((THIS_FILE, " bench test %d", inactive_sock_count));
+
+ // Create pool.
+ pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+ // Allocate buffers for send and receive.
+ send_buf = (char*)pj_pool_alloc(pool, bufsize);
+ recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+ // Allocate sockets for sending and receiving.
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock);
+ if (rc == PJ_SUCCESS) {
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock);
+ } else
+ csock = PJ_INVALID_SOCKET;
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_sock_socket()", rc);
+ goto on_error;
+ }
+
+ // Bind server socket.
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = pj_AF_INET();
+ addr.sin_port = pj_htons(PORT);
+ if (pj_sock_bind(ssock, &addr, sizeof(addr)))
+ goto on_error;
+
+ pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);
+
+ // Create I/O Queue.
+ rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_ioqueue_create()", rc);
+ goto on_error;
+ }
+
+ // Set concurrency
+ rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_ioqueue_set_default_concurrency()", rc);
+ goto on_error;
+ }
+
+ // Allocate inactive sockets, and bind them to some arbitrary address.
+ // Then register them to the I/O queue, and start a read operation.
+ inactive_sock = (pj_sock_t*)pj_pool_alloc(pool,
+ inactive_sock_count*sizeof(pj_sock_t));
+ inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
+ inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = pj_AF_INET();
+ for (i=0; i<inactive_sock_count; ++i) {
+ pj_ssize_t bytes;
+
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &inactive_sock[i]);
+ if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
+ app_perror("...error: pj_sock_socket()", rc);
+ goto on_error;
+ }
+ if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error: pj_sock_bind()", rc);
+ goto on_error;
+ }
+ rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i],
+ NULL, &test_cb, &keys[i]);
+ if (rc != PJ_SUCCESS) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error(1): pj_ioqueue_register_sock()", rc);
+ PJ_LOG(3,(THIS_FILE, "....i=%d", i));
+ goto on_error;
+ }
+ bytes = bufsize;
+ rc = pj_ioqueue_recv(keys[i], &inactive_read_op[i], recv_buf, &bytes, 0);
+ if (rc != PJ_EPENDING) {
+ pj_sock_close(inactive_sock[i]);
+ inactive_sock[i] = PJ_INVALID_SOCKET;
+ app_perror("...error: pj_ioqueue_read()", rc);
+ goto on_error;
+ }
+ }
+
+ // Register server and client socket.
+ // We put this after inactivity socket, hopefully this can represent the
+ // worst waiting time.
+ rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL,
+ &test_cb, &skey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(2): pj_ioqueue_register_sock()", rc);
+ goto on_error;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL,
+ &test_cb, &ckey);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error(3): pj_ioqueue_register_sock()", rc);
+ goto on_error;
+ }
+
+ // Set destination address to send the packet.
+ pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);
+
+ // Test loop.
+ t_elapsed.u64 = 0;
+ for (i=0; i<LOOP; ++i) {
+ pj_ssize_t bytes;
+ pj_ioqueue_op_key_t read_op, write_op;
+
+ // Randomize send buffer.
+ pj_create_random_string(send_buf, bufsize);
+
+ // Start reading on the server side.
+ bytes = bufsize;
+ rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+ if (rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_read()", rc);
+ break;
+ }
+
+ // Starts send on the client side.
+ bytes = bufsize;
+ rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
+ &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+ app_perror("...error: pj_ioqueue_write()", rc);
+ break;
+ }
+ if (rc == PJ_SUCCESS) {
+ if (bytes < 0) {
+ app_perror("...error: pj_ioqueue_sendto()", -bytes);
+ break;
+ }
+ }
+
+ // Begin time.
+ pj_get_timestamp(&t1);
+
+ // Poll the queue until we've got completion event in the server side.
+ callback_read_key = NULL;
+ callback_read_size = 0;
+ TRACE__((THIS_FILE, " waiting for key = %p", skey));
+ do {
+ pj_time_val timeout = { 1, 0 };
+#ifdef PJ_SYMBIAN
+ rc = pj_symbianos_poll(-1, 1000);
+#else
+ rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ TRACE__((THIS_FILE, " poll rc=%d", rc));
+ } while (rc >= 0 && callback_read_key != skey);
+
+ // End time.
+ pj_get_timestamp(&t2);
+ t_elapsed.u64 += (t2.u64 - t1.u64);
+
+ if (rc < 0) {
+ app_perror(" error: pj_ioqueue_poll", -rc);
+ break;
+ }
+
+ // Compare recv buffer with send buffer.
+ if (callback_read_size != bufsize ||
+ pj_memcmp(send_buf, recv_buf, bufsize))
+ {
+ rc = -10;
+ PJ_LOG(3,(THIS_FILE, " error: size/buffer mismatch"));
+ break;
+ }
+
+ // Poll until all events are exhausted, before we start the next loop.
+ do {
+ pj_time_val timeout = { 0, 10 };
+#ifdef PJ_SYMBIAN
+ rc = pj_symbianos_poll(-1, 100);
+#else
+ rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+ } while (rc>0);
+
+ rc = 0;
+ }
+
+ // Print results
+ if (rc == 0) {
+ pj_timestamp tzero;
+ pj_uint32_t usec_delay;
+
+ tzero.u32.hi = tzero.u32.lo = 0;
+ usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);
+
+ PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d",
+ bufsize, inactive_sock_count, usec_delay));
+
+ } else {
+ PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)",
+ rc, bufsize, inactive_sock_count+2));
+ }
+
+ // Cleaning up.
+ for (i=inactive_sock_count-1; i>=0; --i) {
+ pj_ioqueue_unregister(keys[i]);
+ }
+
+ pj_ioqueue_unregister(skey);
+ pj_ioqueue_unregister(ckey);
+
+
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release( pool);
+ return rc;
+
+on_error:
+ PJ_LOG(1,(THIS_FILE, "...ERROR: %s",
+ pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
+ if (ssock)
+ pj_sock_close(ssock);
+ if (csock)
+ pj_sock_close(csock);
+ for (i=0; i<inactive_sock_count && inactive_sock &&
+ inactive_sock[i]!=PJ_INVALID_SOCKET; ++i)
+ {
+ pj_sock_close(inactive_sock[i]);
+ }
+ if (ioque != NULL)
+ pj_ioqueue_destroy(ioque);
+ pj_pool_release( pool);
+ return -1;
+}
+
+static int udp_ioqueue_test_imp(pj_bool_t allow_concur)
+{
+ int status;
+ int bufsize, sock_count;
+
+ PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+ //goto pass1;
+
+ PJ_LOG(3, (THIS_FILE, "...compliance test (%s)", pj_ioqueue_name()));
+ if ((status=compliance_test(allow_concur)) != 0) {
+ return status;
+ }
+ PJ_LOG(3, (THIS_FILE, "....compliance test ok"));
+
+
+ PJ_LOG(3, (THIS_FILE, "...unregister test (%s)", pj_ioqueue_name()));
+ if ((status=unregister_test(allow_concur)) != 0) {
+ return status;
+ }
+ PJ_LOG(3, (THIS_FILE, "....unregister test ok"));
+
+ if ((status=many_handles_test(allow_concur)) != 0) {
+ return status;
+ }
+
+ //return 0;
+
+ PJ_LOG(4, (THIS_FILE, "...benchmarking different buffer size:"));
+ PJ_LOG(4, (THIS_FILE, "... note: buf=bytes sent, fds=# of fds, "
+ "elapsed=in timer ticks"));
+
+//pass1:
+ PJ_LOG(3, (THIS_FILE, "...Benchmarking poll times for %s:", pj_ioqueue_name()));
+ PJ_LOG(3, (THIS_FILE, "...====================================="));
+ PJ_LOG(3, (THIS_FILE, "...Buf.size #inactive-socks Time/poll"));
+ PJ_LOG(3, (THIS_FILE, "... (bytes) (nanosec)"));
+ PJ_LOG(3, (THIS_FILE, "...====================================="));
+
+ //goto pass2;
+
+ for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
+ if ((status=bench_test(allow_concur, bufsize, SOCK_INACTIVE_MIN)) != 0)
+ return status;
+ }
+//pass2:
+ bufsize = 512;
+ for (sock_count=SOCK_INACTIVE_MIN+2;
+ sock_count<=SOCK_INACTIVE_MAX+2;
+ sock_count *= 2)
+ {
+ //PJ_LOG(3,(THIS_FILE, "...testing with %d fds", sock_count));
+ if ((status=bench_test(allow_concur, bufsize, sock_count-2)) != 0)
+ return status;
+ }
+ return 0;
+}
+
+int udp_ioqueue_test()
+{
+ int rc;
+
+ rc = udp_ioqueue_test_imp(PJ_TRUE);
+ if (rc != 0)
+ return rc;
+
+ rc = udp_ioqueue_test_imp(PJ_FALSE);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_udp;
+#endif /* INCLUDE_UDP_IOQUEUE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ioq_unreg.c b/pjlib/src/pjlib-test/ioq_unreg.c
new file mode 100644
index 0000000..c968e4f
--- /dev/null
+++ b/pjlib/src/pjlib-test/ioq_unreg.c
@@ -0,0 +1,387 @@
+/* $Id: ioq_unreg.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_IOQUEUE_UNREG_TEST
+/*
+ * This tests the thread safety of ioqueue unregistration operation.
+ */
+
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "ioq_unreg.c"
+
+
+enum test_method
+{
+ UNREGISTER_IN_APP,
+ UNREGISTER_IN_CALLBACK,
+};
+
+static int thread_quitting;
+static enum test_method test_method;
+static pj_time_val time_to_unregister;
+
+struct sock_data
+{
+ pj_sock_t sock;
+ pj_sock_t csock;
+ pj_pool_t *pool;
+ pj_ioqueue_key_t *key;
+ pj_mutex_t *mutex;
+ pj_ioqueue_op_key_t *op_key;
+ char *buffer;
+ pj_size_t bufsize;
+ pj_bool_t unregistered;
+ unsigned received;
+} sock_data;
+
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_read)
+{
+ pj_ssize_t size;
+ char *sendbuf = "Hello world";
+ pj_status_t status;
+
+ if (sock_data.unregistered)
+ return;
+
+ pj_mutex_lock(sock_data.mutex);
+
+ if (sock_data.unregistered) {
+ pj_mutex_unlock(sock_data.mutex);
+ return;
+ }
+
+ if (bytes_read < 0) {
+ if (-bytes_read != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+ app_perror("ioqueue reported recv error", -bytes_read);
+ } else {
+ sock_data.received += bytes_read;
+ }
+
+ if (test_method == UNREGISTER_IN_CALLBACK) {
+ pj_time_val now;
+
+ pj_gettimeofday(&now);
+ if (PJ_TIME_VAL_GTE(now, time_to_unregister)) {
+ sock_data.unregistered = 1;
+ pj_ioqueue_unregister(key);
+ pj_mutex_unlock(sock_data.mutex);
+ return;
+ }
+ }
+
+ do {
+ size = sock_data.bufsize;
+ status = pj_ioqueue_recv(key, op_key, sock_data.buffer, &size, 0);
+ if (status != PJ_EPENDING && status != PJ_SUCCESS)
+ app_perror("recv() error", status);
+
+ } while (status == PJ_SUCCESS);
+
+ pj_mutex_unlock(sock_data.mutex);
+
+ size = pj_ansi_strlen(sendbuf);
+ status = pj_sock_send(sock_data.csock, sendbuf, &size, 0);
+ if (status != PJ_SUCCESS)
+ app_perror("send() error", status);
+
+ size = pj_ansi_strlen(sendbuf);
+ status = pj_sock_send(sock_data.csock, sendbuf, &size, 0);
+ if (status != PJ_SUCCESS)
+ app_perror("send() error", status);
+
+}
+
+static int worker_thread(void *arg)
+{
+ pj_ioqueue_t *ioqueue = (pj_ioqueue_t*) arg;
+
+ while (!thread_quitting) {
+ pj_time_val timeout = { 0, 20 };
+ pj_ioqueue_poll(ioqueue, &timeout);
+ }
+
+ return 0;
+}
+
+/*
+ * Perform unregistration test.
+ *
+ * This will create ioqueue and register a server socket. Depending
+ * on the test method, either the callback or the main thread will
+ * unregister and destroy the server socket after some period of time.
+ */
+static int perform_unreg_test(pj_ioqueue_t *ioqueue,
+ pj_pool_t *test_pool,
+ const char *title,
+ pj_bool_t other_socket)
+{
+ enum { WORKER_CNT = 1, MSEC = 500, QUIT_MSEC = 500 };
+ int i;
+ pj_thread_t *thread[WORKER_CNT];
+ struct sock_data osd;
+ pj_ioqueue_callback callback;
+ pj_time_val end_time;
+ pj_status_t status;
+
+
+ /* Sometimes its important to have other sockets registered to
+ * the ioqueue, because when no sockets are registered, the ioqueue
+ * will return from the poll early.
+ */
+ if (other_socket) {
+ status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error creating other socket", status);
+ return -12;
+ }
+
+ pj_bzero(&callback, sizeof(callback));
+ status = pj_ioqueue_register_sock(test_pool, ioqueue, osd.sock,
+ NULL, &callback, &osd.key);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error registering other socket", status);
+ return -13;
+ }
+
+ } else {
+ osd.key = NULL;
+ osd.sock = PJ_INVALID_SOCKET;
+ }
+
+ /* Init both time duration of testing */
+ thread_quitting = 0;
+ pj_gettimeofday(&time_to_unregister);
+ time_to_unregister.msec += MSEC;
+ pj_time_val_normalize(&time_to_unregister);
+
+ end_time = time_to_unregister;
+ end_time.msec += QUIT_MSEC;
+ pj_time_val_normalize(&end_time);
+
+
+ /* Create polling thread */
+ for (i=0; i<WORKER_CNT; ++i) {
+ status = pj_thread_create(test_pool, "unregtest", &worker_thread,
+ ioqueue, 0, 0, &thread[i]);
+ if (status != PJ_SUCCESS) {
+ app_perror("Error creating thread", status);
+ return -20;
+ }
+ }
+
+ /* Create pair of client/server sockets */
+ status = app_socketpair(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
+ &sock_data.sock, &sock_data.csock);
+ if (status != PJ_SUCCESS) {
+ app_perror("app_socketpair error", status);
+ return -30;
+ }
+
+
+ /* Initialize test data */
+ sock_data.pool = pj_pool_create(mem, "sd", 1000, 1000, NULL);
+ sock_data.buffer = (char*) pj_pool_alloc(sock_data.pool, 128);
+ sock_data.bufsize = 128;
+ sock_data.op_key = (pj_ioqueue_op_key_t*)
+ pj_pool_alloc(sock_data.pool,
+ sizeof(*sock_data.op_key));
+ sock_data.received = 0;
+ sock_data.unregistered = 0;
+
+ pj_ioqueue_op_key_init(sock_data.op_key, sizeof(*sock_data.op_key));
+
+ status = pj_mutex_create_simple(sock_data.pool, "sd", &sock_data.mutex);
+ if (status != PJ_SUCCESS) {
+ app_perror("create_mutex() error", status);
+ return -35;
+ }
+
+ /* Register socket to ioqueue */
+ pj_bzero(&callback, sizeof(callback));
+ callback.on_read_complete = &on_read_complete;
+ status = pj_ioqueue_register_sock(sock_data.pool, ioqueue, sock_data.sock,
+ NULL, &callback, &sock_data.key);
+ if (status != PJ_SUCCESS) {
+ app_perror("pj_ioqueue_register error", status);
+ return -40;
+ }
+
+ /* Bootstrap the first send/receive */
+ on_read_complete(sock_data.key, sock_data.op_key, 0);
+
+ /* Loop until test time ends */
+ for (;;) {
+ pj_time_val now, timeout;
+ int n;
+
+ pj_gettimeofday(&now);
+
+ if (test_method == UNREGISTER_IN_APP &&
+ PJ_TIME_VAL_GTE(now, time_to_unregister) &&
+ !sock_data.unregistered)
+ {
+ sock_data.unregistered = 1;
+ /* Wait (as much as possible) for callback to complete */
+ pj_mutex_lock(sock_data.mutex);
+ pj_mutex_unlock(sock_data.mutex);
+ pj_ioqueue_unregister(sock_data.key);
+ }
+
+ if (PJ_TIME_VAL_GT(now, end_time) && sock_data.unregistered)
+ break;
+
+ timeout.sec = 0; timeout.msec = 10;
+ n = pj_ioqueue_poll(ioqueue, &timeout);
+ if (n < 0) {
+ app_perror("pj_ioqueue_poll error", -n);
+ pj_thread_sleep(1);
+ }
+ }
+
+ thread_quitting = 1;
+
+ for (i=0; i<WORKER_CNT; ++i) {
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+
+ /* Destroy data */
+ pj_mutex_destroy(sock_data.mutex);
+ pj_pool_release(sock_data.pool);
+ sock_data.pool = NULL;
+
+ if (other_socket) {
+ pj_ioqueue_unregister(osd.key);
+ }
+
+ pj_sock_close(sock_data.csock);
+
+ PJ_LOG(3,(THIS_FILE, "....%s: done (%d KB/s)",
+ title, sock_data.received * 1000 / MSEC / 1000));
+ return 0;
+}
+
+static int udp_ioqueue_unreg_test_imp(pj_bool_t allow_concur)
+{
+ enum { LOOP = 10 };
+ int i, rc;
+ char title[30];
+ pj_ioqueue_t *ioqueue;
+ pj_pool_t *test_pool;
+
+ PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+ test_method = UNREGISTER_IN_APP;
+
+ test_pool = pj_pool_create(mem, "unregtest", 4000, 4000, NULL);
+
+ rc = pj_ioqueue_create(test_pool, 16, &ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("Error creating ioqueue", rc);
+ return -10;
+ }
+
+ rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+ if (rc != PJ_SUCCESS) {
+ app_perror("Error in pj_ioqueue_set_default_concurrency()", rc);
+ return -12;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 0/3, unregister in app (%s)",
+ pj_ioqueue_name()));
+ for (i=0; i<LOOP; ++i) {
+ pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+ rc = perform_unreg_test(ioqueue, test_pool, title, 0);
+ if (rc != 0)
+ return rc;
+ }
+
+
+ PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 1/3, unregister in app (%s)",
+ pj_ioqueue_name()));
+ for (i=0; i<LOOP; ++i) {
+ pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+ rc = perform_unreg_test(ioqueue, test_pool, title, 1);
+ if (rc != 0)
+ return rc;
+ }
+
+ test_method = UNREGISTER_IN_CALLBACK;
+
+ PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 2/3, unregister in cb (%s)",
+ pj_ioqueue_name()));
+ for (i=0; i<LOOP; ++i) {
+ pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+ rc = perform_unreg_test(ioqueue, test_pool, title, 0);
+ if (rc != 0)
+ return rc;
+ }
+
+
+ PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 3/3, unregister in cb (%s)",
+ pj_ioqueue_name()));
+ for (i=0; i<LOOP; ++i) {
+ pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+ rc = perform_unreg_test(ioqueue, test_pool, title, 1);
+ if (rc != 0)
+ return rc;
+ }
+
+ pj_ioqueue_destroy(ioqueue);
+ pj_pool_release(test_pool);
+
+ return 0;
+}
+
+int udp_ioqueue_unreg_test(void)
+{
+ int rc;
+
+ rc = udp_ioqueue_unreg_test_imp(PJ_TRUE);
+ if (rc != 0)
+ return rc;
+
+ rc = udp_ioqueue_unreg_test_imp(PJ_FALSE);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_uiq_unreg;
+#endif /* INCLUDE_IOQUEUE_UNREG_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/list.c b/pjlib/src/pjlib-test/list.c
new file mode 100644
index 0000000..6472857
--- /dev/null
+++ b/pjlib/src/pjlib-test/list.c
@@ -0,0 +1,226 @@
+/* $Id: list.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_list_test Test: Linked List
+ *
+ * This file provides implementation of \b list_test(). It tests the
+ * functionality of the linked-list API.
+ *
+ * \section list_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_list_init()
+ * - pj_list_insert_before()
+ * - pj_list_insert_after()
+ * - pj_list_merge_last()
+ * - pj_list_empty()
+ * - pj_list_insert_nodes_before()
+ * - pj_list_erase()
+ * - pj_list_find_node()
+ * - pj_list_search()
+ *
+ *
+ * This file is <b>pjlib-test/list.c</b>
+ *
+ * \include pjlib-test/list.c
+ */
+
+#if INCLUDE_LIST_TEST
+
+#include <pjlib.h>
+
+typedef struct list_node
+{
+ PJ_DECL_LIST_MEMBER(struct list_node);
+ int value;
+} list_node;
+
+static int compare_node(void *value, const pj_list_type *nd)
+{
+ list_node *node = (list_node*)nd;
+ return ((long)value == node->value) ? 0 : -1;
+}
+
+#define PJ_SIGNED_ARRAY_SIZE(a) ((int)PJ_ARRAY_SIZE(a))
+
+int list_test()
+{
+ list_node nodes[4]; // must be even number of nodes
+ list_node list;
+ list_node list2;
+ list_node *p;
+ int i; // don't change to unsigned!
+
+ //
+ // Test insert_before().
+ //
+ list.value = (unsigned)-1;
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ //
+ // Test insert_after()
+ //
+ pj_list_init(&list);
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+ pj_list_insert_after(&list, &nodes[i]);
+ }
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ //
+ // Test merge_last()
+ //
+ // Init lists
+ pj_list_init(&list);
+ pj_list_init(&list2);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&list2, &nodes[i]);
+ }
+ // merge
+ pj_list_merge_last(&list, &list2);
+ // check.
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+ // check list is empty
+ pj_assert( pj_list_empty(&list2) );
+ if (!pj_list_empty(&list2)) {
+ return -1;
+ }
+
+ //
+ // Check merge_first()
+ //
+ pj_list_init(&list);
+ pj_list_init(&list2);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&list2, &nodes[i]);
+ }
+ // merge
+ pj_list_merge_first(&list2, &list);
+ // check (list2).
+ for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+ // check list is empty
+ pj_assert( pj_list_empty(&list) );
+ if (!pj_list_empty(&list)) {
+ return -1;
+ }
+
+ //
+ // Test insert_nodes_before()
+ //
+ // init list
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ // chain remaining nodes
+ pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]);
+ }
+ // insert nodes
+ pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+ // check
+ for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+ pj_assert(p->value == i);
+ if (p->value != i) {
+ return -1;
+ }
+ }
+
+ // erase test.
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+ int j;
+ pj_list_erase(&nodes[i]);
+ for (j=0, p=list.next; j<i; ++j, p=p->next) {
+ pj_assert(p->value == j);
+ if (p->value != j) {
+ return -1;
+ }
+ }
+ }
+
+ // find and search
+ pj_list_init(&list);
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ nodes[i].value = i;
+ pj_list_insert_before(&list, &nodes[i]);
+ }
+ for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+ p = (list_node*) pj_list_find_node(&list, &nodes[i]);
+ pj_assert( p == &nodes[i] );
+ if (p != &nodes[i]) {
+ return -1;
+ }
+ p = (list_node*) pj_list_search(&list, (void*)(long)i, &compare_node);
+ pj_assert( p == &nodes[i] );
+ if (p != &nodes[i]) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_list_test;
+#endif /* INCLUDE_LIST_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c
new file mode 100644
index 0000000..e0241b6
--- /dev/null
+++ b/pjlib/src/pjlib-test/main.c
@@ -0,0 +1,107 @@
+/* $Id: main.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+#include <stdio.h>
+
+extern int param_echo_sock_type;
+extern const char *param_echo_server;
+extern int param_echo_port;
+
+
+//#if defined(PJ_WIN32) && PJ_WIN32!=0
+#if 0
+#include <windows.h>
+static void boost(void)
+{
+ SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
+}
+#else
+#define boost()
+#endif
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+
+ sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int interractive = 0;
+
+ boost();
+ init_signals();
+
+ while (argc > 1) {
+ char *arg = argv[--argc];
+
+ if (*arg=='-' && *(arg+1)=='i') {
+ interractive = 1;
+
+ } else if (*arg=='-' && *(arg+1)=='p') {
+ pj_str_t port = pj_str(argv[--argc]);
+
+ param_echo_port = pj_strtoul(&port);
+
+ } else if (*arg=='-' && *(arg+1)=='s') {
+ param_echo_server = argv[--argc];
+
+ } else if (*arg=='-' && *(arg+1)=='t') {
+ pj_str_t type = pj_str(argv[--argc]);
+
+ if (pj_stricmp2(&type, "tcp")==0)
+ param_echo_sock_type = pj_SOCK_STREAM();
+ else if (pj_stricmp2(&type, "udp")==0)
+ param_echo_sock_type = pj_SOCK_DGRAM();
+ else {
+ PJ_LOG(3,("", "error: unknown socket type %s", type.ptr));
+ return 1;
+ }
+ }
+ }
+
+ rc = test_main();
+
+ if (interractive) {
+ char s[10];
+ puts("");
+ puts("Press <ENTER> to exit");
+ if (!fgets(s, sizeof(s), stdin))
+ return rc;
+ }
+
+ return rc;
+}
+
diff --git a/pjlib/src/pjlib-test/main_mod.c b/pjlib/src/pjlib-test/main_mod.c
new file mode 100644
index 0000000..bb52a6f
--- /dev/null
+++ b/pjlib/src/pjlib-test/main_mod.c
@@ -0,0 +1,40 @@
+/* $Id: main_mod.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+int init_module(void)
+{
+ printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n");
+
+ test_main();
+
+ /* Prevent module from loading. We've finished test anyway.. */
+ return 1;
+}
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "PJLIB test module unloading...\n");
+}
+
+MODULE_LICENSE("GPL");
+
diff --git a/pjlib/src/pjlib-test/main_rtems.c b/pjlib/src/pjlib-test/main_rtems.c
new file mode 100644
index 0000000..b2c8d9b
--- /dev/null
+++ b/pjlib/src/pjlib-test/main_rtems.c
@@ -0,0 +1,326 @@
+/* $Id: main_rtems.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * - Many thanks for Zetron, Inc. and Phil Torre <ptorre@zetron.com> for
+ * donating this file and the RTEMS port in general!
+ */
+
+#include "test.h"
+
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+
+extern int param_echo_sock_type;
+extern const char *param_echo_server;
+extern int param_echo_port;
+
+#include <bsp.h>
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 300
+#define CONFIGURE_MAXIMUM_TASKS 50
+#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(10)
+#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(10)
+#define CONFIGURE_MAXIMUM_TIMERS 50
+#define CONFIGURE_MAXIMUM_REGIONS 3
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_TIMER_DRIVER
+#define CONFIGURE_TICKS_PER_TIMESLICE 2
+//#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+#define CONFIGURE_POSIX_INIT_THREAD_TABLE
+
+
+#define CONFIGURE_MAXIMUM_POSIX_MUTEXES rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_CONDITION_VARIABLES rtems_resource_unlimited(5)
+#define CONFIGURE_MAXIMUM_POSIX_SEMAPHORES rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_TIMERS rtems_resource_unlimited(5)
+#define CONFIGURE_MAXIMUM_POSIX_THREADS rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_KEYS rtems_resource_unlimited(16)
+
+#define CONFIGURE_POSIX_INIT_THREAD_STACK_SIZE 4096
+
+/* Make sure that stack size is at least 4096 */
+#define SZ (4096-RTEMS_MINIMUM_STACK_SIZE)
+#define CONFIGURE_EXTRA_TASK_STACKS ((SZ)<0 ? 0 : (SZ))
+
+#define CONFIGURE_INIT
+#define STACK_CHECKER_ON
+
+rtems_task Init(rtems_task_argument Argument) ;
+void *POSIX_Init(void *argument);
+
+#include <confdefs.h>
+#include <rtems.h>
+
+/* Any tests that want to build a linked executable for RTEMS must include
+ these headers to get a default config for the network stack. */
+#include <rtems/rtems_bsdnet.h>
+#include "rtems_network_config.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define THIS_FILE "main_rtems.c"
+
+static void* pjlib_test_main(void* unused);
+static void initialize_network();
+static void test_sock(void);
+
+static void my_perror(pj_status_t status, const char *title)
+{
+ char err[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, err, sizeof(err));
+ printf("%s: %s [%d]\n", title, err, status);
+}
+
+#define TEST(expr) { int rc;\
+ /*PJ_LOG(3,(THIS_FILE,"%s", #expr));*/ \
+ /*sleep(1);*/ \
+ rc=expr; \
+ if (rc) my_perror(PJ_STATUS_FROM_OS(rc),#expr); }
+
+
+
+//rtems_task Init(rtems_task_argument Argument)
+void *POSIX_Init(void *argument)
+{
+ pthread_attr_t threadAttr;
+ pthread_t theThread;
+ struct sched_param sched_param;
+ size_t stack_size;
+ int result;
+ char data[1000];
+
+
+ memset(data, 1, sizeof(data));
+
+ /* Set the TOD clock, so that gettimeofday() will work */
+ rtems_time_of_day fakeTime = { 2006, 3, 15, 17, 30, 0, 0 };
+
+ if (RTEMS_SUCCESSFUL != rtems_clock_set(&fakeTime))
+ {
+ assert(0);
+ }
+
+ /* Bring up the network stack so we can run the socket tests. */
+ initialize_network();
+
+ /* Start a POSIX thread for pjlib_test_main(), since that's what it
+ * thinks it is running in.
+ */
+
+ /* Initialize attribute */
+ TEST( pthread_attr_init(&threadAttr) );
+
+ /* Looks like the rest of the attributes must be fully initialized too,
+ * or otherwise pthread_create will return EINVAL.
+ */
+
+ /* Specify explicit scheduling request */
+ TEST( pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED));
+
+ /* Timeslicing is needed by thread test, and this is accomplished by
+ * SCHED_RR.
+ */
+ TEST( pthread_attr_setschedpolicy(&threadAttr, SCHED_RR));
+
+ /* Set priority */
+ TEST( pthread_attr_getschedparam(&threadAttr, &sched_param));
+ sched_param.sched_priority = NETWORK_STACK_PRIORITY - 10;
+ TEST( pthread_attr_setschedparam(&threadAttr, &sched_param));
+
+ /* Must have sufficient stack size (large size is needed by
+ * logger, because default settings for logger is to use message buffer
+ * from the stack).
+ */
+ TEST( pthread_attr_getstacksize(&threadAttr, &stack_size));
+ if (stack_size < 8192)
+ TEST( pthread_attr_setstacksize(&threadAttr, 8192));
+
+
+ /* Create the thread for application */
+ result = pthread_create(&theThread, &threadAttr, &pjlib_test_main, NULL);
+ if (result != 0) {
+ my_perror(PJ_STATUS_FROM_OS(result),
+ "Error creating pjlib_test_main thread");
+ assert(!"Error creating main thread");
+ }
+
+ return NULL;
+}
+
+
+
+#define boost()
+#define init_signals()
+
+static void*
+pjlib_test_main(void* unused)
+{
+ int rc;
+
+ /* Drop our priority to below that of the network stack, otherwise
+ * select() tests will fail. */
+ struct sched_param schedParam;
+ int schedPolicy;
+
+ printf("pjlib_test_main thread started..\n");
+
+ TEST( pthread_getschedparam(pthread_self(), &schedPolicy, &schedParam) );
+
+ schedParam.sched_priority = NETWORK_STACK_PRIORITY - 10;
+
+ TEST( pthread_setschedparam(pthread_self(), schedPolicy, &schedParam) );
+
+ boost();
+ init_signals();
+
+ //my_test_thread("from pjlib_test_main");
+ //test_sock();
+
+ rc = test_main();
+
+ return (void*)rc;
+}
+
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <unistd.h>
+
+/*
+ * Send UDP packet to some host. We can then use Ethereal to sniff the packet
+ * to see if this target really transmits UDP packet.
+ */
+static void
+send_udp(const char *target)
+{
+ int sock, rc;
+ struct sockaddr_in addr;
+
+ PJ_LOG(3,("main_rtems.c", "IP addr=%s/%s, gw=%s",
+ DEFAULT_IP_ADDRESS_STRING,
+ DEFAULT_NETMASK_STRING,
+ DEFAULT_GATEWAY_STRING));
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ assert(sock > 0);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ rc = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
+ assert("bind error" && rc==0);
+
+ addr.sin_addr.s_addr = inet_addr(target);
+ addr.sin_port = htons(4444);
+
+ while(1) {
+ const char *data = "hello";
+
+ rc = sendto(sock, data, 5, 0, (struct sockaddr*)&addr, sizeof(addr));
+ PJ_LOG(3,("main_rtems.c", "pinging %s..(rc=%d)", target, rc));
+ sleep(1);
+ }
+}
+
+
+static void test_sock(void)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int rc;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ printf("socket() error\n");
+ goto end;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = htons(5000);
+
+ rc = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
+ if (rc != 0) {
+ printf("bind() error %d\n", rc);
+ close(sock);
+ goto end;
+ }
+
+ puts("Bind socket success");
+
+ close(sock);
+
+end:
+ while(1) sleep(1);
+}
+
+/*
+ * Initialize the network stack and Ethernet driver, using the configuration
+ * in rtems-network-config.h
+ */
+static void
+initialize_network()
+{
+ unsigned32 fd, result;
+ char ip_address_string[] = DEFAULT_IP_ADDRESS_STRING;
+ char netmask_string[] = DEFAULT_NETMASK_STRING;
+ char gateway_string[] = DEFAULT_GATEWAY_STRING;
+
+ // Write the network config files to /etc/hosts and /etc/host.conf
+ result = mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO);
+ fd = open("/etc/host.conf", O_RDWR | O_CREAT, 0744);
+ result = write(fd, "hosts,bind\n", 11);
+ result = close(fd);
+ fd = open("/etc/hosts", O_RDWR | O_CREAT, 0744);
+ result = write(fd, "127.0.0.1 localhost\n", 41);
+ result = write(fd, ip_address_string, strlen(ip_address_string));
+ result = write(fd, " pjsip-test\n", 32);
+ result = close(fd);
+
+ netdriver_config.ip_address = ip_address_string;
+ netdriver_config.ip_netmask = netmask_string;
+ rtems_bsdnet_config.gateway = gateway_string;
+
+ if (0 != rtems_bsdnet_initialize_network())
+ PJ_LOG(3,(THIS_FILE, "Error: Unable to initialize network stack!"));
+ else
+ PJ_LOG(3,(THIS_FILE, "IP addr=%s/%s, gw=%s",
+ ip_address_string,
+ netmask_string,
+ gateway_string));
+
+ //rtems_rdbg_initialize();
+ //enterRdbg();
+ //send_udp("192.168.0.1");
+ //test_sock();
+}
+
+
diff --git a/pjlib/src/pjlib-test/main_symbian.cpp b/pjlib/src/pjlib-test/main_symbian.cpp
new file mode 100644
index 0000000..a91b671
--- /dev/null
+++ b/pjlib/src/pjlib-test/main_symbian.cpp
@@ -0,0 +1,133 @@
+//Auto-generated file. Please do not modify.
+//#include <e32cmn.h>
+
+//#pragma data_seg(".SYMBIAN")
+//__EMULATOR_IMAGE_HEADER2 (0x1000007a,0x00000000,0x00000000,EPriorityForeground,0x00000000u,0x00000000u,0x00000000,0x00000000,0x00000000,0)
+//#pragma data_seg()
+
+#include "test.h"
+#include <stdlib.h>
+#include <pj/errno.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/unicode.h>
+#include <stdio.h>
+
+#include <e32std.h>
+
+#if 0
+int main()
+{
+ int err = 0;
+ int exp = 0;
+
+ err = test_main();
+ //err = test_main();
+
+ if (err)
+ return err;
+ return exp;
+ //return 0;
+}
+
+#else
+#include <pj/os.h>
+
+#include <e32base.h>
+#include <e32std.h>
+#include <e32cons.h> // Console
+
+
+
+// Global Variables
+
+LOCAL_D CConsoleBase* console; // write all messages to this
+
+
+class MyScheduler : public CActiveScheduler
+{
+public:
+ MyScheduler()
+ {}
+
+ void Error(TInt aError) const;
+};
+
+void MyScheduler::Error(TInt aError) const
+{
+ PJ_UNUSED_ARG(aError);
+}
+
+LOCAL_C void DoStartL()
+ {
+ // Create active scheduler (to run active objects)
+ CActiveScheduler* scheduler = new (ELeave) MyScheduler;
+ CleanupStack::PushL(scheduler);
+ CActiveScheduler::Install(scheduler);
+
+ test_main();
+
+ CActiveScheduler::Install(NULL);
+ CleanupStack::Pop(scheduler);
+ delete scheduler;
+ }
+
+#define WRITE_TO_DEBUG_CONSOLE
+
+#ifdef WRITE_TO_DEBUG_CONSOLE
+#include<e32debug.h>
+#endif
+
+// Global Functions
+static void log_writer(int level, const char *buf, int len)
+{
+ static wchar_t buf16[PJ_LOG_MAX_SIZE];
+
+ PJ_UNUSED_ARG(level);
+
+ pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
+ buf16[len] = 0;
+ buf16[len+1] = 0;
+
+ TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
+ console->Write(aBuf);
+
+#ifdef WRITE_TO_DEBUG_CONSOLE
+ RDebug::Print(aBuf);
+#endif
+}
+
+
+GLDEF_C TInt E32Main()
+ {
+ // Create cleanup stack
+ __UHEAP_MARK;
+ CTrapCleanup* cleanup = CTrapCleanup::New();
+
+ // Create output console
+ TRAPD(createError, console = Console::NewL(_L("Console"), TSize(KConsFullScreen,KConsFullScreen)));
+ if (createError)
+ return createError;
+
+ pj_log_set_log_func(&log_writer);
+
+ // Run application code inside TRAP harness, wait keypress when terminated
+ TRAPD(mainError, DoStartL());
+ if (mainError)
+ console->Printf(_L(" failed, leave code = %d"), mainError);
+
+ console->Printf(_L(" [press any key]\n"));
+ console->Getch();
+
+ delete console;
+ delete cleanup;
+
+ CloseSTDLIB();
+
+ __UHEAP_MARKEND;
+
+ return KErrNone;
+ }
+
+#endif /* if 0 */
+
diff --git a/pjlib/src/pjlib-test/main_win32.c b/pjlib/src/pjlib-test/main_win32.c
new file mode 100644
index 0000000..fd107c3
--- /dev/null
+++ b/pjlib/src/pjlib-test/main_win32.c
@@ -0,0 +1,211 @@
+/* $Id: main_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#include <pj/string.h>
+#include <pj/unicode.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+
+#define WIN32_LEAN_AND_MEAN
+#define NONAMELESSUNION
+#include <windows.h>
+#include <commctrl.h>
+#include <tchar.h>
+
+#define MAX_LOADSTRING 100
+#define THIS_FILE "main_win32.c"
+
+#define IDC_HELLO_WINCE 3
+#define ID_LOGWINDOW 104
+
+
+ATOM MyRegisterClass (HINSTANCE, LPTSTR);
+BOOL InitInstance (HINSTANCE, int);
+LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
+
+
+extern int param_log_decor; // in test.c
+
+static HINSTANCE hInst;
+static HWND hwndLog;
+static HFONT hFixedFont;
+
+
+static void write_log(int level, const char *data, int len)
+{
+ PJ_DECL_UNICODE_TEMP_BUF(wdata,256);
+
+ PJ_UNUSED_ARG(level);
+ PJ_UNUSED_ARG(len);
+ SendMessage(hwndLog, EM_REPLACESEL, FALSE,
+ (LPARAM)PJ_STRING_TO_NATIVE(data,wdata,256));
+}
+
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPTSTR lpCmdLine, int nCmdShow)
+{
+ MSG msg;
+
+ PJ_UNUSED_ARG(lpCmdLine);
+ PJ_UNUSED_ARG(hPrevInstance);
+
+
+ if (!InitInstance (hInstance, nCmdShow))
+ return FALSE;
+
+ pj_log_set_log_func( &write_log );
+ param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR;
+
+ // Run the test!
+ test_main();
+
+ PJ_LOG(3,(THIS_FILE,""));
+ PJ_LOG(3,(THIS_FILE,"Press ESC to quit"));
+
+ // Message loop, waiting to quit.
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DeleteObject(hFixedFont);
+ return msg.wParam;
+}
+
+
+#ifdef _CONSOLE
+int main()
+{
+ return WinMain(GetModuleHandle(NULL), NULL, NULL, SW_SHOW);
+}
+#endif
+
+
+ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
+{
+ WNDCLASS wc;
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ ///wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLO_WINCE));
+ wc.hIcon = NULL;
+ wc.hCursor = 0;
+ wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = szWindowClass;
+
+ return RegisterClass(&wc);
+}
+
+
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ HWND hWnd;
+ TCHAR *szTitle = _T("PJSIP Test");
+ TCHAR *szWindowClass = _T("PJSIP_TEST");
+ LOGFONT lf;
+
+
+ memset(&lf, 0, sizeof(lf));
+ lf.lfHeight = 13;
+#if PJ_NATIVE_STRING_IS_UNICODE
+ wcscpy(lf.lfFaceName, _T("Courier New"));
+#else
+ strcpy(lf.lfFaceName, "Lucida Console");
+#endif
+
+ hFixedFont = CreateFontIndirect(&lf);
+ if (!hFixedFont)
+ return FALSE;
+
+ hInst = hInstance;
+
+ MyRegisterClass(hInstance, szWindowClass);
+
+ hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
+
+ if (!hWnd)
+ return FALSE;
+
+ ShowWindow(hWnd, nCmdShow);
+ UpdateWindow(hWnd);
+
+ if (hwndLog) {
+ SendMessage(hwndLog, WM_SETFONT, (WPARAM) hFixedFont, (LPARAM) 0);
+ ShowWindow(hwndLog, TRUE);
+ }
+
+ return TRUE;
+}
+
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ RECT rt;
+ DWORD dwStyle;
+
+ switch (message)
+ {
+ case WM_CREATE:
+ // Create text control.
+ GetClientRect(hWnd, &rt);
+ dwStyle = WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
+ WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL |
+ ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_READONLY;
+ hwndLog = CreateWindow( TEXT("edit"), // class
+ NULL, // window text
+ dwStyle, // style
+ 0, // x-left
+ 0, // y-top
+ rt.right-rt.left, // w
+ rt.bottom-rt.top, // h
+ hWnd, // parent
+ (HMENU)ID_LOGWINDOW,// id
+ hInst, // instance
+ NULL); // NULL for control.
+ break;
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) == WA_INACTIVE)
+ DestroyWindow(hWnd);
+ break;
+ case WM_CHAR:
+ if (wParam == 27) {
+ DestroyWindow(hWnd);
+ }
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hWnd);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
diff --git a/pjlib/src/pjlib-test/mutex.c b/pjlib/src/pjlib-test/mutex.c
new file mode 100644
index 0000000..0c5fa07
--- /dev/null
+++ b/pjlib/src/pjlib-test/mutex.c
@@ -0,0 +1,234 @@
+/* $Id: mutex.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_MUTEX_TEST
+
+#undef TRACE_
+//#define TRACE_(x) PJ_LOG(3,x)
+#define TRACE_(x)
+
+/* Test witn non-recursive mutex. */
+static int simple_mutex_test(pj_pool_t *pool)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_LOG(3,("", "...testing simple mutex"));
+
+ /* Create mutex. */
+ TRACE_(("", "....create mutex"));
+ rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_create", rc);
+ return -10;
+ }
+
+ /* Normal lock/unlock cycle. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_lock", rc);
+ return -20;
+ }
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_unlock", rc);
+ return -30;
+ }
+
+ /* Lock again. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) return -40;
+
+ /* Try-lock should fail. It should not deadlocked. */
+ TRACE_(("", "....trylock mutex"));
+ rc = pj_mutex_trylock(mutex);
+ if (rc == PJ_SUCCESS)
+ PJ_LOG(3,("", "...info: looks like simple mutex is recursive"));
+
+ /* Unlock and done. */
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -50;
+
+ TRACE_(("", "....destroy mutex"));
+ rc = pj_mutex_destroy(mutex);
+ if (rc != PJ_SUCCESS) return -60;
+
+ TRACE_(("", "....done"));
+ return PJ_SUCCESS;
+}
+
+
+/* Test with recursive mutex. */
+static int recursive_mutex_test(pj_pool_t *pool)
+{
+ pj_status_t rc;
+ pj_mutex_t *mutex;
+
+ PJ_LOG(3,("", "...testing recursive mutex"));
+
+ /* Create mutex. */
+ TRACE_(("", "....create mutex"));
+ rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_create", rc);
+ return -10;
+ }
+
+ /* Normal lock/unlock cycle. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_lock", rc);
+ return -20;
+ }
+ TRACE_(("", "....unlock mutex"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_mutex_unlock", rc);
+ return -30;
+ }
+
+ /* Lock again. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) return -40;
+
+ /* Try-lock should NOT fail. . */
+ TRACE_(("", "....trylock mutex"));
+ rc = pj_mutex_trylock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: recursive mutex is not recursive!", rc);
+ return -40;
+ }
+
+ /* Locking again should not fail. */
+ TRACE_(("", "....lock mutex"));
+ rc = pj_mutex_lock(mutex);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: recursive mutex is not recursive!", rc);
+ return -45;
+ }
+
+ /* Unlock several times and done. */
+ TRACE_(("", "....unlock mutex 3x"));
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -50;
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -51;
+ rc = pj_mutex_unlock(mutex);
+ if (rc != PJ_SUCCESS) return -52;
+
+ TRACE_(("", "....destroy mutex"));
+ rc = pj_mutex_destroy(mutex);
+ if (rc != PJ_SUCCESS) return -60;
+
+ TRACE_(("", "....done"));
+ return PJ_SUCCESS;
+}
+
+#if PJ_HAS_SEMAPHORE
+static int semaphore_test(pj_pool_t *pool)
+{
+ pj_sem_t *sem;
+ pj_status_t status;
+
+ PJ_LOG(3,("", "...testing semaphore"));
+
+ status = pj_sem_create(pool, NULL, 0, 1, &sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_create()", status);
+ return -151;
+ }
+
+ status = pj_sem_post(sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_post()", status);
+ pj_sem_destroy(sem);
+ return -153;
+ }
+
+ status = pj_sem_trywait(sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_trywait()", status);
+ pj_sem_destroy(sem);
+ return -156;
+ }
+
+ status = pj_sem_post(sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_post()", status);
+ pj_sem_destroy(sem);
+ return -159;
+ }
+
+ status = pj_sem_wait(sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_wait()", status);
+ pj_sem_destroy(sem);
+ return -161;
+ }
+
+ status = pj_sem_destroy(sem);
+ if (status != PJ_SUCCESS) {
+ app_perror("...error: pj_sem_destroy()", status);
+ return -163;
+ }
+
+ return 0;
+}
+#endif /* PJ_HAS_SEMAPHORE */
+
+
+int mutex_test(void)
+{
+ pj_pool_t *pool;
+ int rc;
+
+ pool = pj_pool_create(mem, "", 4000, 4000, NULL);
+
+ rc = simple_mutex_test(pool);
+ if (rc != 0)
+ return rc;
+
+ rc = recursive_mutex_test(pool);
+ if (rc != 0)
+ return rc;
+
+#if PJ_HAS_SEMAPHORE
+ rc = semaphore_test(pool);
+ if (rc != 0)
+ return rc;
+#endif
+
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+#else
+int dummy_mutex_test;
+#endif
+
diff --git a/pjlib/src/pjlib-test/os.c b/pjlib/src/pjlib-test/os.c
new file mode 100644
index 0000000..87270ff
--- /dev/null
+++ b/pjlib/src/pjlib-test/os.c
@@ -0,0 +1,116 @@
+/* $Id: os.c 3841 2011-10-24 09:28:13Z ming $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if INCLUDE_OS_TEST
+static int endianness_test32(void)
+{
+ union t
+ {
+ pj_uint32_t u32;
+ pj_uint16_t u16[2];
+ pj_uint8_t u8[4];
+ } t;
+
+ PJ_LOG(3,("", " Testing endianness.."));
+
+ t.u32 = 0x11223344;
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+ PJ_LOG(3,("", " Library is set to little endian"));
+
+# if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+# error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+# endif
+
+ if ((t.u16[0] & 0xFFFF) != 0x3344 ||
+ (t.u16[1] & 0xFFFF) != 0x1122)
+ {
+ PJ_LOG(3,("", " Error: wrong 16bit values 0x%x and 0x%x",
+ (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+ return 10;
+ }
+
+ if ((t.u8[0] & 0xFF) != 0x44 ||
+ (t.u8[1] & 0xFF) != 0x33 ||
+ (t.u8[2] & 0xFF) != 0x22 ||
+ (t.u8[3] & 0xFF) != 0x11)
+ {
+ PJ_LOG(3,("", " Error: wrong 8bit values"));
+ return 12;
+ }
+
+#elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+ PJ_LOG(3,("", " Library is set to big endian"));
+
+ if ((t.u16[0] & 0xFFFF) != 0x1122 ||
+ (t.u16[1] & 0xFFFF) != 0x3344)
+ {
+ PJ_LOG(3,("", " Error: wrong 16bit values 0x%x and 0x%x",
+ (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+ return 20;
+ }
+
+ if ((t.u8[0] & 0xFF) != 0x11 ||
+ (t.u8[1] & 0xFF) != 0x22 ||
+ (t.u8[2] & 0xFF) != 0x33 ||
+ (t.u8[3] & 0xFF) != 0x44)
+ {
+ PJ_LOG(3,("", " Error: wrong 8bit values"));
+ return 22;
+ }
+
+# if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+# error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+# endif
+
+
+#else
+# error Error: Endianness is not set properly!
+#endif
+
+ return 0;
+}
+
+int os_test(void)
+{
+ const pj_sys_info *si;
+ int rc = 0;
+
+ PJ_LOG(3,("", " Sys info:"));
+ si = pj_get_sys_info();
+ PJ_LOG(3,("", " machine: %s", si->machine.ptr));
+ PJ_LOG(3,("", " os_name: %s", si->os_name.ptr));
+ PJ_LOG(3,("", " os_ver: 0x%x", si->os_ver));
+ PJ_LOG(3,("", " sdk_name: %s", si->sdk_name.ptr));
+ PJ_LOG(3,("", " sdk_ver: 0x%x", si->sdk_ver));
+ PJ_LOG(3,("", " info: %s", si->info.ptr));
+
+ rc = endianness_test32();
+
+ return rc;
+}
+
+#else
+int dummy_os_var;
+#endif
+
diff --git a/pjlib/src/pjlib-test/pjlib_test_reg.rss b/pjlib/src/pjlib-test/pjlib_test_reg.rss
new file mode 100644
index 0000000..dd892e4
--- /dev/null
+++ b/pjlib/src/pjlib-test/pjlib_test_reg.rss
@@ -0,0 +1,12 @@
+// Symbian application registration info for pjlib-test
+
+#include <appinfo.rh>
+
+UID2 KUidAppRegistrationResourceFile
+UID3 0xA0000002
+
+RESOURCE APP_REGISTRATION_INFO
+{
+ app_file="pjlib_test";
+}
+
diff --git a/pjlib/src/pjlib-test/pool.c b/pjlib/src/pjlib-test/pool.c
new file mode 100644
index 0000000..76ef79a
--- /dev/null
+++ b/pjlib/src/pjlib-test/pool.c
@@ -0,0 +1,321 @@
+/* $Id: pool.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/pool.h>
+#include <pj/pool_buf.h>
+#include <pj/rand.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include "test.h"
+
+/**
+ * \page page_pjlib_pool_test Test: Pool
+ *
+ * This file provides implementation of \b pool_test(). It tests the
+ * functionality of the memory pool.
+ *
+ *
+ * This file is <b>pjlib-test/pool.c</b>
+ *
+ * \include pjlib-test/pool.c
+ */
+
+
+#if INCLUDE_POOL_TEST
+
+#define SIZE 4096
+
+/* Normally we should throw exception when memory alloc fails.
+ * Here we do nothing so that the flow will go back to original caller,
+ * which will test the result using NULL comparison. Normally caller will
+ * catch the exception instead of checking for NULLs.
+ */
+static void null_callback(pj_pool_t *pool, pj_size_t size)
+{
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+}
+
+#define GET_FREE(p) (pj_pool_get_capacity(p)-pj_pool_get_used_size(p))
+
+/* Test that the capacity and used size reported by the pool is correct.
+ */
+static int capacity_test(void)
+{
+ pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback);
+ pj_size_t freesize;
+
+ PJ_LOG(3,("test", "...capacity_test()"));
+
+ if (!pool)
+ return -200;
+
+ freesize = GET_FREE(pool);
+
+ if (pj_pool_alloc(pool, freesize) == NULL) {
+ PJ_LOG(3,("test", "...error: wrong freesize %u reported",
+ freesize));
+ pj_pool_release(pool);
+ return -210;
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+/* Test that the alignment works. */
+static int pool_alignment_test(void)
+{
+ pj_pool_t *pool;
+ void *ptr;
+ enum { MEMSIZE = 64, LOOP = 100 };
+ unsigned i;
+
+ PJ_LOG(3,("test", "...alignment test"));
+
+ pool = pj_pool_create(mem, NULL, PJ_POOL_SIZE+MEMSIZE, MEMSIZE, NULL);
+ if (!pool)
+ return -300;
+
+#define IS_ALIGNED(p) ((((unsigned long)p) & (PJ_POOL_ALIGNMENT-1)) == 0)
+
+ for (i=0; i<LOOP; ++i) {
+ /* Test first allocation */
+ ptr = pj_pool_alloc(pool, 1);
+ if (!IS_ALIGNED(ptr)) {
+ pj_pool_release(pool);
+ return -310;
+ }
+
+ /* Test subsequent allocation */
+ ptr = pj_pool_alloc(pool, 1);
+ if (!IS_ALIGNED(ptr)) {
+ pj_pool_release(pool);
+ return -320;
+ }
+
+ /* Test allocation after new block is created */
+ ptr = pj_pool_alloc(pool, MEMSIZE*2+1);
+ if (!IS_ALIGNED(ptr)) {
+ pj_pool_release(pool);
+ return -330;
+ }
+
+ /* Reset the pool */
+ pj_pool_reset(pool);
+ }
+
+ /* Done */
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+/* Test that the alignment works for pool on buf. */
+static int pool_buf_alignment_test(void)
+{
+ pj_pool_t *pool;
+ char buf[512];
+ void *ptr;
+ enum { LOOP = 100 };
+ unsigned i;
+
+ PJ_LOG(3,("test", "...pool_buf alignment test"));
+
+ pool = pj_pool_create_on_buf(NULL, buf, sizeof(buf));
+ if (!pool)
+ return -400;
+
+ for (i=0; i<LOOP; ++i) {
+ /* Test first allocation */
+ ptr = pj_pool_alloc(pool, 1);
+ if (!IS_ALIGNED(ptr)) {
+ pj_pool_release(pool);
+ return -410;
+ }
+
+ /* Test subsequent allocation */
+ ptr = pj_pool_alloc(pool, 1);
+ if (!IS_ALIGNED(ptr)) {
+ pj_pool_release(pool);
+ return -420;
+ }
+
+ /* Reset the pool */
+ pj_pool_reset(pool);
+ }
+
+ /* Done */
+ return 0;
+}
+
+/* Test function to drain the pool's space.
+ */
+static int drain_test(pj_size_t size, pj_size_t increment)
+{
+ pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment,
+ &null_callback);
+ pj_size_t freesize;
+ void *p;
+ int status = 0;
+
+ PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment));
+
+ if (!pool)
+ return -10;
+
+ /* Get free size */
+ freesize = GET_FREE(pool);
+ if (freesize < 1) {
+ status=-15;
+ goto on_error;
+ }
+
+ /* Drain the pool until there's nothing left. */
+ while (freesize > 0) {
+ int size;
+
+ if (freesize > 255)
+ size = ((pj_rand() & 0x000000FF) + PJ_POOL_ALIGNMENT) &
+ ~(PJ_POOL_ALIGNMENT - 1);
+ else
+ size = freesize;
+
+ p = pj_pool_alloc(pool, size);
+ if (!p) {
+ status=-20; goto on_error;
+ }
+
+ freesize -= size;
+ }
+
+ /* Check that capacity is zero. */
+ if (GET_FREE(pool) != 0) {
+ PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)",
+ GET_FREE(pool)));
+ status=-30; goto on_error;
+ }
+
+ /* Try to allocate once more */
+ p = pj_pool_alloc(pool, 257);
+ if (!p) {
+ status=-40; goto on_error;
+ }
+
+ /* Check that capacity is NOT zero. */
+ if (GET_FREE(pool) == 0) {
+ status=-50; goto on_error;
+ }
+
+
+on_error:
+ pj_pool_release(pool);
+ return status;
+}
+
+/* Test the buffer based pool */
+static int pool_buf_test(void)
+{
+ enum { STATIC_BUF_SIZE = 40 };
+ /* 16 is the internal struct in pool_buf */
+ static char buf[ STATIC_BUF_SIZE + sizeof(pj_pool_t) +
+ sizeof(pj_pool_block) + 2 * PJ_POOL_ALIGNMENT];
+ pj_pool_t *pool;
+ void *p;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(3,("test", "...pool_buf test"));
+
+ pool = pj_pool_create_on_buf("no name", buf, sizeof(buf));
+ if (!pool)
+ return -70;
+
+ /* Drain the pool */
+ PJ_TRY {
+ if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL)
+ return -75;
+
+ if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL)
+ return -76;
+ }
+ PJ_CATCH_ANY {
+ return -77;
+ }
+ PJ_END;
+
+ /* On the next alloc, exception should be thrown */
+ PJ_TRY {
+ p = pj_pool_alloc(pool, STATIC_BUF_SIZE);
+ if (p != NULL) {
+ /* This is unexpected, the alloc should fail */
+ return -78;
+ }
+ }
+ PJ_CATCH_ANY {
+ /* This is the expected result */
+ }
+ PJ_END;
+
+ /* Done */
+ return 0;
+}
+
+
+int pool_test(void)
+{
+ enum { LOOP = 2 };
+ int loop;
+ int rc;
+
+ rc = capacity_test();
+ if (rc) return rc;
+
+ rc = pool_alignment_test();
+ if (rc) return rc;
+
+ rc = pool_buf_alignment_test();
+ if (rc) return rc;
+
+ for (loop=0; loop<LOOP; ++loop) {
+ /* Test that the pool should grow automaticly. */
+ rc = drain_test(SIZE, SIZE);
+ if (rc != 0) return rc;
+
+ /* Test situation where pool is not allowed to grow.
+ * We expect the test to return correct error.
+ */
+ rc = drain_test(SIZE, 0);
+ if (rc != -40) return rc;
+ }
+
+ rc = pool_buf_test();
+ if (rc != 0)
+ return rc;
+
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_pool_test;
+#endif /* INCLUDE_POOL_TEST */
+
diff --git a/pjlib/src/pjlib-test/pool_perf.c b/pjlib/src/pjlib-test/pool_perf.c
new file mode 100644
index 0000000..d5985cb
--- /dev/null
+++ b/pjlib/src/pjlib-test/pool_perf.c
@@ -0,0 +1,194 @@
+/* $Id: pool_perf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_POOL_PERF_TEST
+
+#include <pjlib.h>
+#include <pj/compat/malloc.h>
+
+#if !PJ_HAS_HIGH_RES_TIMER
+# error Need high resolution timer for this test.
+#endif
+
+#define THIS_FILE "test"
+
+#define LOOP 10
+#define COUNT 1024
+static unsigned sizes[COUNT];
+static char *p[COUNT];
+#define MIN_SIZE 4
+#define MAX_SIZE 512
+static unsigned total_size;
+
+
+static int pool_test_pool()
+{
+ int i;
+ pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL);
+ if (!pool)
+ return -1;
+
+ for (i=0; i<COUNT; ++i) {
+ char *p;
+ if ( (p=(char*)pj_pool_alloc(pool, sizes[i])) == NULL) {
+ PJ_LOG(3,(THIS_FILE," error: pool failed to allocate %d bytes",
+ sizes[i]));
+ pj_pool_release(pool);
+ return -1;
+ }
+ *p = '\0';
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+/* Symbian doesn't have malloc()/free(), so we use new/delete instead */
+//#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
+#if 0
+static int pool_test_malloc_free()
+{
+ int i; /* must be signed */
+
+ for (i=0; i<COUNT; ++i) {
+ p[i] = new char[sizes[i]];
+ if (!p[i]) {
+ PJ_LOG(3,(THIS_FILE," error: malloc failed to allocate %d bytes",
+ sizes[i]));
+ --i;
+ while (i >= 0) {
+ delete [] p[i];
+ --i;
+ }
+ return -1;
+ }
+ *p[i] = '\0';
+ }
+
+ for (i=0; i<COUNT; ++i) {
+ delete [] p[i];
+ }
+
+ return 0;
+}
+
+#else /* PJ_SYMBIAN */
+
+static int pool_test_malloc_free()
+{
+ int i; /* must be signed */
+
+ for (i=0; i<COUNT; ++i) {
+ p[i] = (char*)malloc(sizes[i]);
+ if (!p[i]) {
+ PJ_LOG(3,(THIS_FILE," error: malloc failed to allocate %d bytes",
+ sizes[i]));
+ --i;
+ while (i >= 0)
+ free(p[i]), --i;
+ return -1;
+ }
+ *p[i] = '\0';
+ }
+
+ for (i=0; i<COUNT; ++i) {
+ free(p[i]);
+ }
+
+ return 0;
+}
+
+#endif /* PJ_SYMBIAN */
+
+int pool_perf_test()
+{
+ unsigned i;
+ pj_uint32_t pool_time=0, malloc_time=0, pool_time2=0;
+ pj_timestamp start, end;
+ pj_uint32_t best, worst;
+
+ /* Initialize size of chunks to allocate in for the test. */
+ for (i=0; i<COUNT; ++i) {
+ unsigned aligned_size;
+ sizes[i] = MIN_SIZE + (pj_rand() % MAX_SIZE);
+ aligned_size = sizes[i];
+ if (aligned_size & (PJ_POOL_ALIGNMENT-1))
+ aligned_size = ((aligned_size + PJ_POOL_ALIGNMENT - 1)) & ~(PJ_POOL_ALIGNMENT - 1);
+ total_size += aligned_size;
+ }
+
+ /* Add some more for pool admin area */
+ total_size += 512;
+
+ PJ_LOG(3, (THIS_FILE, "Benchmarking pool.."));
+
+ /* Warmup */
+ pool_test_pool();
+ pool_test_malloc_free();
+
+ for (i=0; i<LOOP; ++i) {
+ pj_get_timestamp(&start);
+ if (pool_test_pool()) {
+ return 1;
+ }
+ pj_get_timestamp(&end);
+ pool_time += (end.u32.lo - start.u32.lo);
+
+ pj_get_timestamp(&start);
+ if (pool_test_malloc_free()) {
+ return 2;
+ }
+ pj_get_timestamp(&end);
+ malloc_time += (end.u32.lo - start.u32.lo);
+
+ pj_get_timestamp(&start);
+ if (pool_test_pool()) {
+ return 4;
+ }
+ pj_get_timestamp(&end);
+ pool_time2 += (end.u32.lo - start.u32.lo);
+ }
+
+ PJ_LOG(4,(THIS_FILE,"..LOOP count: %u",LOOP));
+ PJ_LOG(4,(THIS_FILE,"..number of alloc/dealloc per loop: %u",COUNT));
+ PJ_LOG(4,(THIS_FILE,"..pool allocation/deallocation time: %u",pool_time));
+ PJ_LOG(4,(THIS_FILE,"..malloc/free time: %u",malloc_time));
+ PJ_LOG(4,(THIS_FILE,"..pool again, second invocation: %u",pool_time2));
+
+ if (pool_time2==0) pool_time2=1;
+ if (pool_time < pool_time2)
+ best = pool_time, worst = pool_time2;
+ else
+ best = pool_time2, worst = pool_time;
+
+ /* avoid division by zero */
+ if (best==0) best=1;
+ if (worst==0) worst=1;
+
+ PJ_LOG(3, (THIS_FILE, "..pool speedup over malloc best=%dx, worst=%dx",
+ (int)(malloc_time/best),
+ (int)(malloc_time/worst)));
+ return 0;
+}
+
+
+#endif /* INCLUDE_POOL_PERF_TEST */
+
diff --git a/pjlib/src/pjlib-test/pool_wrap.cpp b/pjlib/src/pjlib-test/pool_wrap.cpp
new file mode 100644
index 0000000..f435ae8
--- /dev/null
+++ b/pjlib/src/pjlib-test/pool_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: pool_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "pool.c"
diff --git a/pjlib/src/pjlib-test/rand.c b/pjlib/src/pjlib-test/rand.c
new file mode 100644
index 0000000..3c442d2
--- /dev/null
+++ b/pjlib/src/pjlib-test/rand.c
@@ -0,0 +1,54 @@
+/* $Id: rand.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/rand.h>
+#include <pj/log.h>
+#include "test.h"
+
+#if INCLUDE_RAND_TEST
+
+#define COUNT 1024
+static int values[COUNT];
+
+/*
+ * rand_test(), simply generates COUNT number of random number and
+ * check that there's no duplicate numbers.
+ */
+int rand_test(void)
+{
+ int i;
+
+ for (i=0; i<COUNT; ++i) {
+ int j;
+
+ values[i] = pj_rand();
+ for (j=0; j<i; ++j) {
+ if (values[i] == values[j]) {
+ PJ_LOG(3,("test", "error: duplicate value %d at %d-th index",
+ values[i], i));
+ return -10;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#endif /* INCLUDE_RAND_TEST */
+
diff --git a/pjlib/src/pjlib-test/rbtree.c b/pjlib/src/pjlib-test/rbtree.c
new file mode 100644
index 0000000..601a163
--- /dev/null
+++ b/pjlib/src/pjlib-test/rbtree.c
@@ -0,0 +1,168 @@
+/* $Id: rbtree.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_RBTREE_TEST
+
+#include <pjlib.h>
+
+#define LOOP 32
+#define MIN_COUNT 64
+#define MAX_COUNT (LOOP * MIN_COUNT)
+#define STRSIZE 16
+#define THIS_FILE "rbtree_test"
+
+typedef struct node_key
+{
+ pj_uint32_t hash;
+ char str[STRSIZE];
+} node_key;
+
+static int compare_node(const node_key *k1, const node_key *k2)
+{
+ if (k1->hash == k2->hash) {
+ return strcmp(k1->str, k2->str);
+ } else {
+ return k1->hash < k2->hash ? -1 : 1;
+ }
+}
+
+void randomize_string(char *str, int len)
+{
+ int i;
+ for (i=0; i<len-1; ++i)
+ str[i] = (char)('a' + pj_rand() % 26);
+ str[len-1] = '\0';
+}
+
+static int test(void)
+{
+ pj_rbtree rb;
+ node_key *key;
+ pj_rbtree_node *node;
+ pj_pool_t *pool;
+ int err=0;
+ int count = MIN_COUNT;
+ int i;
+ unsigned size;
+
+ pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node);
+ size = MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) +
+ PJ_RBTREE_SIZE + PJ_POOL_SIZE;
+ pool = pj_pool_create( mem, "pool", size, 0, NULL);
+ if (!pool) {
+ PJ_LOG(3,("test", "...error: creating pool of %u bytes", size));
+ return -10;
+ }
+
+ key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key));
+ if (!key)
+ return -20;
+
+ node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node));
+ if (!node)
+ return -30;
+
+ for (i=0; i<LOOP; ++i) {
+ int j;
+ pj_rbtree_node *prev, *it;
+ pj_timestamp t1, t2, t_setup, t_insert, t_search, t_erase;
+
+ pj_assert(rb.size == 0);
+
+ t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0;
+
+ for (j=0; j<count; j++) {
+ randomize_string(key[j].str, STRSIZE);
+
+ pj_get_timestamp(&t1);
+ node[j].key = &key[j];
+ node[j].user_data = key[j].str;
+ key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING);
+ pj_get_timestamp(&t2);
+ t_setup.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ pj_get_timestamp(&t1);
+ pj_rbtree_insert(&rb, &node[j]);
+ pj_get_timestamp(&t2);
+ t_insert.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ pj_assert(rb.size == (unsigned)count);
+
+ // Iterate key, make sure they're sorted.
+ prev = NULL;
+ it = pj_rbtree_first(&rb);
+ while (it) {
+ if (prev) {
+ if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) {
+ ++err;
+ PJ_LOG(3, (THIS_FILE, "Error: %s >= %s",
+ (char*)prev->user_data, (char*)it->user_data));
+ }
+ }
+ prev = it;
+ it = pj_rbtree_next(&rb, it);
+ }
+
+ // Search.
+ for (j=0; j<count; j++) {
+ pj_get_timestamp(&t1);
+ it = pj_rbtree_find(&rb, &key[j]);
+ pj_get_timestamp(&t2);
+ t_search.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ pj_assert(it != NULL);
+ if (it == NULL)
+ ++err;
+ }
+
+ // Erase node.
+ for (j=0; j<count; j++) {
+ pj_get_timestamp(&t1);
+ it = pj_rbtree_erase(&rb, &node[j]);
+ pj_get_timestamp(&t2);
+ t_erase.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ PJ_LOG(4, (THIS_FILE,
+ "...count:%d, setup:%d, insert:%d, search:%d, erase:%d",
+ count,
+ t_setup.u32.lo / count, t_insert.u32.lo / count,
+ t_search.u32.lo / count, t_erase.u32.lo / count));
+
+ count = 2 * count;
+ if (count > MAX_COUNT)
+ break;
+ }
+
+ pj_pool_release(pool);
+ return err;
+}
+
+
+int rbtree_test()
+{
+ return test();
+}
+
+#endif /* INCLUDE_RBTREE_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/rtems_network_config.h b/pjlib/src/pjlib-test/rtems_network_config.h
new file mode 100644
index 0000000..b2d5652
--- /dev/null
+++ b/pjlib/src/pjlib-test/rtems_network_config.h
@@ -0,0 +1,149 @@
+/* $Id: rtems_network_config.h 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Thanks Zetron, Inc and Phil Torre <ptorre@zetron.com> for donating PJLIB
+ * port to RTEMS.
+ */
+
+/*
+ * Network configuration
+ *
+ ************************************************************
+ * EDIT THIS FILE TO REFLECT YOUR NETWORK CONFIGURATION *
+ * BEFORE RUNNING ANY RTEMS PROGRAMS WHICH USE THE NETWORK! *
+ ************************************************************
+ *
+ */
+
+#ifndef _RTEMS_NETWORKCONFIG_H_
+#define _RTEMS_NETWORKCONFIG_H_
+
+
+#define DEFAULT_IP_ADDRESS_STRING "192.168.0.2"
+#define DEFAULT_NETMASK_STRING "255.255.255.0"
+#define DEFAULT_GATEWAY_STRING "192.168.0.1"
+
+
+
+
+#ifndef RTEMS_BSP_NETWORK_DRIVER_NAME
+#warning "RTEMS_BSP_NETWORK_DRIVER_NAME is not defined"
+#define RTEMS_BSP_NETWORK_DRIVER_NAME "no_network1"
+#endif
+
+#ifndef RTEMS_BSP_NETWORK_DRIVER_ATTACH
+#warning "RTEMS_BSP_NETWORK_DRIVER_ATTACH is not defined"
+#define RTEMS_BSP_NETWORK_DRIVER_ATTACH 0
+#endif
+
+#define NETWORK_STACK_PRIORITY 128
+/* #define RTEMS_USE_BOOTP */
+
+/* #define RTEMS_USE_LOOPBACK */
+
+#include <bsp.h>
+
+/*
+ * Define RTEMS_SET_ETHERNET_ADDRESS if you want to specify the
+ * Ethernet address here. If RTEMS_SET_ETHERNET_ADDRESS is not
+ * defined the driver will choose an address.
+ */
+// NOTE: The address below is a dummy address that should only ever
+// be used for testing on a private network. DO NOT LET A PRODUCT
+// CONTAINING THIS ETHERNET ADDRESS OUT INTO THE FIELD!
+//#define RTEMS_SET_ETHERNET_ADDRESS
+#if (defined (RTEMS_SET_ETHERNET_ADDRESS))
+static char ethernet_address[6] = { 0x00, 0x80, 0x7F, 0x22, 0x61, 0x77 };
+#endif
+
+#define RTEMS_USE_LOOPBACK
+#ifdef RTEMS_USE_LOOPBACK
+/*
+ * Loopback interface
+ */
+extern int rtems_bsdnet_loopattach(struct rtems_bsdnet_ifconfig* dummy, int unused);
+static struct rtems_bsdnet_ifconfig loopback_config = {
+ "lo0", /* name */
+ rtems_bsdnet_loopattach, /* attach function */
+ NULL, /* link to next interface */
+ "127.0.0.1", /* IP address */
+ "255.0.0.0", /* IP net mask */
+};
+#endif
+
+/*
+ * Default network interface
+ */
+static struct rtems_bsdnet_ifconfig netdriver_config = {
+ RTEMS_BSP_NETWORK_DRIVER_NAME, /* name */
+ RTEMS_BSP_NETWORK_DRIVER_ATTACH, /* attach function */
+
+#ifdef RTEMS_USE_LOOPBACK
+ &loopback_config, /* link to next interface */
+#else
+ NULL, /* No more interfaces */
+#endif
+
+#if (defined (RTEMS_USE_BOOTP))
+ NULL, /* BOOTP supplies IP address */
+ NULL, /* BOOTP supplies IP net mask */
+#else
+ "192.168.0.33", /* IP address */
+ "255.255.255.0", /* IP net mask */
+#endif /* !RTEMS_USE_BOOTP */
+
+#if (defined (RTEMS_SET_ETHERNET_ADDRESS))
+ ethernet_address, /* Ethernet hardware address */
+#else
+ NULL, /* Driver supplies hardware address */
+#endif
+ 0 /* Use default driver parameters */
+};
+
+/*
+ * Network configuration
+ */
+struct rtems_bsdnet_config rtems_bsdnet_config = {
+ &netdriver_config,
+
+#if (defined (RTEMS_USE_BOOTP))
+ rtems_bsdnet_do_bootp,
+#else
+ NULL,
+#endif
+
+ NETWORK_STACK_PRIORITY, /* Default network task priority */
+ 1048576, /* Default mbuf capacity */
+ 1048576, /* Default mbuf cluster capacity */
+
+#if (!defined (RTEMS_USE_BOOTP))
+ "testnode", /* Host name */
+ "example.org", /* Domain name */
+ "192.168.6.9", /* Gateway */
+ "192.168.7.41", /* Log host */
+ {"198.137.231.1" }, /* Name server(s) */
+ {"207.202.190.162" }, /* NTP server(s) */
+#endif /* !RTEMS_USE_BOOTP */
+
+};
+
+#endif /* _RTEMS_NETWORKCONFIG_H_ */
+
diff --git a/pjlib/src/pjlib-test/select.c b/pjlib/src/pjlib-test/select.c
new file mode 100644
index 0000000..d7d891a
--- /dev/null
+++ b/pjlib/src/pjlib-test/select.c
@@ -0,0 +1,221 @@
+/* $Id: select.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_select_test Test: Socket Select()
+ *
+ * This file provides implementation of \b select_test(). It tests the
+ * functionality of the pj_sock_select() API.
+ *
+ *
+ * This file is <b>pjlib-test/select.c</b>
+ *
+ * \include pjlib-test/select.c
+ */
+
+
+#if INCLUDE_SELECT_TEST
+
+#include <pj/sock.h>
+#include <pj/sock_select.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/errno.h>
+
+enum
+{
+ READ_FDS,
+ WRITE_FDS,
+ EXCEPT_FDS
+};
+
+#define UDP_PORT 51232
+#define THIS_FILE "select_test"
+
+/*
+ * do_select()
+ *
+ * Perform pj_sock_select() and find out which sockets
+ * are signalled.
+ */
+static int do_select( pj_sock_t sock1, pj_sock_t sock2,
+ int setcount[])
+{
+ pj_fd_set_t fds[3];
+ pj_time_val timeout;
+ int i, n;
+
+ for (i=0; i<3; ++i) {
+ PJ_FD_ZERO(&fds[i]);
+ PJ_FD_SET(sock1, &fds[i]);
+ PJ_FD_SET(sock2, &fds[i]);
+ setcount[i] = 0;
+ }
+
+ timeout.sec = 1;
+ timeout.msec = 0;
+
+ n = pj_sock_select(PJ_IOQUEUE_MAX_HANDLES, &fds[0], &fds[1], &fds[2],
+ &timeout);
+ if (n < 0)
+ return n;
+ if (n == 0)
+ return 0;
+
+ for (i=0; i<3; ++i) {
+ if (PJ_FD_ISSET(sock1, &fds[i]))
+ setcount[i]++;
+ if (PJ_FD_ISSET(sock2, &fds[i]))
+ setcount[i]++;
+ }
+
+ return n;
+}
+
+/*
+ * select_test()
+ *
+ * Test main entry.
+ */
+int select_test()
+{
+ pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET;
+ pj_sockaddr_in udp_addr;
+ int status;
+ int setcount[3];
+ pj_str_t s;
+ const char data[] = "hello";
+ const int datalen = 5;
+ pj_ssize_t sent, received;
+ char buf[10];
+ pj_status_t rc;
+
+ PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()"));
+
+ // Create two UDP sockets.
+ rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket", rc);
+ status=-10; goto on_return;
+ }
+ rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp2);
+ if (udp2 == PJ_INVALID_SOCKET) {
+ app_perror("...error: unable to create socket", rc);
+ status=-20; goto on_return;
+ }
+
+ // Bind one of the UDP socket.
+ pj_bzero(&udp_addr, sizeof(udp_addr));
+ udp_addr.sin_family = pj_AF_INET();
+ udp_addr.sin_port = UDP_PORT;
+ udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+ if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) {
+ status=-30; goto on_return;
+ }
+
+ // Send data.
+ sent = datalen;
+ rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr));
+ if (rc != PJ_SUCCESS || sent != datalen) {
+ app_perror("...error: sendto() error", rc);
+ status=-40; goto on_return;
+ }
+
+ // Sleep a bit. See http://trac.pjsip.org/repos/ticket/890
+ pj_thread_sleep(10);
+
+ // Check that socket is marked as reable.
+ // Note that select() may also report that sockets are writable.
+ status = do_select(udp1, udp2, setcount);
+ if (status < 0) {
+ char errbuf[128];
+ pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf));
+ PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf));
+ status=-50; goto on_return;
+ }
+ if (status == 0) {
+ status=-60; goto on_return;
+ }
+
+ if (setcount[READ_FDS] != 1) {
+ status=-70; goto on_return;
+ }
+ if (setcount[WRITE_FDS] != 0) {
+ if (setcount[WRITE_FDS] == 2) {
+ PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets"));
+ } else {
+ status=-80; goto on_return;
+ }
+ } else {
+ PJ_LOG(3,(THIS_FILE,
+ "...info: system doesn't report writable sockets"));
+ }
+ if (setcount[EXCEPT_FDS] != 0) {
+ status=-90; goto on_return;
+ }
+
+ // Read the socket to clear readable sockets.
+ received = sizeof(buf);
+ rc = pj_sock_recv(udp2, buf, &received, 0);
+ if (rc != PJ_SUCCESS || received != 5) {
+ status=-100; goto on_return;
+ }
+
+ status = 0;
+
+ // Test timeout on the read part.
+ // This won't necessarily return zero, as select() may report that
+ // sockets are writable.
+ setcount[0] = setcount[1] = setcount[2] = 0;
+ status = do_select(udp1, udp2, setcount);
+ if (status != 0 && status != setcount[WRITE_FDS]) {
+ PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set",
+ status));
+ PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d",
+ setcount[0], setcount[1], setcount[2]));
+ status = -110; goto on_return;
+ }
+ if (setcount[READ_FDS] != 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected"));
+ status = -120; goto on_return;
+ }
+
+ status = 0;
+
+on_return:
+ if (udp1 != PJ_INVALID_SOCKET)
+ pj_sock_close(udp1);
+ if (udp2 != PJ_INVALID_SOCKET)
+ pj_sock_close(udp2);
+ return status;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_select_test;
+#endif /* INCLUDE_SELECT_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/sleep.c b/pjlib/src/pjlib-test/sleep.c
new file mode 100644
index 0000000..03bef7d
--- /dev/null
+++ b/pjlib/src/pjlib-test/sleep.c
@@ -0,0 +1,224 @@
+/* $Id: sleep.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp
+ *
+ * This file provides implementation of \b sleep_test().
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * This tests:
+ * - whether pj_thread_sleep() works.
+ * - whether pj_gettimeofday() works.
+ * - whether pj_get_timestamp() and friends works.
+ *
+ * API tested:
+ * - pj_thread_sleep()
+ * - pj_gettimeofday()
+ * - PJ_TIME_VAL_SUB()
+ * - PJ_TIME_VAL_LTE()
+ * - pj_get_timestamp()
+ * - pj_get_timestamp_freq() (implicitly)
+ * - pj_elapsed_time()
+ * - pj_elapsed_usec()
+ *
+ *
+ * This file is <b>pjlib-test/sleep.c</b>
+ *
+ * \include pjlib-test/sleep.c
+ */
+
+#if INCLUDE_SLEEP_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE "sleep_test"
+
+static int simple_sleep_test(void)
+{
+ enum { COUNT = 10 };
+ int i;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:"));
+
+ for (i=0; i<COUNT; ++i) {
+ pj_time_val tv;
+ pj_parsed_time pt;
+
+ rc = pj_thread_sleep(1000);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -10;
+ }
+
+ rc = pj_gettimeofday(&tv);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_gettimeofday()", rc);
+ return -11;
+ }
+
+ pj_time_decode(&tv, &pt);
+
+ PJ_LOG(3,(THIS_FILE,
+ "...%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ pt.year, pt.mon, pt.day,
+ pt.hour, pt.min, pt.sec, pt.msec));
+
+ }
+
+ return 0;
+}
+
+static int sleep_duration_test(void)
+{
+ enum { MIS = 20};
+ unsigned duration[] = { 2000, 1000, 500, 200, 100 };
+ unsigned i;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "..running sleep duration test"));
+
+ /* Test pj_thread_sleep() and pj_gettimeofday() */
+ for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
+ pj_time_val start, stop;
+ pj_uint32_t msec;
+
+ /* Mark start of test. */
+ rc = pj_gettimeofday(&start);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_gettimeofday()", rc);
+ return -10;
+ }
+
+ /* Sleep */
+ rc = pj_thread_sleep(duration[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -20;
+ }
+
+ /* Mark end of test. */
+ rc = pj_gettimeofday(&stop);
+
+ /* Calculate duration (store in stop). */
+ PJ_TIME_VAL_SUB(stop, start);
+
+ /* Convert to msec. */
+ msec = PJ_TIME_VAL_MSEC(stop);
+
+ /* Check if it's within range. */
+ if (msec < duration[i] * (100-MIS)/100 ||
+ msec > duration[i] * (100+MIS)/100)
+ {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: slept for %d ms instead of %d ms "
+ "(outside %d%% err window)",
+ msec, duration[i], MIS));
+ return -30;
+ }
+ }
+
+
+ /* Test pj_thread_sleep() and pj_get_timestamp() and friends */
+ for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
+ pj_time_val t1, t2;
+ pj_timestamp start, stop;
+ pj_uint32_t msec;
+
+ pj_thread_sleep(0);
+
+ /* Mark start of test. */
+ rc = pj_get_timestamp(&start);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_get_timestamp()", rc);
+ return -60;
+ }
+
+ /* ..also with gettimeofday() */
+ pj_gettimeofday(&t1);
+
+ /* Sleep */
+ rc = pj_thread_sleep(duration[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: pj_thread_sleep()", rc);
+ return -70;
+ }
+
+ /* Mark end of test. */
+ pj_get_timestamp(&stop);
+
+ /* ..also with gettimeofday() */
+ pj_gettimeofday(&t2);
+
+ /* Compare t1 and t2. */
+ if (PJ_TIME_VAL_LT(t2, t1)) {
+ PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!"));
+ return -75;
+ }
+
+ /* Get elapsed time in msec */
+ msec = pj_elapsed_msec(&start, &stop);
+
+ /* Check if it's within range. */
+ if (msec < duration[i] * (100-MIS)/100 ||
+ msec > duration[i] * (100+MIS)/100)
+ {
+ PJ_LOG(3,(THIS_FILE,
+ "...error: slept for %d ms instead of %d ms "
+ "(outside %d%% err window)",
+ msec, duration[i], MIS));
+ PJ_TIME_VAL_SUB(t2, t1);
+ PJ_LOG(3,(THIS_FILE,
+ "...info: gettimeofday() reported duration is "
+ "%d msec",
+ PJ_TIME_VAL_MSEC(t2)));
+
+ return -76;
+ }
+ }
+
+ /* All done. */
+ return 0;
+}
+
+int sleep_test()
+{
+ int rc;
+
+ rc = simple_sleep_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = sleep_duration_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sleep_test;
+#endif /* INCLUDE_SLEEP_TEST */
diff --git a/pjlib/src/pjlib-test/sock.c b/pjlib/src/pjlib-test/sock.c
new file mode 100644
index 0000000..330fb97
--- /dev/null
+++ b/pjlib/src/pjlib-test/sock.c
@@ -0,0 +1,877 @@
+/* $Id: sock.c 3841 2011-10-24 09:28:13Z ming $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjlib.h>
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_sock_test Test: Socket
+ *
+ * This file provides implementation of \b sock_test(). It tests the
+ * various aspects of the socket API.
+ *
+ * \section sock_test_scope_sec Scope of the Test
+ *
+ * The scope of the test:
+ * - verify the validity of the address structs.
+ * - verify that address manipulation API works.
+ * - simple socket creation and destruction.
+ * - simple socket send/recv and sendto/recvfrom.
+ * - UDP connect()
+ * - send/recv big data.
+ * - all for both UDP and TCP.
+ *
+ * The APIs tested in this test:
+ * - pj_inet_aton()
+ * - pj_inet_ntoa()
+ * - pj_inet_pton() (only if IPv6 is enabled)
+ * - pj_inet_ntop() (only if IPv6 is enabled)
+ * - pj_gethostname()
+ * - pj_sock_socket()
+ * - pj_sock_close()
+ * - pj_sock_send()
+ * - pj_sock_sendto()
+ * - pj_sock_recv()
+ * - pj_sock_recvfrom()
+ * - pj_sock_bind()
+ * - pj_sock_connect()
+ * - pj_sock_listen()
+ * - pj_sock_accept()
+ * - pj_gethostbyname()
+ *
+ *
+ * This file is <b>pjlib-test/sock.c</b>
+ *
+ * \include pjlib-test/sock.c
+ */
+
+#if INCLUDE_SOCK_TEST
+
+#define UDP_PORT 51234
+#define TCP_PORT (UDP_PORT+10)
+#define BIG_DATA_LEN 8192
+#define ADDRESS "127.0.0.1"
+
+static char bigdata[BIG_DATA_LEN];
+static char bigbuffer[BIG_DATA_LEN];
+
+/* Macro for checking the value of "sin_len" member of sockaddr
+ * (it must always be zero).
+ */
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+# define CHECK_SA_ZERO_LEN(addr, ret) \
+ if (((pj_addr_hdr*)(addr))->sa_zero_len != 0) \
+ return ret
+#else
+# define CHECK_SA_ZERO_LEN(addr, ret)
+#endif
+
+
+static int format_test(void)
+{
+ pj_str_t s = pj_str(ADDRESS);
+ unsigned char *p;
+ pj_in_addr addr;
+ char zero[64];
+ pj_sockaddr_in addr2;
+ const pj_str_t *hostname;
+ const unsigned char A[] = {127, 0, 0, 1};
+
+ PJ_LOG(3,("test", "...format_test()"));
+
+ /* pj_inet_aton() */
+ if (pj_inet_aton(&s, &addr) != 1)
+ return -10;
+
+ /* Check the result. */
+ p = (unsigned char*)&addr;
+ if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) {
+ PJ_LOG(3,("test", " error: mismatched address. p0=%d, p1=%d, "
+ "p2=%d, p3=%d", p[0] & 0xFF, p[1] & 0xFF,
+ p[2] & 0xFF, p[3] & 0xFF));
+ return -15;
+ }
+
+ /* pj_inet_ntoa() */
+ p = (unsigned char*) pj_inet_ntoa(addr);
+ if (!p)
+ return -20;
+
+ if (pj_strcmp2(&s, (char*)p) != 0)
+ return -22;
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
+ /* pj_inet_pton() */
+ /* pj_inet_ntop() */
+ {
+ const pj_str_t s_ipv4 = pj_str("127.0.0.1");
+ const pj_str_t s_ipv6 = pj_str("fe80::2ff:83ff:fe7c:8b42");
+ char buf_ipv4[PJ_INET_ADDRSTRLEN];
+ char buf_ipv6[PJ_INET6_ADDRSTRLEN];
+ pj_in_addr ipv4;
+ pj_in6_addr ipv6;
+
+ if (pj_inet_pton(pj_AF_INET(), &s_ipv4, &ipv4) != PJ_SUCCESS)
+ return -24;
+
+ p = (unsigned char*)&ipv4;
+ if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) {
+ return -25;
+ }
+
+ if (pj_inet_pton(pj_AF_INET6(), &s_ipv6, &ipv6) != PJ_SUCCESS)
+ return -26;
+
+ p = (unsigned char*)&ipv6;
+ if (p[0] != 0xfe || p[1] != 0x80 || p[2] != 0 || p[3] != 0 ||
+ p[4] != 0 || p[5] != 0 || p[6] != 0 || p[7] != 0 ||
+ p[8] != 0x02 || p[9] != 0xff || p[10] != 0x83 || p[11] != 0xff ||
+ p[12]!=0xfe || p[13]!=0x7c || p[14] != 0x8b || p[15]!=0x42)
+ {
+ return -27;
+ }
+
+ if (pj_inet_ntop(pj_AF_INET(), &ipv4, buf_ipv4, sizeof(buf_ipv4)) != PJ_SUCCESS)
+ return -28;
+ if (pj_stricmp2(&s_ipv4, buf_ipv4) != 0)
+ return -29;
+
+ if (pj_inet_ntop(pj_AF_INET6(), &ipv6, buf_ipv6, sizeof(buf_ipv6)) != PJ_SUCCESS)
+ return -30;
+ if (pj_stricmp2(&s_ipv6, buf_ipv6) != 0)
+ return -31;
+ }
+
+#endif /* PJ_HAS_IPV6 */
+
+ /* Test that pj_sockaddr_in_init() initialize the whole structure,
+ * including sin_zero.
+ */
+ pj_sockaddr_in_init(&addr2, 0, 1000);
+ pj_bzero(zero, sizeof(zero));
+ if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0)
+ return -35;
+
+ /* pj_gethostname() */
+ hostname = pj_gethostname();
+ if (!hostname || !hostname->ptr || !hostname->slen)
+ return -40;
+
+ PJ_LOG(3,("test", "....hostname is %.*s",
+ (int)hostname->slen, hostname->ptr));
+
+ /* pj_gethostaddr() */
+
+ /* Various constants */
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+ if (PJ_AF_INET==0xFFFF) return -5500;
+ if (PJ_AF_INET6==0xFFFF) return -5501;
+
+ /* 0xFFFF could be a valid SOL_SOCKET (e.g: on some Win or Mac) */
+ //if (PJ_SOL_SOCKET==0xFFFF) return -5503;
+
+ if (PJ_SOL_IP==0xFFFF) return -5502;
+ if (PJ_SOL_TCP==0xFFFF) return -5510;
+ if (PJ_SOL_UDP==0xFFFF) return -5520;
+ if (PJ_SOL_IPV6==0xFFFF) return -5530;
+
+ if (PJ_SO_TYPE==0xFFFF) return -5540;
+ if (PJ_SO_RCVBUF==0xFFFF) return -5550;
+ if (PJ_SO_SNDBUF==0xFFFF) return -5560;
+ if (PJ_TCP_NODELAY==0xFFFF) return -5570;
+ if (PJ_SO_REUSEADDR==0xFFFF) return -5580;
+
+ if (PJ_MSG_OOB==0xFFFF) return -5590;
+ if (PJ_MSG_PEEK==0xFFFF) return -5600;
+#endif
+
+ return 0;
+}
+
+static int parse_test(void)
+{
+#define IPv4 1
+#define IPv6 2
+
+ struct test_t {
+ const char *input;
+ int result_af;
+ const char *result_ip;
+ pj_uint16_t result_port;
+ };
+ struct test_t valid_tests[] =
+ {
+ /* IPv4 */
+ { "10.0.0.1:80", IPv4, "10.0.0.1", 80},
+ { "10.0.0.1", IPv4, "10.0.0.1", 0},
+ { "10.0.0.1:", IPv4, "10.0.0.1", 0},
+ { "10.0.0.1:0", IPv4, "10.0.0.1", 0},
+ { ":80", IPv4, "0.0.0.0", 80},
+ { ":", IPv4, "0.0.0.0", 0},
+#if !PJ_SYMBIAN
+ { "localhost", IPv4, "127.0.0.1", 0},
+ { "localhost:", IPv4, "127.0.0.1", 0},
+ { "localhost:80", IPv4, "127.0.0.1", 80},
+#endif
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
+ { "fe::01:80", IPv6, "fe::01:80", 0},
+ { "[fe::01]:80", IPv6, "fe::01", 80},
+ { "fe::01", IPv6, "fe::01", 0},
+ { "[fe::01]", IPv6, "fe::01", 0},
+ { "fe::01:", IPv6, "fe::01", 0},
+ { "[fe::01]:", IPv6, "fe::01", 0},
+ { "::", IPv6, "::0", 0},
+ { "[::]", IPv6, "::", 0},
+ { ":::", IPv6, "::", 0},
+ { "[::]:", IPv6, "::", 0},
+ { ":::80", IPv6, "::", 80},
+ { "[::]:80", IPv6, "::", 80},
+#endif
+ };
+ struct test_t invalid_tests[] =
+ {
+ /* IPv4 */
+ { "10.0.0.1:abcd", IPv4}, /* port not numeric */
+ { "10.0.0.1:-1", IPv4}, /* port contains illegal character */
+ { "10.0.0.1:123456", IPv4}, /* port too big */
+ { "1.2.3.4.5:80", IPv4}, /* invalid IP */
+ { "10:0:80", IPv4}, /* hostname has colon */
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
+ { "[fe::01]:abcd", IPv6}, /* port not numeric */
+ { "[fe::01]:-1", IPv6}, /* port contains illegal character */
+ { "[fe::01]:123456", IPv6}, /* port too big */
+ { "fe::01:02::03:04:80", IPv6}, /* invalid IP */
+ { "[fe::01:02::03:04]:80", IPv6}, /* invalid IP */
+ { "[fe:01", IPv6}, /* Unterminated bracket */
+#endif
+ };
+
+ unsigned i;
+
+ PJ_LOG(3,("test", "...IP address parsing"));
+
+ for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) {
+ pj_status_t status;
+ pj_str_t input;
+ pj_sockaddr addr, result;
+
+ switch (valid_tests[i].result_af) {
+ case IPv4:
+ valid_tests[i].result_af = PJ_AF_INET;
+ break;
+ case IPv6:
+ valid_tests[i].result_af = PJ_AF_INET6;
+ break;
+ default:
+ pj_assert(!"Invalid AF!");
+ continue;
+ }
+
+ /* Try parsing with PJ_AF_UNSPEC */
+ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0,
+ pj_cstr(&input, valid_tests[i].input),
+ &addr);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,("test", ".... failed when parsing %s (i=%d)",
+ valid_tests[i].input, i));
+ return -10;
+ }
+
+ /* Check "sin_len" member of parse result */
+ CHECK_SA_ZERO_LEN(&addr, -20);
+
+ /* Build the correct result */
+ status = pj_sockaddr_init(valid_tests[i].result_af,
+ &result,
+ pj_cstr(&input, valid_tests[i].result_ip),
+ valid_tests[i].result_port);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,("test", ".... error building IP address %s",
+ valid_tests[i].input));
+ return -30;
+ }
+
+ /* Compare the result */
+ if (pj_sockaddr_cmp(&addr, &result) != 0) {
+ PJ_LOG(1,("test", ".... parsed result mismatched for %s",
+ valid_tests[i].input));
+ return -40;
+ }
+
+ /* Parse again with the specified af */
+ status = pj_sockaddr_parse(valid_tests[i].result_af, 0,
+ pj_cstr(&input, valid_tests[i].input),
+ &addr);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(1,("test", ".... failed when parsing %s",
+ valid_tests[i].input));
+ return -50;
+ }
+
+ /* Check "sin_len" member of parse result */
+ CHECK_SA_ZERO_LEN(&addr, -55);
+
+ /* Compare the result again */
+ if (pj_sockaddr_cmp(&addr, &result) != 0) {
+ PJ_LOG(1,("test", ".... parsed result mismatched for %s",
+ valid_tests[i].input));
+ return -60;
+ }
+ }
+
+ for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) {
+ pj_status_t status;
+ pj_str_t input;
+ pj_sockaddr addr;
+
+ switch (invalid_tests[i].result_af) {
+ case IPv4:
+ invalid_tests[i].result_af = PJ_AF_INET;
+ break;
+ case IPv6:
+ invalid_tests[i].result_af = PJ_AF_INET6;
+ break;
+ default:
+ pj_assert(!"Invalid AF!");
+ continue;
+ }
+
+ /* Try parsing with PJ_AF_UNSPEC */
+ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0,
+ pj_cstr(&input, invalid_tests[i].input),
+ &addr);
+ if (status == PJ_SUCCESS) {
+ PJ_LOG(1,("test", ".... expecting failure when parsing %s",
+ invalid_tests[i].input));
+ return -100;
+ }
+ }
+
+ return 0;
+}
+
+static int purity_test(void)
+{
+ PJ_LOG(3,("test", "...purity_test()"));
+
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+ /* Check on "sin_len" member of sockaddr */
+ {
+ const pj_str_t str_ip = {"1.1.1.1", 7};
+ pj_sockaddr addr[16];
+ pj_addrinfo ai[16];
+ unsigned cnt;
+ pj_status_t rc;
+
+ /* pj_enum_ip_interface() */
+ cnt = PJ_ARRAY_SIZE(addr);
+ rc = pj_enum_ip_interface(pj_AF_UNSPEC(), &cnt, addr);
+ if (rc == PJ_SUCCESS) {
+ while (cnt--)
+ CHECK_SA_ZERO_LEN(&addr[cnt], -10);
+ }
+
+ /* pj_gethostip() on IPv4 */
+ rc = pj_gethostip(pj_AF_INET(), &addr[0]);
+ if (rc == PJ_SUCCESS)
+ CHECK_SA_ZERO_LEN(&addr[0], -20);
+
+ /* pj_gethostip() on IPv6 */
+ rc = pj_gethostip(pj_AF_INET6(), &addr[0]);
+ if (rc == PJ_SUCCESS)
+ CHECK_SA_ZERO_LEN(&addr[0], -30);
+
+ /* pj_getdefaultipinterface() on IPv4 */
+ rc = pj_getdefaultipinterface(pj_AF_INET(), &addr[0]);
+ if (rc == PJ_SUCCESS)
+ CHECK_SA_ZERO_LEN(&addr[0], -40);
+
+ /* pj_getdefaultipinterface() on IPv6 */
+ rc = pj_getdefaultipinterface(pj_AF_INET6(), &addr[0]);
+ if (rc == PJ_SUCCESS)
+ CHECK_SA_ZERO_LEN(&addr[0], -50);
+
+ /* pj_getaddrinfo() on a host name */
+ cnt = PJ_ARRAY_SIZE(ai);
+ rc = pj_getaddrinfo(pj_AF_UNSPEC(), pj_gethostname(), &cnt, ai);
+ if (rc == PJ_SUCCESS) {
+ while (cnt--)
+ CHECK_SA_ZERO_LEN(&ai[cnt].ai_addr, -60);
+ }
+
+ /* pj_getaddrinfo() on an IP address */
+ cnt = PJ_ARRAY_SIZE(ai);
+ rc = pj_getaddrinfo(pj_AF_UNSPEC(), &str_ip, &cnt, ai);
+ if (rc == PJ_SUCCESS) {
+ pj_assert(cnt == 1);
+ CHECK_SA_ZERO_LEN(&ai[0].ai_addr, -70);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static int simple_sock_test(void)
+{
+ int types[2];
+ pj_sock_t sock;
+ int i;
+ pj_status_t rc = PJ_SUCCESS;
+
+ types[0] = pj_SOCK_STREAM();
+ types[1] = pj_SOCK_DGRAM();
+
+ PJ_LOG(3,("test", "...simple_sock_test()"));
+
+ for (i=0; i<(int)(sizeof(types)/sizeof(types[0])); ++i) {
+
+ rc = pj_sock_socket(pj_AF_INET(), types[i], 0, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create socket", rc);
+ break;
+ } else {
+ rc = pj_sock_close(sock);
+ if (rc != 0) {
+ app_perror("...error: close socket", rc);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int send_recv_test(int sock_type,
+ pj_sock_t ss, pj_sock_t cs,
+ pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr,
+ int addrlen)
+{
+ enum { DATA_LEN = 16 };
+ char senddata[DATA_LEN+4], recvdata[DATA_LEN+4];
+ pj_ssize_t sent, received, total_received;
+ pj_status_t rc;
+
+ TRACE_(("test", "....create_random_string()"));
+ pj_create_random_string(senddata, DATA_LEN);
+ senddata[DATA_LEN-1] = '\0';
+
+ /*
+ * Test send/recv small data.
+ */
+ TRACE_(("test", "....sendto()"));
+ if (dstaddr) {
+ sent = DATA_LEN;
+ rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen);
+ if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+ app_perror("...sendto error", rc);
+ rc = -140; goto on_error;
+ }
+ } else {
+ sent = DATA_LEN;
+ rc = pj_sock_send(cs, senddata, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+ app_perror("...send error", rc);
+ rc = -145; goto on_error;
+ }
+ }
+
+ TRACE_(("test", "....recv()"));
+ if (srcaddr) {
+ pj_sockaddr_in addr;
+ int srclen = sizeof(addr);
+
+ pj_bzero(&addr, sizeof(addr));
+
+ received = DATA_LEN;
+ rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen);
+ if (rc != PJ_SUCCESS || received != DATA_LEN) {
+ app_perror("...recvfrom error", rc);
+ rc = -150; goto on_error;
+ }
+ if (srclen != addrlen)
+ return -151;
+ if (pj_sockaddr_cmp(&addr, srcaddr) != 0) {
+ char srcaddr_str[32], addr_str[32];
+ strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr));
+ strcpy(addr_str, pj_inet_ntoa(addr.sin_addr));
+ PJ_LOG(3,("test", "...error: src address mismatch (original=%s, "
+ "recvfrom addr=%s)",
+ srcaddr_str, addr_str));
+ return -152;
+ }
+
+ } else {
+ /* Repeat recv() until all data is received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be received in one packet.
+ */
+ total_received = 0;
+ do {
+ received = DATA_LEN-total_received;
+ rc = pj_sock_recv(ss, recvdata+total_received, &received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ rc = -155; goto on_error;
+ }
+ if (received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+ received));
+ rc = -156; goto on_error;
+ }
+ if (received != DATA_LEN-total_received) {
+ if (sock_type != pj_SOCK_STREAM()) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ DATA_LEN-total_received, received));
+ rc = -157; goto on_error;
+ }
+ }
+ total_received += received;
+ } while (total_received < DATA_LEN);
+ }
+
+ TRACE_(("test", "....memcmp()"));
+ if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) {
+ PJ_LOG(3,("","...error: received data mismatch "
+ "(got:'%s' expecting:'%s'",
+ recvdata, senddata));
+ rc = -160; goto on_error;
+ }
+
+ /*
+ * Test send/recv big data.
+ */
+ TRACE_(("test", "....sendto()"));
+ if (dstaddr) {
+ sent = BIG_DATA_LEN;
+ rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen);
+ if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+ app_perror("...sendto error", rc);
+ rc = -161; goto on_error;
+ }
+ } else {
+ sent = BIG_DATA_LEN;
+ rc = pj_sock_send(cs, bigdata, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+ app_perror("...send error", rc);
+ rc = -165; goto on_error;
+ }
+ }
+
+ TRACE_(("test", "....recv()"));
+
+ /* Repeat recv() until all data is received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be received in one packet.
+ */
+ total_received = 0;
+ do {
+ received = BIG_DATA_LEN-total_received;
+ rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ rc = -170; goto on_error;
+ }
+ if (received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+ received));
+ rc = -173; goto on_error;
+ }
+ if (received != BIG_DATA_LEN-total_received) {
+ if (sock_type != pj_SOCK_STREAM()) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ BIG_DATA_LEN-total_received, received));
+ rc = -176; goto on_error;
+ }
+ }
+ total_received += received;
+ } while (total_received < BIG_DATA_LEN);
+
+ TRACE_(("test", "....memcmp()"));
+ if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) {
+ PJ_LOG(3,("", "...error: received data has been altered!"));
+ rc = -180; goto on_error;
+ }
+
+ rc = 0;
+
+on_error:
+ return rc;
+}
+
+static int udp_test(void)
+{
+ pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET;
+ pj_sockaddr_in dstaddr, srcaddr;
+ pj_str_t s;
+ pj_status_t rc = 0, retval;
+
+ PJ_LOG(3,("test", "...udp_test()"));
+
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ss);
+ if (rc != 0) {
+ app_perror("...error: unable to create socket", rc);
+ return -100;
+ }
+
+ rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &cs);
+ if (rc != 0)
+ return -110;
+
+ /* Bind server socket. */
+ pj_bzero(&dstaddr, sizeof(dstaddr));
+ dstaddr.sin_family = pj_AF_INET();
+ dstaddr.sin_port = pj_htons(UDP_PORT);
+ dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS));
+
+ if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) {
+ app_perror("...bind error udp:"ADDRESS, rc);
+ rc = -120; goto on_error;
+ }
+
+ /* Bind client socket. */
+ pj_bzero(&srcaddr, sizeof(srcaddr));
+ srcaddr.sin_family = pj_AF_INET();
+ srcaddr.sin_port = pj_htons(UDP_PORT-1);
+ srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS));
+
+ if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) {
+ app_perror("...bind error", rc);
+ rc = -121; goto on_error;
+ }
+
+ /* Test send/recv, with sendto */
+ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, NULL,
+ sizeof(dstaddr));
+ if (rc != 0)
+ goto on_error;
+
+ /* Test send/recv, with sendto and recvfrom */
+ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr,
+ &srcaddr, sizeof(dstaddr));
+ if (rc != 0)
+ goto on_error;
+
+ /* Disable this test on Symbian since UDP connect()/send() failed
+ * with S60 3rd edition (including MR2).
+ * See http://www.pjsip.org/trac/ticket/264
+ */
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+ /* connect() the sockets. */
+ rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr));
+ if (rc != 0) {
+ app_perror("...connect() error", rc);
+ rc = -122; goto on_error;
+ }
+
+ /* Test send/recv with send() */
+ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, NULL, 0);
+ if (rc != 0)
+ goto on_error;
+
+ /* Test send/recv with send() and recvfrom */
+ rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, &srcaddr,
+ sizeof(srcaddr));
+ if (rc != 0)
+ goto on_error;
+#endif
+
+on_error:
+ retval = rc;
+ if (cs != PJ_INVALID_SOCKET) {
+ rc = pj_sock_close(cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -1000;
+ }
+ }
+ if (ss != PJ_INVALID_SOCKET) {
+ rc = pj_sock_close(ss);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -1010;
+ }
+ }
+
+ return retval;
+}
+
+static int tcp_test(void)
+{
+ pj_sock_t cs, ss;
+ pj_status_t rc = 0, retval;
+
+ PJ_LOG(3,("test", "...tcp_test()"));
+
+ rc = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ss, &cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: app_socketpair():", rc);
+ return -2000;
+ }
+
+ /* Test send/recv with send() and recv() */
+ retval = send_recv_test(pj_SOCK_STREAM(), ss, cs, NULL, NULL, 0);
+
+ rc = pj_sock_close(cs);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -2000;
+ }
+
+ rc = pj_sock_close(ss);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in closing socket", rc);
+ return -2010;
+ }
+
+ return retval;
+}
+
+static int ioctl_test(void)
+{
+ return 0;
+}
+
+static int gethostbyname_test(void)
+{
+ pj_str_t host;
+ pj_hostent he;
+ pj_status_t status;
+
+ /* Testing pj_gethostbyname() with invalid host */
+ host = pj_str("an-invalid-host-name");
+ status = pj_gethostbyname(&host, &he);
+
+ /* Must return failure! */
+ if (status == PJ_SUCCESS)
+ return -20100;
+ else
+ return 0;
+}
+
+#if 0
+#include "../pj/os_symbian.h"
+static int connect_test()
+{
+ RSocketServ rSockServ;
+ RSocket rSock;
+ TInetAddr inetAddr;
+ TRequestStatus reqStatus;
+ char buffer[16];
+ TPtrC8 data((const TUint8*)buffer, (TInt)sizeof(buffer));
+ int rc;
+
+ rc = rSockServ.Connect();
+ if (rc != KErrNone)
+ return rc;
+
+ rc = rSock.Open(rSockServ, KAfInet, KSockDatagram, KProtocolInetUdp);
+ if (rc != KErrNone)
+ {
+ rSockServ.Close();
+ return rc;
+ }
+
+ inetAddr.Init(KAfInet);
+ inetAddr.Input(_L("127.0.0.1"));
+ inetAddr.SetPort(80);
+
+ rSock.Connect(inetAddr, reqStatus);
+ User::WaitForRequest(reqStatus);
+
+ if (reqStatus != KErrNone) {
+ rSock.Close();
+ rSockServ.Close();
+ return rc;
+ }
+
+ rSock.Send(data, 0, reqStatus);
+ User::WaitForRequest(reqStatus);
+
+ if (reqStatus!=KErrNone) {
+ rSock.Close();
+ rSockServ.Close();
+ return rc;
+ }
+
+ rSock.Close();
+ rSockServ.Close();
+ return KErrNone;
+}
+#endif
+
+int sock_test()
+{
+ int rc;
+
+ pj_create_random_string(bigdata, BIG_DATA_LEN);
+
+// Enable this to demonstrate the error witn S60 3rd Edition MR2
+#if 0
+ rc = connect_test();
+ if (rc != 0)
+ return rc;
+#endif
+
+ rc = format_test();
+ if (rc != 0)
+ return rc;
+
+ rc = parse_test();
+ if (rc != 0)
+ return rc;
+
+ rc = purity_test();
+ if (rc != 0)
+ return rc;
+
+ rc = gethostbyname_test();
+ if (rc != 0)
+ return rc;
+
+ rc = simple_sock_test();
+ if (rc != 0)
+ return rc;
+
+ rc = ioctl_test();
+ if (rc != 0)
+ return rc;
+
+ rc = udp_test();
+ if (rc != 0)
+ return rc;
+
+ rc = tcp_test();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sock_test;
+#endif /* INCLUDE_SOCK_TEST */
+
diff --git a/pjlib/src/pjlib-test/sock_perf.c b/pjlib/src/pjlib-test/sock_perf.c
new file mode 100644
index 0000000..3225bcd
--- /dev/null
+++ b/pjlib/src/pjlib-test/sock_perf.c
@@ -0,0 +1,190 @@
+/* $Id: sock_perf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+
+/**
+ * \page page_pjlib_sock_perf_test Test: Socket Performance
+ *
+ * Test the performance of the socket communication. This will perform
+ * simple producer-consumer type of test, where we calculate how long
+ * does it take to send certain number of packets from producer to
+ * consumer.
+ *
+ * This file is <b>pjlib-test/sock_perf.c</b>
+ *
+ * \include pjlib-test/sock_perf.c
+ */
+
+#if INCLUDE_SOCK_PERF_TEST
+
+/*
+ * sock_producer_consumer()
+ *
+ * Simple producer-consumer benchmarking. Send loop number of
+ * buf_size size packets as fast as possible.
+ */
+static int sock_producer_consumer(int sock_type,
+ unsigned buf_size,
+ unsigned loop,
+ unsigned *p_bandwidth)
+{
+ pj_sock_t consumer, producer;
+ pj_pool_t *pool;
+ char *outgoing_buffer, *incoming_buffer;
+ pj_timestamp start, stop;
+ unsigned i;
+ pj_highprec_t elapsed, bandwidth;
+ pj_size_t total_received;
+ pj_status_t rc;
+
+ /* Create pool. */
+ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+ if (!pool)
+ return -10;
+
+ /* Create producer-consumer pair. */
+ rc = app_socketpair(pj_AF_INET(), sock_type, 0, &consumer, &producer);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: create socket pair", rc);
+ return -20;
+ }
+
+ /* Create buffers. */
+ outgoing_buffer = (char*) pj_pool_alloc(pool, buf_size);
+ incoming_buffer = (char*) pj_pool_alloc(pool, buf_size);
+
+ /* Start loop. */
+ pj_get_timestamp(&start);
+ total_received = 0;
+ for (i=0; i<loop; ++i) {
+ pj_ssize_t sent, part_received, received;
+ pj_time_val delay;
+
+ sent = buf_size;
+ rc = pj_sock_send(producer, outgoing_buffer, &sent, 0);
+ if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) {
+ app_perror("...error: send()", rc);
+ return -61;
+ }
+
+ /* Repeat recv() until all data is part_received.
+ * This applies only for non-UDP of course, since for UDP
+ * we would expect all data to be part_received in one packet.
+ */
+ received = 0;
+ do {
+ part_received = buf_size-received;
+ rc = pj_sock_recv(consumer, incoming_buffer+received,
+ &part_received, 0);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...recv error", rc);
+ return -70;
+ }
+ if (part_received <= 0) {
+ PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!",
+ part_received));
+ return -73;
+ }
+ if ((pj_size_t)part_received != buf_size-received) {
+ if (sock_type != pj_SOCK_STREAM()) {
+ PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+ buf_size-received, part_received));
+ return -76;
+ }
+ }
+ received += part_received;
+ } while ((pj_size_t)received < buf_size);
+
+ total_received += received;
+
+ /* Stop test if it's been runnign for more than 10 secs. */
+ pj_get_timestamp(&stop);
+ delay = pj_elapsed_time(&start, &stop);
+ if (delay.sec > 10)
+ break;
+ }
+
+ /* Stop timer. */
+ pj_get_timestamp(&stop);
+
+ elapsed = pj_elapsed_usec(&start, &stop);
+
+ /* bandwidth = total_received * 1000 / elapsed */
+ bandwidth = total_received;
+ pj_highprec_mul(bandwidth, 1000);
+ pj_highprec_div(bandwidth, elapsed);
+
+ *p_bandwidth = (pj_uint32_t)bandwidth;
+
+ /* Close sockets. */
+ pj_sock_close(consumer);
+ pj_sock_close(producer);
+
+ /* Done */
+ pj_pool_release(pool);
+
+ return 0;
+}
+
+/*
+ * sock_perf_test()
+ *
+ * Main test entry.
+ */
+int sock_perf_test(void)
+{
+ enum { LOOP = 64 * 1024 };
+ int rc;
+ unsigned bandwidth;
+
+ PJ_LOG(3,("", "...benchmarking socket "
+ "(2 sockets, packet=512, single threaded):"));
+
+ /* Disable this test on Symbian since UDP connect()/send() failed
+ * with S60 3rd edition (including MR2).
+ * See http://www.pjsip.org/trac/ticket/264
+ */
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+ /* Benchmarking UDP */
+ rc = sock_producer_consumer(pj_SOCK_DGRAM(), 512, LOOP, &bandwidth);
+ if (rc != 0) return rc;
+ PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth));
+#endif
+
+ /* Benchmarking TCP */
+ rc = sock_producer_consumer(pj_SOCK_STREAM(), 512, LOOP, &bandwidth);
+ if (rc != 0) return rc;
+ PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth));
+
+ return rc;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_sock_perf_test;
+#endif /* INCLUDE_SOCK_PERF_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c
new file mode 100644
index 0000000..d05a4db
--- /dev/null
+++ b/pjlib/src/pjlib-test/ssl_sock.c
@@ -0,0 +1,1424 @@
+/* $Id: ssl_sock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+
+#define CERT_DIR "../build/"
+#define CERT_CA_FILE CERT_DIR "cacert.pem"
+#define CERT_FILE CERT_DIR "cacert.pem"
+#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem"
+#define CERT_PRIVKEY_PASS ""
+
+
+#if INCLUDE_SSLSOCK_TEST
+
+/* Global vars */
+static int clients_num;
+
+struct send_key {
+ pj_ioqueue_op_key_t op_key;
+};
+
+
+static int get_cipher_list(void) {
+ pj_status_t status;
+ pj_ssl_cipher ciphers[100];
+ unsigned cipher_num;
+ unsigned i;
+
+ cipher_num = PJ_ARRAY_SIZE(ciphers);
+ status = pj_ssl_cipher_get_availables(ciphers, &cipher_num);
+ if (status != PJ_SUCCESS) {
+ app_perror("...FAILED to get available ciphers", status);
+ return status;
+ }
+
+ PJ_LOG(3, ("", "...Found %u ciphers:", cipher_num));
+ for (i = 0; i < cipher_num; ++i) {
+ const char* st;
+ st = pj_ssl_cipher_name(ciphers[i]);
+ if (st == NULL)
+ st = "[Unknown]";
+
+ PJ_LOG(3, ("", "...%3u: 0x%08x=%s", i+1, ciphers[i], st));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+struct test_state
+{
+ pj_pool_t *pool; /* pool */
+ pj_ioqueue_t *ioqueue; /* ioqueue */
+ pj_bool_t is_server; /* server role flag */
+ pj_bool_t is_verbose; /* verbose flag, e.g: cert info */
+ pj_bool_t echo; /* echo received data */
+ pj_status_t err; /* error flag */
+ unsigned sent; /* bytes sent */
+ unsigned recv; /* bytes received */
+ pj_uint8_t read_buf[256]; /* read buffer */
+ pj_bool_t done; /* test done flag */
+ char *send_str; /* data to send once connected */
+ unsigned send_str_len; /* send data length */
+ pj_bool_t check_echo; /* flag to compare sent & echoed data */
+ const char *check_echo_ptr; /* pointer/cursor for comparing data */
+ struct send_key send_key; /* send op key */
+};
+
+static void dump_ssl_info(const pj_ssl_sock_info *si)
+{
+ const char *tmp_st;
+
+ /* Print cipher name */
+ tmp_st = pj_ssl_cipher_name(si->cipher);
+ if (tmp_st == NULL)
+ tmp_st = "[Unknown]";
+ PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+
+ /* Print remote certificate info and verification result */
+ if (si->remote_cert_info && si->remote_cert_info->subject.info.slen)
+ {
+ char buf[2048];
+ const char *verif_msgs[32];
+ unsigned verif_msg_cnt;
+
+ /* Dump remote TLS certificate info */
+ PJ_LOG(3, ("", ".....Remote certificate info:"));
+ pj_ssl_cert_info_dump(si->remote_cert_info, " ", buf, sizeof(buf));
+ PJ_LOG(3,("", "\n%s", buf));
+
+ /* Dump remote TLS certificate verification result */
+ verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
+ pj_ssl_cert_get_verify_status_strings(si->verify_status,
+ verif_msgs, &verif_msg_cnt);
+ PJ_LOG(3,("", ".....Remote certificate verification result: %s",
+ (verif_msg_cnt == 1? verif_msgs[0]:"")));
+ if (verif_msg_cnt > 1) {
+ unsigned i;
+ for (i = 0; i < verif_msg_cnt; ++i)
+ PJ_LOG(3,("", "..... - %s", verif_msgs[i]));
+ }
+ }
+}
+
+
+static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
+ pj_status_t status)
+{
+ struct test_state *st = (struct test_state*)
+ pj_ssl_sock_get_user_data(ssock);
+ void *read_buf[1];
+ pj_ssl_sock_info info;
+ char buf1[64], buf2[64];
+
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR ssl_on_connect_complete()", status);
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_get_info(ssock, &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_get_info()", status);
+ goto on_return;
+ }
+
+ pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf1, sizeof(buf1), 1);
+ pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
+ PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
+
+ if (st->is_verbose)
+ dump_ssl_info(&info);
+
+ /* Start reading data */
+ read_buf[0] = st->read_buf;
+ status = pj_ssl_sock_start_read2(ssock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+ goto on_return;
+ }
+
+ /* Start sending data */
+ while (st->sent < st->send_str_len) {
+ pj_ssize_t size;
+
+ size = st->send_str_len - st->sent;
+ status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key,
+ st->send_str + st->sent, &size, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR pj_ssl_sock_send()", status);
+ goto on_return;
+ }
+
+ if (status == PJ_SUCCESS)
+ st->sent += size;
+ else
+ break;
+ }
+
+on_return:
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS) {
+ pj_ssl_sock_close(ssock);
+ clients_num--;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+
+static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
+ pj_ssl_sock_t *newsock,
+ const pj_sockaddr_t *src_addr,
+ int src_addr_len)
+{
+ struct test_state *parent_st = (struct test_state*)
+ pj_ssl_sock_get_user_data(ssock);
+ struct test_state *st;
+ void *read_buf[1];
+ pj_ssl_sock_info info;
+ char buf[64];
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(src_addr_len);
+
+ /* Duplicate parent test state to newly accepted test state */
+ st = (struct test_state*)pj_pool_zalloc(parent_st->pool, sizeof(struct test_state));
+ *st = *parent_st;
+ pj_ssl_sock_set_user_data(newsock, st);
+
+ status = pj_ssl_sock_get_info(newsock, &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_get_info()", status);
+ goto on_return;
+ }
+
+ pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
+ PJ_LOG(3, ("", "...Accepted connection from %s", buf));
+
+ if (st->is_verbose)
+ dump_ssl_info(&info);
+
+ /* Start reading data */
+ read_buf[0] = st->read_buf;
+ status = pj_ssl_sock_start_read2(newsock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+ goto on_return;
+ }
+
+ /* Start sending data */
+ while (st->sent < st->send_str_len) {
+ pj_ssize_t size;
+
+ size = st->send_str_len - st->sent;
+ status = pj_ssl_sock_send(newsock, (pj_ioqueue_op_key_t*)&st->send_key,
+ st->send_str + st->sent, &size, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR pj_ssl_sock_send()", status);
+ goto on_return;
+ }
+
+ if (status == PJ_SUCCESS)
+ st->sent += size;
+ else
+ break;
+ }
+
+on_return:
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS) {
+ pj_ssl_sock_close(newsock);
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_bool_t ssl_on_data_read(pj_ssl_sock_t *ssock,
+ void *data,
+ pj_size_t size,
+ pj_status_t status,
+ pj_size_t *remainder)
+{
+ struct test_state *st = (struct test_state*)
+ pj_ssl_sock_get_user_data(ssock);
+
+ PJ_UNUSED_ARG(remainder);
+ PJ_UNUSED_ARG(data);
+
+ if (size > 0) {
+ pj_size_t consumed;
+
+ /* Set random remainder */
+ *remainder = pj_rand() % 100;
+
+ /* Apply zero remainder if:
+ * - remainder is less than size, or
+ * - connection closed/error
+ * - echo/check_eco set
+ */
+ if (*remainder > size || status != PJ_SUCCESS || st->echo || st->check_echo)
+ *remainder = 0;
+
+ consumed = size - *remainder;
+ st->recv += consumed;
+
+ //printf("%.*s", consumed, (char*)data);
+
+ pj_memmove(data, (char*)data + consumed, *remainder);
+
+ /* Echo data when specified to */
+ if (st->echo) {
+ pj_ssize_t size_ = consumed;
+ status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, data, &size_, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR pj_ssl_sock_send()", status);
+ goto on_return;
+ }
+
+ if (status == PJ_SUCCESS)
+ st->sent += size_;
+ }
+
+ /* Verify echoed data when specified to */
+ if (st->check_echo) {
+ if (!st->check_echo_ptr)
+ st->check_echo_ptr = st->send_str;
+
+ if (pj_memcmp(st->check_echo_ptr, data, consumed)) {
+ status = PJ_EINVAL;
+ app_perror("...ERROR echoed data not exact", status);
+ goto on_return;
+ }
+ st->check_echo_ptr += consumed;
+
+ /* Echo received completely */
+ if (st->send_str_len == st->recv) {
+ pj_ssl_sock_info info;
+ char buf[64];
+
+ status = pj_ssl_sock_get_info(ssock, &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_get_info()", status);
+ goto on_return;
+ }
+
+ pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf, sizeof(buf), 1);
+ PJ_LOG(3, ("", "...%s successfully recv %d bytes echo", buf, st->recv));
+ st->done = PJ_TRUE;
+ }
+ }
+ }
+
+ if (status != PJ_SUCCESS) {
+ if (status == PJ_EEOF) {
+ status = PJ_SUCCESS;
+ st->done = PJ_TRUE;
+ } else {
+ app_perror("...ERROR ssl_on_data_read()", status);
+ }
+ }
+
+on_return:
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS || st->done) {
+ pj_ssl_sock_close(ssock);
+ if (!st->is_server)
+ clients_num--;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_bool_t ssl_on_data_sent(pj_ssl_sock_t *ssock,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t sent)
+{
+ struct test_state *st = (struct test_state*)
+ pj_ssl_sock_get_user_data(ssock);
+ PJ_UNUSED_ARG(op_key);
+
+ if (sent < 0) {
+ st->err = -sent;
+ } else {
+ st->sent += sent;
+
+ /* Send more if any */
+ while (st->sent < st->send_str_len) {
+ pj_ssize_t size;
+ pj_status_t status;
+
+ size = st->send_str_len - st->sent;
+ status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key,
+ st->send_str + st->sent, &size, 0);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror("...ERROR pj_ssl_sock_send()", status);
+ st->err = status;
+ break;
+ }
+
+ if (status == PJ_SUCCESS)
+ st->sent += size;
+ else
+ break;
+ }
+ }
+
+ if (st->err != PJ_SUCCESS) {
+ pj_ssl_sock_close(ssock);
+ if (!st->is_server)
+ clients_num--;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+#define HTTP_REQ "GET / HTTP/1.0\r\n\r\n";
+#define HTTP_SERVER_ADDR "trac.pjsip.org"
+#define HTTP_SERVER_PORT 443
+
+static int https_client_test(unsigned ms_timeout)
+{
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_timer_heap_t *timer = NULL;
+ pj_ssl_sock_t *ssock = NULL;
+ pj_ssl_sock_param param;
+ pj_status_t status;
+ struct test_state state = {0};
+ pj_sockaddr local_addr, rem_addr;
+ pj_str_t tmp_st;
+
+ pool = pj_pool_create(mem, "https_get", 256, 256, NULL);
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_timer_heap_create(pool, 4, &timer);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ state.pool = pool;
+ state.send_str = HTTP_REQ;
+ state.send_str_len = pj_ansi_strlen(state.send_str);
+ state.is_verbose = PJ_TRUE;
+
+ pj_ssl_sock_param_default(&param);
+ param.cb.on_connect_complete = &ssl_on_connect_complete;
+ param.cb.on_data_read = &ssl_on_data_read;
+ param.cb.on_data_sent = &ssl_on_data_sent;
+ param.ioqueue = ioqueue;
+ param.user_data = &state;
+ param.server_name = pj_str((char*)HTTP_SERVER_ADDR);
+ param.timer_heap = timer;
+ param.timeout.sec = 0;
+ param.timeout.msec = ms_timeout;
+ param.proto = PJ_SSL_SOCK_PROTO_SSL23;
+ pj_time_val_normalize(&param.timeout);
+
+ status = pj_ssl_sock_create(pool, &param, &ssock);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0);
+ pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT);
+ status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr));
+ if (status == PJ_SUCCESS) {
+ ssl_on_connect_complete(ssock, PJ_SUCCESS);
+ } else if (status == PJ_EPENDING) {
+ status = PJ_SUCCESS;
+ } else {
+ goto on_return;
+ }
+
+ /* Wait until everything has been sent/received */
+ while (state.err == PJ_SUCCESS && !state.done) {
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_time_val delay = {0, 100};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer, &delay);
+#endif
+ }
+
+ if (state.err) {
+ status = state.err;
+ goto on_return;
+ }
+
+ PJ_LOG(3, ("", "...Done!"));
+ PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state.sent, state.recv));
+
+on_return:
+ if (ssock && !state.err && !state.done)
+ pj_ssl_sock_close(ssock);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (timer)
+ pj_timer_heap_destroy(timer);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
+ pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher,
+ pj_bool_t req_client_cert, pj_bool_t client_provide_cert)
+{
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_ssl_sock_t *ssock_serv = NULL;
+ pj_ssl_sock_t *ssock_cli = NULL;
+ pj_ssl_sock_param param;
+ struct test_state state_serv = { 0 };
+ struct test_state state_cli = { 0 };
+ pj_sockaddr addr, listen_addr;
+ pj_ssl_cipher ciphers[1];
+ pj_ssl_cert_t *cert = NULL;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL);
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ pj_ssl_sock_param_default(&param);
+ param.cb.on_accept_complete = &ssl_on_accept_complete;
+ param.cb.on_connect_complete = &ssl_on_connect_complete;
+ param.cb.on_data_read = &ssl_on_data_read;
+ param.cb.on_data_sent = &ssl_on_data_sent;
+ param.ioqueue = ioqueue;
+ param.ciphers = ciphers;
+
+ /* Init default bind address */
+ {
+ pj_str_t tmp_st;
+ pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+ }
+
+ /* === SERVER === */
+ param.proto = srv_proto;
+ param.user_data = &state_serv;
+ param.ciphers_num = (srv_cipher == -1)? 0 : 1;
+ param.require_client_cert = req_client_cert;
+ ciphers[0] = srv_cipher;
+
+ state_serv.pool = pool;
+ state_serv.echo = PJ_TRUE;
+ state_serv.is_server = PJ_TRUE;
+ state_serv.is_verbose = PJ_TRUE;
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Set server cert */
+ {
+ pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+ status = pj_ssl_cert_load_from_files(pool,
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE),
+ pj_strset2(&tmp2, (char*)CERT_FILE),
+ pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
+ pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Get listener address */
+ {
+ pj_ssl_sock_info info;
+
+ pj_ssl_sock_get_info(ssock_serv, &info);
+ pj_sockaddr_cp(&listen_addr, &info.local_addr);
+ }
+
+ /* === CLIENT === */
+ param.proto = cli_proto;
+ param.user_data = &state_cli;
+ param.ciphers_num = (cli_cipher == -1)? 0 : 1;
+ ciphers[0] = cli_cipher;
+
+ state_cli.pool = pool;
+ state_cli.check_echo = PJ_TRUE;
+ state_cli.is_verbose = PJ_TRUE;
+
+ {
+ pj_time_val now;
+
+ pj_gettimeofday(&now);
+ pj_srand((unsigned)now.sec);
+ state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
+ }
+ state_cli.send_str = (char*)pj_pool_alloc(pool, state_cli.send_str_len);
+ {
+ unsigned i;
+ for (i = 0; i < state_cli.send_str_len; ++i)
+ state_cli.send_str[i] = (char)(pj_rand() % 256);
+ }
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_cli);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Set cert for client */
+ {
+
+ if (!client_provide_cert) {
+ pj_str_t tmp1, tmp2;
+
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE);
+ pj_strset2(&tmp2, NULL);
+ status = pj_ssl_cert_load_from_files(pool,
+ &tmp1, &tmp2, &tmp2, &tmp2,
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
+ if (status == PJ_SUCCESS) {
+ ssl_on_connect_complete(ssock_cli, PJ_SUCCESS);
+ } else if (status == PJ_EPENDING) {
+ status = PJ_SUCCESS;
+ } else {
+ goto on_return;
+ }
+
+ /* Wait until everything has been sent/received or error */
+ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
+ {
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_time_val delay = {0, 100};
+ pj_ioqueue_poll(ioqueue, &delay);
+#endif
+ }
+
+ /* Clean up sockets */
+ {
+ pj_time_val delay = {0, 100};
+ while (pj_ioqueue_poll(ioqueue, &delay) > 0);
+ }
+
+ if (state_serv.err || state_cli.err) {
+ if (state_serv.err != PJ_SUCCESS)
+ status = state_serv.err;
+ else
+ status = state_cli.err;
+
+ goto on_return;
+ }
+
+ PJ_LOG(3, ("", "...Done!"));
+ PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state_cli.sent, state_cli.recv));
+
+on_return:
+ if (ssock_serv)
+ pj_ssl_sock_close(ssock_serv);
+ if (ssock_cli && !state_cli.err && !state_cli.done)
+ pj_ssl_sock_close(ssock_cli);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+static pj_bool_t asock_on_data_read(pj_activesock_t *asock,
+ void *data,
+ pj_size_t size,
+ pj_status_t status,
+ pj_size_t *remainder)
+{
+ struct test_state *st = (struct test_state*)
+ pj_activesock_get_user_data(asock);
+
+ PJ_UNUSED_ARG(data);
+ PJ_UNUSED_ARG(size);
+ PJ_UNUSED_ARG(remainder);
+
+ if (status != PJ_SUCCESS) {
+ if (status == PJ_EEOF) {
+ status = PJ_SUCCESS;
+ st->done = PJ_TRUE;
+ } else {
+ app_perror("...ERROR asock_on_data_read()", status);
+ }
+ }
+
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS || st->done) {
+ pj_activesock_close(asock);
+ if (!st->is_server)
+ clients_num--;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+
+static pj_bool_t asock_on_connect_complete(pj_activesock_t *asock,
+ pj_status_t status)
+{
+ struct test_state *st = (struct test_state*)
+ pj_activesock_get_user_data(asock);
+
+ if (status == PJ_SUCCESS) {
+ void *read_buf[1];
+
+ /* Start reading data */
+ read_buf[0] = st->read_buf;
+ status = pj_activesock_start_read2(asock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+ }
+ }
+
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS) {
+ pj_activesock_close(asock);
+ if (!st->is_server)
+ clients_num--;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
+ pj_sock_t newsock,
+ const pj_sockaddr_t *src_addr,
+ int src_addr_len)
+{
+ struct test_state *st;
+ void *read_buf[1];
+ pj_activesock_t *new_asock;
+ pj_activesock_cb asock_cb = { 0 };
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
+
+ st = (struct test_state*) pj_activesock_get_user_data(asock);
+
+ asock_cb.on_data_read = &asock_on_data_read;
+ status = pj_activesock_create(st->pool, newsock, pj_SOCK_STREAM(), NULL,
+ st->ioqueue, &asock_cb, st, &new_asock);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Start reading data */
+ read_buf[0] = st->read_buf;
+ status = pj_activesock_start_read2(new_asock, st->pool,
+ sizeof(st->read_buf),
+ (void**)read_buf, 0);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+ }
+
+on_return:
+ st->err = status;
+
+ if (st->err != PJ_SUCCESS)
+ pj_activesock_close(new_asock);
+
+ return PJ_TRUE;
+}
+
+
+/* Raw TCP socket try to connect to SSL socket server, once
+ * connection established, it will just do nothing, SSL socket
+ * server should be able to close the connection after specified
+ * timeout period (set ms_timeout to 0 to disable timer).
+ */
+static int client_non_ssl(unsigned ms_timeout)
+{
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_timer_heap_t *timer = NULL;
+ pj_ssl_sock_t *ssock_serv = NULL;
+ pj_activesock_t *asock_cli = NULL;
+ pj_activesock_cb asock_cb = { 0 };
+ pj_sock_t sock = PJ_INVALID_SOCKET;
+ pj_ssl_sock_param param;
+ struct test_state state_serv = { 0 };
+ struct test_state state_cli = { 0 };
+ pj_sockaddr listen_addr;
+ pj_ssl_cert_t *cert = NULL;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "ssl_accept_raw_tcp", 256, 256, NULL);
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_timer_heap_create(pool, 4, &timer);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Set cert */
+ {
+ pj_str_t tmp1, tmp2, tmp3, tmp4;
+ status = pj_ssl_cert_load_from_files(pool,
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE),
+ pj_strset2(&tmp2, (char*)CERT_FILE),
+ pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
+ pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ pj_ssl_sock_param_default(&param);
+ param.cb.on_accept_complete = &ssl_on_accept_complete;
+ param.cb.on_data_read = &ssl_on_data_read;
+ param.cb.on_data_sent = &ssl_on_data_sent;
+ param.ioqueue = ioqueue;
+ param.timer_heap = timer;
+ param.timeout.sec = 0;
+ param.timeout.msec = ms_timeout;
+ pj_time_val_normalize(&param.timeout);
+
+ /* SERVER */
+ param.user_data = &state_serv;
+ state_serv.pool = pool;
+ state_serv.is_server = PJ_TRUE;
+ state_serv.is_verbose = PJ_TRUE;
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Init bind address */
+ {
+ pj_str_t tmp_st;
+ pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+ }
+
+ status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, pj_sockaddr_get_len(&listen_addr));
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Update listener address */
+ {
+ pj_ssl_sock_info info;
+
+ pj_ssl_sock_get_info(ssock_serv, &info);
+ pj_sockaddr_cp(&listen_addr, &info.local_addr);
+ }
+
+ /* CLIENT */
+ state_cli.pool = pool;
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ asock_cb.on_connect_complete = &asock_on_connect_complete;
+ asock_cb.on_data_read = &asock_on_data_read;
+ status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL,
+ ioqueue, &asock_cb, &state_cli, &asock_cli);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr,
+ pj_sockaddr_get_len(&listen_addr));
+ if (status == PJ_SUCCESS) {
+ asock_on_connect_complete(asock_cli, PJ_SUCCESS);
+ } else if (status == PJ_EPENDING) {
+ status = PJ_SUCCESS;
+ } else {
+ goto on_return;
+ }
+
+ /* Wait until everything has been sent/received or error */
+ while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
+ {
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_time_val delay = {0, 100};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer, &delay);
+#endif
+ }
+
+ if (state_serv.err || state_cli.err) {
+ if (state_serv.err != PJ_SUCCESS)
+ status = state_serv.err;
+ else
+ status = state_cli.err;
+
+ goto on_return;
+ }
+
+ PJ_LOG(3, ("", "...Done!"));
+
+on_return:
+ if (ssock_serv)
+ pj_ssl_sock_close(ssock_serv);
+ if (asock_cli && !state_cli.err && !state_cli.done)
+ pj_activesock_close(asock_cli);
+ if (timer)
+ pj_timer_heap_destroy(timer);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+/* SSL socket try to connect to raw TCP socket server, once
+ * connection established, SSL socket will try to perform SSL
+ * handshake. SSL client socket should be able to close the
+ * connection after specified timeout period (set ms_timeout to
+ * 0 to disable timer).
+ */
+static int server_non_ssl(unsigned ms_timeout)
+{
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_timer_heap_t *timer = NULL;
+ pj_activesock_t *asock_serv = NULL;
+ pj_ssl_sock_t *ssock_cli = NULL;
+ pj_activesock_cb asock_cb = { 0 };
+ pj_sock_t sock = PJ_INVALID_SOCKET;
+ pj_ssl_sock_param param;
+ struct test_state state_serv = { 0 };
+ struct test_state state_cli = { 0 };
+ pj_sockaddr addr, listen_addr;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "ssl_connect_raw_tcp", 256, 256, NULL);
+
+ status = pj_ioqueue_create(pool, 4, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_timer_heap_create(pool, 4, &timer);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* SERVER */
+ state_serv.pool = pool;
+ state_serv.ioqueue = ioqueue;
+
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Init bind address */
+ {
+ pj_str_t tmp_st;
+ pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+ }
+
+ status = pj_sock_bind(sock, (pj_sockaddr_t*)&listen_addr,
+ pj_sockaddr_get_len((pj_sockaddr_t*)&listen_addr));
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_sock_listen(sock, PJ_SOMAXCONN);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ asock_cb.on_accept_complete = &asock_on_accept_complete;
+ status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL,
+ ioqueue, &asock_cb, &state_serv, &asock_serv);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_activesock_start_accept(asock_serv, pool);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Update listener address */
+ {
+ int addr_len;
+
+ addr_len = sizeof(listen_addr);
+ pj_sock_getsockname(sock, (pj_sockaddr_t*)&listen_addr, &addr_len);
+ }
+
+ /* CLIENT */
+ pj_ssl_sock_param_default(&param);
+ param.cb.on_connect_complete = &ssl_on_connect_complete;
+ param.cb.on_data_read = &ssl_on_data_read;
+ param.cb.on_data_sent = &ssl_on_data_sent;
+ param.ioqueue = ioqueue;
+ param.timer_heap = timer;
+ param.timeout.sec = 0;
+ param.timeout.msec = ms_timeout;
+ pj_time_val_normalize(&param.timeout);
+ param.user_data = &state_cli;
+
+ state_cli.pool = pool;
+ state_cli.is_server = PJ_FALSE;
+ state_cli.is_verbose = PJ_TRUE;
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_cli);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Init default bind address */
+ {
+ pj_str_t tmp_st;
+ pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+ }
+
+ status = pj_ssl_sock_start_connect(ssock_cli, pool,
+ (pj_sockaddr_t*)&addr,
+ (pj_sockaddr_t*)&listen_addr,
+ pj_sockaddr_get_len(&listen_addr));
+ if (status != PJ_EPENDING) {
+ goto on_return;
+ }
+
+ /* Wait until everything has been sent/received or error */
+ while ((!state_serv.err && !state_serv.done) || (!state_cli.err && !state_cli.done))
+ {
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_time_val delay = {0, 100};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer, &delay);
+#endif
+ }
+
+ if (state_serv.err || state_cli.err) {
+ if (state_cli.err != PJ_SUCCESS)
+ status = state_cli.err;
+ else
+ status = state_serv.err;
+
+ goto on_return;
+ }
+
+ PJ_LOG(3, ("", "...Done!"));
+
+on_return:
+ if (asock_serv)
+ pj_activesock_close(asock_serv);
+ if (ssock_cli && !state_cli.err && !state_cli.done)
+ pj_ssl_sock_close(ssock_cli);
+ if (timer)
+ pj_timer_heap_destroy(timer);
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+/* Test will perform multiple clients trying to connect to single server.
+ * Once SSL connection established, echo test will be performed.
+ */
+static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
+{
+ pj_pool_t *pool = NULL;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_timer_heap_t *timer = NULL;
+ pj_ssl_sock_t *ssock_serv = NULL;
+ pj_ssl_sock_t **ssock_cli = NULL;
+ pj_ssl_sock_param param;
+ struct test_state state_serv = { 0 };
+ struct test_state *state_cli = NULL;
+ pj_sockaddr addr, listen_addr;
+ pj_ssl_cert_t *cert = NULL;
+ pj_status_t status;
+ unsigned i, cli_err = 0, tot_sent = 0, tot_recv = 0;
+ pj_time_val start;
+
+ pool = pj_pool_create(mem, "ssl_perf", 256, 256, NULL);
+
+ status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_timer_heap_create(pool, PJ_IOQUEUE_MAX_HANDLES, &timer);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Set cert */
+ {
+ pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+ status = pj_ssl_cert_load_from_files(pool,
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE),
+ pj_strset2(&tmp2, (char*)CERT_FILE),
+ pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
+ pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ pj_ssl_sock_param_default(&param);
+ param.cb.on_accept_complete = &ssl_on_accept_complete;
+ param.cb.on_connect_complete = &ssl_on_connect_complete;
+ param.cb.on_data_read = &ssl_on_data_read;
+ param.cb.on_data_sent = &ssl_on_data_sent;
+ param.ioqueue = ioqueue;
+ param.timer_heap = timer;
+ param.timeout.sec = 0;
+ param.timeout.msec = ms_handshake_timeout;
+ pj_time_val_normalize(&param.timeout);
+
+ /* Init default bind address */
+ {
+ pj_str_t tmp_st;
+ pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+ }
+
+ /* SERVER */
+ param.user_data = &state_serv;
+
+ state_serv.pool = pool;
+ state_serv.echo = PJ_TRUE;
+ state_serv.is_server = PJ_TRUE;
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Get listening address for clients to connect to */
+ {
+ pj_ssl_sock_info info;
+ char buf[64];
+
+ pj_ssl_sock_get_info(ssock_serv, &info);
+ pj_sockaddr_cp(&listen_addr, &info.local_addr);
+
+ pj_sockaddr_print((pj_sockaddr_t*)&listen_addr, buf, sizeof(buf), 1);
+ PJ_LOG(3, ("", "...Listener ready at %s", buf));
+ }
+
+
+ /* CLIENTS */
+ clients_num = clients;
+ param.timeout.sec = 0;
+ param.timeout.msec = 0;
+
+ /* Init random seed */
+ {
+ pj_time_val now;
+
+ pj_gettimeofday(&now);
+ pj_srand((unsigned)now.sec);
+ }
+
+ /* Allocate SSL socket pointers and test state */
+ ssock_cli = (pj_ssl_sock_t**)pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*));
+ state_cli = (struct test_state*)pj_pool_calloc(pool, clients, sizeof(struct test_state));
+
+ /* Get start timestamp */
+ pj_gettimeofday(&start);
+
+ /* Setup clients */
+ for (i = 0; i < clients; ++i) {
+ param.user_data = &state_cli[i];
+
+ state_cli[i].pool = pool;
+ state_cli[i].check_echo = PJ_TRUE;
+ state_cli[i].send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
+ state_cli[i].send_str = (char*)pj_pool_alloc(pool, state_cli[i].send_str_len);
+ {
+ unsigned j;
+ for (j = 0; j < state_cli[i].send_str_len; ++j)
+ state_cli[i].send_str[j] = (char)(pj_rand() % 256);
+ }
+
+ status = pj_ssl_sock_create(pool, &param, &ssock_cli[i]);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_create()", status);
+ cli_err++;
+ clients_num--;
+ continue;
+ }
+
+ status = pj_ssl_sock_start_connect(ssock_cli[i], pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
+ if (status == PJ_SUCCESS) {
+ ssl_on_connect_complete(ssock_cli[i], PJ_SUCCESS);
+ } else if (status == PJ_EPENDING) {
+ status = PJ_SUCCESS;
+ } else {
+ app_perror("...ERROR pj_ssl_sock_create()", status);
+ pj_ssl_sock_close(ssock_cli[i]);
+ ssock_cli[i] = NULL;
+ clients_num--;
+ cli_err++;
+ continue;
+ }
+
+ /* Give chance to server to accept this client */
+ {
+ unsigned n = 5;
+
+#ifdef PJ_SYMBIAN
+ while(n && pj_symbianos_poll(-1, 1000))
+ n--;
+#else
+ pj_time_val delay = {0, 100};
+ while(n && pj_ioqueue_poll(ioqueue, &delay) > 0)
+ n--;
+#endif
+ }
+ }
+
+ /* Wait until everything has been sent/received or error */
+ while (clients_num)
+ {
+#ifdef PJ_SYMBIAN
+ pj_symbianos_poll(-1, 1000);
+#else
+ pj_time_val delay = {0, 100};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer, &delay);
+#endif
+ }
+
+ /* Clean up sockets */
+ {
+ pj_time_val delay = {0, 500};
+ while (pj_ioqueue_poll(ioqueue, &delay) > 0);
+ }
+
+ if (state_serv.err != PJ_SUCCESS) {
+ status = state_serv.err;
+ goto on_return;
+ }
+
+ PJ_LOG(3, ("", "...Done!"));
+
+ /* SSL setup and data transfer duration */
+ {
+ pj_time_val stop;
+
+ pj_gettimeofday(&stop);
+ PJ_TIME_VAL_SUB(stop, start);
+
+ PJ_LOG(3, ("", ".....Setup & data transfer duration: %d.%03ds", stop.sec, stop.msec));
+ }
+
+ /* Check clients status */
+ for (i = 0; i < clients; ++i) {
+ if (state_cli[i].err != PJ_SUCCESS)
+ cli_err++;
+
+ tot_sent += state_cli[1].sent;
+ tot_recv += state_cli[1].recv;
+ }
+
+ PJ_LOG(3, ("", ".....Clients: %d (%d errors)", clients, cli_err));
+ PJ_LOG(3, ("", ".....Total sent/recv: %d/%d bytes", tot_sent, tot_recv));
+
+on_return:
+ if (ssock_serv)
+ pj_ssl_sock_close(ssock_serv);
+
+ for (i = 0; i < clients; ++i) {
+ if (ssock_cli[i] && !state_cli[i].err && !state_cli[i].done)
+ pj_ssl_sock_close(ssock_cli[i]);
+ }
+ if (ioqueue)
+ pj_ioqueue_destroy(ioqueue);
+ if (pool)
+ pj_pool_release(pool);
+
+ return status;
+}
+
+
+int ssl_sock_test(void)
+{
+ int ret;
+
+ PJ_LOG(3,("", "..get cipher list test"));
+ ret = get_cipher_list();
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..https client test"));
+ ret = https_client_test(30000);
+ // Ignore test result as internet connection may not be available.
+ //if (ret != 0)
+ //return ret;
+
+#ifndef PJ_SYMBIAN
+
+ /* On Symbian platforms, SSL socket is implemented using CSecureSocket,
+ * and it hasn't supported server mode, so exclude the following tests,
+ * which require SSL server, for now.
+ */
+
+ PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_DES_CBC_SHA cipher"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1,
+ PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..echo test w/ SSLv23 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23,
+ PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..echo test w/ incompatible proto"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3,
+ PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret == 0)
+ return PJ_EBUG;
+
+ PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret == 0)
+ return PJ_EBUG;
+
+ PJ_LOG(3,("", "..echo test w/ client cert required but not provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_FALSE);
+ if (ret == 0)
+ return PJ_EBUG;
+
+ PJ_LOG(3,("", "..echo test w/ client cert required and provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_TRUE);
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..performance test"));
+ ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0);
+ if (ret != 0)
+ return ret;
+
+ PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)"));
+ ret = client_non_ssl(5000);
+ /* PJ_TIMEDOUT won't be returned as accepted socket is deleted silently */
+ if (ret != 0)
+ return ret;
+
+#endif
+
+ PJ_LOG(3,("", "..server non-SSL (handshake timeout 5 secs)"));
+ ret = server_non_ssl(5000);
+ if (ret != PJ_ETIMEDOUT)
+ return ret;
+
+ return 0;
+}
+
+#else /* INCLUDE_SSLSOCK_TEST */
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_ssl_sock_test;
+#endif /* INCLUDE_SSLSOCK_TEST */
+
diff --git a/pjlib/src/pjlib-test/string.c b/pjlib/src/pjlib-test/string.c
new file mode 100644
index 0000000..b7f73b9
--- /dev/null
+++ b/pjlib/src/pjlib-test/string.c
@@ -0,0 +1,438 @@
+/* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include "test.h"
+
+#define THIS_FILE "string.c"
+
+/**
+ * \page page_pjlib_string_test Test: String
+ *
+ * This file provides implementation of \b string_test(). It tests the
+ * functionality of the string API.
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * API tested:
+ * - pj_str()
+ * - pj_strcmp()
+ * - pj_strcmp2()
+ * - pj_stricmp()
+ * - pj_strlen()
+ * - pj_strncmp()
+ * - pj_strnicmp()
+ * - pj_strchr()
+ * - pj_strdup()
+ * - pj_strdup2()
+ * - pj_strcpy()
+ * - pj_strcat()
+ * - pj_strtrim()
+ * - pj_utoa()
+ * - pj_strtoul()
+ * - pj_strtoul2()
+ * - pj_create_random_string()
+ * - ... and mode..
+ *
+ * This file is <b>pjlib-test/string.c</b>
+ *
+ * \include pjlib-test/string.c
+ */
+
+#if INCLUDE_STRING_TEST
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4204)
+#endif
+
+#define HELLO_WORLD "Hello World"
+#define HELLO_WORLD_LEN 11
+#define JUST_HELLO "Hello"
+#define JUST_HELLO_LEN 5
+#define UL_VALUE 3456789012UL
+
+#if 1
+/* See if both integers have the same sign */
+PJ_INLINE(int) cmp(const char *expr, int i, int j)
+{
+ int r = !((i>0 && j>0) || (i<0 && j<0) || (i==0 && j==0));
+ if (r) {
+ PJ_LOG(3,(THIS_FILE," error: %s: expecting %d, got %d", expr, j, i));
+ }
+ return r;
+}
+#else
+/* For strict comparison, must be equal */
+PJ_INLINE(int) cmp(const char *expr, int i, int j)
+{
+ PJ_UNUSED_ARG(expr);
+ return i!=j;
+}
+#endif
+
+#define C(expr, res) cmp(#expr, expr, res)
+
+static int stricmp_test(void)
+{
+/* This specificly tests and benchmark pj_stricmp(), pj_stricmp_alnum().
+ * In addition, it also tests pj_stricmp2(), pj_strnicmp(), and
+ * pj_strnicmp2().
+ */
+#define STRTEST(res,res2,S1,S2,code) \
+ do { \
+ s1.ptr=S1; s1.slen=(S1)?len:0; \
+ s2.ptr=S2; s2.slen=(S2)?len:0; \
+ pj_get_timestamp(&t1); \
+ if (C(pj_stricmp(&s1,&s2),res)) return code; \
+ pj_get_timestamp(&t2); \
+ pj_sub_timestamp(&t2, &t1); \
+ pj_add_timestamp(&e1, &t2); \
+ pj_get_timestamp(&t1); \
+ if (C(pj_stricmp_alnum(&s1,&s2),res)) return code-1; \
+ pj_get_timestamp(&t2); \
+ pj_sub_timestamp(&t2, &t1); \
+ pj_add_timestamp(&e2, &t2); \
+ if (C(pj_stricmp2(&s1,S2),res2)) return code*10; \
+ if (C(pj_strnicmp(&s1,&s2,len),res)) return code*100; \
+ if (C(pj_strnicmp2(&s1,S2,len),res)) return code*1000; \
+ } while (0)
+
+ char *buf;
+ pj_str_t s1, s2;
+ pj_timestamp t1, t2, e1, e2, zero;
+ pj_uint32_t c1, c2;
+ int len;
+
+ e1.u32.hi = e1.u32.lo = e2.u32.hi = e2.u32.lo = 0;
+
+ pj_thread_sleep(0);
+
+#define SNULL 0
+
+ /* Compare empty strings. */
+ len=0;
+ STRTEST( 0, 0, "","",-500);
+ STRTEST( 0, 0, SNULL,"",-502);
+ STRTEST( 0, 0, "",SNULL,-504);
+ STRTEST( 0, 0, SNULL,SNULL,-506);
+ STRTEST( 0, -1, "hello","world",-508);
+
+ /* equal, length=1
+ * use buffer to simulate non-aligned string.
+ */
+ buf = "a""A";
+ len=1;
+ STRTEST( 0, -1, "a",buf+0,-510);
+ STRTEST( 0, 0, "a",buf+1,-512);
+ STRTEST(-1, -1, "O", "P", -514);
+ STRTEST(-1, -1, SNULL, "a", -516);
+ STRTEST( 1, 1, "a", SNULL, -518);
+
+ /* equal, length=2
+ * use buffer to simulate non-aligned string.
+ */
+ buf = "aa""Aa""aA""AA";
+ len=2;
+ STRTEST( 0, -1, "aa",buf+0,-520);
+ STRTEST( 0, -1, "aa",buf+2,-522);
+ STRTEST( 0, -1, "aa",buf+4,-524);
+ STRTEST( 0, 0, "aa",buf+6,-524);
+
+ /* equal, length=3
+ * use buffer to simulate non-aligned string.
+ */
+ buf = "aaa""Aaa""aAa""aaA""AAa""aAA""AaA""AAA";
+ len=3;
+ STRTEST( 0, -1, "aaa",buf+0,-530);
+ STRTEST( 0, -1, "aaa",buf+3,-532);
+ STRTEST( 0, -1, "aaa",buf+6,-534);
+ STRTEST( 0, -1, "aaa",buf+9,-536);
+ STRTEST( 0, -1, "aaa",buf+12,-538);
+ STRTEST( 0, -1, "aaa",buf+15,-540);
+ STRTEST( 0, -1, "aaa",buf+18,-542);
+ STRTEST( 0, 0, "aaa",buf+21,-534);
+
+ /* equal, length=4 */
+ len=4;
+ STRTEST( 0, 0, "aaaa","aaaa",-540);
+ STRTEST( 0, 0, "aaaa","Aaaa",-542);
+ STRTEST( 0, 0, "aaaa","aAaa",-544);
+ STRTEST( 0, 0, "aaaa","aaAa",-546);
+ STRTEST( 0, 0, "aaaa","aaaA",-548);
+ STRTEST( 0, 0, "aaaa","AAaa",-550);
+ STRTEST( 0, 0, "aaaa","aAAa",-552);
+ STRTEST( 0, 0, "aaaa","aaAA",-554);
+ STRTEST( 0, 0, "aaaa","AaAa",-556);
+ STRTEST( 0, 0, "aaaa","aAaA",-558);
+ STRTEST( 0, 0, "aaaa","AaaA",-560);
+ STRTEST( 0, 0, "aaaa","AAAa",-562);
+ STRTEST( 0, 0, "aaaa","aAAA",-564);
+ STRTEST( 0, 0, "aaaa","AAaA",-566);
+ STRTEST( 0, 0, "aaaa","AaAA",-568);
+ STRTEST( 0, 0, "aaaa","AAAA",-570);
+
+ /* equal, length=5 */
+ buf = "aaaAa""AaaaA""AaAaA""AAAAA";
+ len=5;
+ STRTEST( 0, -1, "aaaaa",buf+0,-580);
+ STRTEST( 0, -1, "aaaaa",buf+5,-582);
+ STRTEST( 0, -1, "aaaaa",buf+10,-584);
+ STRTEST( 0, 0, "aaaaa",buf+15,-586);
+
+ /* not equal, length=1 */
+ len=1;
+ STRTEST( -1, -1, "a", "b", -600);
+
+ /* not equal, length=2 */
+ buf = "ab""ba";
+ len=2;
+ STRTEST( -1, -1, "aa", buf+0, -610);
+ STRTEST( -1, -1, "aa", buf+2, -612);
+
+ /* not equal, length=3 */
+ buf = "aab""aba""baa";
+ len=3;
+ STRTEST( -1, -1, "aaa", buf+0, -620);
+ STRTEST( -1, -1, "aaa", buf+3, -622);
+ STRTEST( -1, -1, "aaa", buf+6, -624);
+
+ /* not equal, length=4 */
+ buf = "aaab""aaba""abaa""baaa";
+ len=4;
+ STRTEST( -1, -1, "aaaa", buf+0, -630);
+ STRTEST( -1, -1, "aaaa", buf+4, -632);
+ STRTEST( -1, -1, "aaaa", buf+8, -634);
+ STRTEST( -1, -1, "aaaa", buf+12, -636);
+
+ /* not equal, length=5 */
+ buf="aaaab""aaaba""aabaa""abaaa""baaaa";
+ len=5;
+ STRTEST( -1, -1, "aaaaa", buf+0, -640);
+ STRTEST( -1, -1, "aaaaa", buf+5, -642);
+ STRTEST( -1, -1, "aaaaa", buf+10, -644);
+ STRTEST( -1, -1, "aaaaa", buf+15, -646);
+ STRTEST( -1, -1, "aaaaa", buf+20, -648);
+
+ zero.u32.hi = zero.u32.lo = 0;
+ c1 = pj_elapsed_cycle(&zero, &e1);
+ c2 = pj_elapsed_cycle(&zero, &e2);
+
+ if (c1 < c2) {
+ PJ_LOG(3,("", " info: pj_stricmp_alnum is slower than pj_stricmp!"));
+ //return -700;
+ }
+
+ /* Avoid division by zero */
+ if (c2 == 0) c2=1;
+
+ PJ_LOG(3, ("", " time: stricmp=%u, stricmp_alnum=%u (speedup=%d.%02dx)",
+ c1, c2,
+ (c1 * 100 / c2) / 100,
+ (c1 * 100 / c2) % 100));
+ return 0;
+#undef STRTEST
+}
+
+/* This tests pj_strcmp(), pj_strcmp2(), pj_strncmp(), pj_strncmp2() */
+static int strcmp_test(void)
+{
+#define STR_TEST(res,S1,S2,code) \
+ do { \
+ s1.ptr=S1; s1.slen=S1?len:0; \
+ s2.ptr=S2; s2.slen=S2?len:0; \
+ if (C(pj_strcmp(&s1,&s2),res)) return code; \
+ if (C(pj_strcmp2(&s1,S2),res)) return code-1; \
+ if (C(pj_strncmp(&s1,&s2,len),res)) return code-2; \
+ if (C(pj_strncmp2(&s1,S2,len),res)) return code-3; \
+ } while (0)
+
+ pj_str_t s1, s2;
+ int len;
+
+ /* Test with length == 0 */
+ len=0;
+ STR_TEST(0, "", "", -400);
+ STR_TEST(0, SNULL, "", -405);
+ STR_TEST(0, "", SNULL, -410);
+ STR_TEST(0, SNULL, SNULL, -415);
+ STR_TEST(0, "hello", "", -420);
+ STR_TEST(0, "hello", SNULL, -425);
+
+ /* Test with length != 0 */
+ len = 2;
+ STR_TEST(0, "12", "12", -430);
+ STR_TEST(1, "12", "1", -435);
+ STR_TEST(-1, "1", "12", -440);
+ STR_TEST(-1, SNULL, "12", -445);
+ STR_TEST(1, "12", SNULL, -450);
+
+ return 0;
+
+#undef STR_TEST
+}
+
+int string_test(void)
+{
+ const pj_str_t hello_world = { HELLO_WORLD, HELLO_WORLD_LEN };
+ const pj_str_t just_hello = { JUST_HELLO, JUST_HELLO_LEN };
+ pj_str_t s1, s2, s3, s4, s5;
+ enum { RCOUNT = 10, RLEN = 16 };
+ pj_str_t random[RCOUNT];
+ pj_pool_t *pool;
+ int i;
+
+ pool = pj_pool_create(mem, SNULL, 4096, 0, SNULL);
+ if (!pool) return -5;
+
+ /*
+ * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(),
+ * pj_strncmp(), pj_strchr()
+ */
+ s1 = pj_str(HELLO_WORLD);
+ if (pj_strcmp(&s1, &hello_world) != 0)
+ return -10;
+ if (pj_stricmp(&s1, &hello_world) != 0)
+ return -20;
+ if (pj_strcmp(&s1, &just_hello) <= 0)
+ return -30;
+ if (pj_stricmp(&s1, &just_hello) <= 0)
+ return -40;
+ if (pj_strlen(&s1) != strlen(HELLO_WORLD))
+ return -50;
+ if (pj_strncmp(&s1, &hello_world, 5) != 0)
+ return -60;
+ if (pj_strnicmp(&s1, &hello_world, 5) != 0)
+ return -70;
+ if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1)
+ return -80;
+
+ /*
+ * pj_strdup()
+ */
+ if (!pj_strdup(pool, &s2, &s1))
+ return -100;
+ if (pj_strcmp(&s1, &s2) != 0)
+ return -110;
+
+ /*
+ * pj_strcpy(), pj_strcat()
+ */
+ s3.ptr = (char*) pj_pool_alloc(pool, 256);
+ if (!s3.ptr)
+ return -200;
+ pj_strcpy(&s3, &s2);
+ pj_strcat(&s3, &just_hello);
+
+ if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0)
+ return -210;
+
+ /*
+ * pj_strdup2(), pj_strtrim().
+ */
+ pj_strdup2(pool, &s4, " " HELLO_WORLD "\t ");
+ pj_strtrim(&s4);
+ if (pj_strcmp2(&s4, HELLO_WORLD) != 0)
+ return -250;
+
+ /*
+ * pj_utoa()
+ */
+ s5.ptr = (char*) pj_pool_alloc(pool, 16);
+ if (!s5.ptr)
+ return -270;
+ s5.slen = pj_utoa(UL_VALUE, s5.ptr);
+
+ /*
+ * pj_strtoul()
+ */
+ if (pj_strtoul(&s5) != UL_VALUE)
+ return -280;
+
+ /*
+ * pj_strtoul2()
+ */
+ s5 = pj_str("123456");
+
+ pj_strtoul2(&s5, SNULL, 10); /* Crash test */
+
+ if (pj_strtoul2(&s5, &s4, 10) != 123456UL)
+ return -290;
+ if (s4.slen != 0)
+ return -291;
+ if (pj_strtoul2(&s5, &s4, 16) != 0x123456UL)
+ return -292;
+
+ s5 = pj_str("0123ABCD");
+ if (pj_strtoul2(&s5, &s4, 10) != 123)
+ return -293;
+ if (s4.slen != 4)
+ return -294;
+ if (s4.ptr == SNULL || *s4.ptr != 'A')
+ return -295;
+ if (pj_strtoul2(&s5, &s4, 16) != 0x123ABCDUL)
+ return -296;
+ if (s4.slen != 0)
+ return -297;
+
+ /*
+ * pj_create_random_string()
+ * Check that no duplicate strings are returned.
+ */
+ for (i=0; i<RCOUNT; ++i) {
+ int j;
+
+ random[i].ptr = (char*) pj_pool_alloc(pool, RLEN);
+ if (!random[i].ptr)
+ return -320;
+
+ random[i].slen = RLEN;
+ pj_create_random_string(random[i].ptr, RLEN);
+
+ for (j=0; j<i; ++j) {
+ if (pj_strcmp(&random[i], &random[j])==0)
+ return -330;
+ }
+ }
+
+ /* Done. */
+ pj_pool_release(pool);
+
+ /* Case sensitive comparison test. */
+ i = strcmp_test();
+ if (i != 0)
+ return i;
+
+ /* Caseless comparison test. */
+ i = stricmp_test();
+ if (i != 0)
+ return i;
+
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_string_test;
+#endif /* INCLUDE_STRING_TEST */
+
diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c
new file mode 100644
index 0000000..5c6f57d
--- /dev/null
+++ b/pjlib/src/pjlib-test/test.c
@@ -0,0 +1,234 @@
+/* $Id: test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+#ifdef _MSC_VER
+# pragma warning(disable:4127)
+#endif
+
+#define DO_TEST(test) do { \
+ PJ_LOG(3, ("test", "Running %s...", #test)); \
+ rc = test; \
+ PJ_LOG(3, ("test", \
+ "%s(%d)", \
+ (rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
+
+
+pj_pool_factory *mem;
+
+int param_echo_sock_type;
+const char *param_echo_server = ECHO_SERVER_ADDRESS;
+int param_echo_port = ECHO_SERVER_START_PORT;
+int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC;
+
+int null_func()
+{
+ return 0;
+}
+
+int test_inner(void)
+{
+ pj_caching_pool caching_pool;
+ const char *filename;
+ int line;
+ int rc = 0;
+
+ mem = &caching_pool.factory;
+
+ pj_log_set_level(3);
+ pj_log_set_decor(param_log_decor);
+
+ rc = pj_init();
+ if (rc != 0) {
+ app_perror("pj_init() error!!", rc);
+ return rc;
+ }
+
+ //pj_dump_config();
+ pj_caching_pool_init( &caching_pool, NULL, 0 );
+
+#if INCLUDE_ERRNO_TEST
+ DO_TEST( errno_test() );
+#endif
+
+#if INCLUDE_EXCEPTION_TEST
+ DO_TEST( exception_test() );
+#endif
+
+#if INCLUDE_OS_TEST
+ DO_TEST( os_test() );
+#endif
+
+#if INCLUDE_RAND_TEST
+ DO_TEST( rand_test() );
+#endif
+
+#if INCLUDE_LIST_TEST
+ DO_TEST( list_test() );
+#endif
+
+#if INCLUDE_POOL_TEST
+ DO_TEST( pool_test() );
+#endif
+
+#if INCLUDE_POOL_PERF_TEST
+ DO_TEST( pool_perf_test() );
+#endif
+
+#if INCLUDE_STRING_TEST
+ DO_TEST( string_test() );
+#endif
+
+#if INCLUDE_FIFOBUF_TEST
+ DO_TEST( fifobuf_test() );
+#endif
+
+#if INCLUDE_RBTREE_TEST
+ DO_TEST( rbtree_test() );
+#endif
+
+#if INCLUDE_HASH_TEST
+ DO_TEST( hash_test() );
+#endif
+
+#if INCLUDE_TIMESTAMP_TEST
+ DO_TEST( timestamp_test() );
+#endif
+
+#if INCLUDE_ATOMIC_TEST
+ DO_TEST( atomic_test() );
+#endif
+
+#if INCLUDE_MUTEX_TEST
+ DO_TEST( mutex_test() );
+#endif
+
+#if INCLUDE_TIMER_TEST
+ DO_TEST( timer_test() );
+#endif
+
+#if INCLUDE_SLEEP_TEST
+ DO_TEST( sleep_test() );
+#endif
+
+#if INCLUDE_THREAD_TEST
+ DO_TEST( thread_test() );
+#endif
+
+#if INCLUDE_SOCK_TEST
+ DO_TEST( sock_test() );
+#endif
+
+#if INCLUDE_SOCK_PERF_TEST
+ DO_TEST( sock_perf_test() );
+#endif
+
+#if INCLUDE_SELECT_TEST
+ DO_TEST( select_test() );
+#endif
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+ DO_TEST( udp_ioqueue_test() );
+#endif
+
+#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST
+ DO_TEST( tcp_ioqueue_test() );
+#endif
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+ DO_TEST( ioqueue_perf_test() );
+#endif
+
+#if INCLUDE_IOQUEUE_UNREG_TEST
+ DO_TEST( udp_ioqueue_unreg_test() );
+#endif
+
+#if INCLUDE_ACTIVESOCK_TEST
+ DO_TEST( activesock_test() );
+#endif
+
+#if INCLUDE_FILE_TEST
+ DO_TEST( file_test() );
+#endif
+
+#if INCLUDE_SSLSOCK_TEST
+ DO_TEST( ssl_sock_test() );
+#endif
+
+#if INCLUDE_ECHO_SERVER
+ //echo_server();
+ //echo_srv_sync();
+ udp_echo_srv_ioqueue();
+
+#elif INCLUDE_ECHO_CLIENT
+ if (param_echo_sock_type == 0)
+ param_echo_sock_type = pj_SOCK_DGRAM();
+
+ echo_client( param_echo_sock_type,
+ param_echo_server,
+ param_echo_port);
+#endif
+
+ goto on_return;
+
+on_return:
+
+ pj_caching_pool_destroy( &caching_pool );
+
+ PJ_LOG(3,("test", ""));
+
+ pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
+ PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u",
+ pj_thread_get_stack_max_usage(pj_thread_this()),
+ filename, line));
+ if (rc == 0)
+ PJ_LOG(3,("test", "Looks like everything is okay!.."));
+ else
+ PJ_LOG(3,("test", "Test completed with error(s)"));
+
+ pj_shutdown();
+
+ return 0;
+}
+
+#include <pj/sock.h>
+
+int test_main(void)
+{
+ int i;
+ PJ_USE_EXCEPTION;
+
+ i = pj_AF_INET();
+
+ PJ_TRY {
+ return test_inner();
+ }
+ PJ_CATCH_ANY {
+ int id = PJ_GET_EXCEPTION();
+ PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
+ id, pj_exception_id_name(id)));
+ }
+ PJ_END;
+
+ return -1;
+}
diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h
new file mode 100644
index 0000000..de69ea4
--- /dev/null
+++ b/pjlib/src/pjlib-test/test.h
@@ -0,0 +1,129 @@
+/* $Id: test.h 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJLIB_TEST_H__
+#define __PJLIB_TEST_H__
+
+#include <pj/types.h>
+
+#define GROUP_LIBC 1
+#define GROUP_OS 1
+#define GROUP_DATA_STRUCTURE 1
+#define GROUP_NETWORK 1
+#if defined(PJ_SYMBIAN)
+# define GROUP_FILE 0
+#else
+# define GROUP_FILE 1
+#endif
+
+#define INCLUDE_ERRNO_TEST GROUP_LIBC
+#define INCLUDE_TIMESTAMP_TEST GROUP_OS
+#define INCLUDE_EXCEPTION_TEST GROUP_LIBC
+#define INCLUDE_RAND_TEST GROUP_LIBC
+#define INCLUDE_LIST_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_HASH_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_POOL_TEST GROUP_LIBC
+#define INCLUDE_POOL_PERF_TEST GROUP_LIBC
+#define INCLUDE_STRING_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_FIFOBUF_TEST 0 // GROUP_DATA_STRUCTURE
+#define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE
+#define INCLUDE_ATOMIC_TEST GROUP_OS
+#define INCLUDE_MUTEX_TEST (PJ_HAS_THREADS && GROUP_OS)
+#define INCLUDE_SLEEP_TEST GROUP_OS
+#define INCLUDE_OS_TEST GROUP_OS
+#define INCLUDE_THREAD_TEST (PJ_HAS_THREADS && GROUP_OS)
+#define INCLUDE_SOCK_TEST GROUP_NETWORK
+#define INCLUDE_SOCK_PERF_TEST GROUP_NETWORK
+#define INCLUDE_SELECT_TEST GROUP_NETWORK
+#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK
+#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK
+#define INCLUDE_ACTIVESOCK_TEST GROUP_NETWORK
+#define INCLUDE_SSLSOCK_TEST (PJ_HAS_SSL_SOCK && GROUP_NETWORK)
+#define INCLUDE_IOQUEUE_PERF_TEST (PJ_HAS_THREADS && GROUP_NETWORK)
+#define INCLUDE_IOQUEUE_UNREG_TEST (PJ_HAS_THREADS && GROUP_NETWORK)
+#define INCLUDE_FILE_TEST GROUP_FILE
+
+#define INCLUDE_ECHO_SERVER 0
+#define INCLUDE_ECHO_CLIENT 0
+
+
+#define ECHO_SERVER_MAX_THREADS 2
+#define ECHO_SERVER_START_PORT 65000
+#define ECHO_SERVER_ADDRESS "compaq.home"
+#define ECHO_SERVER_DURATION_MSEC (60*60*1000)
+
+#define ECHO_CLIENT_MAX_THREADS 6
+
+PJ_BEGIN_DECL
+
+extern int errno_test(void);
+extern int timestamp_test(void);
+extern int exception_test(void);
+extern int rand_test(void);
+extern int list_test(void);
+extern int hash_test(void);
+extern int os_test(void);
+extern int pool_test(void);
+extern int pool_perf_test(void);
+extern int string_test(void);
+extern int fifobuf_test(void);
+extern int timer_test(void);
+extern int rbtree_test(void);
+extern int atomic_test(void);
+extern int mutex_test(void);
+extern int sleep_test(void);
+extern int thread_test(void);
+extern int sock_test(void);
+extern int sock_perf_test(void);
+extern int select_test(void);
+extern int udp_ioqueue_test(void);
+extern int udp_ioqueue_unreg_test(void);
+extern int tcp_ioqueue_test(void);
+extern int ioqueue_perf_test(void);
+extern int activesock_test(void);
+extern int file_test(void);
+extern int ssl_sock_test(void);
+
+extern int echo_server(void);
+extern int echo_client(int sock_type, const char *server, int port);
+
+extern int echo_srv_sync(void);
+extern int udp_echo_srv_ioqueue(void);
+extern int echo_srv_common_loop(pj_atomic_t *bytes_counter);
+
+
+extern pj_pool_factory *mem;
+
+extern int test_main(void);
+extern void app_perror(const char *msg, pj_status_t err);
+extern pj_status_t app_socket(int family, int type, int proto, int port,
+ pj_sock_t *ptr_sock);
+extern pj_status_t app_socketpair(int family, int type, int protocol,
+ pj_sock_t *server, pj_sock_t *client);
+extern int null_func(void);
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+#define HALT(msg) { PJ_LOG(3,(THIS_FILE,"%s halted",msg)); for(;;) sleep(1); }
+
+PJ_END_DECL
+
+#endif /* __PJLIB_TEST_H__ */
+
diff --git a/pjlib/src/pjlib-test/test_wrap.cpp b/pjlib/src/pjlib-test/test_wrap.cpp
new file mode 100644
index 0000000..7341204
--- /dev/null
+++ b/pjlib/src/pjlib-test/test_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: test_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "test.c"
diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c
new file mode 100644
index 0000000..976317f
--- /dev/null
+++ b/pjlib/src/pjlib-test/thread.c
@@ -0,0 +1,336 @@
+/* $Id: thread.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_thread_test Test: Thread Test
+ *
+ * This file contains \a thread_test() definition.
+ *
+ * \section thread_test_scope_sec Scope of Test
+ * This tests:
+ * - whether PJ_THREAD_SUSPENDED flag works.
+ * - whether multithreading works.
+ * - whether thread timeslicing works, and threads have equal
+ * time-slice proportion.
+ *
+ * APIs tested:
+ * - pj_thread_create()
+ * - pj_thread_register()
+ * - pj_thread_this()
+ * - pj_thread_get_name()
+ * - pj_thread_destroy()
+ * - pj_thread_resume()
+ * - pj_thread_sleep()
+ * - pj_thread_join()
+ * - pj_thread_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/thread.c</b>
+ *
+ * \include pjlib-test/thread.c
+ */
+#if INCLUDE_THREAD_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE "thread_test"
+
+static volatile int quit_flag=0;
+
+#if 0
+# define TRACE__(args) PJ_LOG(3,args)
+#else
+# define TRACE__(args)
+#endif
+
+
+/*
+ * The thread's entry point.
+ *
+ * Each of the thread mainly will just execute the loop which
+ * increments a variable.
+ */
+static void* thread_proc(pj_uint32_t *pcounter)
+{
+ /* Test that pj_thread_register() works. */
+ pj_thread_desc desc;
+ pj_thread_t *this_thread;
+ unsigned id;
+ pj_status_t rc;
+
+ id = *pcounter;
+ TRACE__((THIS_FILE, " thread %d running..", id));
+
+ pj_bzero(desc, sizeof(desc));
+
+ rc = pj_thread_register("thread", desc, &this_thread);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error in pj_thread_register", rc);
+ return NULL;
+ }
+
+ /* Test that pj_thread_this() works */
+ this_thread = pj_thread_this();
+ if (this_thread == NULL) {
+ PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!"));
+ return NULL;
+ }
+
+ /* Test that pj_thread_get_name() works */
+ if (pj_thread_get_name(this_thread) == NULL) {
+ PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!"));
+ return NULL;
+ }
+
+ /* Main loop */
+ for (;!quit_flag;) {
+ (*pcounter)++;
+ //Must sleep if platform doesn't do time-slicing.
+ //pj_thread_sleep(0);
+ }
+
+ TRACE__((THIS_FILE, " thread %d quitting..", id));
+ return NULL;
+}
+
+/*
+ * simple_thread()
+ */
+static int simple_thread(const char *title, unsigned flags)
+{
+ pj_pool_t *pool;
+ pj_thread_t *thread;
+ pj_status_t rc;
+ pj_uint32_t counter = 0;
+
+ PJ_LOG(3,(THIS_FILE, "..%s", title));
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -1000;
+
+ quit_flag = 0;
+
+ TRACE__((THIS_FILE, " Creating thread 0.."));
+ rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
+ &counter,
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ flags,
+ &thread);
+
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create thread", rc);
+ return -1010;
+ }
+
+ TRACE__((THIS_FILE, " Main thread waiting.."));
+ pj_thread_sleep(1500);
+ TRACE__((THIS_FILE, " Main thread resuming.."));
+
+ if (flags & PJ_THREAD_SUSPENDED) {
+
+ /* Check that counter is still zero */
+ if (counter != 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: thread is not suspended"));
+ return -1015;
+ }
+
+ rc = pj_thread_resume(thread);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: resume thread error", rc);
+ return -1020;
+ }
+ }
+
+ PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit.."));
+
+ pj_thread_sleep(1500);
+
+ quit_flag = 1;
+ pj_thread_join(thread);
+
+ pj_pool_release(pool);
+
+ if (counter == 0) {
+ PJ_LOG(3,(THIS_FILE, "...error: thread is not running"));
+ return -1025;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "...%s success", title));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * timeslice_test()
+ */
+static int timeslice_test(void)
+{
+ enum { NUM_THREADS = 4 };
+ pj_pool_t *pool;
+ pj_uint32_t counter[NUM_THREADS], lowest, highest, diff;
+ pj_thread_t *thread[NUM_THREADS];
+ unsigned i;
+ pj_status_t rc;
+
+ quit_flag = 0;
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -10;
+
+ PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS));
+
+ /* Create all threads in suspended mode. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ counter[i] = i;
+ rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
+ &counter[i],
+ PJ_THREAD_DEFAULT_STACK_SIZE,
+ PJ_THREAD_SUSPENDED,
+ &thread[i]);
+ if (rc!=PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_create()", rc);
+ return -20;
+ }
+ }
+
+ /* Sleep for 1 second.
+ * The purpose of this is to test whether all threads are suspended.
+ */
+ TRACE__((THIS_FILE, " Main thread waiting.."));
+ pj_thread_sleep(1000);
+ TRACE__((THIS_FILE, " Main thread resuming.."));
+
+ /* Check that all counters are still zero. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ if (counter[i] > i) {
+ PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!",
+ i));
+ return -30;
+ }
+ }
+
+ /* Now resume all threads. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ TRACE__((THIS_FILE, " Resuming thread %d [%p]..", i, thread[i]));
+ rc = pj_thread_resume(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_resume()", rc);
+ return -40;
+ }
+ }
+
+ /* Main thread sleeps for some time to allow threads to run.
+ * The longer we sleep, the more accurate the calculation will be,
+ * but it'll make user waits for longer for the test to finish.
+ */
+ TRACE__((THIS_FILE, " Main thread waiting (5s).."));
+ pj_thread_sleep(5000);
+ TRACE__((THIS_FILE, " Main thread resuming.."));
+
+ /* Signal all threads to quit. */
+ quit_flag = 1;
+
+ /* Wait until all threads quit, then destroy. */
+ for (i=0; i<NUM_THREADS; ++i) {
+ TRACE__((THIS_FILE, " Main thread joining thread %d [%p]..",
+ i, thread[i]));
+ rc = pj_thread_join(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_join()", rc);
+ return -50;
+ }
+ TRACE__((THIS_FILE, " Destroying thread %d [%p]..", i, thread[i]));
+ rc = pj_thread_destroy(thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR in pj_thread_destroy()", rc);
+ return -60;
+ }
+ }
+
+ TRACE__((THIS_FILE, " Main thread calculating time slices.."));
+
+ /* Now examine the value of the counters.
+ * Check that all threads had equal proportion of processing.
+ */
+ lowest = 0xFFFFFFFF;
+ highest = 0;
+ for (i=0; i<NUM_THREADS; ++i) {
+ if (counter[i] < lowest)
+ lowest = counter[i];
+ if (counter[i] > highest)
+ highest = counter[i];
+ }
+
+ /* Check that all threads are running. */
+ if (lowest < 2) {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!"));
+ return -70;
+ }
+
+ /* The difference between lowest and higest should be lower than 50%.
+ */
+ diff = (highest-lowest)*100 / ((highest+lowest)/2);
+ if ( diff >= 50) {
+ PJ_LOG(3,(THIS_FILE,
+ "...ERROR: thread didn't have equal timeslice!"));
+ PJ_LOG(3,(THIS_FILE,
+ ".....lowest counter=%u, highest counter=%u, diff=%u%%",
+ lowest, highest, diff));
+ return -80;
+ } else {
+ PJ_LOG(3,(THIS_FILE,
+ "...info: timeslice diff between lowest & highest=%u%%",
+ diff));
+ }
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+int thread_test(void)
+{
+ int rc;
+
+ rc = simple_thread("simple thread test", 0);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ rc = timeslice_test();
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ return rc;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_thread_test;
+#endif /* INCLUDE_THREAD_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/timer.c b/pjlib/src/pjlib-test/timer.c
new file mode 100644
index 0000000..371a397
--- /dev/null
+++ b/pjlib/src/pjlib-test/timer.c
@@ -0,0 +1,196 @@
+/* $Id: timer.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_timer_test Test: Timer
+ *
+ * This file provides implementation of \b timer_test(). It tests the
+ * functionality of the timer heap.
+ *
+ *
+ * This file is <b>pjlib-test/timer.c</b>
+ *
+ * \include pjlib-test/timer.c
+ */
+
+
+#if INCLUDE_TIMER_TEST
+
+#include <pjlib.h>
+
+#define LOOP 16
+#define MIN_COUNT 250
+#define MAX_COUNT (LOOP * MIN_COUNT)
+#define MIN_DELAY 2
+#define D (MAX_COUNT / 32000)
+#define DELAY (D < MIN_DELAY ? MIN_DELAY : D)
+#define THIS_FILE "timer_test"
+
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+ PJ_UNUSED_ARG(ht);
+ PJ_UNUSED_ARG(e);
+}
+
+static int test_timer_heap(void)
+{
+ int i, j;
+ pj_timer_entry *entry;
+ pj_pool_t *pool;
+ pj_timer_heap_t *timer;
+ pj_time_val delay;
+ pj_status_t rc; int err=0;
+ unsigned size, count;
+
+ size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry);
+ pool = pj_pool_create( mem, NULL, size, 4000, NULL);
+ if (!pool) {
+ PJ_LOG(3,("test", "...error: unable to create pool of %u bytes",
+ size));
+ return -10;
+ }
+
+ entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
+ if (!entry)
+ return -20;
+
+ for (i=0; i<MAX_COUNT; ++i) {
+ entry[i].cb = &timer_callback;
+ }
+ rc = pj_timer_heap_create(pool, MAX_COUNT, &timer);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error: unable to create timer heap", rc);
+ return -30;
+ }
+
+ count = MIN_COUNT;
+ for (i=0; i<LOOP; ++i) {
+ int early = 0;
+ int done=0;
+ int cancelled=0;
+ int rc;
+ pj_timestamp t1, t2, t_sched, t_cancel, t_poll;
+ pj_time_val now, expire;
+
+ pj_gettimeofday(&now);
+ pj_srand(now.sec);
+ t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
+
+ // Register timers
+ for (j=0; j<(int)count; ++j) {
+ delay.sec = pj_rand() % DELAY;
+ delay.msec = pj_rand() % 1000;
+
+ // Schedule timer
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_schedule(timer, &entry[j], &delay);
+ if (rc != 0)
+ return -40;
+ pj_get_timestamp(&t2);
+
+ t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+ // Poll timers.
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_poll(timer, NULL);
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+ early += rc;
+ }
+ }
+
+ // Set the time where all timers should finish
+ pj_gettimeofday(&expire);
+ delay.sec = DELAY;
+ delay.msec = 0;
+ PJ_TIME_VAL_ADD(expire, delay);
+
+ // Wait unfil all timers finish, cancel some of them.
+ do {
+ int index = pj_rand() % count;
+ pj_get_timestamp(&t1);
+ rc = pj_timer_heap_cancel(timer, &entry[index]);
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ cancelled += rc;
+ t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ pj_gettimeofday(&now);
+
+ pj_get_timestamp(&t1);
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
+ /* On Symbian, we must use OS poll (Active Scheduler poll) since
+ * timer is implemented using Active Object.
+ */
+ rc = 0;
+ while (pj_symbianos_poll(-1, 0))
+ ++rc;
+#else
+ rc = pj_timer_heap_poll(timer, NULL);
+#endif
+ pj_get_timestamp(&t2);
+ if (rc > 0) {
+ done += rc;
+ t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+ }
+
+ } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0);
+
+ if (pj_timer_heap_count(timer)) {
+ PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left",
+ pj_timer_heap_count(timer)));
+ ++err;
+ }
+ t_sched.u32.lo /= count;
+ t_cancel.u32.lo /= count;
+ t_poll.u32.lo /= count;
+ PJ_LOG(4, (THIS_FILE,
+ "...ok (count:%d, early:%d, cancelled:%d, "
+ "sched:%d, cancel:%d poll:%d)",
+ count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
+ t_poll.u32.lo));
+
+ count = count * 2;
+ if (count > MAX_COUNT)
+ break;
+ }
+
+ pj_pool_release(pool);
+ return err;
+}
+
+
+int timer_test()
+{
+ return test_timer_heap();
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_timer_test;
+#endif /* INCLUDE_TIMER_TEST */
+
+
diff --git a/pjlib/src/pjlib-test/timestamp.c b/pjlib/src/pjlib-test/timestamp.c
new file mode 100644
index 0000000..3b04000
--- /dev/null
+++ b/pjlib/src/pjlib-test/timestamp.c
@@ -0,0 +1,235 @@
+/* $Id: timestamp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+
+
+/**
+ * \page page_pjlib_timestamp_test Test: Timestamp
+ *
+ * This file provides implementation of timestamp_test()
+ *
+ * \section timestamp_test_sec Scope of the Test
+ *
+ * This tests whether timestamp API works.
+ *
+ * API tested:
+ * - pj_get_timestamp_freq()
+ * - pj_get_timestamp()
+ * - pj_elapsed_usec()
+ * - PJ_LOG()
+ *
+ *
+ * This file is <b>pjlib-test/timestamp.c</b>
+ *
+ * \include pjlib-test/timestamp.c
+ */
+
+#if INCLUDE_TIMESTAMP_TEST
+
+#define THIS_FILE "timestamp"
+
+static int timestamp_accuracy()
+{
+ pj_timestamp freq, t1, t2;
+ pj_time_val tv1, tv2, tvtmp;
+ pj_int64_t msec, tics;
+ pj_int64_t diff;
+
+ PJ_LOG(3,(THIS_FILE, "...testing frequency accuracy (pls wait)"));
+
+ pj_get_timestamp_freq(&freq);
+
+ /* Get the start time */
+ pj_gettimeofday(&tvtmp);
+ do {
+ pj_gettimeofday(&tv1);
+ } while (PJ_TIME_VAL_EQ(tvtmp, tv1));
+ pj_get_timestamp(&t1);
+
+ /* Sleep for 10 seconds */
+ pj_thread_sleep(10000);
+
+ /* Get end time */
+ pj_gettimeofday(&tvtmp);
+ do {
+ pj_gettimeofday(&tv2);
+ } while (PJ_TIME_VAL_EQ(tvtmp, tv2));
+ pj_get_timestamp(&t2);
+
+ /* Get the elapsed time */
+ PJ_TIME_VAL_SUB(tv2, tv1);
+ msec = PJ_TIME_VAL_MSEC(tv2);
+
+ /* Check that the frequency match the elapsed time */
+ tics = t2.u64 - t1.u64;
+ diff = tics - (msec * freq.u64 / 1000);
+ if (diff < 0)
+ diff = -diff;
+
+ /* Only allow 1 msec mismatch */
+ if (diff > (pj_int64_t)(freq.u64 / 1000)) {
+ PJ_LOG(3,(THIS_FILE, "....error: timestamp drifted by %d usec after "
+ "%d msec",
+ (pj_uint32_t)(diff * 1000000 / freq.u64),
+ msec));
+ return -2000;
+
+ /* Otherwise just print warning if timestamp drifted by >1 usec */
+ } else if (diff > (pj_int64_t)(freq.u64 / 1000000)) {
+ PJ_LOG(3,(THIS_FILE, "....warning: timestamp drifted by %d usec after "
+ "%d msec",
+ (pj_uint32_t)(diff * 1000000 / freq.u64),
+ msec));
+ } else {
+ PJ_LOG(3,(THIS_FILE, "....good. Timestamp is accurate down to"
+ " nearest usec."));
+ }
+
+ return 0;
+}
+
+
+int timestamp_test(void)
+{
+ enum { CONSECUTIVE_LOOP = 100 };
+ volatile unsigned i;
+ pj_timestamp freq, t1, t2;
+ pj_time_val tv1, tv2;
+ unsigned elapsed;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)"));
+
+ /* Get and display timestamp frequency. */
+ if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) {
+ app_perror("...ERROR: get timestamp freq", rc);
+ return -1000;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu",
+ freq.u32.hi, freq.u32.lo));
+
+ PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait).."));
+
+ /*
+ * Check if consecutive readings should yield timestamp value
+ * that is bigger than previous value.
+ * First we get the first timestamp.
+ */
+ rc = pj_get_timestamp(&t1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: pj_get_timestamp", rc);
+ return -1001;
+ }
+ rc = pj_gettimeofday(&tv1);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: pj_gettimeofday", rc);
+ return -1002;
+ }
+ for (i=0; i<CONSECUTIVE_LOOP; ++i) {
+
+ pj_thread_sleep(pj_rand() % 100);
+
+ rc = pj_get_timestamp(&t2);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: pj_get_timestamp", rc);
+ return -1003;
+ }
+ rc = pj_gettimeofday(&tv2);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...ERROR: pj_gettimeofday", rc);
+ return -1004;
+ }
+
+ /* compare t2 with t1, expecting t2 >= t1. */
+ if (t2.u32.hi < t1.u32.hi ||
+ (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo))
+ {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp run backwards!"));
+ return -1005;
+ }
+
+ /* compare tv2 with tv1, expecting tv2 >= tv1. */
+ if (PJ_TIME_VAL_LT(tv2, tv1)) {
+ PJ_LOG(3,(THIS_FILE, "...ERROR: time run backwards!"));
+ return -1006;
+ }
+ }
+
+ /*
+ * Simple test to time some loop.
+ */
+ PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop"));
+
+
+ /* Mark start time. */
+ if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) {
+ app_perror("....error: cat't get timestamp", rc);
+ return -1010;
+ }
+
+ /* Loop.. */
+ for (i=0; i<1000000; ++i) {
+ /* Try to do something so that smart compilers wont
+ * remove this silly loop.
+ */
+ null_func();
+ }
+
+ pj_thread_sleep(0);
+
+ /* Mark end time. */
+ pj_get_timestamp(&t2);
+
+ /* Get elapsed time in usec. */
+ elapsed = pj_elapsed_usec(&t1, &t2);
+ PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed));
+
+ /* See if elapsed time is "reasonable".
+ * This should be good even on 50Mhz embedded powerpc.
+ */
+ if (elapsed < 1 || elapsed > 1000000) {
+ PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, "
+ "t1.u32.hi=%u, t1.u32.lo=%u, "
+ "t2.u32.hi=%u, t2.u32.lo=%u)",
+ elapsed,
+ t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo));
+ return -1030;
+ }
+
+ /* Testing time/timestamp accuracy */
+ rc = timestamp_accuracy();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_timestamp_test;
+#endif /* INCLUDE_TIMESTAMP_TEST */
+
diff --git a/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c b/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
new file mode 100644
index 0000000..217a8d8
--- /dev/null
+++ b/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
@@ -0,0 +1,214 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjlib.h>
+#include "test.h"
+
+static pj_ioqueue_key_t *key;
+static pj_atomic_t *total_bytes;
+static pj_bool_t thread_quit_flag;
+
+struct op_key
+{
+ pj_ioqueue_op_key_t op_key_;
+ struct op_key *peer;
+ char *buffer;
+ pj_size_t size;
+ int is_pending;
+ pj_status_t last_err;
+ pj_sockaddr_in addr;
+ int addrlen;
+};
+
+static void on_read_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_received)
+{
+ pj_status_t rc;
+ struct op_key *recv_rec = (struct op_key *)op_key;
+
+ for (;;) {
+ struct op_key *send_rec = recv_rec->peer;
+ recv_rec->is_pending = 0;
+
+ if (bytes_received < 0) {
+ if (-bytes_received != recv_rec->last_err) {
+ recv_rec->last_err = -bytes_received;
+ app_perror("...error receiving data", -bytes_received);
+ }
+ } else if (bytes_received == 0) {
+ /* note: previous error, or write callback */
+ } else {
+ pj_atomic_add(total_bytes, bytes_received);
+
+ if (!send_rec->is_pending) {
+ pj_ssize_t sent = bytes_received;
+ pj_memcpy(send_rec->buffer, recv_rec->buffer, bytes_received);
+ pj_memcpy(&send_rec->addr, &recv_rec->addr, recv_rec->addrlen);
+ send_rec->addrlen = recv_rec->addrlen;
+ rc = pj_ioqueue_sendto(key, &send_rec->op_key_,
+ send_rec->buffer, &sent, 0,
+ &send_rec->addr, send_rec->addrlen);
+ send_rec->is_pending = (rc==PJ_EPENDING);
+
+ if (rc!=PJ_SUCCESS && rc!=PJ_EPENDING) {
+ app_perror("...send error(1)", rc);
+ }
+ }
+ }
+
+ if (!send_rec->is_pending) {
+ bytes_received = recv_rec->size;
+ rc = pj_ioqueue_recvfrom(key, &recv_rec->op_key_,
+ recv_rec->buffer, &bytes_received, 0,
+ &recv_rec->addr, &recv_rec->addrlen);
+ recv_rec->is_pending = (rc==PJ_EPENDING);
+ if (rc == PJ_SUCCESS) {
+ /* fall through next loop. */
+ } else if (rc == PJ_EPENDING) {
+ /* quit callback. */
+ break;
+ } else {
+ /* error */
+ app_perror("...recv error", rc);
+ recv_rec->last_err = rc;
+
+ bytes_received = 0;
+ /* fall through next loop. */
+ }
+ } else {
+ /* recv will be done when write completion callback is called. */
+ break;
+ }
+ }
+}
+
+static void on_write_complete(pj_ioqueue_key_t *key,
+ pj_ioqueue_op_key_t *op_key,
+ pj_ssize_t bytes_sent)
+{
+ struct op_key *send_rec = (struct op_key*)op_key;
+
+ if (bytes_sent <= 0) {
+ pj_status_t rc = -bytes_sent;
+ if (rc != send_rec->last_err) {
+ send_rec->last_err = rc;
+ app_perror("...send error(2)", rc);
+ }
+ }
+
+ send_rec->is_pending = 0;
+ on_read_complete(key, &send_rec->peer->op_key_, 0);
+}
+
+static int worker_thread(void *arg)
+{
+ pj_ioqueue_t *ioqueue = (pj_ioqueue_t*) arg;
+ struct op_key read_op, write_op;
+ char recv_buf[512], send_buf[512];
+ pj_ssize_t length;
+ pj_status_t rc;
+
+ read_op.peer = &write_op;
+ read_op.is_pending = 0;
+ read_op.last_err = 0;
+ read_op.buffer = recv_buf;
+ read_op.size = sizeof(recv_buf);
+ read_op.addrlen = sizeof(read_op.addr);
+
+ write_op.peer = &read_op;
+ write_op.is_pending = 0;
+ write_op.last_err = 0;
+ write_op.buffer = send_buf;
+ write_op.size = sizeof(send_buf);
+
+ length = sizeof(recv_buf);
+ rc = pj_ioqueue_recvfrom(key, &read_op.op_key_, recv_buf, &length, 0,
+ &read_op.addr, &read_op.addrlen);
+ if (rc == PJ_SUCCESS) {
+ read_op.is_pending = 1;
+ on_read_complete(key, &read_op.op_key_, length);
+ }
+
+ while (!thread_quit_flag) {
+ pj_time_val timeout;
+ timeout.sec = 0; timeout.msec = 10;
+ rc = pj_ioqueue_poll(ioqueue, &timeout);
+ }
+ return 0;
+}
+
+int udp_echo_srv_ioqueue(void)
+{
+ pj_pool_t *pool;
+ pj_sock_t sock;
+ pj_ioqueue_t *ioqueue;
+ pj_ioqueue_callback callback;
+ int i;
+ pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+ pj_status_t rc;
+
+ pj_bzero(&callback, sizeof(callback));
+ callback.on_read_complete = &on_read_complete;
+ callback.on_write_complete = &on_write_complete;
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -10;
+
+ rc = pj_ioqueue_create(pool, 2, &ioqueue);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...pj_ioqueue_create error", rc);
+ return -20;
+ }
+
+ rc = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
+ ECHO_SERVER_START_PORT, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...app_socket error", rc);
+ return -30;
+ }
+
+ rc = pj_ioqueue_register_sock(pool, ioqueue, sock, NULL,
+ &callback, &key);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error registering socket", rc);
+ return -40;
+ }
+
+ rc = pj_atomic_create(pool, 0, &total_bytes);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...error creating atomic variable", rc);
+ return -45;
+ }
+
+ for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+ rc = pj_thread_create(pool, NULL, &worker_thread, ioqueue,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...create thread error", rc);
+ return -50;
+ }
+ }
+
+ echo_srv_common_loop(total_bytes);
+
+ return 0;
+}
diff --git a/pjlib/src/pjlib-test/udp_echo_srv_sync.c b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
new file mode 100644
index 0000000..728e0b2
--- /dev/null
+++ b/pjlib/src/pjlib-test/udp_echo_srv_sync.c
@@ -0,0 +1,167 @@
+/* $Id: udp_echo_srv_sync.c 4108 2012-04-27 01:32:12Z bennylp $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+static pj_atomic_t *total_bytes;
+static pj_bool_t thread_quit_flag = 0;
+
+static int worker_thread(void *arg)
+{
+ pj_sock_t sock = (pj_sock_t)arg;
+ char buf[512];
+ pj_status_t last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS;
+
+ while (!thread_quit_flag) {
+ pj_ssize_t len;
+ pj_status_t rc;
+ pj_sockaddr_in addr;
+ int addrlen;
+
+ len = sizeof(buf);
+ addrlen = sizeof(addr);
+ rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen);
+ if (rc != 0) {
+ if (rc != last_recv_err) {
+ app_perror("...recv error", rc);
+ last_recv_err = rc;
+ }
+ continue;
+ }
+
+ pj_atomic_add(total_bytes, len);
+
+ rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen);
+ if (rc != PJ_SUCCESS) {
+ if (rc != last_write_err) {
+ app_perror("...send error", rc);
+ last_write_err = rc;
+ }
+ continue;
+ }
+ }
+ return 0;
+}
+
+
+int echo_srv_sync(void)
+{
+ pj_pool_t *pool;
+ pj_sock_t sock;
+ pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+ pj_status_t rc;
+ int i;
+
+ pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+ if (!pool)
+ return -5;
+
+ rc = pj_atomic_create(pool, 0, &total_bytes);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create atomic_var", rc);
+ return -6;
+ }
+
+ rc = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(),0, ECHO_SERVER_START_PORT, &sock);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...socket error", rc);
+ return -10;
+ }
+
+ for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+ rc = pj_thread_create(pool, NULL, &worker_thread, (void*)sock,
+ PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+ &thread[i]);
+ if (rc != PJ_SUCCESS) {
+ app_perror("...unable to create thread", rc);
+ return -20;
+ }
+ }
+
+ PJ_LOG(3,("", "...UDP echo server running with %d threads at port %d",
+ ECHO_SERVER_MAX_THREADS, ECHO_SERVER_START_PORT));
+ PJ_LOG(3,("", "...Press Ctrl-C to abort"));
+
+ echo_srv_common_loop(total_bytes);
+ return 0;
+}
+
+
+int echo_srv_common_loop(pj_atomic_t *bytes_counter)
+{
+ pj_highprec_t last_received, avg_bw, highest_bw;
+ pj_time_val last_print;
+ unsigned count;
+ const char *ioqueue_name;
+
+ last_received = 0;
+ pj_gettimeofday(&last_print);
+ avg_bw = highest_bw = 0;
+ count = 0;
+
+ ioqueue_name = pj_ioqueue_name();
+
+ for (;;) {
+ pj_highprec_t received, cur_received, bw;
+ unsigned msec;
+ pj_time_val now, duration;
+
+ pj_thread_sleep(1000);
+
+ received = cur_received = pj_atomic_get(bytes_counter);
+ cur_received = cur_received - last_received;
+
+ pj_gettimeofday(&now);
+ duration = now;
+ PJ_TIME_VAL_SUB(duration, last_print);
+ msec = PJ_TIME_VAL_MSEC(duration);
+
+ bw = cur_received;
+ pj_highprec_mul(bw, 1000);
+ pj_highprec_div(bw, msec);
+
+ last_print = now;
+ last_received = received;
+
+ avg_bw = avg_bw + bw;
+ count++;
+
+ PJ_LOG(3,("", "%s UDP (%d threads): %u KB/s (avg=%u KB/s) %s",
+ ioqueue_name,
+ ECHO_SERVER_MAX_THREADS,
+ (unsigned)(bw / 1000),
+ (unsigned)(avg_bw / count / 1000),
+ (count==20 ? "<ses avg>" : "")));
+
+ if (count==20) {
+ if (avg_bw/count > highest_bw)
+ highest_bw = avg_bw/count;
+
+ count = 0;
+ avg_bw = 0;
+
+ PJ_LOG(3,("", "Highest average bandwidth=%u KB/s",
+ (unsigned)(highest_bw/1000)));
+ }
+ }
+ PJ_UNREACHED(return 0;)
+}
+
+
diff --git a/pjlib/src/pjlib-test/util.c b/pjlib/src/pjlib-test/util.c
new file mode 100644
index 0000000..32aa938
--- /dev/null
+++ b/pjlib/src/pjlib-test/util.c
@@ -0,0 +1,140 @@
+/* $Id: util.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#define THIS_FILE "util.c"
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+ char errbuf[PJ_ERR_MSG_SIZE];
+
+ PJ_CHECK_STACK();
+
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ PJ_LOG(3,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define SERVER 0
+#define CLIENT 1
+
+pj_status_t app_socket(int family, int type, int proto, int port,
+ pj_sock_t *ptr_sock)
+{
+ pj_sockaddr_in addr;
+ pj_sock_t sock;
+ pj_status_t rc;
+
+ rc = pj_sock_socket(family, type, proto, &sock);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = (pj_uint16_t)family;
+ addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0);
+ rc = pj_sock_bind(sock, &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+#if PJ_HAS_TCP
+ if (type == pj_SOCK_STREAM()) {
+ rc = pj_sock_listen(sock, 5);
+ if (rc != PJ_SUCCESS)
+ return rc;
+ }
+#endif
+
+ *ptr_sock = sock;
+ return PJ_SUCCESS;
+}
+
+pj_status_t app_socketpair(int family, int type, int protocol,
+ pj_sock_t *serverfd, pj_sock_t *clientfd)
+{
+ int i;
+ static unsigned short port = 11000;
+ pj_sockaddr_in addr;
+ pj_str_t s;
+ pj_status_t rc = 0;
+ pj_sock_t sock[2];
+
+ /* Create both sockets. */
+ for (i=0; i<2; ++i) {
+ rc = pj_sock_socket(family, type, protocol, &sock[i]);
+ if (rc != PJ_SUCCESS) {
+ if (i==1)
+ pj_sock_close(sock[0]);
+ return rc;
+ }
+ }
+
+ /* Retry bind */
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = pj_AF_INET();
+ for (i=0; i<5; ++i) {
+ addr.sin_port = pj_htons(port++);
+ rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr));
+ if (rc == PJ_SUCCESS)
+ break;
+ }
+
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* For TCP, listen the socket. */
+#if PJ_HAS_TCP
+ if (type == pj_SOCK_STREAM()) {
+ rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN);
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+ }
+#endif
+
+ /* Connect client socket. */
+ addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+ rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr));
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* For TCP, must accept(), and get the new socket. */
+#if PJ_HAS_TCP
+ if (type == pj_SOCK_STREAM()) {
+ pj_sock_t newserver;
+
+ rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL);
+ if (rc != PJ_SUCCESS)
+ goto on_error;
+
+ /* Replace server socket with new socket. */
+ pj_sock_close(sock[SERVER]);
+ sock[SERVER] = newserver;
+ }
+#endif
+
+ *serverfd = sock[SERVER];
+ *clientfd = sock[CLIENT];
+
+ return rc;
+
+on_error:
+ for (i=0; i<2; ++i)
+ pj_sock_close(sock[i]);
+ return rc;
+}