From cdd43808a15a1439f62ed84936303ca64193f6bd Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 8 Jun 2024 17:32:34 +0700 Subject: [PATCH 01/79] Initial work on unittest framework, tested --- pjlib/build/Makefile | 4 +- pjlib/build/pjlib.vcproj | 112 +++++ pjlib/build/pjlib.vcxproj | 1 + pjlib/build/pjlib.vcxproj.filters | 3 + pjlib/build/pjlib_test.vcproj | 112 +++++ pjlib/build/pjlib_test.vcxproj | 1 + pjlib/build/pjlib_test.vcxproj.filters | 3 + pjlib/include/pj/fifobuf.h | 107 ++++- pjlib/include/pj/unittest.h | 585 ++++++++++++++++++++++++ pjlib/include/pjlib.h | 1 + pjlib/src/pj/fifobuf.c | 53 ++- pjlib/src/pj/unittest.c | 600 +++++++++++++++++++++++++ pjlib/src/pjlib-test/fifobuf.c | 100 ++++- pjlib/src/pjlib-test/test.c | 4 + pjlib/src/pjlib-test/test.h | 2 + pjlib/src/pjlib-test/unittest_test.c | 467 +++++++++++++++++++ 16 files changed, 2107 insertions(+), 48 deletions(-) create mode 100644 pjlib/include/pj/unittest.h create mode 100644 pjlib/src/pj/unittest.c create mode 100644 pjlib/src/pjlib-test/unittest_test.c diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index 7d16780435..d13951b042 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -36,7 +36,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ rbtree.o sock_common.o sock_qos_common.o \ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ - ssl_sock_darwin.o string.o timer.o types.o + ssl_sock_darwin.o string.o timer.o types.o unittest.o export PJLIB_CFLAGS += $(_CFLAGS) export PJLIB_CXXFLAGS += $(_CXXFLAGS) export PJLIB_LDFLAGS += $(_LDFLAGS) @@ -52,7 +52,7 @@ export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ select.o sleep.o sock.o sock_perf.o ssl_sock.o \ string.o test.o thread.o timer.o timestamp.o \ udp_echo_srv_sync.o udp_echo_srv_ioqueue.o \ - util.o + unittest_test.o util.o export TEST_CFLAGS += $(_CFLAGS) export TEST_CXXFLAGS += $(_CXXFLAGS) export TEST_LDFLAGS += $(PJLIB_LDLIB) $(_LDFLAGS) diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index e3043794c2..281b80f32f 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -8420,6 +8420,118 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 90ffe174f7..4055991ce1 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -1029,6 +1029,7 @@ + diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index f4b5682a5d..0d2deda55a 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -173,6 +173,9 @@ Source Files + + Source Files + Source Files\Other Targets diff --git a/pjlib/build/pjlib_test.vcproj b/pjlib/build/pjlib_test.vcproj index 6876328118..1196a15823 100644 --- a/pjlib/build/pjlib_test.vcproj +++ b/pjlib/build/pjlib_test.vcproj @@ -7334,6 +7334,118 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pjlib/build/pjlib_test.vcxproj b/pjlib/build/pjlib_test.vcxproj index 69d0bef86c..0801881867 100644 --- a/pjlib/build/pjlib_test.vcxproj +++ b/pjlib/build/pjlib_test.vcxproj @@ -795,6 +795,7 @@ + diff --git a/pjlib/build/pjlib_test.vcxproj.filters b/pjlib/build/pjlib_test.vcxproj.filters index 181327a4cc..64451af637 100644 --- a/pjlib/build/pjlib_test.vcxproj.filters +++ b/pjlib/build/pjlib_test.vcxproj.filters @@ -114,6 +114,9 @@ Source Files + + Source Files + Source Files diff --git a/pjlib/include/pj/fifobuf.h b/pjlib/include/pj/fifobuf.h index 26b8fb776e..c457960afe 100644 --- a/pjlib/include/pj/fifobuf.h +++ b/pjlib/include/pj/fifobuf.h @@ -19,25 +19,112 @@ #ifndef __PJ_FIFOBUF_H__ #define __PJ_FIFOBUF_H__ +/** + * @file fifobuf.h + * @brief Circular buffer + */ +/** + * @defgroup PJ_FIFOBUF Circular buffer + * @ingroup PJ_DS + * @{ + */ + #include PJ_BEGIN_DECL -typedef struct pj_fifobuf_t pj_fifobuf_t; -struct pj_fifobuf_t + +/** + * FIFO buffer or circular buffer. + */ +typedef struct pj_fifobuf_t { - char *first, *last; - char *ubegin, *uend; + /** The start of the buffer */ + char *first; + + /** The end of the buffer */ + char *last; + + /** The start of empty area in the buffer */ + char *ubegin; + + /** The end of empty area in the buffer */ + char *uend; + + /** Full flag when ubegin==uend */ int full; -}; -PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); -PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); -PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); -PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); -PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); +} pj_fifobuf_t; + + +/** + * Initialize the circular buffer by giving it a buffer and size. + * + * @param fb The fifobuf/circular buffer + * @param buffer Buffer to be used to allocate/free chunks of memory from by + * the circular buffer. + * @param size The size of the buffer. + */ +PJ_DECL(void) pj_fifobuf_init(pj_fifobuf_t *fb, void *buffer, unsigned size); + +/** + * Returns the capacity (initial size) of the buffer. + * + * @param fb The fifobuf/circular buffer + * + * @return Capacity in bytes. + */ +PJ_DECL(unsigned) pj_fifobuf_capacity(pj_fifobuf_t *fb); + +/** + * Returns maximum buffer size that can be allocated from the circular buffer. + * + * @param fb The fifobuf/circular buffer + * + * @return Free size in bytes + */ +PJ_DECL(unsigned) pj_fifobuf_available_size(pj_fifobuf_t *fb); + +/** + * Allocate a buffer from the circular buffer. + * + * @param fb The fifobuf/circular buffer + * @param size Size to allocate + * + * @return Allocated buffer or NULL if the buffer cannot be allocated. + */ +PJ_DECL(void*) pj_fifobuf_alloc(pj_fifobuf_t *fb, unsigned size); + +/** + * Free up space used by the last allocated buffer. For example, if you + * allocated ptr0, ptr1, and ptr2, this function is used to free ptr2. + * + * @param fb The fifobuf/circular buffer + * @param buf The buffer to be freed. This is the pointer returned by + * pj_fifobuf_alloc() + * + * @return PJ_SUCCESS or the appropriate error. + */ +PJ_DECL(pj_status_t) pj_fifobuf_unalloc(pj_fifobuf_t *fb, void *buf); + +/** + * Free up space used by the earliest allocated buffer. For example, if you + * allocated ptr0, ptr1, and ptr2, this function is used to free ptr0. + * + * @param fb The fifobuf/circular buffer + * @param buf The buffer to be freed. This is the pointer returned by + * pj_fifobuf_alloc() + * + * @return PJ_SUCCESS or the appropriate error. + */ +PJ_DECL(pj_status_t) pj_fifobuf_free(pj_fifobuf_t *fb, void *buf); + PJ_END_DECL +/** + * @} // PJ_FIFOBUF group + */ + #endif /* __PJ_FIFOBUF_H__ */ diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h new file mode 100644 index 0000000000..5b780f2dbc --- /dev/null +++ b/pjlib/include/pj/unittest.h @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2008-2024 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 + */ +#ifndef __PJ_UNITTEST_H__ +#define __PJ_UNITTEST_H__ + +/** + * @file testing.h + * @brief PJLIB unit testing framework + */ +/** + * @defgroup PJ_UNITTEST Unit testing framework + * @ingroup PJ_MISC + * @{ + */ +#include +#include +#include +#include + +PJ_BEGIN_DECL + +/* + * These various PJ_TEST_XXX macros can be used in any programs without + * having to use the unit-test framework. + */ + +/** + * Special constant as retval in the various PJ_TEST_XXX macros, to tell + * the macro NOT to issue return when the test fails. + */ +#define PJ_TEST_NO_RET 0xbe2decb1 + +/** + * Check that an expression is non-zero. If the check fails, informative error + * message will be displayed, and the test will return with the value specified + * in retval, unless retval is PJ_TEST_NO_RET which in this case the test + * function will not return on failure. + * + * @param expr The expression to check + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_NON_ZERO(expr, retval, reason) \ + do { \ + if (!(expr)) { \ + const char *tmp_reason_ = reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test \"%s\" != 0 fails in " \ + "%s:%d%s%s%s", \ + #expr, THIS_FILE,__LINE__,sep0_, \ + tmp_reason_,sep1_));\ + if (retval != PJ_TEST_NO_RET) return retval; \ + } \ + } while (0) + +/** + * Generic check for binary operation. If the check fails, informative error + * message will be displayed, and the test will return with the value specified + * in retval, unless retval is PJ_TEST_NO_RET which in this case the test + * function will not return on failure. + * + * @param expr0 First expression + * @param op The operator + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_BINARY_OP(expr0, op, expr1, retval, reason) \ + do { \ + long tmp_value0_ = (long)(expr0); \ + long tmp_value1_ = (long)(expr1); \ + if (!(tmp_value0_ op tmp_value1_)) { \ + const char *tmp_reason_ = reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test \"%s\" (value=%ld) " #op \ + " \"%s\" (value=%ld) fails in %s:%d%s%s%s", \ + #expr0, tmp_value0_, #expr1, tmp_value1_, \ + THIS_FILE, __LINE__, \ + sep0_, tmp_reason_, sep1_)); \ + if (retval != PJ_TEST_NO_RET) return retval; \ + } \ + } while (0) + +/** + * Check that an expression is PJ_SUCCESS. If the check fails, error message + * explaining the error code will be displayed, and the test will return with + * the value specified in retval, unless retval is PJ_TEST_NO_RET which in + * this case the test function will not return on failure. + * + * @param expr The expression to check + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_SUCCESS(expr, retval, reason) \ + do { \ + pj_status_t tmp_status_ = (expr); \ + if (tmp_status_ != PJ_SUCCESS) { \ + char errbuf[80]; \ + const char *tmp_reason_ = reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + pj_strerror(tmp_status_, errbuf, sizeof(errbuf)); \ + PJ_LOG(1,(THIS_FILE, "\"%s\" fails in %s:%d, " \ + "status=%d (%s)%s%s%s", \ + #expr, THIS_FILE, __LINE__, tmp_status_,errbuf,\ + sep0_, tmp_reason_, sep1_)); \ + if (retval != PJ_TEST_NO_RET) return retval; \ + } \ + } while (0) + +/** + * Alias for PJ_TEST_NON_ZERO() + */ +#define PJ_TEST_TRUE(expr, retval, reason) \ + PJ_TEST_NON_ZERO(expr, retval, reason) + +/** + * Alias for PJ_TEST_NON_ZERO() + */ +#define PJ_TEST_NOT_NULL(expr, retval, reason) \ + PJ_TEST_NON_ZERO(expr, retval, reason) + +/** + * Check that expr0 equals expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_EQ(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, ==, expr1, retval, reason) + +/** + * Check that expr0 not equals expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_NEQ(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, !=, expr1, retval, reason) + +/** + * Check that expr0 is less than expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_LT(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, <, expr1, retval, reason) + +/** + * Check that expr0 is less than or equal to expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_LTE(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, <=, expr1, retval, reason) + +/** + * Check that expr0 is greater than expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_GT(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, >, expr1, retval, reason) + +/** + * Check that expr0 is greater than or equal to expr1. + * If the check fails, informative error message will be displayed, and + * the test will return with the value specified in retval, unless retval + * is PJ_TEST_NO_RET which in this case the test function will not return + * on failure. + * + * @param expr0 First expression + * @param expr1 Second expression + * @param retval Return value on error, or PJ_TEST_NO_RET. + * @param reason NULL or extra text to be displayed when the check fails + */ +#define PJ_TEST_GTE(expr0, expr1, retval, reason) \ + PJ_TEST_BINARY_OP(expr0, >=, expr1, retval, reason) + + +/** + * Bitwise constants that can be used in test case flags (see + * pj_test_case_init()). + */ +typedef enum pj_test_case_flag +{ + /** + * Allow next test case to run in parallel. If this flag is not specified, + * next test case will have to wait until current test finishes before + * it can run. Note this only works for test runners that support + * worker threads. + */ + PJ_TEST_PARALLEL = 1, + + /** + * Specify that the test function must be called without argument. + * This is mainly for backward compatibility with existing PJ test + * functions which take no argument. + */ + PJ_TEST_FUNC_NO_ARG = 2, + + /** + * Write the original log at the time it is called instead of pooling + * the logs to be printed after all tests finish. + */ + PJ_TEST_ORIGINAL_LOG = 4, + +} pj_test_case_flag; + + +/** + * An internal structure to represent one logging item that is saved + * inside pj_test_case. + */ +typedef struct pj_test_log_item +{ + PJ_DECL_LIST_MEMBER(struct pj_test_log_item); + + /** level */ + int level; + + /** len */ + int len; + + /** The log message. The actual buffer is longer. */ + char msg[1]; + +} pj_test_log_item; + + +/** Forward declaration of test runner */ +typedef struct pj_test_runner pj_test_runner; + + +/** + * Additional parameters for creating test case. Use + * pj_test_case_param_default() to initialize this structure. + */ +typedef struct pj_test_case_param +{ + /** + * Custom log level for this test case, to filter out logs that are more + * detail than this level. Default is 6, meaning it will accept all log + * levels. + */ + int log_level; + +} pj_test_case_param; + + +/** + * A test case is unit-test object to perform test against a user defined + * function. + */ +typedef struct pj_test_case +{ + PJ_DECL_LIST_MEMBER(struct pj_test_case); + + /** Test name */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** + * The test function to be called to perform the test. By convention, the + * function must return zero for the test to be considered successful, + * non-zero on failure, and MUST NEVER return PJ_EPENDING, otherwise the + * return value will be silently changed to -12345. + */ + int (*test_func)(void*); + + /** Argument to be passed to the test function */ + void *arg; + + /** Flags, combination of pj_test_flag constants */ + unsigned flags; + + /** Circular buffer for logging */ + pj_fifobuf_t fb; + + /** Parameters */ + pj_test_case_param prm; + + /** + * The return value of the test function. Zero indicates success. Initially + * the value is PJ_EPENDING before the test is run. + */ + int result; + + /** List of saved logging messages */ + pj_test_log_item logs; + + /** Pointer to the runner running this test case */ + pj_test_runner *runner; + + /** Index/position in the suite */ + unsigned index; + + /** Total number of test cases in the suite */ + unsigned total; + +} pj_test_case; + + +/** + * Test suite is a collection of test cases. + */ +typedef struct pj_test_suite +{ + /** List of tests */ + pj_test_case tests; + + /** Start time */ + pj_timestamp start_time; + + /** End time */ + pj_timestamp end_time; + +} pj_test_suite; + + +/** + * Test statistics. Collect the statistics after the test runner finishes + * with pj_test_get_stat(). + */ +typedef struct pj_test_stat +{ + /** Total duration */ + pj_time_val duration; + + /** Total number of tests in the test suite */ + unsigned ntests; + + /** Number of tests run */ + unsigned nruns; + + /** Number of failed tests */ + unsigned nfailed; + + /** + * Array of failed test names. Be careful that the number elements are + * fixed, hence it may not be able to store all failed test names (in case + * nfailed is more than the capacity, not all failed test names will be + * stored, hence be careful in the loop). + */ + const char *failed_names[32]; + +} pj_test_stat; + + +/** + * Options for creating text runner. + */ +typedef struct pj_test_text_runner_param +{ + /** Number of worker threads. Set to zero to disable parallel testings */ + unsigned nthreads; + +} pj_test_text_runner_param; + + +/** + * This structure represents a test runner. Currently there are two types + * of test runners, the basic runner and text runner. The basic runner is the + * simplest test runner that can be used without pool and threads, and can be + * created with pj_test_init_basic_runner(). The text runner is more powerful + * since it supports worker threads, and it is mostly suitable for console + * based environments. It is created with pj_test_create_text_runner(). + */ +struct pj_test_runner +{ + /** The test suite being run */ + pj_test_suite *suite; + + /** Saving the original log writer */ + pj_log_func *orig_log_writer; + + /** main method */ + void (*main)(pj_test_runner*); + + /** callback when test case completes. Default is to write to log */ + void (*on_test_complete)(pj_test_runner*, pj_test_case*); + + /** destroy method */ + void (*destroy)(pj_test_runner*); +}; + +/** Option to select tests (e.g. in pj_test_dump_log_messages()) */ +typedef enum pj_test_select_tests +{ + /** Select all tests*/ + PJ_TEST_ALL_TESTS = 0, + + /** Select only failed tests */ + PJ_TEST_FAILED_TESTS = 1, + + /** Select only successful tests */ + PJ_TEST_SUCCESSFUL_TESTS = 2, + +} pj_test_select_tests; + + +/** + * Initialize test suite. + * + * @param suite The test suite + */ +PJ_DECL(void) pj_test_suite_init(pj_test_suite *suite); + +/** + * Initialize pj_test_case_param with default values. If app only uses + * default values in params, alternatively it doesn't need to use param + * at all and just specify NULL in pj_test_case_init(). + * + * @param prm The parameter. + */ +PJ_DECL(void) pj_test_case_param_default(pj_test_case_param *prm); + +/** + * Initialize test case. + * + * @param tc The test case + * @param obj_name Name that will appear as test name/title + * @param flags Bitwise of pj_test_case_flag to control threading, + * function calling, logging, etc. + * @param test_func The test function to be called to perform the test. + * By convention, the function must return zero for the + * test to be considered successful, non-zero on failure, + * and MUST NEVER return PJ_EPENDING, otherwise the + * return value will be silently changed to -12345. + * @param arg Argument to give to the test function + * @param fifobuf_buf Buffer for saving the logs, if required. + * @param buf_size Size of the buffer for saving the logs. + * @param prm Optional additional settings for the test case or NULL + */ +PJ_DECL(void) pj_test_case_init(pj_test_case *tc, + const char *obj_name, + unsigned flags, + int (*test_func)(void*), + void *arg, + void *fifobuf_buf, + unsigned buf_size, + const pj_test_case_param *prm); + +/** + * Add test case to test suite. A test case can only be added to one suite. + * + * @param suite The test suite + * @param tc The test case + */ +PJ_DECL(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc); + +/** + * Initialize a basic test runner. A basic runner can be declared in the stack + * and it does not require pool nor multithreading. + * + * @param runner The runner. + */ +PJ_DECL(void) pj_test_init_basic_runner(pj_test_runner *runner); + +/** + * Initialize parameters with reasonable default values. This usually means + * using one worker thread if threading is enabled, and zero worker thread + * (i.e. only use the main thread) otherwise. + * + * @param prm Text runner parameter + */ +PJ_DECL(void) pj_test_text_runner_param_default( + pj_test_text_runner_param *prm); + +/** + * Create console based test runner. + * + * @param pool The pool to use to allocate memory + * @param prm Text runner parameter, or NULL for default values. + * @param p_runner Pointer to receive the text runner + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_test_create_text_runner( + pj_pool_t *pool, + const pj_test_text_runner_param *prm, + pj_test_runner **p_runner); + +/** + * Run test suite with the specified runner. + * + * @param runner The test runner + * @param suite The test suite + */ +PJ_DECL(void) pj_test_run(pj_test_runner *runner, + pj_test_suite *suite); + +/** + * Get the test statistics after the run completes. The test suite and + * test cases instances must be kept alive in order to get and access the + * statistics or log messages. + * + * @param suite The test suite + * @param stat The test statistics result. + */ +PJ_DECL(void) pj_test_get_stat(const pj_test_suite *suite, pj_test_stat *stat); + +/** + * Dump previously saved log messages in the test cases to logging. + * Note that log messages emited during test case's run are only saved + * when fifobuf of the test case is configured with a suitable buffer. + * Also note that the test suite and test cases instances must be kept alive + * in order to get and access the statistics or log messages. + * + * @param suite The test suite + * @param which Which test cases to dump + */ +PJ_DECL(void) pj_test_dump_log_messages(const pj_test_suite *suite, + pj_test_select_tests which); + +/** + * Destroy the runner. Runner may be destroyed right after it is run, + * but the test suite and test cases instances must be kept alive in order + * to get the statistics or log messages. + * + * @param runner The test runner. + */ +PJ_DECL(void) pj_test_runner_destroy(pj_test_runner *runner); + + +PJ_END_DECL + +/** + * @} // PJ_UNITTEST group + */ + +#endif /* __PJ_UNITTEST_H__ */ + diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index dfb9e53f24..1bc591be51 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -55,6 +55,7 @@ #include #include #include +#include #include diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c index 464f3114cc..5acd3220bc 100644 --- a/pjlib/src/pj/fifobuf.c +++ b/pjlib/src/pj/fifobuf.c @@ -20,11 +20,25 @@ #include #include #include +#include #define THIS_FILE "fifobuf" #define SZ sizeof(unsigned) +/* put and get size at arbitrary, possibly unaligned location */ +PJ_INLINE(void) put_size(void *ptr, unsigned size) +{ + pj_memcpy(ptr, &size, sizeof(size)); +} + +PJ_INLINE(unsigned) get_size(const void *ptr) +{ + unsigned size; + pj_memcpy(&size, ptr, sizeof(size)); + return size; +} + PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) { PJ_CHECK_STACK(); @@ -36,23 +50,38 @@ PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size fifobuf->first = (char*)buffer; fifobuf->last = fifobuf->first + size; fifobuf->ubegin = fifobuf->uend = fifobuf->first; - fifobuf->full = 0; + fifobuf->full = (fifobuf->last==fifobuf->first); } -PJ_DEF(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) +PJ_DEF(unsigned) pj_fifobuf_capacity (pj_fifobuf_t *fifobuf) { - unsigned s1, s2; + unsigned cap = (unsigned)(fifobuf->last - fifobuf->first); + return (cap > 0) ? cap-SZ : 0; +} +PJ_DEF(unsigned) pj_fifobuf_available_size (pj_fifobuf_t *fifobuf) +{ PJ_CHECK_STACK(); + if (fifobuf->full) + return 0; + if (fifobuf->uend >= fifobuf->ubegin) { - s1 = (unsigned)(fifobuf->last - fifobuf->uend); - s2 = (unsigned)(fifobuf->ubegin - fifobuf->first); + unsigned s; + unsigned s1 = (unsigned)(fifobuf->last - fifobuf->uend); + unsigned s2 = (unsigned)(fifobuf->ubegin - fifobuf->first); + if (s1 <= SZ) + s = s2; + else if (s2 <= SZ) + s = s1; + else + s = s1=SZ) ? s-SZ : 0; } else { - s1 = s2 = (unsigned)(fifobuf->ubegin - fifobuf->uend); + unsigned s = (unsigned)(fifobuf->ubegin - fifobuf->uend); + return (s>=SZ) ? s-SZ : 0; } - - return s1uend = fifobuf->first; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; + put_size(ptr, size+SZ); ptr += SZ; PJ_LOG(6, (THIS_FILE, @@ -97,7 +126,7 @@ PJ_DEF(void*) pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size) fifobuf->uend = start + size + SZ; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; + put_size(ptr, size+SZ); ptr += SZ; PJ_LOG(6, (THIS_FILE, @@ -121,7 +150,7 @@ PJ_DEF(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) PJ_CHECK_STACK(); ptr -= SZ; - sz = *(unsigned*)ptr; + sz = get_size(ptr); endptr = fifobuf->uend; if (endptr == fifobuf->first) @@ -162,7 +191,7 @@ PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) } end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; - sz = *(unsigned*)ptr; + sz = get_size(ptr); if (ptr+sz > end) { pj_assert(!"Invalid size!"); return -1; diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c new file mode 100644 index 0000000000..83b30c1895 --- /dev/null +++ b/pjlib/src/pj/unittest.c @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2008-2024 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 + */ +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "unittest.c" +#define INVALID_TLS_ID -1 + +static long tls_id = INVALID_TLS_ID; +static pj_test_case *tc_main_thread; + +/* Forward decls. */ +static void unittest_log_callback(int level, const char *data, int len); +static int get_completion_line( const pj_test_case *tc, const char *end_line, + char *log_buf, unsigned buf_size); + +/* atexit() callback to free TLS */ +static void unittest_shutdown(void) +{ + if (tls_id != INVALID_TLS_ID) { + pj_thread_local_free(tls_id); + tls_id = INVALID_TLS_ID; + } +} + +/* initialize unittest subsystem. can be called many times. */ +static pj_status_t unittest_init(void) +{ +#if PJ_HAS_THREADS + if (tls_id == INVALID_TLS_ID) { + pj_status_t status; + status = pj_thread_local_alloc(&tls_id); + if (status != PJ_SUCCESS) { + tls_id = INVALID_TLS_ID; + return status; + } + + pj_atexit(&unittest_shutdown); + } +#endif + return PJ_SUCCESS; +} + +/* Initialize param with default values */ +PJ_DEF(void) pj_test_case_param_default( pj_test_case_param *prm) +{ + pj_bzero(prm, sizeof(*prm)); + prm->log_level = 6; +} + +/* Initialize test case */ +PJ_DEF(void) pj_test_case_init( pj_test_case *tc, + const char *obj_name, + unsigned flags, + int (*test_func)(void*), + void *arg, + void *fifobuf_buf, + unsigned buf_size, + const pj_test_case_param *prm) +{ + pj_bzero(tc, sizeof(*tc)); + + /* Parameters */ + if (prm) { + pj_memcpy(&tc->prm, prm, sizeof(*prm)); + } else { + pj_test_case_param_default(&tc->prm); + } + pj_ansi_strxcpy(tc->obj_name, obj_name, sizeof(tc->obj_name)); + tc->flags = flags; + tc->test_func = test_func; + tc->arg = arg; + pj_fifobuf_init(&tc->fb, fifobuf_buf, buf_size); + + /* Run-time state */ + tc->result = PJ_EPENDING; + pj_list_init(&tc->logs); +} + +/* Init test suite */ +PJ_DEF(void) pj_test_suite_init(pj_test_suite *suite) +{ + pj_bzero(suite, sizeof(*suite)); + pj_list_init(&suite->tests); +} + +/* Add test case */ +PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) +{ + pj_list_push_back(&suite->tests, tc); +} + +/* Initialize text runner param with default values */ +PJ_DEF(void) pj_test_text_runner_param_default( + pj_test_text_runner_param *prm) +{ + pj_bzero(prm, sizeof(*prm)); +#if PJ_HAS_THREADS + prm->nthreads = 1; +#endif +} + +/* Main API to start running a test runner */ +PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) +{ + unsigned index, total; + pj_test_case *tc; + + /* Redirect logging to our custom callback */ + runner->orig_log_writer = pj_log_get_log_func(); + pj_log_set_log_func(&unittest_log_callback); + + /* Initialize suite and test cases */ + runner->suite = suite; + total = pj_list_size(&suite->tests); + for (tc=suite->tests.next, index=0; tc!=&suite->tests; + tc=tc->next, ++index) + { + tc->result = PJ_EPENDING; + tc->runner = runner; + tc->index = index; + tc->total = total; + } + + /* Call the run method to perform runner specific loop */ + pj_get_timestamp(&suite->start_time); + runner->main(runner); + pj_get_timestamp(&suite->end_time); + + /* Restore logging */ + pj_log_set_log_func(runner->orig_log_writer); +} + +/* Calculate statistics */ +PJ_DEF(void) pj_test_get_stat( const pj_test_suite *suite, pj_test_stat *stat) +{ + const pj_test_case *tc; + + pj_bzero(stat, sizeof(*stat)); + stat->duration = pj_elapsed_time(&suite->start_time, &suite->end_time); + stat->ntests = pj_list_size(&suite->tests); + + for (tc=suite->tests.next; tc!=&suite->tests; tc=tc->next) { + if (tc->result != PJ_EPENDING) { + stat->nruns++; + if (tc->result != PJ_SUCCESS) { + if (stat->nfailed < PJ_ARRAY_SIZE(stat->failed_names)) { + stat->failed_names[stat->nfailed] = tc->obj_name; + } + stat->nfailed++; + } + } + } +} + +/* Dump previously saved log messages */ +PJ_DEF(void) pj_test_dump_log_messages( const pj_test_suite *suite, + pj_test_select_tests which) +{ + const pj_test_case *tc = suite->tests.next; + pj_log_func *log_writer = pj_log_get_log_func(); + char test_line[80]; + + while (tc != &suite->tests) { + const pj_test_log_item *log_item = tc->logs.next; + + if ((tc->result == PJ_EPENDING) || + (which==PJ_TEST_FAILED_TESTS && tc->result==0) || + (which==PJ_TEST_SUCCESSFUL_TESTS && tc->result!=0)) + { + /* Test doesn't meet criteria */ + tc = tc->next; + continue; + } + + if (log_item != &tc->logs) { + get_completion_line(tc, " logs:", test_line, sizeof(test_line)); + log_writer(3, test_line, pj_ansi_strlen(test_line)); + + do { + log_writer(log_item->level, log_item->msg, log_item->len); + log_item = log_item->next; + } while (log_item != &tc->logs); + } + tc = tc->next; + } +} + +/* Destroy runner */ +PJ_DEF(void) pj_test_runner_destroy(pj_test_runner *runner) +{ + runner->destroy(runner); +} + +/**************************** Common for runners ****************************/ + +/* Set the current test case being run by a thread. The logging callback + * needs this info. + */ +static void set_current_test_case(pj_test_case *tc) +{ + if (tls_id == INVALID_TLS_ID) + tc_main_thread = tc; + else + pj_thread_local_set(tls_id, tc); +} + + +/* Get the current test case being run by a thread. The logging callback + * needs this info. + */ +static pj_test_case *get_current_test_case() +{ + if (tls_id == INVALID_TLS_ID) + return tc_main_thread; + else + return (pj_test_case*) pj_thread_local_get(tls_id); +} + +/* Logging callback */ +static void unittest_log_callback(int level, const char *data, int len) +{ + pj_test_case *tc = get_current_test_case(); + unsigned req_size, free_size; + pj_bool_t truncated; + pj_test_log_item *log_item; + + PJ_ASSERT_ON_FAIL(len > 0, return); + PJ_ASSERT_ON_FAIL(tc, return); + + /* Filter out unwanted log */ + if (level > tc->prm.log_level) + return; + + /* If the test case wants to display the original log as they are called, + * then write it using the original logging writer now. + */ + if (tc->flags & PJ_TEST_ORIGINAL_LOG) { + tc->runner->orig_log_writer(level, data, len); + return; + } + + /* If fifobuf is not configured on this test case, there's nothing + * we can do. We assume tester doesn't want logging. + */ + if (pj_fifobuf_capacity(&tc->fb)==0) + return; + + /* Required size is the message length plus sizeof(pj_test_log_item). + * This should be enough to save the message INCLUDING the null + * character (because of msg[1] in pj_test_log_item) + */ + req_size = len + sizeof(pj_test_log_item); + + /* Free the buffer until it's enough to save the message. */ + while ((free_size = pj_fifobuf_available_size(&tc->fb)) < req_size && + !pj_list_empty(&tc->logs)) + { + pj_test_log_item *first = tc->logs.next; + + /* Free the oldest */ + pj_list_erase(first); + pj_fifobuf_free(&tc->fb, first); + } + + if (free_size < sizeof(pj_test_log_item) + 10) { + /* Tester has set the fifobuf's size too small */ + return; + } + + if (free_size < req_size) { + /* Truncate message */ + len = free_size - sizeof(pj_test_log_item); + req_size = free_size; + truncated = PJ_TRUE; + } else { + truncated = PJ_FALSE; + } + + log_item = (pj_test_log_item*)pj_fifobuf_alloc(&tc->fb, req_size); + PJ_ASSERT_ON_FAIL(log_item, return); + log_item->level = level; + log_item->len = len; + pj_memcpy(log_item->msg, data, len+1); + if (truncated) + log_item->msg[len-1] = '\n'; + pj_list_push_back(&tc->logs, log_item); +} + +/* Create test case completion line, i.e. the one that looks like: + * [2/24] pool_test [OK] + */ +static int get_completion_line( const pj_test_case *tc, const char *end_line, + char *log_buf, unsigned buf_size) +{ + char res_buf[20]; + int log_len; + + if (tc->result==0) { + pj_ansi_strxcpy(res_buf, "OK", sizeof(res_buf)); + } else if (tc->result==PJ_EPENDING) { + pj_ansi_strxcpy(res_buf, "pending", sizeof(res_buf)); + } else { + pj_ansi_snprintf(res_buf, sizeof(res_buf), "Err: %d", tc->result); + } + + log_len = pj_ansi_snprintf(log_buf, buf_size, "[%d/%d] %-32s [%s]%s\n", + tc->index+1, tc->total, tc->obj_name, res_buf, + end_line); + + if (log_len < 1 || log_len >= sizeof(log_buf)) + log_len = pj_ansi_strlen(log_buf); + + return log_len; +} + +/* Default runner's callback when a test case completes. This must be + * reentrant from multiple threads + */ +static void on_test_complete(pj_test_runner *runner, pj_test_case *tc) +{ + char line[80]; + int len; + + len = get_completion_line(tc, "", line, sizeof(line)); + tc->runner->orig_log_writer(3, line, len); +} + +/* This is the main function to run a single test case. It may + * be used by the basic runner, which has no threads (=no TLS), + * no fifobuf, no pool, or by multiple threads. + */ +static void run_test_case(pj_test_runner *runner, pj_test_case *tc) +{ + /* Set the test case being worked on by this thread */ + set_current_test_case(tc); + + /* Call the test case's function */ + if (tc->flags & PJ_TEST_FUNC_NO_ARG) { + /* Function without argument */ + typedef int (*func_t)(void); + func_t func = (func_t)tc->test_func; + tc->result = func(); + } else { + tc->result = tc->test_func(tc->arg); + } + + if (tc->result == PJ_EPENDING) + tc->result = -12345; + + runner->on_test_complete(runner, tc); + + /* Reset the test case being worked on by this thread */ + set_current_test_case(NULL); +} + +/******************************* Basic Runner *******************************/ + +/* This is the "main()" function for basic runner. It just runs the tests + * sequentially + */ +static void basic_runner_main(pj_test_runner *runner) +{ + pj_test_case *tc; + for (tc = runner->suite->tests.next; + tc != &runner->suite->tests; + tc = tc->next) + { + run_test_case(runner, tc); + } +} + +/* Destroy for basic runner */ +static void basic_runner_destroy(pj_test_runner *runner) +{ + /* Nothing to do for basic runner */ +} + +/* Initialize a basic runner. */ +PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner) +{ + pj_bzero(runner, sizeof(*runner)); + runner->main = &basic_runner_main; + runner->destroy = &basic_runner_destroy; + runner->on_test_complete = &on_test_complete; +} + +/******************************* Text Runner *******************************/ + +typedef struct text_runner_t +{ + pj_test_runner base; + pj_test_text_runner_param prm; + + pj_test_case *cur_case; + pj_mutex_t *mutex; + pj_thread_t **threads; +} text_runner_t; + +/* This is called by thread(s) to get the next test case to run. + * The rule is: + * 1. if current test case (tc) doesn't have PJ_TEST_PARALLEL flag, then we + * cannot get next tc until that tc completes + * 2. if tc has PJ_TEST_PARALLEL flag, free to return next tc, until we + * meet tc with no PJ_TEST_PARALLEL flag, in this case that tc is + * returned and we'll have rule 1. + * + * Returns: + * - PJ_SUCCESS if we successfully returns next tc + * - PJ_EPENDING if there is tc left but we must wait for completion of + * non-parallel tc + * - PJ_ENOTFOUND if there is no further tests, which in this case the + * thread can exit. + * - other error is not anticipated, and will cause thread to exit. + */ +static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, + pj_test_case **p_test_case) +{ + pj_status_t status; + + *p_test_case = NULL; + pj_mutex_lock(runner->mutex); + + if (runner->cur_case == NULL) { + if (pj_list_empty(&runner->base.suite->tests)) { + status = PJ_ENOTFOUND; + goto on_return; + } + runner->cur_case = *p_test_case = runner->base.suite->tests.next; + status = PJ_SUCCESS; + } else if (runner->cur_case == &runner->base.suite->tests) { + /* All done */ + status = PJ_ENOTFOUND; + } else { + if (runner->cur_case->result == PJ_EPENDING) { + /* Test is still running. */ + if (runner->cur_case->flags & PJ_TEST_PARALLEL) { + /* Allow other test to run */ + runner->cur_case = runner->cur_case->next; + if (runner->cur_case == &runner->base.suite->tests) { + status = PJ_ENOTFOUND; + } else { + *p_test_case = runner->cur_case; + status = PJ_SUCCESS; + } + } else { + if (runner->cur_case->next == &runner->base.suite->tests) { + /* The current case is the last one. The calling thread + * can quit now. + */ + status = PJ_ENOTFOUND; + } else { + /* Current test case does not allow parallel run */ + status = PJ_EPENDING; + } + } + } else { + /* Current test is done, get next text */ + runner->cur_case = runner->cur_case->next; + if (runner->cur_case == &runner->base.suite->tests) { + status = PJ_ENOTFOUND; + } else { + *p_test_case = runner->cur_case; + status = PJ_SUCCESS; + } + } + } + +on_return: + pj_mutex_unlock(runner->mutex); + return status; +} + +/* Thread loop */ +static int text_runner_thread_proc(void *arg) +{ + text_runner_t *runner = (text_runner_t*)arg; + + for (;;) { + pj_test_case *tc; + pj_status_t status; + + status = text_runner_get_next_test_case(runner, &tc); + if (status==PJ_SUCCESS) { + run_test_case(&runner->base, tc); + } else if (status==PJ_EPENDING) { + /* Yeah sleep, but the "correct" solution is probably an order of + * magnitute more complicated, so this is good I think. + */ + pj_thread_sleep(250); + } else { + break; + } + } + return 0; +} + +/* This is the "main()" function for text runner. */ +static void text_runner_main(pj_test_runner *base) +{ + text_runner_t *runner = (text_runner_t*)base; + unsigned i; + + for (i=0; iprm.nthreads; ++i) { + pj_thread_resume(runner->threads[i]); + } + + /* The main thread behaves like another worker thread */ + text_runner_thread_proc(base); + + for (i=0; iprm.nthreads; ++i) { + pj_thread_join(runner->threads[i]); + } +} + +/* text runner destructor */ +static void text_runner_destroy(pj_test_runner *base) +{ + text_runner_t *runner = (text_runner_t*)base; + unsigned i; + + for (i=0; iprm.nthreads; ++i) { + pj_thread_destroy(runner->threads[i]); + } + if (runner->mutex) + pj_mutex_destroy(runner->mutex); + +} + +/* Create text runner */ +PJ_DEF(pj_status_t) pj_test_create_text_runner( + pj_pool_t *pool, + const pj_test_text_runner_param *prm, + pj_test_runner **p_runner) +{ + text_runner_t *runner; + unsigned i; + pj_status_t status; + + *p_runner = NULL; + + status = unittest_init(); + if (status != PJ_SUCCESS) + return status; + + runner = PJ_POOL_ZALLOC_T(pool, text_runner_t); + runner->base.main = text_runner_main; + runner->base.destroy = text_runner_destroy; + runner->base.on_test_complete = &on_test_complete; + + status = pj_mutex_create(pool, "unittest%p", PJ_MUTEX_RECURSE, + &runner->mutex); + if (status != PJ_SUCCESS) + goto on_error; + + if (prm) { + pj_memcpy(&runner->prm, prm, sizeof(*prm)); + } else { + pj_test_text_runner_param_default(&runner->prm); + } + runner->prm.nthreads = 0; + runner->threads = (pj_thread_t**) pj_pool_calloc(pool, prm->nthreads, + sizeof(pj_thread_t*)); + for (i=0; inthreads; ++i) { + status = pj_thread_create(pool, "unittest%p", + text_runner_thread_proc, runner, + 0, PJ_THREAD_SUSPENDED, + &runner->threads[i]); + if (status != PJ_SUCCESS) + goto on_error; + runner->prm.nthreads++; + } + + *p_runner = (pj_test_runner*)runner; + return PJ_SUCCESS; + +on_error: + text_runner_destroy(&runner->base); + return status; +} + diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index fb38460aa4..9ddc1aaf7a 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -18,6 +18,7 @@ */ #include "test.h" + /* To prevent warning about "translation unit is empty" * when this test is disabled. */ @@ -27,29 +28,87 @@ int dummy_fifobuf_test; #include +#define THIS_FILE "fifobuf.c" + +static int fifobuf_size_test() +{ + enum { SIZE = 32, SZ=sizeof(unsigned) }; + char before[8]; + char buffer[SIZE]; + char after[8]; + char zero[8]; + void *p0, *p1; + pj_fifobuf_t fifo; + + pj_bzero(before, sizeof(before)); + pj_bzero(after, sizeof(after)); + pj_bzero(zero, sizeof(zero)); + + pj_fifobuf_init (&fifo, buffer, sizeof(buffer)); + + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -11, NULL); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -12, NULL); + + p0 = pj_fifobuf_alloc(&fifo, 16); + p1 = pj_fifobuf_alloc(&fifo, 4); + PJ_UNUSED_ARG(p1); + + pj_fifobuf_free(&fifo, p0); + + PJ_TEST_EQ( pj_fifobuf_available_size(&fifo), 16, -14, NULL); + PJ_TEST_EQ( pj_memcmp(before, zero, sizeof(zero)), 0, -18, NULL); + PJ_TEST_EQ( pj_memcmp(after, zero, sizeof(zero)), 0, -19, NULL); + return 0; +} + int fifobuf_test() { enum { SIZE = 1024, MAX_ENTRIES = 128, - MIN_SIZE = 4, MAX_SIZE = 64, + MIN_SIZE = 4, MAX_SIZE = 64, SZ = sizeof(unsigned), LOOP=10000 }; pj_pool_t *pool; pj_fifobuf_t fifo; - unsigned available = SIZE; + pj_size_t available = SIZE; void *entries[MAX_ENTRIES]; void *buffer; int i; + i = fifobuf_size_test(); + if (i != 0) + return i; + pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); - if (!pool) - return -10; + PJ_TEST_NOT_NULL(pool, -10, NULL); buffer = pj_pool_alloc(pool, SIZE); - if (!buffer) - return -20; + PJ_TEST_NOT_NULL(buffer, -20, NULL); pj_fifobuf_init (&fifo, buffer, SIZE); - // Test 1 + /* Capacity and maximum alloc */ + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -21, NULL); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -22, NULL); + + entries[0] = pj_fifobuf_alloc(&fifo, SIZE-SZ); + PJ_TEST_NOT_NULL(entries[0], -23, NULL); + + pj_fifobuf_free(&fifo, entries[0]); + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -21, NULL); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -22, NULL); + + /* Alignment test */ + for (i=0; i<30; ++i) { + entries[i] = pj_fifobuf_alloc(&fifo, i+1); + PJ_TEST_NOT_NULL(entries[i], -50, NULL); + //fifobuf is no longer aligned. + //PJ_TEST_EQ(((pj_size_t)entries[i]) % sizeof(unsigned), 0, -60, + // "alignment error"); + } + for (i=0; i<30; ++i) { + PJ_TEST_SUCCESS( pj_fifobuf_free(&fifo, entries[i]), -70, NULL); + } + + /* alloc() and free() */ for (i=0; i=MIN_SIZE+4 && count < MAX_ENTRIES;) { + available = pj_fifobuf_available_size(&fifo); + for (count=0; available>MIN_SIZE+SZ+MAX_SIZE/2 && count < MAX_ENTRIES;) { int size = MIN_SIZE+(pj_rand() % MAX_SIZE); entries[count] = pj_fifobuf_alloc (&fifo, size); if (entries[count]) { - available -= (size+4); + available = pj_fifobuf_available_size(&fifo); ++count; } } for (j=0; j + + +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_unittest_test; + +#if INCLUDE_UNITTEST_TEST + + +#define THIS_FILE "unittest_test.c" + +static char log_buffer[1024], *log_buffer_ptr = log_buffer; +static pj_log_func *old_log_func; + +static void reset_log_buffer() +{ + log_buffer_ptr = log_buffer; + *log_buffer_ptr = '\0'; +} + +#if 0 +static void print_log_buffer(char *title) +{ + printf("------ log buffer %s: ------\n", title); + printf("%s", log_buffer); + printf("%s\n", "------ end buffer: ------"); +} +#else +#define print_log_buffer(title) +#endif + +/* This log callback appends the log to the log_buffer */ +static void log_callback(int level, const char *data, int len) +{ + unsigned max_len = (log_buffer+sizeof(log_buffer))-log_buffer_ptr-1; + + /* make sure len is correct */ + len = strlen(data); + if (len > max_len) + len = max_len; + + pj_ansi_strxcpy(log_buffer_ptr, data, max_len); + log_buffer_ptr += len; +} + +static void start_capture_log() +{ + old_log_func = pj_log_get_log_func(); + pj_log_set_log_func(&log_callback); + reset_log_buffer(); +} + +static void end_capture_log() +{ + pj_log_set_log_func(old_log_func); +} + +static int test_true(int is_true, const char *reason) +{ + PJ_TEST_TRUE(is_true, -100, reason); + return 0; +} + +static int test_eq(int value0, int value1, const char *reason) +{ + PJ_TEST_EQ(value0, value1, -200, reason); + return 0; +} + +static int test_success(pj_status_t status, const char *reason) +{ + PJ_TEST_SUCCESS(status, -300, reason); + return 0; +} + +/* + * Test various assertion tests + */ +static int assertion_tests() +{ + int ret; + + /* Unary PJ_TEST_TRUE successful */ + ret = test_true(1, NULL); + if (ret != 0) return ret; + + /* PJ_TEST_TRUE successful, reason is specified (but not used) */ + ret = test_true(1, "logic error"); + if (ret != 0) return ret; + + /* PJ_TEST_TRUE fails, without reason */ + reset_log_buffer(); + ret = test_true(0, NULL); + if (ret != -100) return -110; + + /* Check log message, should be something like: + 09:47:09.692 Test "is_true" fails in unittest_test.c:44 + */ + if (pj_ansi_strlen(log_buffer) < 10) return -120; + if (!pj_ansi_strstr(log_buffer, "is_true")) return -121; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -122; + + /* PJ_TEST_TRUE fails, reason is specified */ + reset_log_buffer(); + ret = test_true(0, "logic error"); + if (ret != -100) return -110; + + /* Check log message, should be something like: + 09:47:37.145 Test "is_true" fails in unittest_test.c:44 (logic error) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -130; + if (!pj_ansi_strstr(log_buffer, "is_true")) return -131; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -132; + if (!pj_ansi_strstr(log_buffer, " (logic error)")) return -132; + + /* Binary PJ_TEST_EQ successful */ + ret = test_eq(999, 999, NULL); + if (ret != 0) return ret; + + ret = test_eq(999, 999, "not used"); + if (ret != 0) return ret; + + /* Binary comparison PJ_TEST_EQ fails, reason not given */ + reset_log_buffer(); + ret = test_eq(998, 999, NULL); + if (ret != -200) return -210; + + /* Check log message, should be something like: + 09:47:56.315 Test "value0" (998) == "value1" (999) fails in unittest_test.c:50 + */ + if (pj_ansi_strlen(log_buffer) < 10) return -220; + if (!pj_ansi_strstr(log_buffer, "value0")) return -221; + if (!pj_ansi_strstr(log_buffer, "value1")) return -222; + if (!pj_ansi_strstr(log_buffer, "998")) return -223; + if (!pj_ansi_strstr(log_buffer, "999")) return -224; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -225; + + /* Binary comparison PJ_TEST_EQ fails, reason is given */ + reset_log_buffer(); + ret = test_eq(998, 999, "values are different"); + if (ret != -200) return -250; + + /* Check log message, should be something like: + 09:51:37.866 Test "value0" (998) == "value1" (999) fails in unittest_test.c:50 (values are different) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -260; + if (!pj_ansi_strstr(log_buffer, "value0")) return -261; + if (!pj_ansi_strstr(log_buffer, "value1")) return -262; + if (!pj_ansi_strstr(log_buffer, "998")) return -263; + if (!pj_ansi_strstr(log_buffer, "999")) return -264; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -265; + if (!pj_ansi_strstr(log_buffer, " (values are different)")) return -266; + + /* PJ_TEST_SUCCESS successful scenario */ + ret = test_success(PJ_SUCCESS, NULL); + if (ret != 0) return ret; + + /* PJ_TEST_SUCCESS successful, reason is specified (but not used) */ + ret = test_success(PJ_SUCCESS, "logic error"); + if (ret != 0) return ret; + + /* PJ_TEST_SUCCESS fails, without reason */ + reset_log_buffer(); + ret = test_success(PJ_EPENDING, NULL); + if (ret != -300) return -310; + + /* Check log message, should be something like: + 09:52:22.654 "status" fails in unittest_test.c:56, status=70002 (Pending operation (PJ_EPENDING)) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -320; + if (!pj_ansi_strstr(log_buffer, "Pending operation")) return -321; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -322; + + /* PJ_TEST_SUCCESS fails, reason given */ + reset_log_buffer(); + ret = test_success(PJ_EPENDING, "should be immediate"); + if (ret != -300) return -350; + + /* Check log message, should be something like: + 09:52:49.717 "status" fails in unittest_test.c:56, status=70002 (Pending operation (PJ_EPENDING)) (should be immediate) + */ + if (pj_ansi_strlen(log_buffer) < 10) return -350; + if (!pj_ansi_strstr(log_buffer, "Pending operation")) return -351; + if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -352; + if (!pj_ansi_strstr(log_buffer, " (should be immediate)")) return -353; + + return 0; +} + + +enum test_flags +{ + /* value 0-31 is reserved for test id */ + TEST_LOG_DETAIL = 32, + TEST_LOG_INFO = 64, + TEST_LOG_ALL = (TEST_LOG_DETAIL | TEST_LOG_INFO), + + TEST_RETURN_ERROR = 256, +}; + +/** Dummy test */ +static int func_to_test(void *arg) +{ + unsigned flags = (unsigned)(long)arg; + unsigned test_id = (flags & 31); + + /* Note that for simplicity, make the length of log messages the same + * (otherwise freeing one oldest log may not be enough to fit in the + * later log) + */ + if (flags & TEST_LOG_DETAIL) { + PJ_LOG(4,(THIS_FILE, "Entering func_to_test(%d).....", test_id)); + } + + if (flags & TEST_LOG_INFO) { + PJ_LOG(3,(THIS_FILE, "Performing func_to_test(%d)...", test_id)); + } + + if (flags & TEST_RETURN_ERROR) { + PJ_LOG(1,(THIS_FILE, "Some error in func_to_test(%d)", test_id)); + } + + /* Simulate some work and additional sleep to ensure tests + * completes in correct order + */ + pj_thread_sleep(100+test_id*100); + + return (flags & TEST_RETURN_ERROR) ? -123 : 0; +} + +enum { + /* approx len of each log msg in func_to_test() + * Note that logging adds decor e.g. time, plus overhead in fifobuf. + */ + MSG_LEN = 45 + 4 + sizeof(pj_test_log_item), +}; + +/** + * Simple demonstration on how to use the unittest framework. + * Here we use the unittest framework to test the unittest framework itself. + * We test both the basic and text runner. + */ +static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, + unsigned log_size) +{ + enum { + TEST_CASE_LOG_SIZE = 256, + }; + char test_title[80]; + pj_test_suite suite; + unsigned flags; + char buffer0[TEST_CASE_LOG_SIZE], buffer1[TEST_CASE_LOG_SIZE]; + pj_test_case test_case0, test_case1; + pj_test_runner *runner; + pj_test_runner basic_runner; + pj_test_stat stat; + + /* to differentiate different invocations of this function */ + pj_ansi_snprintf(test_title, sizeof(test_title), + "basic=%d, parallel=%d, log_size=%d", + basic, parallel, log_size); + //PJ_LOG(3,(THIS_FILE, "Unittest usage_test: %s", test_title)); + + PJ_TEST_LTE(log_size, TEST_CASE_LOG_SIZE, -1, test_title); + + /* Init test suite */ + pj_test_suite_init(&suite); + + if (basic) + flags = 0; + else + flags = parallel? PJ_TEST_PARALLEL : 0; + + /* Add test case 0. This test case writes some logs and returns + * success. + */ + + pj_test_case_init(&test_case0, "successful test", flags, &func_to_test, + (void*)(long)(0+TEST_LOG_ALL), + buffer0, log_size, NULL); + pj_test_suite_add_case(&suite, &test_case0); + + /* Add test case 1. This test case simulates error. It writes + * error to log and returns non-zero error. + */ + pj_test_case_init(&test_case1, "failure test", flags, &func_to_test, + (void*)(long)(1+TEST_LOG_ALL+TEST_RETURN_ERROR), + buffer1, log_size, NULL); + pj_test_suite_add_case(&suite, &test_case1); + + /* Create runner */ + if (basic) { + runner = &basic_runner; + pj_test_init_basic_runner(runner); + } else { + pj_test_text_runner_param prm; + pj_test_text_runner_param_default(&prm); + prm.nthreads = 4; /* more threads than we need, for testing */ + PJ_TEST_SUCCESS(pj_test_create_text_runner(pool, &prm, &runner), + -10, test_title); + } + + /* Run runner */ + pj_test_run(runner, &suite); + + /* Runner can be safely destroyed now */ + pj_test_runner_destroy(runner); + + /* test the statistics returned by pj_test_get_stat() */ + pj_bzero(&stat, sizeof(stat)); + pj_test_get_stat(&suite, &stat); + PJ_TEST_EQ(stat.ntests, 2, -100, test_title); + PJ_TEST_EQ(stat.nruns, 2, -110, test_title); + PJ_TEST_EQ(stat.nfailed, 1, -120, test_title); + PJ_TEST_EQ(stat.failed_names[0], test_case1.obj_name, -130, test_title); + PJ_TEST_EQ(strcmp(stat.failed_names[0], "failure test"), 0, + -135, test_title); + PJ_TEST_GTE( PJ_TIME_VAL_MSEC(stat.duration), 200, -140, test_title); + + /* test logging. + * Since gave the test cases buffer to store log messages, we can dump + * the logs now and check the contents. + */ + start_capture_log(); + /* Dumping all test logs. both test 0 and 1 must be present */ + pj_test_dump_log_messages(&suite, PJ_TEST_ALL_TESTS); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + /* We should only have space for the last log */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), + -201, test_title); + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), + -202, test_title); + } else if (log_size < 10) { + /* buffer is too small, writing log will be rejected */ + PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, + -203, test_title); + } else { + /* buffer is small, message will be truncated */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), + -204, test_title); + } + + if (log_size >= 2*MSG_LEN) { + /* We should have space for two last log messages */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), + -205, test_title); + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), + -206, test_title); + } + if (log_size >= 3*MSG_LEN) { + /* We should have space for three last log messages */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), + -207, test_title); + } + + /* Dumping only failed test. Only test 1 must be present */ + start_capture_log(); + pj_test_dump_log_messages(&suite, PJ_TEST_FAILED_TESTS); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), + -211, test_title); + } else if (log_size < 10) { + /* buffer is too small, writing log will be rejected */ + PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, + -212, test_title); + } else { + /* buffer is small, message will be truncated */ + PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), + -213, test_title); + } + if (log_size >= 2*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), + -214, test_title); + } + if (log_size >= 3*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), + -215, test_title); + } + PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(0)"), NULL, + -216, test_title); + + /* Dumping only successful test. Only test 0 must be present */ + start_capture_log(); + pj_test_dump_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS); + print_log_buffer(test_title); + end_capture_log(); + if (log_size >= MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), + -221, test_title); + } + if (log_size >= 2*MSG_LEN) { + PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), + -222, test_title); + } + PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(1)"), NULL, + -223, test_title); + + return 0; +} + +int unittest_test(void) +{ + int ret, log_level = pj_log_get_level(); + pj_bool_t basic_flags[] = { PJ_FALSE }; //, PJ_FALSE }; + unsigned i; + + /* We wants to get detailed logging */ + pj_log_set_level(4); + + start_capture_log(); + ret = assertion_tests(); + end_capture_log(); + if (ret) goto on_return; + + for (i=0; i Date: Tue, 11 Jun 2024 11:32:59 +0700 Subject: [PATCH 02/79] Finished reorganizing pjlib-test to use unit-test framework --- pjlib/include/pj/argparse.h | 160 +++++++++++++++ pjlib/include/pj/unittest.h | 18 ++ pjlib/include/pjlib.h | 3 + pjlib/src/pj/unittest.c | 36 +++- pjlib/src/pjlib-test/main.c | 136 +++++++++--- pjlib/src/pjlib-test/test.c | 296 ++++++++++++++++++++------- pjlib/src/pjlib-test/test.h | 4 +- pjlib/src/pjlib-test/unittest_test.c | 74 ++++--- 8 files changed, 592 insertions(+), 135 deletions(-) create mode 100644 pjlib/include/pj/argparse.h diff --git a/pjlib/include/pj/argparse.h b/pjlib/include/pj/argparse.h new file mode 100644 index 0000000000..abc6f7027c --- /dev/null +++ b/pjlib/include/pj/argparse.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008-2024 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 + */ +#ifndef __PJ_ARGPARSE_H__ +#define __PJ_ARGPARSE_H__ + +/** + * @file argparse.h + * @brief Command line argument parser + */ +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_ARGPARSE Command line argument parser + * @ingroup PJ_MISC + * @{ + * + * This module provides header only utilities to parse command line arguments. + * This is mostly used by PJSIP test and sample apps. Note that there is + * getopt() implementation in PJLIB-UTIL (but it's in PJLIB-UTIL, so it can't + * be used by PJLIB) + */ + +/** + * Check that an option exists, without modifying argv. + * + * @param opt The option to find, e.g. "-h", "--help" + * @param argv The argv, which must be null terminated. + * + * @return PJ_TRUE if the option exists, else PJ_FALSE. + */ +static pj_bool_t pj_argparse_exists(const char *opt, const char *const argv[]) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) + return PJ_TRUE; + } + return PJ_FALSE; +} + +/** + * Check for an option and if it exists, returns PJ_TRUE remove that option + * from argc/argv. + * + * @param opt The option to find, e.g. "-h", "--help" + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * + * @return PJ_TRUE if the option exists, else PJ_FALSE. + */ +static pj_bool_t pj_argparse_get(const char *opt, int *argc, char *argv[]) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) { + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + return PJ_TRUE; + } + } + return PJ_FALSE; +} + +/** + * Check for an option and if it exists, get integer value and remove both + * the option the the value from argc/argv. Note that the function only + * supports whitespace as separator between option and value (i.e. equal + * sign is not supported). + * + * @param opt The option to find, e.g. "-t", "--type" + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * @param ptr_value Pointer to receive the value. + * + * @return PJ_SUCCESS if the option exists and value is found, + * PJ_EINVAL if the option exits but value is not found, + * PJ_ENOTFOUND if the option does not exist. + */ +static pj_status_t pj_argparse_get_str( const char *opt, int *argc, + char *argv[], char **ptr_value) +{ + int i; + for (i=1; argv[i]; ++i) { + if (pj_ansi_strcmp(argv[i], opt)==0) { + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + + if (argv[i]) { + char *val = argv[i]; + pj_memmove(&argv[i], &argv[i+1], ((*argc)-i)*sizeof(char*)); + (*argc)--; + *ptr_value = val; + return PJ_SUCCESS; + } else { + return PJ_EINVAL; + } + } + } + return PJ_ENOTFOUND; +} + +/** + * Check for an option and if it exists, get the integer value and remove both + * the option the the value from argc/argv. Note that the function only + * supports whitespace as separator between option and value (i.e. equal + * sign is not supported) + * + * @param opt The option to find, e.g. "-h", "--help" + * @param argc Pointer to argc. + * @param argv Null terminated argv. + * @param ptr_value Pointer to receive the value. + * + * @return PJ_SUCCESS if the option exists and value is found, + * PJ_EINVAL if the option exits but value is not found or invalid, + * PJ_ENOTFOUND if the option does not exist. + */ +static pj_status_t pj_argparse_get_int( char *opt, int *argc, char *argv[], + int *ptr_value) +{ + char *endptr, *sval; + long val; + pj_status_t status = pj_argparse_get_str(opt, argc, argv, &sval); + if (status != PJ_SUCCESS) + return status; + + val = strtol(sval, &endptr, 10); + if (*endptr) + return PJ_EINVAL; + + *ptr_value = (int)val; + return PJ_SUCCESS; +} + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJ_ARGPARSE_H__ */ + diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 5b780f2dbc..57e56e6d36 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -350,6 +350,12 @@ typedef struct pj_test_case /** Total number of test cases in the suite */ unsigned total; + /** Start time */ + pj_timestamp start_time; + + /** End time */ + pj_timestamp end_time; + } pj_test_case; @@ -448,6 +454,9 @@ typedef enum pj_test_select_tests /** Select only successful tests */ PJ_TEST_SUCCESSFUL_TESTS = 2, + /** Select no test*/ + PJ_TEST_NO_TEST = 4, + } pj_test_select_tests; @@ -542,6 +551,15 @@ PJ_DECL(pj_status_t) pj_test_create_text_runner( PJ_DECL(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite); +/** + * This is a crude test to detect if thread is currently running under + * a test. It is mainly used to prevent nested unit testing. + * + * @return PJ_TRUE if we are currently running in the context of a test case + * being run. + */ +PJ_DECL(pj_bool_t) pj_test_is_under_test(void); + /** * Get the test statistics after the run completes. The test suite and * test cases instances must be kept alive in order to get and access the diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index 1bc591be51..4bf7dd272d 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -25,6 +25,9 @@ * @brief Include all PJLIB header files. */ +/* argparse is a utility for test apps, not really a feature of pjlib. */ +/*#include */ + #include #include #include diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 83b30c1895..8e457c4167 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -150,6 +150,12 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) pj_log_set_log_func(runner->orig_log_writer); } +/* Check if we are under test */ +PJ_DEF(pj_bool_t) pj_test_is_under_test(void) +{ + return pj_log_get_log_func()==&unittest_log_callback; +} + /* Calculate statistics */ PJ_DEF(void) pj_test_get_stat( const pj_test_suite *suite, pj_test_stat *stat) { @@ -244,8 +250,18 @@ static void unittest_log_callback(int level, const char *data, int len) pj_bool_t truncated; pj_test_log_item *log_item; - PJ_ASSERT_ON_FAIL(len > 0, return); - PJ_ASSERT_ON_FAIL(tc, return); + if (len < 1) + return; + + if (tc==NULL) { + /* We are being called by thread that is not part of unit-test. + * Call the original log writer, hoping that the thread did not + * change the writer before this.. (note: this can only be solved + * by setting pj_log_set/get_log_func() to be thread specific.) + */ + pj_log_write(level, data, len); + return; + } /* Filter out unwanted log */ if (level > tc->prm.log_level) @@ -312,18 +328,23 @@ static void unittest_log_callback(int level, const char *data, int len) static int get_completion_line( const pj_test_case *tc, const char *end_line, char *log_buf, unsigned buf_size) { - char res_buf[20]; + char res_buf[64]; + pj_time_val elapsed; int log_len; + elapsed = pj_elapsed_time(&tc->start_time, &tc->end_time); + if (tc->result==0) { - pj_ansi_strxcpy(res_buf, "OK", sizeof(res_buf)); + pj_ansi_snprintf(res_buf, sizeof(res_buf), "[OK] [%d.%03ds]", + (int)elapsed.sec, (int)elapsed.msec); } else if (tc->result==PJ_EPENDING) { pj_ansi_strxcpy(res_buf, "pending", sizeof(res_buf)); } else { - pj_ansi_snprintf(res_buf, sizeof(res_buf), "Err: %d", tc->result); + pj_ansi_snprintf(res_buf, sizeof(res_buf), "[Err: %d] [%d.%03ds]", + tc->result, (int)elapsed.sec, (int)elapsed.msec); } - log_len = pj_ansi_snprintf(log_buf, buf_size, "[%d/%d] %-32s [%s]%s\n", + log_len = pj_ansi_snprintf(log_buf, buf_size, "[% 2d/%d] %-32s %s%s\n", tc->index+1, tc->total, tc->obj_name, res_buf, end_line); @@ -354,6 +375,8 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) /* Set the test case being worked on by this thread */ set_current_test_case(tc); + pj_get_timestamp(&tc->start_time); + /* Call the test case's function */ if (tc->flags & PJ_TEST_FUNC_NO_ARG) { /* Function without argument */ @@ -367,6 +390,7 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) if (tc->result == PJ_EPENDING) tc->result = -12345; + pj_get_timestamp(&tc->end_time); runner->on_test_complete(runner, tc); /* Reset the test case being worked on by this thread */ diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index 43c29b00da..087b0d5062 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include extern int param_echo_sock_type; @@ -28,6 +29,8 @@ extern const char *param_echo_server; extern int param_echo_port; extern pj_bool_t param_ci_mode; +extern pj_test_select_tests param_unittest_logging_policy; +extern int param_unittest_nthreads; //#if defined(PJ_WIN32) && PJ_WIN32!=0 #if 0 @@ -82,55 +85,120 @@ static void init_signals(void) #define init_signals() #endif +static void usage() +{ + puts("Usage:"); + puts(" pjlib-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + puts(" --ci-mode Running in slow CI mode"); + puts(" -l 0,1,2 0: Don't show logging after tests"); + puts(" 1: Show logs of failed tests (default)"); + puts(" 2: Show logs of all tests"); + puts(" -w N Set N worker threads (0: disable worker threads)"); + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); + puts(" -p PORT Use port PORT for echo port"); + puts(" -s SERVER Use SERVER as ech oserver"); + puts(" -t ucp,tcp Set echo socket type to UDP or TCP"); +} + + int main(int argc, char *argv[]) { - int iarg=1, rc; + int i, rc; int interractive = 0; int no_trap = 0; + pj_status_t status; + char *s; boost(); - while (iarg < argc) { - char *arg = argv[iarg++]; - - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; - - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } else if (*arg=='-' && *(arg+1)=='p') { - pj_str_t port = pj_str(argv[iarg++]); - - param_echo_port = pj_strtoul(&port); - - } else if (*arg=='-' && *(arg+1)=='s') { - param_echo_server = argv[iarg++]; - - } else if (*arg=='-' && *(arg+1)=='t') { - pj_str_t type = pj_str(argv[iarg++]); - - 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 { - printf("Error: unknown socket type %s\n", type.ptr); - return 1; - } - } else if (strcmp(arg, "--ci-mode")==0) { - param_ci_mode = PJ_TRUE; - - } else { - printf("Error in argument \"%s\"\n", arg); + /* + * Parse arguments + */ + if (pj_argparse_get("-h", &argc, argv) || + pj_argparse_get("--help", &argc, argv)) + { + usage(); + return 0; + } + interractive = pj_argparse_get("-i", &argc, argv); + no_trap = pj_argparse_get("-n", &argc, argv); + status = pj_argparse_get_int("-p", &argc, argv, ¶m_echo_port); + if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { + puts("Error: invalid/missing value for -p option"); + usage(); + return 1; + } + status = pj_argparse_get_str("-s", &argc, argv, + (char**)¶m_echo_server); + if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { + puts("Error: value is required for -s option"); + usage(); + return 1; + } + + status = pj_argparse_get_str("-t", &argc, argv, &s); + if (status==PJ_SUCCESS) { + if (pj_ansi_stricmp(s, "tcp")==0) + param_echo_sock_type = pj_SOCK_STREAM(); + else if (pj_ansi_stricmp(s, "udp")==0) + param_echo_sock_type = pj_SOCK_DGRAM(); + else { + printf("Error: unknown socket type %s for -t option\n", s); + usage(); + return 1; + } + } else if (status!=PJ_ENOTFOUND) { + puts("Error: value is required for -t option"); + usage(); + return 1; + } + + param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); + + status = pj_argparse_get_int("-l", &argc, argv, &i); + if (status==PJ_SUCCESS) { + if (i==0) + param_unittest_logging_policy = PJ_TEST_NO_TEST; + else if (i==1) + param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; + else if (i==2) + param_unittest_logging_policy = PJ_TEST_ALL_TESTS; + else { + printf("Error: invalid value %d for -l option\n", i); + usage(); + return 1; + } + } else if (status!=PJ_ENOTFOUND) { + puts("Error: invalid/missing value for -l option"); + usage(); + return 1; + } + + status = pj_argparse_get_int("-w", &argc, argv, + (int*)¶m_unittest_nthreads); + if (status==PJ_SUCCESS) { + if (param_unittest_nthreads > 100 || param_unittest_nthreads < 0) { + printf("Error: value %d is not valid for -w option\n", + param_unittest_nthreads); + usage(); return 1; } + } else if (status!=PJ_ENOTFOUND) { + puts("Error: invalid/missing value for -w option"); + usage(); } if (!no_trap) { init_signals(); } - rc = test_main(); + /* argc/argv now contains option values only, if any */ + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 5316d868c0..d42fbdbd91 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -19,6 +19,8 @@ #include "test.h" #include +#define THIS_FILE "test.c" + #ifdef _MSC_VER # pragma warning(disable:4127) @@ -33,9 +35,9 @@ #endif #define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ + PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ rc = test; \ - PJ_LOG(3, ("test", \ + PJ_LOG(3, (THIS_FILE, \ "%s(%d)", \ (rc ? "..ERROR" : "..success"), rc)); \ if (rc!=0) goto on_return; \ @@ -50,152 +52,304 @@ 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 | PJ_LOG_HAS_INDENT; pj_bool_t param_ci_mode = PJ_FALSE; /* GH CI mode: more lenient tests */ +pj_test_select_tests param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; +int param_unittest_nthreads = -1; + int null_func() { return 0; } -int test_inner(void) +static pj_bool_t test_included(const char *name, int argc, char *argv[]) { - pj_caching_pool caching_pool; - const char *filename; - int line; - int rc = 0; - - mem = &caching_pool.factory; + if (argc <= 1) + return PJ_TRUE; + + ++argv; + while (*argv) { + if (pj_ansi_strcmp(name, *argv)==0) + return PJ_TRUE; + ++argv; + } + return PJ_FALSE; +} - pj_log_set_level(3); - pj_log_set_decor(param_log_decor); +static pj_test_case *init_test_case( int (*test_func)(void), const char *obj_name, + unsigned flags, pj_test_case *tc, + char *log_buf, unsigned log_buf_size) +{ + flags |= PJ_TEST_FUNC_NO_ARG; + pj_test_case_init(tc, obj_name, flags, (int (*)(void*))test_func, NULL, + log_buf, log_buf_size, NULL); + return tc; +} - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - return rc; +static int essential_tests(int argc, char *argv[]) +{ + pj_test_suite suite; + pj_test_runner runner; + pj_test_stat stat; + enum { + MAX_TESTS = 12, + LOG_BUF_SIZE = 256, + }; + char log_bufs[MAX_TESTS][LOG_BUF_SIZE]; + pj_test_case test_cases[MAX_TESTS]; + int ntests = 0; + + /* Test the unit-testing framework first, outside unit-test! */ + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); + if (unittest_basic_test()) + return 1; + + /* Now that the basic unit-testing framework has been tested, + * perform essential tests using basic unit-testing framework. + */ + pj_test_suite_init(&suite); + +#define ADD_TEST(test_func, flags) \ + if (ntests < MAX_TESTS) { \ + const char *test_name = #test_func; \ + if (test_included(test_name, argc, argv)) { \ + pj_test_case *tc = init_test_case( &test_func, test_name, flags, \ + &test_cases[ntests], \ + log_bufs[ntests], \ + LOG_BUF_SIZE); \ + pj_test_suite_add_case( &suite, tc); \ + ++ntests; \ + } \ + } else { \ + PJ_LOG(1,(THIS_FILE, "Too many essential tests (%d)", ntests)); \ } - pj_dump_config(); - pj_caching_pool_init( &caching_pool, NULL, 0 ); - - if (param_ci_mode) - PJ_LOG(3,("test", "Using ci-mode")); #if INCLUDE_ERRNO_TEST - DO_TEST( errno_test() ); -#endif - -#if INCLUDE_UNITTEST_TEST - DO_TEST( unittest_test() ); + ADD_TEST( errno_test, 0); #endif #if INCLUDE_EXCEPTION_TEST - DO_TEST( exception_test() ); + ADD_TEST( exception_test, 0); #endif #if INCLUDE_OS_TEST - DO_TEST( os_test() ); -#endif - -#if INCLUDE_RAND_TEST - DO_TEST( rand_test() ); + ADD_TEST( os_test, 0); #endif #if INCLUDE_LIST_TEST - DO_TEST( list_test() ); + ADD_TEST( list_test, 0); #endif #if INCLUDE_POOL_TEST - DO_TEST( pool_test() ); -#endif - -#if INCLUDE_POOL_PERF_TEST - DO_TEST( pool_perf_test() ); + ADD_TEST( pool_test, 0); #endif #if INCLUDE_STRING_TEST - DO_TEST( string_test() ); + ADD_TEST( string_test, 0); #endif #if INCLUDE_FIFOBUF_TEST - DO_TEST( fifobuf_test() ); + ADD_TEST( fifobuf_test, 0); +#endif + +#if INCLUDE_MUTEX_TEST + ADD_TEST( mutex_test, 0); +#endif + +#if INCLUDE_THREAD_TEST + ADD_TEST( thread_test, 0); +#endif + +#undef ADD_TEST + + PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); + pj_test_init_basic_runner(&runner); + pj_test_run(&runner, &suite); + pj_test_get_stat(&suite, &stat); + pj_test_dump_log_messages(&suite, param_unittest_logging_policy); + + if (stat.nfailed) + return 1; + + /* Now that the essential components have been tested, test the + * multithreaded unit-testing framework. + */ + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); + if (unittest_test()) + return 1; + + return 0; +} + +static int features_tests(int argc, char *argv[]) +{ + pj_test_suite suite; + pj_test_runner *runner; + pj_test_text_runner_param prm; + pj_test_stat stat; + pj_pool_t *pool; + enum { + MAX_TESTS = 24, + LOG_BUF_SIZE = 1000, + }; + pj_test_case test_cases[MAX_TESTS]; + int ntests = 0; + pj_status_t status; + + pool = pj_pool_create(mem, "test.c", 4000, 4000, NULL); + if (!pool) { + PJ_LOG(1,(THIS_FILE, "Pool creation error")); + return 1; + } + pj_test_suite_init(&suite); + +#define ADD_TEST(test_func, flags) \ + if (ntests < MAX_TESTS) { \ + const char *test_name = #test_func; \ + if (test_included(test_name, argc, argv)) { \ + char *log_buf = (char*)pj_pool_alloc(pool, LOG_BUF_SIZE); \ + pj_test_case *tc = init_test_case( &test_func, test_name, flags, \ + &test_cases[ntests], \ + log_buf, LOG_BUF_SIZE); \ + pj_test_suite_add_case( &suite, tc); \ + ++ntests; \ + } \ + } else { \ + PJ_LOG(1,(THIS_FILE, "Too many features tests (%d)", ntests)); \ + } + +#if INCLUDE_RAND_TEST + ADD_TEST( rand_test, 0); +#endif + +#if INCLUDE_POOL_PERF_TEST + ADD_TEST( pool_perf_test, 0); #endif #if INCLUDE_RBTREE_TEST - DO_TEST( rbtree_test() ); + ADD_TEST( rbtree_test, 0); #endif #if INCLUDE_HASH_TEST - DO_TEST( hash_test() ); + ADD_TEST( hash_test, 0); #endif #if INCLUDE_TIMESTAMP_TEST - DO_TEST( timestamp_test() ); + ADD_TEST( timestamp_test, 0); #endif #if INCLUDE_ATOMIC_TEST - DO_TEST( atomic_test() ); -#endif - -#if INCLUDE_MUTEX_TEST - DO_TEST( mutex_test() ); + ADD_TEST( atomic_test, 0); #endif #if INCLUDE_TIMER_TEST - DO_TEST( timer_test() ); + ADD_TEST( timer_test, 0); #endif #if INCLUDE_SLEEP_TEST - DO_TEST( sleep_test() ); -#endif - -#if INCLUDE_THREAD_TEST - DO_TEST( thread_test() ); + ADD_TEST( sleep_test, 0); #endif #if INCLUDE_SOCK_TEST - DO_TEST( sock_test() ); + ADD_TEST( sock_test, 0); #endif #if INCLUDE_SOCK_PERF_TEST - DO_TEST( sock_perf_test() ); + ADD_TEST( sock_perf_test, 0); #endif #if INCLUDE_SELECT_TEST - DO_TEST( select_test() ); + ADD_TEST( select_test, 0); #endif #if INCLUDE_UDP_IOQUEUE_TEST - DO_TEST( udp_ioqueue_test() ); + ADD_TEST( udp_ioqueue_test, 0); #endif #if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST - DO_TEST( tcp_ioqueue_test() ); + ADD_TEST( tcp_ioqueue_test, 0); #endif #if INCLUDE_IOQUEUE_UNREG_TEST - DO_TEST( udp_ioqueue_unreg_test() ); + ADD_TEST( udp_ioqueue_unreg_test, 0); #endif #if INCLUDE_IOQUEUE_STRESS_TEST - DO_TEST( ioqueue_stress_test() ); + ADD_TEST( ioqueue_stress_test, 0); #endif #if INCLUDE_IOQUEUE_PERF_TEST - DO_TEST( ioqueue_perf_test() ); + ADD_TEST( ioqueue_perf_test, 0); #endif #if INCLUDE_ACTIVESOCK_TEST - DO_TEST( activesock_test() ); + ADD_TEST( activesock_test, 0); #endif #if INCLUDE_FILE_TEST - DO_TEST( file_test() ); + ADD_TEST( file_test, 0); #endif #if INCLUDE_SSLSOCK_TEST - DO_TEST( ssl_sock_test() ); + ADD_TEST( ssl_sock_test, 0 ); #endif + +#undef ADD_TEST + + pj_test_text_runner_param_default(&prm); + if (param_unittest_nthreads > 0) + prm.nthreads = param_unittest_nthreads; + status = pj_test_create_text_runner(pool, &prm, &runner); + if (status != PJ_SUCCESS) { + app_perror("Error creating text runner", status); + return 1; + } + + PJ_LOG(3,(THIS_FILE, + "Performing %d features tests with %d worker threads", + ntests, prm.nthreads)); + pj_test_run(runner, &suite); + pj_test_runner_destroy(runner); + pj_test_get_stat(&suite, &stat); + pj_test_dump_log_messages(&suite, param_unittest_logging_policy); + pj_pool_release(pool); + + return stat.nfailed ? 1 : 0; +} + +int test_inner(int argc, char *argv[]) +{ + 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 (param_ci_mode) + PJ_LOG(3,(THIS_FILE, "Using ci-mode")); + + rc = essential_tests(argc, argv); + if (rc) + return rc; + + rc = features_tests(argc, argv); + if (rc) + return rc; + #if INCLUDE_ECHO_SERVER //echo_server(); //echo_srv_sync(); @@ -216,16 +370,16 @@ int test_inner(void) pj_caching_pool_destroy( &caching_pool ); - PJ_LOG(3,("test", " ")); + PJ_LOG(3,(THIS_FILE, " ")); pj_thread_get_stack_info(pj_thread_this(), &filename, &line); - PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", + PJ_LOG(3,(THIS_FILE, "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!..")); + PJ_LOG(3,(THIS_FILE, "Looks like everything is okay!..")); else - PJ_LOG(3,("test", "Test completed with error(s)")); + PJ_LOG(3,(THIS_FILE, "Test completed with error(s)")); pj_shutdown(); @@ -234,16 +388,16 @@ int test_inner(void) #include -int test_main(void) +int test_main(int argc, char *argv[]) { PJ_USE_EXCEPTION; PJ_TRY { - return test_inner(); + return test_inner(argc, argv); } PJ_CATCH_ANY { int id = PJ_GET_EXCEPTION(); - PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", + PJ_LOG(3,(THIS_FILE, "FATAL: unhandled exception id %d (%s)", id, pj_exception_id_name(id))); } PJ_END; diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 75798174e9..dc7a06f934 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -20,6 +20,7 @@ #define __PJLIB_TEST_H__ #include +#include #define TEST_DEFAULT 1 @@ -110,6 +111,7 @@ extern int ioqueue_stress_test(void); extern int activesock_test(void); extern int file_test(void); extern int ssl_sock_test(void); +extern int unittest_basic_test(void); extern int unittest_test(void); extern int echo_server(void); @@ -122,7 +124,7 @@ extern int echo_srv_common_loop(pj_atomic_t *bytes_counter); extern pj_pool_factory *mem; -extern int test_main(void); +extern int test_main(int argc, char *argv[]); 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); diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index f428cdacc8..f600e7d386 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -422,11 +422,23 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, return 0; } -int unittest_test(void) +static int log_msg_sizes[] = { + 1*MSG_LEN, /* log buffer enough for 1 message */ + 2*MSG_LEN, /* log buffer enough for 2 messages */ + 3*MSG_LEN, /* log buffer enough for 3 message */ + 0, /* no log buffer */ + 64, /* log will be truncated */ +}; + +int unittest_basic_test(void) { int ret, log_level = pj_log_get_level(); - pj_bool_t basic_flags[] = { PJ_FALSE }; //, PJ_FALSE }; - unsigned i; + unsigned j; + + if (pj_test_is_under_test()) { + PJ_LOG(1,(THIS_FILE, "Cannot run unittest_test under unit-test!")); + return -1; + } /* We wants to get detailed logging */ pj_log_set_level(4); @@ -436,26 +448,42 @@ int unittest_test(void) end_capture_log(); if (ret) goto on_return; - for (i=0; i Date: Wed, 12 Jun 2024 18:03:16 +0700 Subject: [PATCH 03/79] Fix big problem where performance improvement is not observed with the new framework (reason: because parallel flag is not set, doh!) --- pjlib/include/pj/argparse.h | 2 +- pjlib/include/pj/unittest.h | 27 ++++-- pjlib/src/pj/unittest.c | 120 +++++++++++++++++++------ pjlib/src/pjlib-test/ioq_stress_test.c | 3 +- pjlib/src/pjlib-test/rbtree.c | 13 +-- pjlib/src/pjlib-test/test.c | 54 +++++------ pjlib/src/pjlib-test/unittest_test.c | 6 +- unittest.md | 60 +++++++++++++ 8 files changed, 207 insertions(+), 78 deletions(-) create mode 100644 unittest.md diff --git a/pjlib/include/pj/argparse.h b/pjlib/include/pj/argparse.h index abc6f7027c..357086658e 100644 --- a/pjlib/include/pj/argparse.h +++ b/pjlib/include/pj/argparse.h @@ -80,7 +80,7 @@ static pj_bool_t pj_argparse_get(const char *opt, int *argc, char *argv[]) } /** - * Check for an option and if it exists, get integer value and remove both + * Check for an option and if it exists, get the value and remove both * the option the the value from argc/argv. Note that the function only * supports whitespace as separator between option and value (i.e. equal * sign is not supported). diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 57e56e6d36..74eefbb40b 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -344,12 +344,6 @@ typedef struct pj_test_case /** Pointer to the runner running this test case */ pj_test_runner *runner; - /** Index/position in the suite */ - unsigned index; - - /** Total number of test cases in the suite */ - unsigned total; - /** Start time */ pj_timestamp start_time; @@ -432,6 +426,12 @@ struct pj_test_runner /** Saving the original log writer */ pj_log_func *orig_log_writer; + /** Number of tests */ + unsigned ntests; + + /** Number of completed tests */ + unsigned nruns; + /** main method */ void (*main)(pj_test_runner*); @@ -571,7 +571,16 @@ PJ_DECL(pj_bool_t) pj_test_is_under_test(void); PJ_DECL(void) pj_test_get_stat(const pj_test_suite *suite, pj_test_stat *stat); /** - * Dump previously saved log messages in the test cases to logging. + * Display statistics to the log. + * + * @param stat The test statistics result. + */ +PJ_DECL(void) pj_test_display_stat(const pj_test_stat *stat, + const char *test_name, + const char *log_sender); + +/** + * Display previously saved log messages in the test cases to logging. * Note that log messages emited during test case's run are only saved * when fifobuf of the test case is configured with a suitable buffer. * Also note that the test suite and test cases instances must be kept alive @@ -580,8 +589,8 @@ PJ_DECL(void) pj_test_get_stat(const pj_test_suite *suite, pj_test_stat *stat); * @param suite The test suite * @param which Which test cases to dump */ -PJ_DECL(void) pj_test_dump_log_messages(const pj_test_suite *suite, - pj_test_select_tests which); +PJ_DECL(void) pj_test_display_log_messages(const pj_test_suite *suite, + pj_test_select_tests which); /** * Destroy the runner. Runner may be destroyed right after it is run, diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 8e457c4167..951993c61d 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -28,6 +28,28 @@ static long tls_id = INVALID_TLS_ID; static pj_test_case *tc_main_thread; +#if 0 +# define TC_TRACE(tc__, msg__) \ + {\ + pj_time_val tv = pj_elapsed_time(&tc__->runner->suite->start_time, \ + &tc__->start_time); \ + printf("%02ld:%02ld %s %s\n", tv.sec/60, tv.sec%60, \ + tc__->obj_name, msg__); \ + } + +# define RUNNER_TRACE(runner__, msg__) \ + { \ + pj_timestamp now; \ + pj_time_val tv; \ + pj_get_timestamp(&now); \ + tv = pj_elapsed_time(&((pj_test_runner*)runner__)->suite->start_time, &now); \ + printf("%02ld:%02ld %s\n", tv.sec/60, tv.sec%60, msg__); \ + } +#else +# define TC_TRACE(tc__, msg__) +# define RUNNER_TRACE(runner__, msg__) +#endif + /* Forward decls. */ static void unittest_log_callback(int level, const char *data, int len); static int get_completion_line( const pj_test_case *tc, const char *end_line, @@ -122,7 +144,6 @@ PJ_DEF(void) pj_test_text_runner_param_default( /* Main API to start running a test runner */ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) { - unsigned index, total; pj_test_case *tc; /* Redirect logging to our custom callback */ @@ -131,14 +152,14 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) /* Initialize suite and test cases */ runner->suite = suite; - total = pj_list_size(&suite->tests); - for (tc=suite->tests.next, index=0; tc!=&suite->tests; - tc=tc->next, ++index) + runner->ntests = pj_list_size(&suite->tests); + runner->nruns = 0; + + for (tc=suite->tests.next; tc!=&suite->tests; + tc=tc->next) { tc->result = PJ_EPENDING; tc->runner = runner; - tc->index = index; - tc->total = total; } /* Call the run method to perform runner specific loop */ @@ -178,13 +199,25 @@ PJ_DEF(void) pj_test_get_stat( const pj_test_suite *suite, pj_test_stat *stat) } } +/* Display statistics */ +PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, + const char *test_name, + const char *log_sender) +{ + PJ_LOG(3,(log_sender, "Unit test statistics for %s:", test_name)); + PJ_LOG(3,(log_sender, " Total number of tests: %d", stat->ntests)); + PJ_LOG(3,(log_sender, " Number of failed test: %d", stat->nfailed)); + PJ_LOG(3,(log_sender, " Total duration: %dm%d.%03ds", + (int)stat->duration.sec/60, (int)stat->duration.sec%60, + (int)stat->duration.msec)); +} + /* Dump previously saved log messages */ -PJ_DEF(void) pj_test_dump_log_messages( const pj_test_suite *suite, - pj_test_select_tests which) +PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, + pj_test_select_tests which) { const pj_test_case *tc = suite->tests.next; pj_log_func *log_writer = pj_log_get_log_func(); - char test_line[80]; while (tc != &suite->tests) { const pj_test_log_item *log_item = tc->logs.next; @@ -199,8 +232,8 @@ PJ_DEF(void) pj_test_dump_log_messages( const pj_test_suite *suite, } if (log_item != &tc->logs) { - get_completion_line(tc, " logs:", test_line, sizeof(test_line)); - log_writer(3, test_line, pj_ansi_strlen(test_line)); + PJ_LOG(3,(THIS_FILE, "Logs for %s [rc:%d]:", + tc->obj_name, tc->result)); do { log_writer(log_item->level, log_item->msg, log_item->len); @@ -344,9 +377,8 @@ static int get_completion_line( const pj_test_case *tc, const char *end_line, tc->result, (int)elapsed.sec, (int)elapsed.msec); } - log_len = pj_ansi_snprintf(log_buf, buf_size, "[% 2d/%d] %-32s %s%s\n", - tc->index+1, tc->total, tc->obj_name, res_buf, - end_line); + log_len = pj_ansi_snprintf(log_buf, buf_size, "%-32s %s%s\n", + tc->obj_name, res_buf, end_line); if (log_len < 1 || log_len >= sizeof(log_buf)) log_len = pj_ansi_strlen(log_buf); @@ -354,18 +386,6 @@ static int get_completion_line( const pj_test_case *tc, const char *end_line, return log_len; } -/* Default runner's callback when a test case completes. This must be - * reentrant from multiple threads - */ -static void on_test_complete(pj_test_runner *runner, pj_test_case *tc) -{ - char line[80]; - int len; - - len = get_completion_line(tc, "", line, sizeof(line)); - tc->runner->orig_log_writer(3, line, len); -} - /* This is the main function to run a single test case. It may * be used by the basic runner, which has no threads (=no TLS), * no fifobuf, no pool, or by multiple threads. @@ -376,6 +396,7 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) set_current_test_case(tc); pj_get_timestamp(&tc->start_time); + TC_TRACE(tc, "starting"); /* Call the test case's function */ if (tc->flags & PJ_TEST_FUNC_NO_ARG) { @@ -390,6 +411,7 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) if (tc->result == PJ_EPENDING) tc->result = -12345; + TC_TRACE(tc, "done"); pj_get_timestamp(&tc->end_time); runner->on_test_complete(runner, tc); @@ -413,6 +435,23 @@ static void basic_runner_main(pj_test_runner *runner) } } +/* Basic runner's callback when a test case completes. */ +static void basic_on_test_complete(pj_test_runner *runner, pj_test_case *tc) +{ + char line[80]; + int len; + + runner->nruns++; + + len = pj_ansi_snprintf( line, sizeof(line), "[%2d/%d] ", + runner->nruns, runner->ntests); + if (len < 1 || len >= sizeof(line)) + len = pj_ansi_strlen(line); + + len += get_completion_line(tc, "", line+len, sizeof(line)-len); + tc->runner->orig_log_writer(3, line, len); +} + /* Destroy for basic runner */ static void basic_runner_destroy(pj_test_runner *runner) { @@ -425,7 +464,7 @@ PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner) pj_bzero(runner, sizeof(*runner)); runner->main = &basic_runner_main; runner->destroy = &basic_runner_destroy; - runner->on_test_complete = &on_test_complete; + runner->on_test_complete = &basic_on_test_complete; } /******************************* Text Runner *******************************/ @@ -465,6 +504,7 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, pj_mutex_lock(runner->mutex); if (runner->cur_case == NULL) { + /* Only on the very first invocation */ if (pj_list_empty(&runner->base.suite->tests)) { status = PJ_ENOTFOUND; goto on_return; @@ -517,6 +557,10 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, /* Thread loop */ static int text_runner_thread_proc(void *arg) { + static int global_tid = 0; + int tid = global_tid++; + char tmp[80]; + text_runner_t *runner = (text_runner_t*)arg; for (;;) { @@ -525,16 +569,24 @@ static int text_runner_thread_proc(void *arg) status = text_runner_get_next_test_case(runner, &tc); if (status==PJ_SUCCESS) { + snprintf(tmp, sizeof(tmp), "thread %d running %s", tid, tc->obj_name); + RUNNER_TRACE(runner, tmp); run_test_case(&runner->base, tc); + snprintf(tmp, sizeof(tmp), "thread %d done running %s", tid, tc->obj_name); + RUNNER_TRACE(runner, tmp); } else if (status==PJ_EPENDING) { /* Yeah sleep, but the "correct" solution is probably an order of * magnitute more complicated, so this is good I think. */ - pj_thread_sleep(250); + snprintf(tmp, sizeof(tmp), "thread %d waiting", tid); + RUNNER_TRACE(runner, tmp); + pj_thread_sleep(1000); } else { break; } } + + RUNNER_TRACE(runner, "thread exiting"); return 0; } @@ -556,6 +608,16 @@ static void text_runner_main(pj_test_runner *base) } } +/* text runner's callback when a test case completes. */ +static void text_runner_on_test_complete(pj_test_runner *base, + pj_test_case *tc) +{ + text_runner_t *runner = (text_runner_t*)base; + pj_mutex_lock(runner->mutex); + basic_on_test_complete(base, tc); + pj_mutex_unlock(runner->mutex); +} + /* text runner destructor */ static void text_runner_destroy(pj_test_runner *base) { @@ -589,7 +651,7 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( runner = PJ_POOL_ZALLOC_T(pool, text_runner_t); runner->base.main = text_runner_main; runner->base.destroy = text_runner_destroy; - runner->base.on_test_complete = &on_test_complete; + runner->base.on_test_complete = &text_runner_on_test_complete; status = pj_mutex_create(pool, "unittest%p", PJ_MUTEX_RECURSE, &runner->mutex); diff --git a/pjlib/src/pjlib-test/ioq_stress_test.c b/pjlib/src/pjlib-test/ioq_stress_test.c index a0f7beebac..ed27ef1217 100644 --- a/pjlib/src/pjlib-test/ioq_stress_test.c +++ b/pjlib/src/pjlib-test/ioq_stress_test.c @@ -20,7 +20,8 @@ #define THIS_FILE "ioq_stress_test.c" #define MAX_THREADS 16 -#define TRACE(log) PJ_LOG(3,log) +//#define TRACE(log) PJ_LOG(3,log) +#define TRACE(log) #define MAX_ASYNC 16 #define RETCODE_CONNECT_FAILED 650 diff --git a/pjlib/src/pjlib-test/rbtree.c b/pjlib/src/pjlib-test/rbtree.c index 7a96ac2855..932fa2b8cc 100644 --- a/pjlib/src/pjlib-test/rbtree.c +++ b/pjlib/src/pjlib-test/rbtree.c @@ -66,18 +66,13 @@ static int test(void) 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; - } + PJ_TEST_NOT_NULL(pool, -10, NULL); key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key)); - if (!key) - return -20; + PJ_TEST_NOT_NULL(key, -20, NULL); node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node)); - if (!node) - return -30; + PJ_TEST_NOT_NULL(node, -30, NULL); for (i=0; i 0) + if (param_unittest_nthreads >= 0) prm.nthreads = param_unittest_nthreads; status = pj_test_create_text_runner(pool, &prm, &runner); if (status != PJ_SUCCESS) { @@ -307,12 +308,13 @@ static int features_tests(int argc, char *argv[]) } PJ_LOG(3,(THIS_FILE, - "Performing %d features tests with %d worker threads", - ntests, prm.nthreads)); + "Performing %d features tests with %d worker thread%s", + ntests, prm.nthreads, prm.nthreads>1?"s":"")); pj_test_run(runner, &suite); pj_test_runner_destroy(runner); pj_test_get_stat(&suite, &stat); - pj_test_dump_log_messages(&suite, param_unittest_logging_policy); + pj_test_display_stat(&stat, "features tests", THIS_FILE); + pj_test_display_log_messages(&suite, param_unittest_logging_policy); pj_pool_release(pool); return stat.nfailed ? 1 : 0; diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index f600e7d386..496953f3a8 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -343,7 +343,7 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, */ start_capture_log(); /* Dumping all test logs. both test 0 and 1 must be present */ - pj_test_dump_log_messages(&suite, PJ_TEST_ALL_TESTS); + pj_test_display_log_messages(&suite, PJ_TEST_ALL_TESTS); print_log_buffer(test_title); end_capture_log(); if (log_size >= MSG_LEN) { @@ -377,7 +377,7 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, /* Dumping only failed test. Only test 1 must be present */ start_capture_log(); - pj_test_dump_log_messages(&suite, PJ_TEST_FAILED_TESTS); + pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS); print_log_buffer(test_title); end_capture_log(); if (log_size >= MSG_LEN) { @@ -405,7 +405,7 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, /* Dumping only successful test. Only test 0 must be present */ start_capture_log(); - pj_test_dump_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS); + pj_test_display_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS); print_log_buffer(test_title); end_capture_log(); if (log_size >= MSG_LEN) { diff --git a/unittest.md b/unittest.md new file mode 100644 index 0000000000..3b68bfb2d3 --- /dev/null +++ b/unittest.md @@ -0,0 +1,60 @@ +(Work in progress, do not review etc) + +### Features + +The unit test framework has the following features. + +#### Parallel execution + +The main objective of this project is to speed up testing. Parallelism should be configurable on test by test case basis. The test designer will be able to control which tests can run in parallel and which can't. + +#### Familiarity + +Use common architecture as described in https://en.wikipedia.org/wiki/XUnit + +#### Easy to use + +Enable porting of existing tests with minimal effort, e.g. just need changing the `test.c` and not each of the test file. + +#### Nice output + +Nice console output, e.g.: + +``` +[1/24] errno_test.. [OK] +[2/24] exception_test.. [OK] +[3/24] os_test. [OK] +``` +Must capture logging of each test so output is not cluttered when the test is run in parallel. Most likely will show the log only after all tests are done (to maintain the niceness of console output in the above point), and will only show logs for failed tests. + +#### Selective test + +Run specific tests from cmdline, e.g. `./pjlib-test fifobuf_test os_test` + +#### Work in restricted targets + +Able to run without pool etc, because crucial/basic tests such as list, pool, thread tests can only be run after the test suite creation is finished! + +#### Useful test macros + +Make it a complete unit-testing framework by implementing various assertion macros. While changing existing tests to use this new assertion macros is tedious and without significant benefit, providing the macros may be nice for writing future tests. + +Also added argparse.h + +Some considerations: + +1. When a unit-test is run (in any thread), it will change (globally) the log writer function to an internal unittest log writer function (in order to capture the log). This has some implications: + a. If apps change the log writer before running the unittest, this should be okay. The unittest will restore the log writer to any writer prior to it being run, and will use that writer to display the logs after it is run. + b. If apps change the log writer somewhere inside a test function, I think this should be okay as long as it restores back the writer. + c. if another thread (that is not aware about testing) is writing to the log, then the unittest log writer will pass that log message to pj_log_write() (i.e. the "official" log writer). I think this is okay and it is the desired behavior, but it will clutter the test output. + +Test times: + +- 0 thread: 6m57.421s +- 1 thread: 4m3.933s +- 2 threads: 2m18.979s (error in rbtree_test:102) +- 4 threads: 2m16.407s (error in rbtree_test:102) + +Auxiliary features: + +- pj/argparse.h From 51d6d93b347f6f6dc73a5e8d54ffebe904b8387c Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 13 Jun 2024 08:38:03 +0700 Subject: [PATCH 04/79] Tidying up global vars in pjlib-test. Add test options: stop on error, skip essential tests, list tests --- pjlib/include/pj/argparse.h | 22 ++++- pjlib/include/pj/unittest.h | 45 +++++---- pjlib/src/pj/unittest.c | 53 +++++++---- pjlib/src/pjlib-test/main.c | 46 ++++++---- pjlib/src/pjlib-test/main_rtems.c | 4 - pjlib/src/pjlib-test/main_win32.c | 4 +- pjlib/src/pjlib-test/sleep.c | 4 +- pjlib/src/pjlib-test/test.c | 132 ++++++++++++++++++--------- pjlib/src/pjlib-test/test.h | 17 +++- pjlib/src/pjlib-test/unittest_test.c | 6 +- 10 files changed, 223 insertions(+), 110 deletions(-) diff --git a/pjlib/include/pj/argparse.h b/pjlib/include/pj/argparse.h index 357086658e..92673f0fc1 100644 --- a/pjlib/include/pj/argparse.h +++ b/pjlib/include/pj/argparse.h @@ -22,6 +22,7 @@ * @file argparse.h * @brief Command line argument parser */ +#include #include #include @@ -38,6 +39,25 @@ PJ_BEGIN_DECL * be used by PJLIB) */ +/** + * Peek the next possible option from argv. An argument is considered an + * option if it starts with "-" and followed by at least another letter that + * is not digit. + * + * @param argv The argv, which must be null terminated. + * + * @return next option or NULL. + */ +static char* pj_argparse_peek_next_option(char *const argv[]) +{ + while (*argv) { + if (**argv=='-' && **argv && !pj_isdigit(**argv) ) + return *argv; + ++argv; + } + return NULL; +} + /** * Check that an option exists, without modifying argv. * @@ -46,7 +66,7 @@ PJ_BEGIN_DECL * * @return PJ_TRUE if the option exists, else PJ_FALSE. */ -static pj_bool_t pj_argparse_exists(const char *opt, const char *const argv[]) +static pj_bool_t pj_argparse_exists(const char *opt, char *const argv[]) { int i; for (i=1; argv[i]; ++i) { diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 74eefbb40b..473891c838 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -400,14 +400,20 @@ typedef struct pj_test_stat /** - * Options for creating text runner. + * Test runner parameters. Use pj_test_runner_param_default() to initialize + * this structure. */ -typedef struct pj_test_text_runner_param +typedef struct pj_test_runner_param { - /** Number of worker threads. Set to zero to disable parallel testings */ + /** Stop the test on error (default: false) */ + pj_bool_t stop_on_error; + + /** Number of worker threads. Set to zero to disable parallel testings. + * Only applicable to test text runner. + */ unsigned nthreads; -} pj_test_text_runner_param; +} pj_test_runner_param; /** @@ -420,6 +426,9 @@ typedef struct pj_test_text_runner_param */ struct pj_test_runner { + /** Parameters */ + pj_test_runner_param prm; + /** The test suite being run */ pj_test_suite *suite; @@ -432,6 +441,9 @@ struct pj_test_runner /** Number of completed tests */ unsigned nruns; + /** Stopping */ + pj_bool_t stopping; + /** main method */ void (*main)(pj_test_runner*); @@ -511,35 +523,36 @@ PJ_DECL(void) pj_test_case_init(pj_test_case *tc, PJ_DECL(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc); /** - * Initialize a basic test runner. A basic runner can be declared in the stack - * and it does not require pool nor multithreading. + * Initialize parameters with reasonable default values. This usually means + * using one worker thread if threading is enabled, and zero worker thread + * (i.e. only use the main thread) otherwise. * - * @param runner The runner. + * @param prm Test runner parameter */ -PJ_DECL(void) pj_test_init_basic_runner(pj_test_runner *runner); +PJ_DECL(void) pj_test_runner_param_default(pj_test_runner_param *prm); /** - * Initialize parameters with reasonable default values. This usually means - * using one worker thread if threading is enabled, and zero worker thread - * (i.e. only use the main thread) otherwise. + * Initialize a basic test runner. A basic runner can be declared in the stack + * and it does not require pool nor multithreading. * - * @param prm Text runner parameter + * @param runner The runner. + * @param prm Runner params, or NULL to accept default values. */ -PJ_DECL(void) pj_test_text_runner_param_default( - pj_test_text_runner_param *prm); +PJ_DECL(void) pj_test_init_basic_runner(pj_test_runner *runner, + const pj_test_runner_param *prm); /** * Create console based test runner. * * @param pool The pool to use to allocate memory - * @param prm Text runner parameter, or NULL for default values. + * @param prm Test runner parameter, or NULL for default values. * @param p_runner Pointer to receive the text runner * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_test_create_text_runner( pj_pool_t *pool, - const pj_test_text_runner_param *prm, + const pj_test_runner_param *prm, pj_test_runner **p_runner); /** diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 951993c61d..323e40303e 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -27,8 +27,8 @@ static long tls_id = INVALID_TLS_ID; static pj_test_case *tc_main_thread; - -#if 0 +#define TRACE 0 +#if TRACE # define TC_TRACE(tc__, msg__) \ {\ pj_time_val tv = pj_elapsed_time(&tc__->runner->suite->start_time, \ @@ -132,8 +132,7 @@ PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) } /* Initialize text runner param with default values */ -PJ_DEF(void) pj_test_text_runner_param_default( - pj_test_text_runner_param *prm) +PJ_DEF(void) pj_test_runner_param_default(pj_test_runner_param *prm) { pj_bzero(prm, sizeof(*prm)); #if PJ_HAS_THREADS @@ -206,6 +205,7 @@ PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, { PJ_LOG(3,(log_sender, "Unit test statistics for %s:", test_name)); PJ_LOG(3,(log_sender, " Total number of tests: %d", stat->ntests)); + PJ_LOG(3,(log_sender, " Number of test run: %d", stat->nruns)); PJ_LOG(3,(log_sender, " Number of failed test: %d", stat->nfailed)); PJ_LOG(3,(log_sender, " Total duration: %dm%d.%03ds", (int)stat->duration.sec/60, (int)stat->duration.sec%60, @@ -411,6 +411,9 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) if (tc->result == PJ_EPENDING) tc->result = -12345; + if (tc->result && runner->prm.stop_on_error) + runner->stopping = PJ_TRUE; + TC_TRACE(tc, "done"); pj_get_timestamp(&tc->end_time); runner->on_test_complete(runner, tc); @@ -428,7 +431,7 @@ static void basic_runner_main(pj_test_runner *runner) { pj_test_case *tc; for (tc = runner->suite->tests.next; - tc != &runner->suite->tests; + tc != &runner->suite->tests && !runner->stopping; tc = tc->next) { run_test_case(runner, tc); @@ -459,9 +462,14 @@ static void basic_runner_destroy(pj_test_runner *runner) } /* Initialize a basic runner. */ -PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner) +PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner, + const pj_test_runner_param *prm) { pj_bzero(runner, sizeof(*runner)); + if (prm) + pj_memcpy(&runner->prm, prm, sizeof(*prm)); + else + pj_test_runner_param_default(&runner->prm); runner->main = &basic_runner_main; runner->destroy = &basic_runner_destroy; runner->on_test_complete = &basic_on_test_complete; @@ -472,8 +480,6 @@ PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner) typedef struct text_runner_t { pj_test_runner base; - pj_test_text_runner_param prm; - pj_test_case *cur_case; pj_mutex_t *mutex; pj_thread_t **threads; @@ -503,7 +509,9 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, *p_test_case = NULL; pj_mutex_lock(runner->mutex); - if (runner->cur_case == NULL) { + if (runner->base.stopping) { + status = PJ_ENOTFOUND; + } else if (runner->cur_case == NULL) { /* Only on the very first invocation */ if (pj_list_empty(&runner->base.suite->tests)) { status = PJ_ENOTFOUND; @@ -557,9 +565,11 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, /* Thread loop */ static int text_runner_thread_proc(void *arg) { + #if TRACE static int global_tid = 0; int tid = global_tid++; char tmp[80]; + #endif text_runner_t *runner = (text_runner_t*)arg; @@ -569,17 +579,26 @@ static int text_runner_thread_proc(void *arg) status = text_runner_get_next_test_case(runner, &tc); if (status==PJ_SUCCESS) { + #if TRACE snprintf(tmp, sizeof(tmp), "thread %d running %s", tid, tc->obj_name); RUNNER_TRACE(runner, tmp); + #endif + run_test_case(&runner->base, tc); + + #if TRACE snprintf(tmp, sizeof(tmp), "thread %d done running %s", tid, tc->obj_name); RUNNER_TRACE(runner, tmp); + #endif } else if (status==PJ_EPENDING) { /* Yeah sleep, but the "correct" solution is probably an order of * magnitute more complicated, so this is good I think. */ + #if TRACE snprintf(tmp, sizeof(tmp), "thread %d waiting", tid); RUNNER_TRACE(runner, tmp); + #endif + pj_thread_sleep(1000); } else { break; @@ -596,14 +615,14 @@ static void text_runner_main(pj_test_runner *base) text_runner_t *runner = (text_runner_t*)base; unsigned i; - for (i=0; iprm.nthreads; ++i) { + for (i=0; iprm.nthreads; ++i) { pj_thread_resume(runner->threads[i]); } /* The main thread behaves like another worker thread */ text_runner_thread_proc(base); - for (i=0; iprm.nthreads; ++i) { + for (i=0; iprm.nthreads; ++i) { pj_thread_join(runner->threads[i]); } } @@ -624,7 +643,7 @@ static void text_runner_destroy(pj_test_runner *base) text_runner_t *runner = (text_runner_t*)base; unsigned i; - for (i=0; iprm.nthreads; ++i) { + for (i=0; iprm.nthreads; ++i) { pj_thread_destroy(runner->threads[i]); } if (runner->mutex) @@ -635,7 +654,7 @@ static void text_runner_destroy(pj_test_runner *base) /* Create text runner */ PJ_DEF(pj_status_t) pj_test_create_text_runner( pj_pool_t *pool, - const pj_test_text_runner_param *prm, + const pj_test_runner_param *prm, pj_test_runner **p_runner) { text_runner_t *runner; @@ -659,11 +678,11 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( goto on_error; if (prm) { - pj_memcpy(&runner->prm, prm, sizeof(*prm)); + pj_memcpy(&runner->base.prm, prm, sizeof(*prm)); } else { - pj_test_text_runner_param_default(&runner->prm); + pj_test_runner_param_default(&runner->base.prm); } - runner->prm.nthreads = 0; + runner->base.prm.nthreads = 0; runner->threads = (pj_thread_t**) pj_pool_calloc(pool, prm->nthreads, sizeof(pj_thread_t*)); for (i=0; inthreads; ++i) { @@ -673,7 +692,7 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( &runner->threads[i]); if (status != PJ_SUCCESS) goto on_error; - runner->prm.nthreads++; + runner->base.prm.nthreads++; } *p_runner = (pj_test_runner*)runner; diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index 087b0d5062..e0ad534af0 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -24,14 +24,6 @@ #include #include -extern int param_echo_sock_type; -extern const char *param_echo_server; -extern int param_echo_port; -extern pj_bool_t param_ci_mode; - -extern pj_test_select_tests param_unittest_logging_policy; -extern int param_unittest_nthreads; - //#if defined(PJ_WIN32) && PJ_WIN32!=0 #if 0 #include @@ -98,6 +90,9 @@ static void usage() puts(" 1: Show logs of failed tests (default)"); puts(" 2: Show logs of all tests"); puts(" -w N Set N worker threads (0: disable worker threads)"); + puts(" -L, --list List the tests and exit"); + puts(" --stop-err Stop testing on error"); + puts(" --skip-e Skip essential tests"); puts(" -i Ask ENTER before quitting"); puts(" -n Do not trap signals"); puts(" -p PORT Use port PORT for echo port"); @@ -127,14 +122,14 @@ int main(int argc, char *argv[]) } interractive = pj_argparse_get("-i", &argc, argv); no_trap = pj_argparse_get("-n", &argc, argv); - status = pj_argparse_get_int("-p", &argc, argv, ¶m_echo_port); + status = pj_argparse_get_int("-p", &argc, argv, &test_app.param_echo_port); if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { puts("Error: invalid/missing value for -p option"); usage(); return 1; } status = pj_argparse_get_str("-s", &argc, argv, - (char**)¶m_echo_server); + (char**)&test_app.param_echo_server); if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { puts("Error: value is required for -s option"); usage(); @@ -144,9 +139,9 @@ int main(int argc, char *argv[]) status = pj_argparse_get_str("-t", &argc, argv, &s); if (status==PJ_SUCCESS) { if (pj_ansi_stricmp(s, "tcp")==0) - param_echo_sock_type = pj_SOCK_STREAM(); + test_app.param_echo_sock_type = pj_SOCK_STREAM(); else if (pj_ansi_stricmp(s, "udp")==0) - param_echo_sock_type = pj_SOCK_DGRAM(); + test_app.param_echo_sock_type = pj_SOCK_DGRAM(); else { printf("Error: unknown socket type %s for -t option\n", s); usage(); @@ -158,16 +153,20 @@ int main(int argc, char *argv[]) return 1; } - param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); + test_app.param_list_test = pj_argparse_get("-L", &argc, argv) || + pj_argparse_get("--list", &argc, argv); + test_app.param_stop_on_error = pj_argparse_get("--stop-err", &argc, argv); + test_app.param_skip_essentials = pj_argparse_get("--skip-e", &argc, argv); + test_app.param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); status = pj_argparse_get_int("-l", &argc, argv, &i); if (status==PJ_SUCCESS) { if (i==0) - param_unittest_logging_policy = PJ_TEST_NO_TEST; + test_app.param_unittest_logging_policy = PJ_TEST_NO_TEST; else if (i==1) - param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; + test_app.param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; else if (i==2) - param_unittest_logging_policy = PJ_TEST_ALL_TESTS; + test_app.param_unittest_logging_policy = PJ_TEST_ALL_TESTS; else { printf("Error: invalid value %d for -l option\n", i); usage(); @@ -180,11 +179,13 @@ int main(int argc, char *argv[]) } status = pj_argparse_get_int("-w", &argc, argv, - (int*)¶m_unittest_nthreads); + (int*)&test_app.param_unittest_nthreads); if (status==PJ_SUCCESS) { - if (param_unittest_nthreads > 100 || param_unittest_nthreads < 0) { + if (test_app.param_unittest_nthreads > 100 || + test_app.param_unittest_nthreads < 0) + { printf("Error: value %d is not valid for -w option\n", - param_unittest_nthreads); + test_app.param_unittest_nthreads); usage(); return 1; } @@ -197,6 +198,13 @@ int main(int argc, char *argv[]) init_signals(); } + if (pj_argparse_peek_next_option(argv)) { + printf("Error: unknown argument %s\n", + pj_argparse_peek_next_option(argv)); + usage(); + return 1; + } + /* argc/argv now contains option values only, if any */ rc = test_main(argc, argv); diff --git a/pjlib/src/pjlib-test/main_rtems.c b/pjlib/src/pjlib-test/main_rtems.c index 881b7b6d49..c382a12a4a 100644 --- a/pjlib/src/pjlib-test/main_rtems.c +++ b/pjlib/src/pjlib-test/main_rtems.c @@ -29,10 +29,6 @@ #include #include -extern int param_echo_sock_type; -extern const char *param_echo_server; -extern int param_echo_port; - #include #define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM diff --git a/pjlib/src/pjlib-test/main_win32.c b/pjlib/src/pjlib-test/main_win32.c index 1a89f41a15..1d8b6e9b7d 100644 --- a/pjlib/src/pjlib-test/main_win32.c +++ b/pjlib/src/pjlib-test/main_win32.c @@ -41,8 +41,6 @@ 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; @@ -72,7 +70,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, return FALSE; pj_log_set_log_func( &write_log ); - param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; + test_app.param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; // Run the test! test_main(); diff --git a/pjlib/src/pjlib-test/sleep.c b/pjlib/src/pjlib-test/sleep.c index 3bf08897ca..fba7d737df 100644 --- a/pjlib/src/pjlib-test/sleep.c +++ b/pjlib/src/pjlib-test/sleep.c @@ -52,8 +52,6 @@ #define THIS_FILE "sleep_test" -extern pj_bool_t param_ci_mode; - static int simple_sleep_test(void) { enum { COUNT = 10 }; @@ -92,7 +90,7 @@ static int simple_sleep_test(void) static int sleep_duration_test(void) { - const unsigned MAX_SLIP = param_ci_mode? 200 : 20; + const unsigned MAX_SLIP = test_app.param_ci_mode? 200 : 20; long duration[] = { 2000, 1000, 500, 200, 100 }; unsigned i; unsigned avg_diff, max_diff; diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 7e08995b16..8e83768878 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -46,15 +46,16 @@ 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 | PJ_LOG_HAS_INDENT; -pj_bool_t param_ci_mode = PJ_FALSE; /* GH CI mode: more lenient tests */ -pj_test_select_tests param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; -int param_unittest_nthreads = -1; - +struct test_app_t test_app = { + 0, + ECHO_SERVER_ADDRESS, + ECHO_SERVER_START_PORT, + PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + PJ_FALSE, + PJ_TEST_FAILED_TESTS, + -1, +}; int null_func() { @@ -85,6 +86,21 @@ static pj_test_case *init_test_case( int (*test_func)(void), const char *obj_nam return tc; } +static void list_tests(const pj_test_suite *suite, const char *title) +{ + unsigned d = pj_log_get_decor(); + const pj_test_case *tc; + + pj_log_set_decor(d ^ PJ_LOG_HAS_NEWLINE); + PJ_LOG(3,(THIS_FILE, "%ld %s:", pj_list_size(&suite->tests), title)); + pj_log_set_decor(0); + for (tc=suite->tests.next; tc!=&suite->tests; tc=tc->next) { + PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); + } + PJ_LOG(3,(THIS_FILE, "\n")); + pj_log_set_decor(d); +} + static int essential_tests(int argc, char *argv[]) { pj_test_suite suite; @@ -98,11 +114,6 @@ static int essential_tests(int argc, char *argv[]) pj_test_case test_cases[MAX_TESTS]; int ntests = 0; - /* Test the unit-testing framework first, outside unit-test! */ - PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); - if (unittest_basic_test()) - return 1; - /* Now that the basic unit-testing framework has been tested, * perform essential tests using basic unit-testing framework. */ @@ -123,7 +134,6 @@ static int essential_tests(int argc, char *argv[]) PJ_LOG(1,(THIS_FILE, "Too many tests for adding %s", #test_func)); \ } - #if INCLUDE_ERRNO_TEST ADD_TEST( errno_test, 0); #endif @@ -162,23 +172,48 @@ static int essential_tests(int argc, char *argv[]) #undef ADD_TEST - PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); - pj_test_init_basic_runner(&runner); - pj_test_run(&runner, &suite); - pj_test_get_stat(&suite, &stat); - pj_test_display_stat(&stat, "essential tests", THIS_FILE); - pj_test_display_log_messages(&suite, param_unittest_logging_policy); + if (test_app.param_list_test) { + list_tests(&suite, "essential tests"); + return 0; + } - if (stat.nfailed) - return 1; + /* Test the unit-testing framework first, outside unit-test! + * Only perform the test if user is not requesting specific test. + */ + if (argc==1) { + pj_dump_config(); + + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); + if (unittest_basic_test()) + return 1; + } + + if (ntests > 0) { + pj_test_runner_param runner_prm; + pj_test_runner_param_default(&runner_prm); + runner_prm.stop_on_error = test_app.param_stop_on_error; + + PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); + pj_test_init_basic_runner(&runner, &runner_prm); + pj_test_run(&runner, &suite); + pj_test_get_stat(&suite, &stat); + pj_test_display_stat(&stat, "essential tests", THIS_FILE); + pj_test_display_log_messages(&suite, + test_app.param_unittest_logging_policy); + + if (stat.nfailed) + return 1; + } /* Now that the essential components have been tested, test the * multithreaded unit-testing framework. */ - PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); - if (unittest_test()) - return 1; - + if (argc==1) { + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); + if (unittest_test()) + return 1; + } + return 0; } @@ -186,7 +221,7 @@ static int features_tests(int argc, char *argv[]) { pj_test_suite suite; pj_test_runner *runner; - pj_test_text_runner_param prm; + pj_test_runner_param prm; pj_test_stat stat; pj_pool_t *pool; enum { @@ -298,9 +333,18 @@ static int features_tests(int argc, char *argv[]) #undef ADD_TEST - pj_test_text_runner_param_default(&prm); - if (param_unittest_nthreads >= 0) - prm.nthreads = param_unittest_nthreads; + if (ntests==0) + return 0; + + if (test_app.param_list_test) { + list_tests(&suite, "features tests"); + return 0; + } + + pj_test_runner_param_default(&prm); + prm.stop_on_error = test_app.param_stop_on_error; + if (test_app.param_unittest_nthreads >= 0) + prm.nthreads = test_app.param_unittest_nthreads; status = pj_test_create_text_runner(pool, &prm, &runner); if (status != PJ_SUCCESS) { app_perror("Error creating text runner", status); @@ -314,7 +358,8 @@ static int features_tests(int argc, char *argv[]) pj_test_runner_destroy(runner); pj_test_get_stat(&suite, &stat); pj_test_display_stat(&stat, "features tests", THIS_FILE); - pj_test_display_log_messages(&suite, param_unittest_logging_policy); + pj_test_display_log_messages(&suite, + test_app.param_unittest_logging_policy); pj_pool_release(pool); return stat.nfailed ? 1 : 0; @@ -330,7 +375,7 @@ int test_inner(int argc, char *argv[]) mem = &caching_pool.factory; pj_log_set_level(3); - pj_log_set_decor(param_log_decor); + pj_log_set_decor(test_app.param_log_decor); rc = pj_init(); if (rc != 0) { @@ -338,15 +383,16 @@ int test_inner(int argc, char *argv[]) return rc; } - pj_dump_config(); pj_caching_pool_init( &caching_pool, NULL, 0 ); - if (param_ci_mode) + if (test_app.param_ci_mode) PJ_LOG(3,(THIS_FILE, "Using ci-mode")); - rc = essential_tests(argc, argv); - if (rc) - return rc; + if (!test_app.param_skip_essentials) { + rc = essential_tests(argc, argv); + if (rc) + return rc; + } rc = features_tests(argc, argv); if (rc) @@ -358,12 +404,12 @@ int test_inner(int argc, char *argv[]) udp_echo_srv_ioqueue(); #elif INCLUDE_ECHO_CLIENT - if (param_echo_sock_type == 0) - param_echo_sock_type = pj_SOCK_DGRAM(); + if (test_app.param_echo_sock_type == 0) + test_app.param_echo_sock_type = pj_SOCK_DGRAM(); - echo_client( param_echo_sock_type, - param_echo_server, - param_echo_port); + echo_client( test_app.param_echo_sock_type, + test_app.param_echo_server, + test_app.param_echo_port); #endif goto on_return; diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index dc7a06f934..bff1cf8169 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -21,6 +21,7 @@ #include #include +#include #define TEST_DEFAULT 1 @@ -121,8 +122,22 @@ extern int echo_srv_sync(void); extern int udp_echo_srv_ioqueue(void); extern int echo_srv_common_loop(pj_atomic_t *bytes_counter); - +/* Global vars */ extern pj_pool_factory *mem; +struct test_app_t +{ + int param_echo_sock_type; + const char *param_echo_server; + int param_echo_port; + int param_log_decor; + pj_bool_t param_ci_mode; + pj_test_select_tests param_unittest_logging_policy; + int param_unittest_nthreads; + int param_list_test; + pj_bool_t param_skip_essentials; + pj_bool_t param_stop_on_error; +}; +extern struct test_app_t test_app; extern int test_main(int argc, char *argv[]); extern void app_perror(const char *msg, pj_status_t err); diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index 496953f3a8..99bbc4a5c4 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -311,10 +311,10 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, /* Create runner */ if (basic) { runner = &basic_runner; - pj_test_init_basic_runner(runner); + pj_test_init_basic_runner(runner, NULL); } else { - pj_test_text_runner_param prm; - pj_test_text_runner_param_default(&prm); + pj_test_runner_param prm; + pj_test_runner_param_default(&prm); prm.nthreads = 4; /* more threads than we need, for testing */ PJ_TEST_SUCCESS(pj_test_create_text_runner(pool, &prm, &runner), -10, test_title); From 9383baa221a6994518bc0ebe04684b949fe99a5a Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 13 Jun 2024 09:50:01 +0700 Subject: [PATCH 05/79] Change the PJ_TEST_XX() signature to be more generic and does not force returning value --- pjlib/include/pj/unittest.h | 191 ++++++++++++--------------- pjlib/src/pjlib-test/fifobuf.c | 34 ++--- pjlib/src/pjlib-test/rbtree.c | 36 ++--- pjlib/src/pjlib-test/unittest_test.c | 55 ++++---- unittest.md | 1 + 5 files changed, 143 insertions(+), 174 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 473891c838..2ca8e7f51b 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -39,26 +39,18 @@ PJ_BEGIN_DECL * having to use the unit-test framework. */ -/** - * Special constant as retval in the various PJ_TEST_XXX macros, to tell - * the macro NOT to issue return when the test fails. - */ -#define PJ_TEST_NO_RET 0xbe2decb1 - /** * Check that an expression is non-zero. If the check fails, informative error - * message will be displayed, and the test will return with the value specified - * in retval, unless retval is PJ_TEST_NO_RET which in this case the test - * function will not return on failure. + * message will be displayed, and the code in err_action will be executed. * - * @param expr The expression to check - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr The expression to check + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_NON_ZERO(expr, retval, reason) \ - do { \ +#define PJ_TEST_NON_ZERO(expr, err_reason, err_action) \ + { \ if (!(expr)) { \ - const char *tmp_reason_ = reason; \ + const char *tmp_reason_ = err_reason; \ const char *sep0_ = (tmp_reason_ ? " (": ""); \ const char *sep1_ = (tmp_reason_ ? ")": ""); \ if (!tmp_reason_) tmp_reason_=""; \ @@ -66,28 +58,26 @@ PJ_BEGIN_DECL "%s:%d%s%s%s", \ #expr, THIS_FILE,__LINE__,sep0_, \ tmp_reason_,sep1_));\ - if (retval != PJ_TEST_NO_RET) return retval; \ + err_action; \ } \ - } while (0) + } /** * Generic check for binary operation. If the check fails, informative error - * message will be displayed, and the test will return with the value specified - * in retval, unless retval is PJ_TEST_NO_RET which in this case the test - * function will not return on failure. + * message will be displayed, and the code in err_action will be executed. * - * @param expr0 First expression - * @param op The operator - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails - */ -#define PJ_TEST_BINARY_OP(expr0, op, expr1, retval, reason) \ - do { \ + * @param expr0 First expression + * @param op The operator + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_BINARY_OP(expr0, op, expr1, err_reason, err_action) \ + { \ long tmp_value0_ = (long)(expr0); \ long tmp_value1_ = (long)(expr1); \ if (!(tmp_value0_ op tmp_value1_)) { \ - const char *tmp_reason_ = reason; \ + const char *tmp_reason_ = err_reason; \ const char *sep0_ = (tmp_reason_ ? " (": ""); \ const char *sep1_ = (tmp_reason_ ? ")": ""); \ if (!tmp_reason_) tmp_reason_=""; \ @@ -96,26 +86,25 @@ PJ_BEGIN_DECL #expr0, tmp_value0_, #expr1, tmp_value1_, \ THIS_FILE, __LINE__, \ sep0_, tmp_reason_, sep1_)); \ - if (retval != PJ_TEST_NO_RET) return retval; \ + err_action; \ } \ - } while (0) + } /** * Check that an expression is PJ_SUCCESS. If the check fails, error message - * explaining the error code will be displayed, and the test will return with - * the value specified in retval, unless retval is PJ_TEST_NO_RET which in - * this case the test function will not return on failure. + * explaining the error code will be displayed, and the code in err_action + * will be executed. * - * @param expr The expression to check - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr The expression to check + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_SUCCESS(expr, retval, reason) \ - do { \ +#define PJ_TEST_SUCCESS(expr, err_reason, err_action) \ + { \ pj_status_t tmp_status_ = (expr); \ if (tmp_status_ != PJ_SUCCESS) { \ char errbuf[80]; \ - const char *tmp_reason_ = reason; \ + const char *tmp_reason_ = err_reason; \ const char *sep0_ = (tmp_reason_ ? " (": ""); \ const char *sep1_ = (tmp_reason_ ? ")": ""); \ if (!tmp_reason_) tmp_reason_=""; \ @@ -124,111 +113,99 @@ PJ_BEGIN_DECL "status=%d (%s)%s%s%s", \ #expr, THIS_FILE, __LINE__, tmp_status_,errbuf,\ sep0_, tmp_reason_, sep1_)); \ - if (retval != PJ_TEST_NO_RET) return retval; \ + err_action; \ } \ - } while (0) + } /** * Alias for PJ_TEST_NON_ZERO() */ -#define PJ_TEST_TRUE(expr, retval, reason) \ - PJ_TEST_NON_ZERO(expr, retval, reason) +#define PJ_TEST_TRUE(expr, err_reason, err_action) \ + PJ_TEST_NON_ZERO(expr, err_reason, err_action) /** * Alias for PJ_TEST_NON_ZERO() */ -#define PJ_TEST_NOT_NULL(expr, retval, reason) \ - PJ_TEST_NON_ZERO(expr, retval, reason) +#define PJ_TEST_NOT_NULL(expr, err_reason, err_action) \ + PJ_TEST_NON_ZERO(expr, err_reason, err_action) /** * Check that expr0 equals expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_EQ(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, ==, expr1, retval, reason) +#define PJ_TEST_EQ(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, ==, expr1, err_reason, err_action) /** - * Check that expr0 not equals expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * Check that expr0 does not equal expr1. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_NEQ(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, !=, expr1, retval, reason) +#define PJ_TEST_NEQ(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, !=, expr1, err_reason, err_action) /** * Check that expr0 is less than expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_LT(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, <, expr1, retval, reason) +#define PJ_TEST_LT(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, <, expr1, err_reason, err_action) /** * Check that expr0 is less than or equal to expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_LTE(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, <=, expr1, retval, reason) +#define PJ_TEST_LTE(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, <=, expr1, err_reason, err_action) /** * Check that expr0 is greater than expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_GT(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, >, expr1, retval, reason) +#define PJ_TEST_GT(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, >, expr1, err_reason, err_action) /** * Check that expr0 is greater than or equal to expr1. - * If the check fails, informative error message will be displayed, and - * the test will return with the value specified in retval, unless retval - * is PJ_TEST_NO_RET which in this case the test function will not return - * on failure. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. * - * @param expr0 First expression - * @param expr1 Second expression - * @param retval Return value on error, or PJ_TEST_NO_RET. - * @param reason NULL or extra text to be displayed when the check fails + * @param expr0 First expression + * @param expr1 Second expression + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails */ -#define PJ_TEST_GTE(expr0, expr1, retval, reason) \ - PJ_TEST_BINARY_OP(expr0, >=, expr1, retval, reason) +#define PJ_TEST_GTE(expr0, expr1, err_reason, err_action) \ + PJ_TEST_BINARY_OP(expr0, >=, expr1, err_reason, err_action) /** diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index 9ddc1aaf7a..cf1bb9eef8 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -46,8 +46,8 @@ static int fifobuf_size_test() pj_fifobuf_init (&fifo, buffer, sizeof(buffer)); - PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -11, NULL); - PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -12, NULL); + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -11); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -12); p0 = pj_fifobuf_alloc(&fifo, 16); p1 = pj_fifobuf_alloc(&fifo, 4); @@ -55,9 +55,9 @@ static int fifobuf_size_test() pj_fifobuf_free(&fifo, p0); - PJ_TEST_EQ( pj_fifobuf_available_size(&fifo), 16, -14, NULL); - PJ_TEST_EQ( pj_memcmp(before, zero, sizeof(zero)), 0, -18, NULL); - PJ_TEST_EQ( pj_memcmp(after, zero, sizeof(zero)), 0, -19, NULL); + PJ_TEST_EQ( pj_fifobuf_available_size(&fifo), 16, NULL, return -14); + PJ_TEST_EQ( pj_memcmp(before, zero, sizeof(zero)), 0, NULL, return -18); + PJ_TEST_EQ( pj_memcmp(after, zero, sizeof(zero)), 0, NULL, return -19); return 0; } @@ -78,34 +78,34 @@ int fifobuf_test() return i; pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); - PJ_TEST_NOT_NULL(pool, -10, NULL); + PJ_TEST_NOT_NULL(pool, NULL, return -10); buffer = pj_pool_alloc(pool, SIZE); - PJ_TEST_NOT_NULL(buffer, -20, NULL); + PJ_TEST_NOT_NULL(buffer, NULL, return -20); pj_fifobuf_init (&fifo, buffer, SIZE); /* Capacity and maximum alloc */ - PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -21, NULL); - PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -22, NULL); + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -21); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -22); entries[0] = pj_fifobuf_alloc(&fifo, SIZE-SZ); - PJ_TEST_NOT_NULL(entries[0], -23, NULL); + PJ_TEST_NOT_NULL(entries[0], NULL, return -23); pj_fifobuf_free(&fifo, entries[0]); - PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, -21, NULL); - PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -22, NULL); + PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -21); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -22); /* Alignment test */ for (i=0; i<30; ++i) { entries[i] = pj_fifobuf_alloc(&fifo, i+1); - PJ_TEST_NOT_NULL(entries[i], -50, NULL); + PJ_TEST_NOT_NULL(entries[i], NULL, return -50); //fifobuf is no longer aligned. //PJ_TEST_EQ(((pj_size_t)entries[i]) % sizeof(unsigned), 0, -60, // "alignment error"); } for (i=0; i<30; ++i) { - PJ_TEST_SUCCESS( pj_fifobuf_free(&fifo, entries[i]), -70, NULL); + PJ_TEST_SUCCESS( pj_fifobuf_free(&fifo, entries[i]), NULL, return -70); } /* alloc() and free() */ @@ -125,7 +125,7 @@ int fifobuf_test() if (entries[(i+1)%2]) pj_fifobuf_free(&fifo, entries[(i+1)%2]); - PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, -1, NULL); + PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -1); /* alloc() and unalloc() */ entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE); @@ -137,7 +137,7 @@ int fifobuf_test() pj_fifobuf_unalloc(&fifo, entries[1]); } pj_fifobuf_unalloc(&fifo, entries[0]); - PJ_TEST_GTE(pj_fifobuf_available_size(&fifo), SIZE-SZ, -2, NULL); + PJ_TEST_GTE(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -2); /* Allocate as much as possible, then free them all. */ for (i=0; ikey,(node_key*)it->key)>=0) { - ++err; PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", (char*)prev->user_data, (char*)it->user_data)); + rc=-40; + goto on_error; } } prev = it; @@ -119,13 +113,10 @@ static int test(void) // Search. for (j=0; j= MSG_LEN) { /* We should only have space for the last log */ PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), - -201, test_title); + test_title, return -201); PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), - -202, test_title); + test_title, return -202); } else if (log_size < 10) { /* buffer is too small, writing log will be rejected */ PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, - -203, test_title); + test_title, return -203); } else { /* buffer is small, message will be truncated */ PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), - -204, test_title); + test_title, return -204); } if (log_size >= 2*MSG_LEN) { /* We should have space for two last log messages */ PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), - -205, test_title); + test_title, return -205); PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), - -206, test_title); + test_title, return -206); } if (log_size >= 3*MSG_LEN) { /* We should have space for three last log messages */ PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), - -207, test_title); + test_title, return -207); } /* Dumping only failed test. Only test 1 must be present */ @@ -382,26 +381,26 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, end_capture_log(); if (log_size >= MSG_LEN) { PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in func_to_test(1)"), - -211, test_title); + test_title, return -211); } else if (log_size < 10) { /* buffer is too small, writing log will be rejected */ PJ_TEST_EQ(strstr(log_buffer, "Some error"), NULL, - -212, test_title); + test_title, return -212); } else { /* buffer is small, message will be truncated */ PJ_TEST_NOT_NULL(strstr(log_buffer, "Some error in"), - -213, test_title); + test_title, return -213); } if (log_size >= 2*MSG_LEN) { PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(1)"), - -214, test_title); + test_title, return -214); } if (log_size >= 3*MSG_LEN) { PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(1)"), - -215, test_title); + test_title, return -215); } PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(0)"), NULL, - -216, test_title); + test_title, return -216); /* Dumping only successful test. Only test 0 must be present */ start_capture_log(); @@ -410,14 +409,14 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, end_capture_log(); if (log_size >= MSG_LEN) { PJ_TEST_NOT_NULL(strstr(log_buffer, "Performing func_to_test(0)"), - -221, test_title); + test_title, return -221); } if (log_size >= 2*MSG_LEN) { PJ_TEST_NOT_NULL(strstr(log_buffer, "Entering func_to_test(0)"), - -222, test_title); + test_title, return -222); } PJ_TEST_EQ(strstr(log_buffer, "Entering func_to_test(1)"), NULL, - -223, test_title); + test_title, return -223); return 0; } diff --git a/unittest.md b/unittest.md index 3b68bfb2d3..c19562532b 100644 --- a/unittest.md +++ b/unittest.md @@ -47,6 +47,7 @@ Some considerations: a. If apps change the log writer before running the unittest, this should be okay. The unittest will restore the log writer to any writer prior to it being run, and will use that writer to display the logs after it is run. b. If apps change the log writer somewhere inside a test function, I think this should be okay as long as it restores back the writer. c. if another thread (that is not aware about testing) is writing to the log, then the unittest log writer will pass that log message to pj_log_write() (i.e. the "official" log writer). I think this is okay and it is the desired behavior, but it will clutter the test output. +2. Use {} instead of do {} while (0) in case user pus "break" as action. Test times: From 1d842dbfeb273ce100e73ad3dc792059ce8fa7cb Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 13 Jun 2024 17:23:29 +0700 Subject: [PATCH 06/79] Modifications to some existing tests to use unit-test test macros --- pjlib/include/pj/unittest.h | 16 ++- pjlib/src/pj/unittest.c | 30 ++++- pjlib/src/pjlib-test/activesock.c | 164 +++++++++------------------ pjlib/src/pjlib-test/atomic.c | 42 +++---- pjlib/src/pjlib-test/file.c | 108 +++++------------- pjlib/src/pjlib-test/hash_test.c | 70 ++++-------- pjlib/src/pjlib-test/ioq_perf.c | 119 +++++++++---------- pjlib/src/pjlib-test/test.c | 9 +- pjlib/src/pjlib-test/test.h | 3 +- pjlib/src/pjlib-test/unittest_test.c | 9 +- unittest.md | 96 +++++++++++++++- 11 files changed, 312 insertions(+), 354 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 2ca8e7f51b..1310adac98 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -434,8 +434,8 @@ struct pj_test_runner /** Option to select tests (e.g. in pj_test_dump_log_messages()) */ typedef enum pj_test_select_tests { - /** Select all tests*/ - PJ_TEST_ALL_TESTS = 0, + /** Select no test*/ + PJ_TEST_NO_TEST = 0, /** Select only failed tests */ PJ_TEST_FAILED_TESTS = 1, @@ -443,9 +443,12 @@ typedef enum pj_test_select_tests /** Select only successful tests */ PJ_TEST_SUCCESSFUL_TESTS = 2, - /** Select no test*/ - PJ_TEST_NO_TEST = 4, + /** Select all tests*/ + PJ_TEST_ALL_TESTS = 3, + /** No header/footer separator */ + PJ_TEST_NO_HEADER_FOOTER = 4, + } pj_test_select_tests; @@ -577,10 +580,11 @@ PJ_DECL(void) pj_test_display_stat(const pj_test_stat *stat, * in order to get and access the statistics or log messages. * * @param suite The test suite - * @param which Which test cases to dump + * @param flags Select which test logs to display by choosing + * from pj_test_select_tests. */ PJ_DECL(void) pj_test_display_log_messages(const pj_test_suite *suite, - pj_test_select_tests which); + unsigned flags); /** * Destroy the runner. Runner may be destroyed right after it is run, diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 323e40303e..44335c4602 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -214,17 +214,29 @@ PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, /* Dump previously saved log messages */ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, - pj_test_select_tests which) + unsigned flags) { const pj_test_case *tc = suite->tests.next; pj_log_func *log_writer = pj_log_get_log_func(); + const char *title; + + if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_ALL_TESTS) + title = "all"; + else if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_FAILED_TESTS) + title = "failed"; + else if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_SUCCESSFUL_TESTS) + title = "successful"; + else + title = "unknown"; while (tc != &suite->tests) { const pj_test_log_item *log_item = tc->logs.next; if ((tc->result == PJ_EPENDING) || - (which==PJ_TEST_FAILED_TESTS && tc->result==0) || - (which==PJ_TEST_SUCCESSFUL_TESTS && tc->result!=0)) + ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_FAILED_TESTS && + tc->result==0) || + ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_SUCCESSFUL_TESTS && + tc->result!=0)) { /* Test doesn't meet criteria */ tc = tc->next; @@ -232,6 +244,13 @@ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, } if (log_item != &tc->logs) { + if (title && (flags & PJ_TEST_NO_HEADER_FOOTER)==0) { + PJ_LOG(3,(THIS_FILE, + "------------ Displaying %s test logs: ------------", + title)); + title = NULL; + } + PJ_LOG(3,(THIS_FILE, "Logs for %s [rc:%d]:", tc->obj_name, tc->result)); @@ -242,6 +261,11 @@ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, } tc = tc->next; } + + if (!title) { + PJ_LOG(3,(THIS_FILE, + "--------------------------------------------------------")); + } } /* Destroy runner */ diff --git a/pjlib/src/pjlib-test/activesock.c b/pjlib/src/pjlib-test/activesock.c index 29f8117667..98ce4f9688 100644 --- a/pjlib/src/pjlib-test/activesock.c +++ b/pjlib/src/pjlib-test/activesock.c @@ -31,6 +31,7 @@ #define THIS_FILE "activesock.c" +#define ERR(r__) { ret=r__; goto on_return; } /******************************************************************* * Simple UDP echo server. @@ -142,7 +143,8 @@ static void udp_echo_srv_destroy(struct udp_echo_srv *srv) * UDP ping pong test (send packet back and forth between two UDP echo * servers. */ -static int udp_ping_pong_test(void) +/* was udp_ping_pong_test(void) */ +static int activesock_test0(void) { pj_ioqueue_t *ioqueue = NULL; pj_pool_t *pool = NULL; @@ -153,27 +155,13 @@ static int udp_ping_pong_test(void) pj_status_t status; pool = pj_pool_create(mem, "pingpong", 512, 512, NULL); - if (!pool) - return -10; + PJ_TEST_NOT_NULL(pool, NULL, 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; - } + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 4, &ioqueue), NULL, ERR(-20)) + PJ_TEST_SUCCESS(udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1), + NULL, ERR(-30)); + PJ_TEST_SUCCESS(udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2), + NULL, ERR(-40)); /* initiate the first send */ for (count=0; count<1000; ++count) { @@ -193,11 +181,8 @@ static int udp_ping_pong_test(void) 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; - } + PJ_TEST_TRUE(status==PJ_SUCCESS || status==PJ_EPENDING, + "pj_activesock_sendto()", ERR(-50)); need_send = PJ_FALSE; } @@ -215,20 +200,10 @@ static int udp_ping_pong_test(void) #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; - } + PJ_TEST_EQ(srv1->rx_err_cnt+srv1->tx_err_cnt, 0, NULL, ERR(-60)); + PJ_TEST_EQ(srv2->rx_err_cnt+srv2->tx_err_cnt, 0, NULL, ERR(-62)); + PJ_TEST_TRUE(last_rx1 != srv1->rx_cnt || last_rx2 != srv2->rx_cnt, + "timeout/packets have been lost", ERR(-70)); } ret = 0; @@ -331,7 +306,8 @@ static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock, return PJ_TRUE; } -static int tcp_perf_test(void) +/* was tcp_perf_test() */ +static int activesock_test1(void) { enum { COUNT=10000 }; pj_pool_t *pool = NULL; @@ -341,48 +317,33 @@ static int tcp_perf_test(void) pj_activesock_cb cb; struct tcp_state *state1, *state2; unsigned i; - pj_status_t status; - - pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL); + int ret = 0; + pj_status_t status = PJ_SUCCESS; - status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, - &sock2); - if (status != PJ_SUCCESS) { - status = -100; - goto on_return; - } + pool = pj_pool_create(mem, "activesock_test1", 256, 256, NULL); + PJ_TEST_NOT_NULL(pool, NULL, return -10); - status = pj_ioqueue_create(pool, 4, &ioqueue); - if (status != PJ_SUCCESS) { - status = -110; - goto on_return; - } + PJ_TEST_SUCCESS( app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, + &sock2), + NULL, ERR(-100)); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 4, &ioqueue), + NULL, ERR(-110)); 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; - } + PJ_TEST_SUCCESS(pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), + NULL, ioqueue, &cb, state1, &asock1), + NULL, ERR(-120)); 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; - } + PJ_TEST_SUCCESS(pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, + ioqueue, &cb, state2, &asock2), + NULL, ERR(-130)); + PJ_TEST_SUCCESS(pj_activesock_start_read(asock1, pool, 1000, 0), + NULL, ERR(-140)); /* Send packet as quickly as possible */ for (i=0; ierr && !state2->err; ++i) { @@ -420,16 +381,10 @@ static int tcp_perf_test(void) */ 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; - } + PJ_TEST_SUCCESS(status, "send error", ERR(-180)); + if (status == PJ_SUCCESS) { + PJ_TEST_EQ(len, sizeof(*pkt), + "shouldn't report partial sent", ERR(-190)); } } @@ -458,23 +413,13 @@ static int tcp_perf_test(void) 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; - } + /* this should not happen. non-success should have been dealt with */ + PJ_TEST_SUCCESS(status, NULL, ERR(-182)); + + PJ_TEST_EQ(state1->err, 0, NULL, ERR(-183)); + PJ_TEST_EQ(state2->err, 0, NULL, ERR(-186)); + PJ_TEST_EQ(state1->next_recv_seq, COUNT, + "not all packets are received", ERR(-195)); on_return: if (asock2) @@ -486,24 +431,17 @@ static int tcp_perf_test(void) if (pool) pj_pool_release(pool); - return status; + return ret; } - - int activesock_test(void) { - int ret; - - PJ_LOG(3,("", "..udp ping/pong test")); - ret = udp_ping_pong_test(); - if (ret != 0) - return ret; + int rc; + if ((rc=activesock_test0()) != 0) + return rc; - PJ_LOG(3,("", "..tcp perf test")); - ret = tcp_perf_test(); - if (ret != 0) - return ret; + if ((rc=activesock_test1()) != 0) + return rc; return 0; } diff --git a/pjlib/src/pjlib-test/atomic.c b/pjlib/src/pjlib-test/atomic.c index 8bd0a5a222..83f99178c1 100644 --- a/pjlib/src/pjlib-test/atomic.c +++ b/pjlib/src/pjlib-test/atomic.c @@ -44,58 +44,48 @@ #if INCLUDE_ATOMIC_TEST +#define THIS_FILE "atomic.c" +#define ERR(r__) { rc=r__; goto on_return; } + 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; + int rc=0; + PJ_TEST_NOT_NULL( (pool=pj_pool_create(mem, NULL, 4096, 0, NULL)), + NULL, ERR(-10)); /* create() */ - rc = pj_atomic_create(pool, 111, &atomic_var); - if (rc != 0) { - return -20; - } + PJ_TEST_SUCCESS( pj_atomic_create(pool, 111, &atomic_var), NULL, ERR(-20)); /* get: check the value. */ - if (pj_atomic_get(atomic_var) != 111) - return -30; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 111, NULL, ERR(-30)); /* increment. */ pj_atomic_inc(atomic_var); - if (pj_atomic_get(atomic_var) != 112) - return -40; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 112, NULL, ERR(-40)); /* decrement. */ pj_atomic_dec(atomic_var); - if (pj_atomic_get(atomic_var) != 111) - return -50; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 111, NULL, ERR(-50)); /* set */ pj_atomic_set(atomic_var, 211); - if (pj_atomic_get(atomic_var) != 211) - return -60; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 211, NULL, ERR(-60)); /* add */ pj_atomic_add(atomic_var, 10); - if (pj_atomic_get(atomic_var) != 221) - return -60; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 221, NULL, ERR(-60)); /* check the value again. */ - if (pj_atomic_get(atomic_var) != 221) - return -70; + PJ_TEST_EQ( pj_atomic_get(atomic_var), 221, NULL, ERR(-70)); /* destroy */ - rc = pj_atomic_destroy(atomic_var); - if (rc != 0) - return -80; + PJ_TEST_SUCCESS( pj_atomic_destroy(atomic_var), NULL, ERR(-80)); +on_return: pj_pool_release(pool); - - return 0; + return rc; } diff --git a/pjlib/src/pjlib-test/file.c b/pjlib/src/pjlib-test/file.c index ed34d5621a..afdddb4f01 100644 --- a/pjlib/src/pjlib-test/file.c +++ b/pjlib/src/pjlib-test/file.c @@ -24,6 +24,7 @@ #define FILENAME "testfil1.txt" #define NEWNAME "testfil2.txt" #define INCLUDE_FILE_TIME_TEST 0 +#define THIS_FILE "file.c" static char buffer[11] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; @@ -51,43 +52,25 @@ static int file_test_internal(void) /* * 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; - } + PJ_TEST_SUCCESS(pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd), + NULL, 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; - } + PJ_TEST_SUCCESS(pj_file_write(fd, buffer, &size), NULL, + { pj_file_close(fd); return -20; }); + PJ_TEST_EQ(size, sizeof(buffer), NULL, return -25 ); + PJ_TEST_SUCCESS( pj_file_close(fd), NULL, return -30 ); /* Check the file existance and size. */ - if (!pj_file_exists(FILENAME)) - return -40; + PJ_TEST_TRUE( pj_file_exists(FILENAME), NULL, return -40 ); - if (pj_file_size(FILENAME) != sizeof(buffer)) - return -50; + PJ_TEST_EQ( pj_file_size(FILENAME), sizeof(buffer), NULL, return -50 ); /* Get file stat. */ - status = pj_file_getstat(FILENAME, &stat); - if (status != PJ_SUCCESS) - return -60; + PJ_TEST_SUCCESS(pj_file_getstat(FILENAME, &stat), NULL, return -60 ); /* Check stat size. */ - if (stat.size != sizeof(buffer)) - return -70; + PJ_TEST_EQ(stat.size, sizeof(buffer), NULL, return -70 ); #if INCLUDE_FILE_TIME_TEST /* Check file creation time >= start_time. */ @@ -118,11 +101,8 @@ static int file_test_internal(void) /* * 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; - } + PJ_TEST_SUCCESS(pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd), + NULL, return -100); size = 0; while (size < (pj_ssize_t)sizeof(readbuf)) { @@ -133,6 +113,7 @@ static int file_test_internal(void) PJ_LOG(3,("", "...error reading file after %ld bytes " "(error follows)", size)); app_perror("...error", status); + pj_file_close(fd); return -110; } if (read == 0) { @@ -142,68 +123,41 @@ static int file_test_internal(void) size += read; } - if (size != sizeof(buffer)) - return -120; + PJ_TEST_EQ(size, sizeof(buffer), NULL, + {pj_file_close(fd); return -120; }); /* if (!pj_file_eof(fd, PJ_O_RDONLY)) return -130; */ - if (pj_memcmp(readbuf, buffer, size) != 0) - return -140; + PJ_TEST_EQ(pj_memcmp(readbuf, buffer, size), 0, NULL, + {pj_file_close(fd); 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; - } + PJ_TEST_SUCCESS(pj_file_setpos(fd, 4, PJ_SEEK_SET), NULL, + {pj_file_close(fd); 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; + PJ_TEST_SUCCESS(pj_file_getpos(fd, &pos), NULL, + {pj_file_close(fd); return -142; }); + PJ_TEST_EQ(pos, 4, NULL, {pj_file_close(fd); return -143; }); - status = pj_file_close(fd); - if (status != PJ_SUCCESS) { - app_perror("...file_close() error", status); - return -150; - } + PJ_TEST_SUCCESS(pj_file_close(fd), NULL, 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; + PJ_TEST_SUCCESS(pj_file_move(FILENAME, NEWNAME), NULL, return -160); + PJ_TEST_EQ(pj_file_exists(FILENAME), 0, NULL, return -170); + PJ_TEST_TRUE(pj_file_exists(NEWNAME), NULL, return -180); + PJ_TEST_EQ(pj_file_size(NEWNAME), sizeof(buffer), NULL, 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_TEST_SUCCESS(pj_file_delete(NEWNAME), NULL, return -200); + PJ_TEST_EQ(pj_file_exists(NEWNAME), 0, NULL, return -210); - PJ_LOG(3,("", "...success")); - return PJ_SUCCESS; + return 0; } diff --git a/pjlib/src/pjlib-test/hash_test.c b/pjlib/src/pjlib-test/hash_test.c index fb59331309..05c093e87c 100644 --- a/pjlib/src/pjlib-test/hash_test.c +++ b/pjlib/src/pjlib-test/hash_test.c @@ -25,6 +25,8 @@ #if INCLUDE_HASH_TEST #define HASH_COUNT 31 +#define THIS_FILE "hash_test.c" + static int hash_test_with_key(pj_pool_t *pool, unsigned char key) { @@ -33,51 +35,28 @@ static int hash_test_with_key(pj_pool_t *pool, unsigned char key) pj_hash_iterator_t it_buf, *it; unsigned *entry; - ht = pj_hash_create(pool, HASH_COUNT); - if (!ht) - return -10; + PJ_TEST_NOT_NULL( (ht=pj_hash_create(pool, HASH_COUNT)), NULL, 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; + PJ_TEST_NOT_NULL((entry=(unsigned*)pj_hash_get(ht,&key,sizeof(key),NULL)), + NULL, return -20); + PJ_TEST_EQ( *entry, value, NULL, return -25); + PJ_TEST_EQ(pj_hash_count(ht), 1, NULL, return -30); + PJ_TEST_NOT_NULL((it=pj_hash_first(ht, &it_buf)), NULL, return -40); + PJ_TEST_NOT_NULL((entry=(unsigned*)pj_hash_this(ht, it)), NULL, + return -50); + PJ_TEST_EQ(*entry, value, NULL, return -60); + PJ_TEST_EQ(pj_hash_next(ht, it), NULL, 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; - + PJ_TEST_EQ(pj_hash_get(ht, &key, sizeof(key), NULL), NULL, NULL, + return -80); + PJ_TEST_EQ(pj_hash_count(ht), 0, NULL, return -90); + PJ_TEST_EQ(pj_hash_first(ht, &it_buf), NULL, NULL, return -100); return 0; } @@ -92,9 +71,7 @@ static int hash_collision_test(pj_pool_t *pool) unsigned char *values; unsigned i; - ht = pj_hash_create(pool, HASH_COUNT); - if (!ht) - return -200; + PJ_TEST_NOT_NULL((ht=pj_hash_create(pool, HASH_COUNT)), NULL, return -200); values = (unsigned char*) pj_pool_alloc(pool, COUNT); @@ -103,16 +80,13 @@ static int hash_collision_test(pj_pool_t *pool) pj_hash_set(pool, ht, &i, sizeof(i), 0, &values[i]); } - if (pj_hash_count(ht) != COUNT) - return -210; + PJ_TEST_EQ(pj_hash_count(ht), COUNT, NULL, return -210); for (i=0; iid = i; arg->ioqueue = ioqueue; - 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; - } + PJ_TEST_SUCCESS( pj_thread_create( pool, NULL, + &worker_thread, + arg, + PJ_THREAD_DEFAULT_STACK_SIZE, + PJ_THREAD_SUSPENDED, &thread[i] ), + NULL, return -80); } /* Mark start time. */ - rc = pj_get_timestamp(&start); - if (rc != PJ_SUCCESS) - return -90; + PJ_TEST_SUCCESS( pj_get_timestamp(&start), NULL, return -90); /* Start the thread. */ TRACE_((THIS_FILE, " resuming all threads..")); for (i=0; i= MSG_LEN) { @@ -376,7 +377,8 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, /* Dumping only failed test. Only test 1 must be present */ start_capture_log(); - pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS); + pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS | + PJ_TEST_NO_HEADER_FOOTER); print_log_buffer(test_title); end_capture_log(); if (log_size >= MSG_LEN) { @@ -404,7 +406,8 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, /* Dumping only successful test. Only test 0 must be present */ start_capture_log(); - pj_test_display_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS); + pj_test_display_log_messages(&suite, PJ_TEST_SUCCESSFUL_TESTS | + PJ_TEST_NO_HEADER_FOOTER); print_log_buffer(test_title); end_capture_log(); if (log_size >= MSG_LEN) { diff --git a/unittest.md b/unittest.md index c19562532b..00987e3d06 100644 --- a/unittest.md +++ b/unittest.md @@ -1,6 +1,89 @@ (Work in progress, do not review etc) -### Features +This PR is part one of two to speed up testing for PJSIP. This first part attempts to speed up the C/C++ based tests and part two will address Python based testing framework. + +The plan is to implement unit testing framework in PJLIB, that that is what the bulk of this PR presents. More specifically, this PR contains several work: + +1. the (new) unit testing framework, is in `` and `pj/unittest.c`. +2. largish modifications to `pjlib-test`, `pjlib-util-test`, `pjnath-test`, `pjmedia-test`, and `pjsip-test`. +3. minor developments: + + - modifications to `pj/fifobuf.[hc]`, an old feature (part of pjsip initial commit!) that has never been used until now + - new auxiliary feature: ``, header only utilities to parse command line arguments. + +Let's discuss the work in more detail. + +### 1. Unit testing framework + +#### Overview + +The unit testing framework `` provides two parts. + +First part is test macros that can be used to replace manual `if`s in the test codes. This can be used anywhere without having to use the unit test framework (apart from including the header file of course). Example: + +``` + PJ_TEST_NON_ZERO(pool, "pool allocation failed", {rc=-1; goto on_error;}); + PJ_TEST_EQ(count, expected, "invalid read count", return -100); + PJ_TEST_SUCCESS(pj_ioqueue_read(...), NULL, {rc=-80; goto on_return; }); +``` + +On failure, the macros above will display the expressions being checked, the value of the expressions, and the status message (for PJ_TEST_SUCCESS), saving the developer from having to write all these things. + +The second part is the unit test framework. The framework uses common architecture as described in https://en.wikipedia.org/wiki/XUnit. The global workflow to create/run unit test is something like this: + +``` + pj_test_case tc0, tc1; + pj_test_suite suite; + pj_test_runner *runner; + pj_test_stat stat; + + pj_test_suite_init(&suite); + + pj_test_case_init(&tc0, ..., &func_to_test0, arg0, ... ); + pj_test_suite_add_case(&suite, &tc0); + + pj_test_case_init(&tc1, ..., &func_to_test1, arg1, ... ); + pj_test_suite_add_case(&suite, &tc1); + + pj_test_create_text_runner(pool, .., &runner); + pj_test_run(runner, &suite); + pj_test_runner_destroy(runner); + + pj_test_get_stat(&suite, &stat); + pj_test_display_stat(&stat, ..); + pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS); +``` + + +### 2. Modifications to test apps + +#### Speedup + +In PJLIB-TEST: + +- 0 worker thread: 9m22.228s (original) +- 1 worker thread: 4m51.940s +- 2 worker threads: 4m9.322s +- 3 worker threads: 2m52.350s +- 4 worker threads: 2m31.399s +- 6 worker threads: 2m28.450s +- 8 worker threads: 2m19.779s + +So it looks like the sweet spot is with 3 worker threads. This is because some tests takes quite a long time t o finish: + +``` +[14/20] udp_ioqueue_unreg_test [OK] [92.759s] +[15/20] ioqueue_stress_test [OK] [111.061s] +[17/20] ioqueue_perf_test1 [OK] [110.149s] +[18/20] activesock_test [OK] [101.890s] +``` + +I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner). + + +### 3. Other (Minor) developments + + The unit test framework has the following features. @@ -51,10 +134,13 @@ Some considerations: Test times: -- 0 thread: 6m57.421s -- 1 thread: 4m3.933s -- 2 threads: 2m18.979s (error in rbtree_test:102) -- 4 threads: 2m16.407s (error in rbtree_test:102) +- 0 thread: 9m22.228s +- 1 thread: 4m51.940s +- 2 threads: 4m9.322s +- 3 threads: 2m52.350s +- 4 threads: 2m31.399s +- 6 threads: 2m28.450s +- 8 threads: 2m19.779s Auxiliary features: From d8bd1308b42d59fd2f3e762f7b5c2b14994ab630 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 14 Jun 2024 14:10:32 +0700 Subject: [PATCH 07/79] Updated VS projects with argparse.h and unittest.h --- pjlib/build/pjlib.vcproj | 8 ++++++++ pjlib/build/pjlib.vcxproj | 2 ++ pjlib/build/pjlib.vcxproj.filters | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index 281b80f32f..bc4e2b2c75 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -12645,6 +12645,10 @@ RelativePath="..\include\pj\addr_resolv.h" > + + @@ -12793,6 +12797,10 @@ RelativePath="..\include\pj\unicode.h" > + + diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 4055991ce1..ad50bbf69e 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -1036,6 +1036,7 @@ + @@ -1101,6 +1102,7 @@ + diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index 0d2deda55a..edfa1069ac 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -235,6 +235,9 @@ Header Files + + Header Files + Header Files @@ -340,6 +343,9 @@ Header Files + + Header Files + Header Files\compat From c8f93dd06204fb9401479bf65990f181010bb252 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 19 Jun 2024 17:39:40 +0700 Subject: [PATCH 08/79] Fix warnings on Windows --- pjlib/src/pj/unittest.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 44335c4602..d69073040a 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -151,7 +151,7 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) /* Initialize suite and test cases */ runner->suite = suite; - runner->ntests = pj_list_size(&suite->tests); + runner->ntests = (unsigned)pj_list_size(&suite->tests); runner->nruns = 0; for (tc=suite->tests.next; tc!=&suite->tests; @@ -183,7 +183,7 @@ PJ_DEF(void) pj_test_get_stat( const pj_test_suite *suite, pj_test_stat *stat) pj_bzero(stat, sizeof(*stat)); stat->duration = pj_elapsed_time(&suite->start_time, &suite->end_time); - stat->ntests = pj_list_size(&suite->tests); + stat->ntests = (unsigned)pj_list_size(&suite->tests); for (tc=suite->tests.next; tc!=&suite->tests; tc=tc->next) { if (tc->result != PJ_EPENDING) { @@ -405,7 +405,7 @@ static int get_completion_line( const pj_test_case *tc, const char *end_line, tc->obj_name, res_buf, end_line); if (log_len < 1 || log_len >= sizeof(log_buf)) - log_len = pj_ansi_strlen(log_buf); + log_len = (int)pj_ansi_strlen(log_buf); return log_len; } @@ -473,7 +473,7 @@ static void basic_on_test_complete(pj_test_runner *runner, pj_test_case *tc) len = pj_ansi_snprintf( line, sizeof(line), "[%2d/%d] ", runner->nruns, runner->ntests); if (len < 1 || len >= sizeof(line)) - len = pj_ansi_strlen(line); + len = (int)pj_ansi_strlen(line); len += get_completion_line(tc, "", line+len, sizeof(line)-len); tc->runner->orig_log_writer(3, line, len); @@ -483,6 +483,7 @@ static void basic_on_test_complete(pj_test_runner *runner, pj_test_case *tc) static void basic_runner_destroy(pj_test_runner *runner) { /* Nothing to do for basic runner */ + PJ_UNUSED_ARG(runner); } /* Initialize a basic runner. */ From 56d221a49cca775e179766c30b229355a321718c Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 19 Jun 2024 18:20:56 +0700 Subject: [PATCH 09/79] Disable parallel unit-testing for ioqueue stress test on Windows because of errors when running on Windows virtual machine --- pjlib/src/pjlib-test/test.c | 45 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 4ca16fc176..b4b8511046 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -34,16 +34,6 @@ #endif -#define DO_TEST(test) do { \ - PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; struct test_app_t test_app = { @@ -286,6 +276,10 @@ static int features_tests(int argc, char *argv[]) ADD_TEST( sleep_test, PJ_TEST_PARALLEL); #endif +#if INCLUDE_FILE_TEST + ADD_TEST( file_test, PJ_TEST_PARALLEL); +#endif + #if INCLUDE_SOCK_TEST ADD_TEST( sock_test, PJ_TEST_PARALLEL); #endif @@ -306,12 +300,31 @@ static int features_tests(int argc, char *argv[]) ADD_TEST( tcp_ioqueue_test, PJ_TEST_PARALLEL); #endif -#if INCLUDE_IOQUEUE_UNREG_TEST - ADD_TEST( udp_ioqueue_unreg_test, PJ_TEST_PARALLEL); + /* Consistently encountered retcode 520 on Windows virtual machine + with 8 vcpu and 16GB RAM when multithread unit test is used, + with the following logs: + 17:50:57.761 .tcp (multithreads) + 17:50:58.254 ...pj_ioqueue_send() error: Object is busy (PJ_EBUSY) + 17:50:58.264 ..test failed (retcode=520) + 17:50:58.264 .tcp (multithreads, sequenced, concur=0) + 17:51:06.084 .tcp (multithreads, sequenced, concur=1) + 17:51:06.484 ...pj_ioqueue_send() error: Object is busy (PJ_EBUSY) + 17:51:06.486 ..test failed (retcode=520) + + I suspect it's because the ioq stress test also uses a lot of threads + and couldn't keep up with processing the data. + Therefore we'll disable parallelism on Windows for this test. [blp] + */ +#if INCLUDE_IOQUEUE_STRESS_TEST +# if defined(PJ_WIN32) && PJ_WIN32!=0 + ADD_TEST(ioqueue_stress_test, 0); +# else + ADD_TEST(ioqueue_stress_test, PJ_TEST_PARALLEL); +# endif #endif -#if INCLUDE_IOQUEUE_STRESS_TEST - ADD_TEST( ioqueue_stress_test, PJ_TEST_PARALLEL); +#if INCLUDE_IOQUEUE_UNREG_TEST + ADD_TEST(udp_ioqueue_unreg_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_IOQUEUE_PERF_TEST @@ -323,10 +336,6 @@ static int features_tests(int argc, char *argv[]) ADD_TEST( activesock_test, PJ_TEST_PARALLEL); #endif -#if INCLUDE_FILE_TEST - ADD_TEST( file_test, PJ_TEST_PARALLEL); -#endif - #if INCLUDE_SSLSOCK_TEST ADD_TEST( ssl_sock_test, PJ_TEST_PARALLEL); #endif From cbefbdf4b78477724a1ae02030ce599122cbc95f Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 20 Jun 2024 17:26:02 +0700 Subject: [PATCH 10/79] Non-parallel test case will now run exclusively (previously it did not wait the previous test to complete) --- pjlib/include/pj/unittest.h | 108 ++++++++++++++- pjlib/src/pj/unittest.c | 107 +++++++++------ pjlib/src/pjlib-test/unittest_test.c | 103 +++++++++++++++ unittest.md | 191 ++++++++++++++++++--------- 4 files changed, 404 insertions(+), 105 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 1310adac98..68f3471574 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -90,6 +90,39 @@ PJ_BEGIN_DECL } \ } +/** + * Generic check for (PJ) string comparison operation. If the check fails, + * informative error message will be displayed, and the code in err_action + * will be executed. + * + * @param str_op The string operation (e.g. pj_strcmp) + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==) + * @param res Expected return value of str_op(&s0, &s1) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STR_OP(str_op, ps0, ps1, res_op, res, err_reason, err_action) \ + { \ + int result__ = str_op(ps0, ps1); \ + if (!(result__ res_op res)) { \ + const char *fn_name = #str_op; \ + const char *tmp_reason_ = err_reason; \ + const char *sep0_ = (tmp_reason_ ? " (": ""); \ + const char *sep1_ = (tmp_reason_ ? ")": ""); \ + if (!tmp_reason_) tmp_reason_=""; \ + PJ_LOG(1,(THIS_FILE, "Test %s(\"%.*s\", \"%.*s\")%s%d" \ + " fails (%s result=%d) in %s:%d%s%s%s", \ + fn_name, (int)ps0->slen, ps0->ptr, \ + (int)ps1->slen, ps1->ptr, #res_op, res, \ + fn_name, result__, \ + THIS_FILE, __LINE__, \ + sep0_, tmp_reason_, sep1_)); \ + err_action; \ + } \ + } + /** * Check that an expression is PJ_SUCCESS. If the check fails, error message * explaining the error code will be displayed, and the code in err_action @@ -208,6 +241,65 @@ PJ_BEGIN_DECL PJ_TEST_BINARY_OP(expr0, >=, expr1, err_reason, err_action) +/** + * Check string comparison result. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==, <, >) + * @param exp_result Expected result (e.g. zero for equal string) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRCMP(ps0, ps1, res_op, exp_result, err_reason, err_action) \ + PJ_TEST_STR_OP(pj_strcmp, ps0, ps1, res_op, exp_result, \ + err_reason, err_action) + +/** + * Check case-insensitive string comparison result. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param res_op Operator to compare result (e.g. ==, <, >) + * @param exp_result Expected result (e.g. zero for equal) + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRICMP(ps0, ps1, res_op, exp_result, err_reason, err_action) \ + PJ_TEST_STR_OP(pj_stricmp, ps0, ps1, res_op, exp_result, \ + err_reason, err_action) + +/** + * Check that two strings are equal. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STREQ(ps0, ps1, err_reason, err_action) \ + PJ_TEST_STRCMP(ps0, ps1, ==, 0, err_reason, err_action) + +/** + * Check that two strings are not equal. + * If the check fails, informative error message will be displayed and + * the code in err_action will be executed. + * + * @param ps0 Pointer to first string + * @param ps1 Pointer to second string + * @param err_reason NULL or extra text to display when the check fails + * @param err_action Action to perform when the check fails + */ +#define PJ_TEST_STRNEQ(ps0, ps1, err_reason, err_action) \ + PJ_TEST_STRCMP(ps0, ps1, !=, 0, err_reason, err_action) + + /** * Bitwise constants that can be used in test case flags (see * pj_test_case_init()). @@ -216,9 +308,9 @@ typedef enum pj_test_case_flag { /** * Allow next test case to run in parallel. If this flag is not specified, - * next test case will have to wait until current test finishes before - * it can run. Note this only works for test runners that support - * worker threads. + * the test case will run exclusively without other test cases running. + * Note this only works for test runners that support worker threads. + * Basic runner will always run serially. */ PJ_TEST_PARALLEL = 1, @@ -233,7 +325,7 @@ typedef enum pj_test_case_flag * Write the original log at the time it is called instead of pooling * the logs to be printed after all tests finish. */ - PJ_TEST_ORIGINAL_LOG = 4, + PJ_TEST_LOG_NO_CACHE = 4, } pj_test_case_flag; @@ -596,6 +688,14 @@ PJ_DECL(void) pj_test_display_log_messages(const pj_test_suite *suite, PJ_DECL(void) pj_test_runner_destroy(pj_test_runner *runner); +/** + * Macro to control how long worker thread should sleep waiting for next + * ready test. + */ +#ifndef PJ_TEST_THREAD_WAIT_MSEC +# define PJ_TEST_THREAD_WAIT_MSEC 100 +#endif + PJ_END_DECL /** diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index d69073040a..06a66069b6 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -158,7 +158,7 @@ PJ_DEF(void) pj_test_run(pj_test_runner *runner, pj_test_suite *suite) tc=tc->next) { tc->result = PJ_EPENDING; - tc->runner = runner; + tc->runner = NULL; /* WIll be assigned runner when is run */ } /* Call the run method to perform runner specific loop */ @@ -327,7 +327,7 @@ static void unittest_log_callback(int level, const char *data, int len) /* If the test case wants to display the original log as they are called, * then write it using the original logging writer now. */ - if (tc->flags & PJ_TEST_ORIGINAL_LOG) { + if (tc->flags & PJ_TEST_LOG_NO_CACHE) { tc->runner->orig_log_writer(level, data, len); return; } @@ -419,6 +419,7 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) /* Set the test case being worked on by this thread */ set_current_test_case(tc); + tc->runner = runner; pj_get_timestamp(&tc->start_time); TC_TRACE(tc, "starting"); @@ -446,6 +447,28 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) set_current_test_case(NULL); } +static pj_test_case *get_first_running(pj_test_case *tests) +{ + pj_test_case *tc; + for (tc=tests->next; tc!=tests; tc=tc->next) { + if (tc->runner && tc->result==PJ_EPENDING) + return tc; + } + return NULL; +} + +static pj_test_case *get_next_to_run(pj_test_case *tests) +{ + pj_test_case *tc; + for (tc=tests->next; tc!=tests; tc=tc->next) { + if (tc->runner==NULL) { + assert(tc->result==PJ_EPENDING); + return tc; + } + } + return NULL; +} + /******************************* Basic Runner *******************************/ /* This is the "main()" function for basic runner. It just runs the tests @@ -505,7 +528,6 @@ PJ_DEF(void) pj_test_init_basic_runner(pj_test_runner *runner, typedef struct text_runner_t { pj_test_runner base; - pj_test_case *cur_case; pj_mutex_t *mutex; pj_thread_t **threads; } text_runner_t; @@ -529,60 +551,61 @@ typedef struct text_runner_t static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, pj_test_case **p_test_case) { + pj_test_suite *suite; + pj_test_case *cur, *next; pj_status_t status; *p_test_case = NULL; pj_mutex_lock(runner->mutex); + suite = runner->base.suite; + if (runner->base.stopping) { - status = PJ_ENOTFOUND; - } else if (runner->cur_case == NULL) { - /* Only on the very first invocation */ - if (pj_list_empty(&runner->base.suite->tests)) { + pj_mutex_unlock(runner->mutex); + return PJ_ENOTFOUND; + } + + cur = get_first_running(&suite->tests); + next = get_next_to_run(&suite->tests); + + if (cur == NULL) { + if (next==NULL) { status = PJ_ENOTFOUND; - goto on_return; + } else { + *p_test_case = next; + /* Mark as running so it won't get picked up by other threads + * when we exit this function + */ + next->runner = &runner->base; + status = PJ_SUCCESS; } - runner->cur_case = *p_test_case = runner->base.suite->tests.next; - status = PJ_SUCCESS; - } else if (runner->cur_case == &runner->base.suite->tests) { - /* All done */ - status = PJ_ENOTFOUND; } else { - if (runner->cur_case->result == PJ_EPENDING) { - /* Test is still running. */ - if (runner->cur_case->flags & PJ_TEST_PARALLEL) { - /* Allow other test to run */ - runner->cur_case = runner->cur_case->next; - if (runner->cur_case == &runner->base.suite->tests) { - status = PJ_ENOTFOUND; - } else { - *p_test_case = runner->cur_case; - status = PJ_SUCCESS; - } - } else { - if (runner->cur_case->next == &runner->base.suite->tests) { - /* The current case is the last one. The calling thread - * can quit now. - */ - status = PJ_ENOTFOUND; - } else { - /* Current test case does not allow parallel run */ - status = PJ_EPENDING; - } - } + /* Test is still running. */ + if ((cur->flags & PJ_TEST_PARALLEL) && next != NULL && + (next->flags & PJ_TEST_PARALLEL)) + { + /* Allowed other test to run because test also allows + * parallel test + */ + *p_test_case = next; + /* Mark as running so it won't get picked up by other threads + * when we exit this function + */ + next->runner = &runner->base; + status = PJ_SUCCESS; } else { - /* Current test is done, get next text */ - runner->cur_case = runner->cur_case->next; - if (runner->cur_case == &runner->base.suite->tests) { + if (next==NULL) { + /* The current case is the last one. The calling thread + * can quit now. + */ status = PJ_ENOTFOUND; } else { - *p_test_case = runner->cur_case; - status = PJ_SUCCESS; + /* Current test case or next test do not allow parallel run */ + status = PJ_EPENDING; } } } -on_return: pj_mutex_unlock(runner->mutex); return status; } @@ -624,7 +647,7 @@ static int text_runner_thread_proc(void *arg) RUNNER_TRACE(runner, tmp); #endif - pj_thread_sleep(1000); + pj_thread_sleep(PJ_TEST_THREAD_WAIT_MSEC); } else { break; } diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index fe869b4c74..659c7acf44 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -97,6 +97,7 @@ static int test_success(pj_status_t status, const char *reason) */ static int assertion_tests() { + pj_str_t s0, s1; int ret; /* Unary PJ_TEST_TRUE successful */ @@ -203,6 +204,27 @@ static int assertion_tests() if (!pj_ansi_strstr(log_buffer, THIS_FILE)) return -352; if (!pj_ansi_strstr(log_buffer, " (should be immediate)")) return -353; + /* String tests */ + PJ_TEST_STREQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), NULL, + return -400); + PJ_TEST_STRICMP(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), ==, 0, + NULL, return -405); + + ret = -1; + PJ_TEST_STREQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "135"), NULL, + ret=0); + if (ret) + PJ_TEST_EQ(ret, 0, "PJ_TEST_STREQ was expected to fail", return -410); + + PJ_TEST_STRNEQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "000000"), NULL, + return -420); + + ret = -1; + PJ_TEST_STRNEQ(pj_cstr(&s0, "123456"), pj_cstr(&s1, "123456"), NULL, + ret=0); + if (ret) + PJ_TEST_EQ(ret, 0, "PJ_TEST_STRNEQ was expected to fail", return -410); + return 0; } @@ -463,6 +485,87 @@ int unittest_basic_test(void) return ret; } +struct parallel_param_t +{ + unsigned flags; + int sleep; + const char *id; +}; + +static char parallel_msg[128]; +static int parallel_func(void *arg) +{ + struct parallel_param_t *prm = (struct parallel_param_t*)arg; + pj_thread_sleep(prm->sleep); + pj_ansi_strxcat(parallel_msg, prm->id, sizeof(parallel_msg)); + return 0; +} + +/* + * Test that PJ_TEST_PARALLEL flag (or lack of) works. + */ +int unittest_parallel_test() +{ + enum { + MAX_TESTS = 11, + LOG_SIZE = 128, + MS = PJ_TEST_THREAD_WAIT_MSEC, + }; + pj_pool_t *pool; + pj_test_suite suite; + char buffers[MAX_TESTS][LOG_SIZE]; + pj_test_case test_cases[MAX_TESTS]; + pj_test_runner_param prm; + struct parallel_param_t parallel_params[MAX_TESTS] = { + {0, MS+2*MS, "a"}, + {0, MS+1*MS, "b"}, /* b have to wait for a */ + {PJ_TEST_PARALLEL, MS+7*MS, "c"}, /* c have to wait for b */ + {PJ_TEST_PARALLEL, MS+1*MS, "d"}, /* d have to wait for b */ + {PJ_TEST_PARALLEL, MS+4*MS, "e"}, /* e have to wait for b */ + {0, MS+2*MS, "f"}, /* f have to wait for c, d, e */ + {0, MS+0*MS, "g"}, /* g have to wait for f */ + {0, MS+5*MS, "h"}, /* h have to wait for g */ + {PJ_TEST_PARALLEL, MS+4*MS, "i"}, /* i will finish last */ + {PJ_TEST_PARALLEL, MS+2*MS, "j"}, /* i will finish second last */ + {PJ_TEST_PARALLEL, MS+0*MS, "k"}, /* i will finish third last */ + }; + const char *correct_msg = "abdecfghkji"; + pj_str_t stmp0, stmp1; + pj_test_runner *runner; + int i; + + pj_test_suite_init(&suite); + PJ_TEST_NOT_NULL((pool=pj_pool_create( mem, NULL, 4000, 4000, NULL)), + NULL, return -1); + + for (i=0; i` and `pj/unittest.c`. +1. A (new) unit testing framework, in `` and `pj/unittest.c`. 2. largish modifications to `pjlib-test`, `pjlib-util-test`, `pjnath-test`, `pjmedia-test`, and `pjsip-test`. 3. minor developments: @@ -19,7 +19,7 @@ Let's discuss the work in more detail. The unit testing framework `` provides two parts. -First part is test macros that can be used to replace manual `if`s in the test codes. This can be used anywhere without having to use the unit test framework (apart from including the header file of course). Example: +First part is test macros that can be used to replace manual `if`s in the test codes. This can be used anywhere (in the test code though, not in library code, due to size concern of the expansion macro) without having to use the unit test framework (apart from including the header file of course). Example: ``` PJ_TEST_NON_ZERO(pool, "pool allocation failed", {rc=-1; goto on_error;}); @@ -27,7 +27,30 @@ First part is test macros that can be used to replace manual `if`s in the test c PJ_TEST_SUCCESS(pj_ioqueue_read(...), NULL, {rc=-80; goto on_return; }); ``` -On failure, the macros above will display the expressions being checked, the value of the expressions, and the status message (for PJ_TEST_SUCCESS), saving the developer from having to write all these things. +On failure, the macros above will display the expressions being checked, the value of the expressions, and the status message (for PJ_TEST_SUCCESS), saving the developer from having to write all these things. For example: + +``` +15:57:24.365 Test pj_strcmp("abdefcghkji", "abdecfghkji")==0 fails (pj_strcmp result=3) in \ + unittest_test.c:563 (wrong test scheduling) +``` + +A list of the test macros currently implemented are as follows: + +- `PJ_TEST_SUCCESS(expr, err_reason, err_action)` +- `PJ_TEST_NON_ZERO(expr, err_reason, err_action)` +- `PJ_TEST_TRUE(expr, err_reason, err_action)` +- `PJ_TEST_NOT_NULL(expr, err_reason, err_action)` +- `PJ_TEST_EQ(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_NEQ(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_LT(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_LTE(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_GT(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_GTE(expr0, expr1, err_reason, err_action)` +- `PJ_TEST_STRCMP(ps0, ps1, res_op, exp_result, err_reason, err_action)` +- `PJ_TEST_STRICMP(ps0, ps1, res_op, exp_result, err_reason, err_action)` +- `PJ_TEST_STREQ(ps0, ps1, err_reason, err_action)` +- `PJ_TEST_STRNEQ(ps0, ps1, err_reason, err_action)` + The second part is the unit test framework. The framework uses common architecture as described in https://en.wikipedia.org/wiki/XUnit. The global workflow to create/run unit test is something like this: @@ -37,31 +60,116 @@ The second part is the unit test framework. The framework uses common architectu pj_test_runner *runner; pj_test_stat stat; + /* Init test suite */ pj_test_suite_init(&suite); + /* Init test cases */ pj_test_case_init(&tc0, ..., &func_to_test0, arg0, ... ); pj_test_suite_add_case(&suite, &tc0); pj_test_case_init(&tc1, ..., &func_to_test1, arg1, ... ); pj_test_suite_add_case(&suite, &tc1); + /* Create runner and run the test suite */ pj_test_create_text_runner(pool, .., &runner); pj_test_run(runner, &suite); pj_test_runner_destroy(runner); + /* Get/show stats, display logs for failed tests */ pj_test_get_stat(&suite, &stat); pj_test_display_stat(&stat, ..); pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS); ``` +#### Features + +Below are the features of the unit-test and also other enhancements done in this PR: + +- Supports parallel execution on per test case basis, resulting in up to four times speed up in PJLIB-TEST. +- Familiarity with common architecture as described in https://en.wikipedia.org/wiki/XUnit +- Easy to port existing test functions. In fact, no modification is needed to the test functions +- PJ_TEST_XXX() macros can be used in place of manual testing +- All (C based) PJ unit testing apps have nicer output: + +``` +07:34:32.878 Performing 20 features tests with 4 worker threads +[ 1/20] hash_test [OK] [0.000s] +[ 2/20] atomic_test [OK] [0.000s] +[ 3/20] rand_test [OK] [0.002s] +... +``` + +- Even nicer output, logging emited during test is captured and by default only displayed if the test fails (or can be displayed all the time, configurable by cmdline option) +- All (C based) PJ unit testing apps can select test(s) to invoke from cmdline ### 2. Modifications to test apps -#### Speedup +#### Console front-end + +The main front-end (`main.c`) was modified to be more nice as command line apps, now it can be invoked with arguments: + +``` +Usage: + pjlib-test [OPTION] [test_to_run] [..] + +where OPTIONS: + + -h, --help Show this help screen + -l 0,1,2 0: Don't show logging after tests + 1: Show logs of failed tests (default) + 2: Show logs of all tests + -w N Set N worker threads (0: disable. Default: 1) + -L, --list List the tests and exit + --stop-err Stop testing on error + --skip-e Skip essential tests + --ci-mode Running in slow CI mode + -i Ask ENTER before quitting + -n Do not trap signals + -p PORT Use port PORT for echo port + -s SERVER Use SERVER as ech oserver + -t ucp,tcp Set echo socket type to UDP or TCP +``` + +Other test apps have been modified to produce similar look. + + +#### Test body -In PJLIB-TEST: +The main test body (`test.c`) was modified to use the unit-test framework, resulting in nicer output: -- 0 worker thread: 9m22.228s (original) +``` +07:34:32.878 Performing 20 features tests with 4 worker threads +[ 1/20] hash_test [OK] [0.000s] +[ 2/20] atomic_test [OK] [0.000s] +[ 3/20] rand_test [OK] [0.002s] +[ 4/20] pool_perf_test [OK] [0.003s] +[ 5/20] sock_test [OK] [0.003s] +[ 6/20] rbtree_test [Err: -40] [0.007s] +[ 7/20] select_test [OK] [0.010s] +... +... +... +[19/20] ioqueue_perf_test1 [OK] [110.046s] +[20/20] ioqueue_stress_test [OK] [132.287s] +07:37:02.192 Unit test statistics for features tests: +07:37:02.192 Total number of tests: 20 +07:37:02.192 Number of test run: 20 +07:37:02.192 Number of failed test: 1 +07:37:02.192 Total duration: 2m29.313s +07:37:02.192 ------------ Displaying failed test logs: ------------ +07:37:02.192 Logs for rbtree_test [rc:-40]: +07:34:32.885 Error: ..... +07:37:02.192 -------------------------------------------------------- +07:37:02.192 +07:37:02.192 Stack max usage: 0, deepest: :0 +07:37:02.192 **Test completed with error(s)** +``` + +#### Speed result: PJLIB-TEST + +In PJLIB-TEST, test time is speed up by up to four times: + +- 0 worker thread: 9m22.228s (resembles the original file) - 1 worker thread: 4m51.940s - 2 worker threads: 4m9.322s - 3 worker threads: 2m52.350s @@ -69,7 +177,7 @@ In PJLIB-TEST: - 6 worker threads: 2m28.450s - 8 worker threads: 2m19.779s -So it looks like the sweet spot is with 3 worker threads. This is because some tests takes quite a long time t o finish: +It looks like the sweet spot is with 3 worker threads. This is because some tests takes quite a long time t o finish, so using many threads won't help these tests: ``` [14/20] udp_ioqueue_unreg_test [OK] [92.759s] @@ -80,68 +188,33 @@ So it looks like the sweet spot is with 3 worker threads. This is because some t I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner). +#### Speed result: PJLIB-UTIL-TEST -### 3. Other (Minor) developments - - - -The unit test framework has the following features. - -#### Parallel execution +In PJLIB-UTIL-TEST, there is about 2x speed up from 5m52.500s to 3m3.615s. We couldn't achieve more speed up due to long running tests such as resolver_test() and http_client_test() which couldn't be broken up due to the use of global states. -The main objective of this project is to speed up testing. Parallelism should be configurable on test by test case basis. The test designer will be able to control which tests can run in parallel and which can't. - -#### Familiarity - -Use common architecture as described in https://en.wikipedia.org/wiki/XUnit - -#### Easy to use - -Enable porting of existing tests with minimal effort, e.g. just need changing the `test.c` and not each of the test file. - -#### Nice output - -Nice console output, e.g.: - -``` -[1/24] errno_test.. [OK] -[2/24] exception_test.. [OK] -[3/24] os_test. [OK] -``` -Must capture logging of each test so output is not cluttered when the test is run in parallel. Most likely will show the log only after all tests are done (to maintain the niceness of console output in the above point), and will only show logs for failed tests. -#### Selective test +### 3. Other developments -Run specific tests from cmdline, e.g. `./pjlib-test fifobuf_test os_test` +#### `` -#### Work in restricted targets +This is header only feature to parse command line options. We have `pj_getopt()`, but unfortunately this is in `pjlib-util` thus can't be used by PJLIB. -Able to run without pool etc, because crucial/basic tests such as list, pool, thread tests can only be run after the test suite creation is finished! +#### Working `fifobuf` -#### Useful test macros +The `fifobuf` feature has been there for the longest time (it was part of pjlib first ever commit) and finally has got some use. -Make it a complete unit-testing framework by implementing various assertion macros. While changing existing tests to use this new assertion macros is tedious and without significant benefit, providing the macros may be nice for writing future tests. -Also added argparse.h +### 4. Known issues and considerations -Some considerations: +### Cluttered logging -1. When a unit-test is run (in any thread), it will change (globally) the log writer function to an internal unittest log writer function (in order to capture the log). This has some implications: - a. If apps change the log writer before running the unittest, this should be okay. The unittest will restore the log writer to any writer prior to it being run, and will use that writer to display the logs after it is run. - b. If apps change the log writer somewhere inside a test function, I think this should be okay as long as it restores back the writer. - c. if another thread (that is not aware about testing) is writing to the log, then the unittest log writer will pass that log message to pj_log_write() (i.e. the "official" log writer). I think this is okay and it is the desired behavior, but it will clutter the test output. -2. Use {} instead of do {} while (0) in case user pus "break" as action. +When a unit-test is run (in any thread), it will change (globally) the log writer function to an internal unittest log writer function (in order to capture the log). This has some implications: -Test times: +1. If apps change the log writer before running the unittest, this should be okay. The unittest will restore the log writer to any writer prior to it being run, and will use that writer to display the logs after it is run. +2. If apps change the log writer somewhere inside a test function, I think this should be okay as long as it restores back the writer. +3. if another thread (that is not aware about testing) is writing to the log, then the unittest log writer will pass that log message to pj_log_write() (i.e. the "official" log writer). I think this is okay and it is the desired behavior, but it will clutter the test output. -- 0 thread: 9m22.228s -- 1 thread: 4m51.940s -- 2 threads: 4m9.322s -- 3 threads: 2m52.350s -- 4 threads: 2m31.399s -- 6 threads: 2m28.450s -- 8 threads: 2m19.779s +### Other -Auxiliary features: +1. Just rationale: in `PJ_TEST_XXX()` macros, we use `{ }` instead of `do {} while (0)` block to support user putting "`break`" as action. -- pj/argparse.h From 19f9f1702b79bf41879e4dd3dd6f7c30b345399c Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 20 Jun 2024 17:27:16 +0700 Subject: [PATCH 11/79] Add pjlib-test/test_util.h for common utilities for parsing, configuring, and running unit test. This can be used by all test apps. pjlib-util-test has been ported to use this utilities --- pjlib-util/src/pjlib-util-test/main.c | 61 +++-- .../src/pjlib-util-test/resolver_test.c | 8 + pjlib-util/src/pjlib-util-test/test.c | 71 +++--- pjlib-util/src/pjlib-util-test/test.h | 12 +- pjlib/src/pjlib-test/main.c | 67 ++--- pjlib/src/pjlib-test/main_win32.c | 3 +- pjlib/src/pjlib-test/rbtree.c | 2 +- pjlib/src/pjlib-test/test.c | 153 ++++-------- pjlib/src/pjlib-test/test.h | 10 +- pjlib/src/pjlib-test/test_util.h | 235 ++++++++++++++++++ 10 files changed, 403 insertions(+), 219 deletions(-) create mode 100644 pjlib/src/pjlib-test/test_util.h diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c index a5413f162c..7a25d859e4 100644 --- a/pjlib-util/src/pjlib-util-test/main.c +++ b/pjlib-util/src/pjlib-util-test/main.c @@ -18,6 +18,8 @@ */ #include "test.h" #include +#include + #if defined(PJ_SUNOS) && PJ_SUNOS!=0 @@ -34,19 +36,19 @@ static void init_signals() #elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -64,6 +66,22 @@ static void init_signals(void) #define boost() +static void usage() +{ + puts("Usage:"); + puts(" pjlib-util-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + + int main(int argc, char *argv[]) { int rc; @@ -72,22 +90,25 @@ int main(int argc, char *argv[]) boost(); - while (argc > 1) { - char *arg = argv[--argc]; - - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; - - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } + if (pj_argparse_get("-h", &argc, argv) || + pj_argparse_get("--help", &argc, argv)) + { + usage(); + return 0; } + ut_app_init0(&test_app.ut_app); + + interractive = pj_argparse_get("-i", &argc, argv); + no_trap = pj_argparse_get("-n", &argc, argv); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; + if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 40a6ad3260..a0d133dd0f 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -1897,34 +1897,42 @@ int resolver_test(void) { int rc; + PJ_LOG(3,(THIS_FILE, "init")); rc = init(PJ_FALSE); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "a_parser_test")); rc = a_parser_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "addr_parser_test")); rc = addr_parser_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "simple_test")); rc = simple_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "dns_test")); rc = dns_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_test")); rc = srv_resolver_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_fallback_test")); rc = srv_resolver_fallback_test(); if (rc != 0) goto on_error; + PJ_LOG(3,(THIS_FILE, "srv_resolver_many_test")); rc = srv_resolver_many_test(); if (rc != 0) goto on_error; diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index f83e4f6946..e1bde9a9be 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -20,6 +20,14 @@ #include #include +#define THIS_FILE "test.c" + +pj_pool_factory *mem; +struct test_app_t test_app = { + .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, +}; + void app_perror(const char *msg, pj_status_t rc) { char errbuf[256]; @@ -30,80 +38,67 @@ void app_perror(const char *msg, pj_status_t rc) PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); } -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (char*)(rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - -pj_pool_factory *mem; - -int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT; - -static int test_inner(void) +static int test_inner(int argc, char *argv[]) { pj_caching_pool caching_pool; - int rc = 0; mem = &caching_pool.factory; pj_log_set_level(3); - pj_log_set_decor(param_log_decor); + pj_log_set_decor(test_app.param_log_decor); - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - return rc; - } - - rc = pjlib_util_init(); - pj_assert(rc == 0); + PJ_TEST_SUCCESS(pj_init(), NULL, { return 1; }) + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, { return 2; }); + pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); + + if (ut_app_init1(&test_app.ut_app, mem) != PJ_SUCCESS) + return 1; pj_dump_config(); - pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); #if INCLUDE_XML_TEST - DO_TEST(xml_test()); + UT_ADD_TEST(&test_app.ut_app, xml_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_JSON_TEST - DO_TEST(json_test()); + UT_ADD_TEST(&test_app.ut_app, json_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_ENCRYPTION_TEST - DO_TEST(encryption_test()); + UT_ADD_TEST(&test_app.ut_app, encryption_test, PJ_TEST_PARALLEL); # if WITH_BENCHMARK - DO_TEST(encryption_benchmark()); + UT_ADD_TEST(&test_app.ut_app, encryption_benchmark, PJ_TEST_PARALLEL); # endif #endif #if INCLUDE_STUN_TEST - DO_TEST(stun_test()); + UT_ADD_TEST(&test_app.ut_app, stun_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_RESOLVER_TEST - DO_TEST(resolver_test()); + UT_ADD_TEST(&test_app.ut_app, resolver_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_HTTP_CLIENT_TEST - DO_TEST(http_client_test()); + UT_ADD_TEST(&test_app.ut_app, http_client_test, PJ_TEST_PARALLEL); #endif -on_return: - return rc; + + if (ut_run_tests(&test_app.ut_app, "pjlib-util tests", argc, argv)) { + ut_app_destroy(&test_app.ut_app); + return 1; + } + + ut_app_destroy(&test_app.ut_app); + return 0; } -int test_main(void) +int test_main(int argc, char *argv[]) { PJ_USE_EXCEPTION; PJ_TRY { - return test_inner(); + return test_inner(argc, argv); } PJ_CATCH_ANY { int id = PJ_GET_EXCEPTION(); diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h index 9469e1f74f..be7b704b13 100644 --- a/pjlib-util/src/pjlib-util-test/test.h +++ b/pjlib-util/src/pjlib-util-test/test.h @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #if defined(PJ_EXCLUDE_BENCHMARK_TESTS) && (PJ_EXCLUDE_BENCHMARK_TESTS==1) # define WITH_BENCHMARK 0 @@ -36,10 +37,19 @@ extern int json_test(void); extern int encryption_test(); extern int encryption_benchmark(); extern int stun_test(); -extern int test_main(void); +extern int test_main(int argc, char *argv[]); extern int resolver_test(void); extern int http_client_test(); extern void app_perror(const char *title, pj_status_t rc); extern pj_pool_factory *mem; +#define UT_MAX_TESTS 16 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; + int param_log_decor; +}; +extern struct test_app_t test_app; diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index e0ad534af0..6d516edd2f 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -84,32 +84,30 @@ static void usage() puts(""); puts("where OPTIONS:"); puts(""); - puts(" -h, --help Show this help screen"); - puts(" --ci-mode Running in slow CI mode"); - puts(" -l 0,1,2 0: Don't show logging after tests"); - puts(" 1: Show logs of failed tests (default)"); - puts(" 2: Show logs of all tests"); - puts(" -w N Set N worker threads (0: disable worker threads)"); - puts(" -L, --list List the tests and exit"); - puts(" --stop-err Stop testing on error"); - puts(" --skip-e Skip essential tests"); - puts(" -i Ask ENTER before quitting"); - puts(" -n Do not trap signals"); - puts(" -p PORT Use port PORT for echo port"); - puts(" -s SERVER Use SERVER as ech oserver"); - puts(" -t ucp,tcp Set echo socket type to UDP or TCP"); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" --skip-e Skip essential tests"); + puts(" --ci-mode Running in slow CI mode"); + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); + puts(" -p PORT Use port PORT for echo port"); + puts(" -s SERVER Use SERVER as ech oserver"); + puts(" -t ucp,tcp Set echo socket type to UDP or TCP"); } int main(int argc, char *argv[]) { - int i, rc; + int rc; int interractive = 0; int no_trap = 0; pj_status_t status; char *s; boost(); + ut_app_init0(&test_app.ut_app); /* * Parse arguments @@ -153,46 +151,13 @@ int main(int argc, char *argv[]) return 1; } - test_app.param_list_test = pj_argparse_get("-L", &argc, argv) || - pj_argparse_get("--list", &argc, argv); - test_app.param_stop_on_error = pj_argparse_get("--stop-err", &argc, argv); - test_app.param_skip_essentials = pj_argparse_get("--skip-e", &argc, argv); - test_app.param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); - - status = pj_argparse_get_int("-l", &argc, argv, &i); - if (status==PJ_SUCCESS) { - if (i==0) - test_app.param_unittest_logging_policy = PJ_TEST_NO_TEST; - else if (i==1) - test_app.param_unittest_logging_policy = PJ_TEST_FAILED_TESTS; - else if (i==2) - test_app.param_unittest_logging_policy = PJ_TEST_ALL_TESTS; - else { - printf("Error: invalid value %d for -l option\n", i); - usage(); - return 1; - } - } else if (status!=PJ_ENOTFOUND) { - puts("Error: invalid/missing value for -l option"); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) { usage(); return 1; } + test_app.param_skip_essentials = pj_argparse_get("--skip-e", &argc, argv); + test_app.param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); - status = pj_argparse_get_int("-w", &argc, argv, - (int*)&test_app.param_unittest_nthreads); - if (status==PJ_SUCCESS) { - if (test_app.param_unittest_nthreads > 100 || - test_app.param_unittest_nthreads < 0) - { - printf("Error: value %d is not valid for -w option\n", - test_app.param_unittest_nthreads); - usage(); - return 1; - } - } else if (status!=PJ_ENOTFOUND) { - puts("Error: invalid/missing value for -w option"); - usage(); - } if (!no_trap) { init_signals(); diff --git a/pjlib/src/pjlib-test/main_win32.c b/pjlib/src/pjlib-test/main_win32.c index 1d8b6e9b7d..b5487c5b5d 100644 --- a/pjlib/src/pjlib-test/main_win32.c +++ b/pjlib/src/pjlib-test/main_win32.c @@ -61,6 +61,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; + char *argv[] = {"pjlib-test", NULL}; PJ_UNUSED_ARG(lpCmdLine); PJ_UNUSED_ARG(hPrevInstance); @@ -73,7 +74,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, test_app.param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; // Run the test! - test_main(); + test_main(1, argv); PJ_LOG(3,(THIS_FILE,"")); PJ_LOG(3,(THIS_FILE,"Press ESC to quit")); diff --git a/pjlib/src/pjlib-test/rbtree.c b/pjlib/src/pjlib-test/rbtree.c index db8179478c..846b4847c7 100644 --- a/pjlib/src/pjlib-test/rbtree.c +++ b/pjlib/src/pjlib-test/rbtree.c @@ -102,7 +102,7 @@ static int test(void) if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) { PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", (char*)prev->user_data, (char*)it->user_data)); - rc=-40; + rc=-45; goto on_error; } } diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index b4b8511046..14360c46f4 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -37,14 +37,12 @@ pj_pool_factory *mem; struct test_app_t test_app = { - 0, - ECHO_SERVER_ADDRESS, - ECHO_SERVER_START_PORT, - PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, - PJ_FALSE, - PJ_TEST_FAILED_TESTS, - -1, + .param_echo_sock_type = 0, + .param_echo_server = ECHO_SERVER_ADDRESS, + .param_echo_port = ECHO_SERVER_START_PORT, + .param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | + PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT, + .param_ci_mode = PJ_FALSE, }; int null_func() @@ -104,6 +102,17 @@ static int essential_tests(int argc, char *argv[]) pj_test_case test_cases[MAX_TESTS]; int ntests = 0; + /* Test the unit-testing framework first, outside unit-test! + * Only perform the test if user is not requesting specific test. + */ + if (argc==1) { + pj_dump_config(); + + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); + if (unittest_basic_test()) + return 1; + } + /* Now that the basic unit-testing framework has been tested, * perform essential tests using basic unit-testing framework. */ @@ -162,26 +171,15 @@ static int essential_tests(int argc, char *argv[]) #undef ADD_TEST - if (test_app.param_list_test) { + if (test_app.ut_app.prm_list_test) { list_tests(&suite, "essential tests"); return 0; } - /* Test the unit-testing framework first, outside unit-test! - * Only perform the test if user is not requesting specific test. - */ - if (argc==1) { - pj_dump_config(); - - PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); - if (unittest_basic_test()) - return 1; - } - if (ntests > 0) { pj_test_runner_param runner_prm; pj_test_runner_param_default(&runner_prm); - runner_prm.stop_on_error = test_app.param_stop_on_error; + runner_prm.stop_on_error = test_app.ut_app.prm_stop_on_error; PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); pj_test_init_basic_runner(&runner, &runner_prm); @@ -189,7 +187,7 @@ static int essential_tests(int argc, char *argv[]) pj_test_get_stat(&suite, &stat); pj_test_display_stat(&stat, "essential tests", THIS_FILE); pj_test_display_log_messages(&suite, - test_app.param_unittest_logging_policy); + test_app.ut_app.prm_logging_policy); if (stat.nfailed) return 1; @@ -199,6 +197,10 @@ static int essential_tests(int argc, char *argv[]) * multithreaded unit-testing framework. */ if (argc==1) { + PJ_LOG(3,(THIS_FILE, "Testing the unit-test test scheduling")); + if (unittest_parallel_test()) + return 1; + PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); if (unittest_test()) return 1; @@ -209,95 +211,63 @@ static int essential_tests(int argc, char *argv[]) static int features_tests(int argc, char *argv[]) { - pj_test_suite suite; - pj_test_runner *runner; - pj_test_runner_param prm; - pj_test_stat stat; - pj_pool_t *pool; - enum { - MAX_TESTS = 24, - LOG_BUF_SIZE = 1000, - }; - pj_test_case test_cases[MAX_TESTS]; - int ntests = 0; - pj_status_t status; - - pool = pj_pool_create(mem, "test.c", 4000, 4000, NULL); - if (!pool) { - PJ_LOG(1,(THIS_FILE, "Pool creation error")); + if (ut_app_init1(&test_app.ut_app, mem) != PJ_SUCCESS) return 1; - } - pj_test_suite_init(&suite); - -#define ADD_TEST(test_func, flags) \ - if (ntests < MAX_TESTS) { \ - const char *test_name = #test_func; \ - if (test_included(test_name, argc, argv)) { \ - char *log_buf = (char*)pj_pool_alloc(pool, LOG_BUF_SIZE); \ - pj_test_case *tc = init_test_case( &test_func, test_name, flags, \ - &test_cases[ntests], \ - log_buf, LOG_BUF_SIZE); \ - pj_test_suite_add_case( &suite, tc); \ - ++ntests; \ - } \ - } else { \ - PJ_LOG(1,(THIS_FILE, "Too many tests for adding %s", #test_func)); \ - } #if INCLUDE_RAND_TEST - ADD_TEST( rand_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, rand_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_POOL_PERF_TEST - ADD_TEST( pool_perf_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, pool_perf_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_RBTREE_TEST - ADD_TEST( rbtree_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, rbtree_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_HASH_TEST - ADD_TEST( hash_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, hash_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_TIMESTAMP_TEST - ADD_TEST( timestamp_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, timestamp_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_ATOMIC_TEST - ADD_TEST( atomic_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, atomic_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_TIMER_TEST - ADD_TEST( timer_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, timer_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_SLEEP_TEST - ADD_TEST( sleep_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sleep_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_FILE_TEST - ADD_TEST( file_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, file_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_SOCK_TEST - ADD_TEST( sock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sock_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_SOCK_PERF_TEST - ADD_TEST( sock_perf_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sock_perf_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_SELECT_TEST - ADD_TEST( select_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, select_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_UDP_IOQUEUE_TEST - ADD_TEST( udp_ioqueue_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_test, PJ_TEST_PARALLEL); #endif #if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST - ADD_TEST( tcp_ioqueue_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, tcp_ioqueue_test, PJ_TEST_PARALLEL); #endif /* Consistently encountered retcode 520 on Windows virtual machine @@ -317,62 +287,39 @@ static int features_tests(int argc, char *argv[]) */ #if INCLUDE_IOQUEUE_STRESS_TEST # if defined(PJ_WIN32) && PJ_WIN32!=0 - ADD_TEST(ioqueue_stress_test, 0); + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, 0); # else - ADD_TEST(ioqueue_stress_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, PJ_TEST_PARALLEL); # endif #endif #if INCLUDE_IOQUEUE_UNREG_TEST - ADD_TEST(udp_ioqueue_unreg_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_unreg_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_IOQUEUE_PERF_TEST - ADD_TEST( ioqueue_perf_test0, PJ_TEST_PARALLEL); - ADD_TEST( ioqueue_perf_test1, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test0, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test1, PJ_TEST_PARALLEL); #endif #if INCLUDE_ACTIVESOCK_TEST - ADD_TEST( activesock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, activesock_test, PJ_TEST_PARALLEL); #endif #if INCLUDE_SSLSOCK_TEST - ADD_TEST( ssl_sock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ssl_sock_test, PJ_TEST_PARALLEL); #endif #undef ADD_TEST - if (ntests==0) - return 0; - - if (test_app.param_list_test) { - list_tests(&suite, "features tests"); - return 0; - } - - pj_test_runner_param_default(&prm); - prm.stop_on_error = test_app.param_stop_on_error; - if (test_app.param_unittest_nthreads >= 0) - prm.nthreads = test_app.param_unittest_nthreads; - status = pj_test_create_text_runner(pool, &prm, &runner); - if (status != PJ_SUCCESS) { - app_perror("Error creating text runner", status); + if (ut_run_tests(&test_app.ut_app, "features tests", argc, argv)) { + ut_app_destroy(&test_app.ut_app); return 1; } - PJ_LOG(3,(THIS_FILE, - "Performing %d features tests with %d worker thread%s", - ntests, prm.nthreads, prm.nthreads>1?"s":"")); - pj_test_run(runner, &suite); - pj_test_runner_destroy(runner); - pj_test_get_stat(&suite, &stat); - pj_test_display_stat(&stat, "features tests", THIS_FILE); - pj_test_display_log_messages(&suite, - test_app.param_unittest_logging_policy); - pj_pool_release(pool); - - return stat.nfailed ? 1 : 0; + ut_app_destroy(&test_app.ut_app); + return 0; } int test_inner(int argc, char *argv[]) diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 8e499a0d3f..5da32b312d 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -114,6 +114,7 @@ extern int activesock_test(void); extern int file_test(void); extern int ssl_sock_test(void); extern int unittest_basic_test(void); +extern int unittest_parallel_test(void); extern int unittest_test(void); extern int echo_server(void); @@ -123,20 +124,21 @@ extern int echo_srv_sync(void); extern int udp_echo_srv_ioqueue(void); extern int echo_srv_common_loop(pj_atomic_t *bytes_counter); +#define UT_MAX_TESTS 24 +#include "test_util.h" + /* Global vars */ extern pj_pool_factory *mem; struct test_app_t { + ut_app_t ut_app; + int param_echo_sock_type; const char *param_echo_server; int param_echo_port; int param_log_decor; pj_bool_t param_ci_mode; - pj_test_select_tests param_unittest_logging_policy; - int param_unittest_nthreads; - int param_list_test; pj_bool_t param_skip_essentials; - pj_bool_t param_stop_on_error; }; extern struct test_app_t test_app; diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h new file mode 100644 index 0000000000..19c1b71917 --- /dev/null +++ b/pjlib/src/pjlib-test/test_util.h @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include + +/* Overrideable max tests */ +#ifndef UT_MAX_TESTS +# define UT_MAX_TESTS 16 +#endif + +/* Overrideable log max buffer size */ +#ifndef UT_LOG_BUF_SIZE +# define UT_LOG_BUF_SIZE 1000 +#endif + +/* Usually app won't supply THIS_FILE when including test_util.h, so + * create a temporary one + */ +#ifndef THIS_FILE +# define THIS_FILE "test.c" +# define UNDEF_THIS_FILE 1 +#endif + +/* Unit testing app */ +typedef struct ut_app_t +{ + pj_test_select_tests prm_logging_policy; + int prm_nthreads; + int prm_list_test; + pj_bool_t prm_stop_on_error; + unsigned flags; + + pj_pool_t *pool; + pj_test_suite suite; + pj_test_runner *runner; + + int ntests; + pj_test_case test_cases[UT_MAX_TESTS]; +} ut_app_t; + +/* Call this in main.c before parsing arguments */ +static void ut_app_init0(ut_app_t *ut_app) +{ + pj_bzero(ut_app, sizeof(*ut_app)); + ut_app->prm_logging_policy = PJ_TEST_FAILED_TESTS; + ut_app->prm_nthreads = -1; + ut_app->flags = PJ_TEST_FUNC_NO_ARG; +} + +/* Call this in test.c before adding test cases */ +static pj_status_t ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) +{ + ut_app->pool = pj_pool_create(mem, THIS_FILE, 4000, 4000, NULL); + PJ_TEST_NOT_NULL(ut_app->pool, NULL, return PJ_ENOMEM); + pj_test_suite_init(&ut_app->suite); + return PJ_SUCCESS; +} + +/* Don't forget to call this */ +static void ut_app_destroy(ut_app_t *ut_app) +{ + pj_pool_release(ut_app->pool); + ut_app->pool = NULL; +} + +#define UT_ADD_TEST(ut_app, test_func, flags) \ + ut_add_test(ut_app, test_func, #test_func, flags, argc, argv) + +/* Check if a test is specified/requested in cmdline */ +static pj_bool_t ut_test_included(const char *name, int argc, char *argv[]) +{ + if (argc <= 1) + return PJ_TRUE; + + ++argv; + while (*argv) { + if (pj_ansi_strcmp(name, *argv)==0) + return PJ_TRUE; + ++argv; + } + return PJ_FALSE; +} + +/* Add test case */ +static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void), + const char *test_name, unsigned flags, + int argc, char *argv[]) +{ + char *log_buf; + pj_test_case *tc; + + if (ut_app->ntests >= UT_MAX_TESTS) { + PJ_LOG(1,(THIS_FILE, "Too many tests for adding %s", test_name)); + return PJ_ETOOMANY; + } + + if (!ut_test_included(test_name, argc, argv)) { + return PJ_ENOTFOUND; + } + + log_buf = (char*)pj_pool_alloc(ut_app->pool, UT_LOG_BUF_SIZE); + tc = &ut_app->test_cases[ut_app->ntests]; + flags |= ut_app->flags; + pj_test_case_init(tc, test_name, flags, (int (*)(void*))test_func, NULL, + log_buf, UT_LOG_BUF_SIZE, NULL); + + pj_test_suite_add_case( &ut_app->suite, tc); + ++ut_app->ntests; + + return PJ_SUCCESS; +} + +static void ut_list_tests(ut_app_t *ut_app, const char *title) +{ + unsigned d = pj_log_get_decor(); + const pj_test_case *tc; + + pj_log_set_decor(d ^ PJ_LOG_HAS_NEWLINE); + PJ_LOG(3,(THIS_FILE, "%ld %s:", pj_list_size(&ut_app->suite.tests), + title)); + pj_log_set_decor(0); + for (tc=ut_app->suite.tests.next; tc!=&ut_app->suite.tests; tc=tc->next) { + PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); + } + PJ_LOG(3,(THIS_FILE, "\n")); + pj_log_set_decor(d); +} + +static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, + int argc, char *argv[]) +{ + pj_test_runner_param runner_prm; + pj_test_runner_param_default(&runner_prm); + pj_test_runner *runner; + pj_test_stat stat; + pj_status_t status; + + if (ut_app->prm_list_test) { + ut_list_tests(ut_app, title); + return PJ_SUCCESS; + } + + if (argc > 1) { + int i; + for (i=1; isuite.tests.next; tc!=&ut_app->suite.tests; + tc=tc->next) + { + if (pj_ansi_strcmp(argv[i], tc->obj_name)==0) + break; + } + if (tc==&ut_app->suite.tests) { + PJ_LOG(1,(THIS_FILE, "Test \"%s\" is not found", argv[i])); + return PJ_ENOTFOUND; + } + } + } + + if (ut_app->ntests <= 0) + return PJ_SUCCESS; + + pj_test_runner_param_default(&runner_prm); + runner_prm.stop_on_error = ut_app->prm_stop_on_error; + if (ut_app->prm_nthreads >= 0) + runner_prm.nthreads = ut_app->prm_nthreads; + status = pj_test_create_text_runner(ut_app->pool, &runner_prm, &runner); + PJ_TEST_SUCCESS(status, "error creating text runner", return status); + + PJ_LOG(3,(THIS_FILE, + "Performing %d %s with %d worker thread%s", + ut_app->ntests, title, runner_prm.nthreads, + runner_prm.nthreads>1?"s":"")); + pj_test_run(runner, &ut_app->suite); + pj_test_runner_destroy(runner); + pj_test_get_stat(&ut_app->suite, &stat); + pj_test_display_stat(&stat, title, THIS_FILE); + pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); + + return stat.nfailed ? PJ_EBUG : PJ_SUCCESS; +} + +static void ut_usage() +{ + puts(" -l 0,1,2,3 0: Don't show logging after tests"); + puts(" 1: Show logs of only failed tests (default)"); + puts(" 2: Show logs of only successful tests"); + puts(" 3: Show logs of all tests"); + puts(" --log-no-cache Do not cache logging"); + printf(" -w N Set N worker threads (0: disable. Default: %d)\n", PJ_HAS_THREADS); + puts(" -L, --list List the tests and exit"); + puts(" --stop-err Stop testing on error"); +} + + +static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) +{ + int itmp; + pj_status_t status; + + ut_app->prm_list_test = pj_argparse_get("-L", argc, argv) || + pj_argparse_get("--list", argc, argv); + ut_app->prm_stop_on_error = pj_argparse_get("--stop-err", argc, argv); + if (pj_argparse_get("--log-no-cache", argc, argv)) { + ut_app->flags |= PJ_TEST_LOG_NO_CACHE; + } + + status = pj_argparse_get_int("-l", argc, argv, &itmp); + if (status==PJ_SUCCESS && itmp>=0 && itmp<=3) { + ut_app->prm_logging_policy = (pj_test_select_tests)itmp; + } else if (status!=PJ_ENOTFOUND) { + puts("Error: invalid/missing value for -l option"); + return PJ_EINVAL; + } + + status = pj_argparse_get_int("-w", argc, argv, + (int*)&ut_app->prm_nthreads); + if (status==PJ_SUCCESS) { + if (ut_app->prm_nthreads > 50 || ut_app->prm_nthreads < 0) { + printf("Error: value %d is not valid for -w option\n", + ut_app->prm_nthreads); + return PJ_EINVAL; + } + } else if (status!=PJ_ENOTFOUND) { + puts("Error: invalid/missing value for -w option"); + return PJ_EINVAL; + } + return PJ_SUCCESS; +} + +#ifdef UNDEF_THIS_FILE +# undef THIS_FILE +#endif From b2e3108370f731798bafa90a8a6057737f0bb818 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 20 Jun 2024 19:58:53 +0700 Subject: [PATCH 12/79] Dirty hack to fix error message being displayed when user selected essential test because it does not exist in features test (in pjlib-test) --- pjlib-util/src/pjlib-util-test/test.c | 1 - pjlib-util/src/pjlib-util-test/test.h | 2 +- pjlib/src/pjlib-test/test.c | 49 ++++++++++++++++++--------- pjlib/src/pjlib-test/test_util.h | 4 +-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index e1bde9a9be..4c0ce91048 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -83,7 +83,6 @@ static int test_inner(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, http_client_test, PJ_TEST_PARALLEL); #endif - if (ut_run_tests(&test_app.ut_app, "pjlib-util tests", argc, argv)) { ut_app_destroy(&test_app.ut_app); return 1; diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h index be7b704b13..d076b1eca8 100644 --- a/pjlib-util/src/pjlib-util-test/test.h +++ b/pjlib-util/src/pjlib-util-test/test.h @@ -44,7 +44,7 @@ extern int http_client_test(); extern void app_perror(const char *title, pj_status_t rc); extern pj_pool_factory *mem; -#define UT_MAX_TESTS 16 +#define UT_MAX_TESTS 20 #include "../../../pjlib/src/pjlib-test/test_util.h" struct test_app_t diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 14360c46f4..394bd2ca95 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -89,7 +89,7 @@ static void list_tests(const pj_test_suite *suite, const char *title) pj_log_set_decor(d); } -static int essential_tests(int argc, char *argv[]) +static pj_test_stat essential_tests(int argc, char *argv[]) { pj_test_suite suite; pj_test_runner runner; @@ -102,15 +102,19 @@ static int essential_tests(int argc, char *argv[]) pj_test_case test_cases[MAX_TESTS]; int ntests = 0; + pj_bzero(&stat, sizeof(stat)); + /* Test the unit-testing framework first, outside unit-test! * Only perform the test if user is not requesting specific test. */ - if (argc==1) { + if (argc==1 && !test_app.ut_app.prm_list_test) { pj_dump_config(); PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); - if (unittest_basic_test()) - return 1; + if (unittest_basic_test()) { + stat.nfailed = 1; + return stat; + } } /* Now that the basic unit-testing framework has been tested, @@ -173,7 +177,7 @@ static int essential_tests(int argc, char *argv[]) if (test_app.ut_app.prm_list_test) { list_tests(&suite, "essential tests"); - return 0; + return stat; } if (ntests > 0) { @@ -190,7 +194,7 @@ static int essential_tests(int argc, char *argv[]) test_app.ut_app.prm_logging_policy); if (stat.nfailed) - return 1; + return stat; } /* Now that the essential components have been tested, test the @@ -198,15 +202,19 @@ static int essential_tests(int argc, char *argv[]) */ if (argc==1) { PJ_LOG(3,(THIS_FILE, "Testing the unit-test test scheduling")); - if (unittest_parallel_test()) - return 1; + if (unittest_parallel_test()) { + stat.nfailed = 1; + return stat; + } PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (multithread)")); - if (unittest_test()) - return 1; + if (unittest_test()) { + stat.nfailed = 1; + return stat; + } } - return 0; + return stat; } static int features_tests(int argc, char *argv[]) @@ -325,6 +333,7 @@ static int features_tests(int argc, char *argv[]) int test_inner(int argc, char *argv[]) { pj_caching_pool caching_pool; + pj_test_stat stat; const char *filename; int line; int rc = 0; @@ -346,14 +355,22 @@ int test_inner(int argc, char *argv[]) PJ_LOG(3,(THIS_FILE, "Using ci-mode")); if (!test_app.param_skip_essentials) { - rc = essential_tests(argc, argv); - if (rc) + stat = essential_tests(argc, argv); + if (stat.nfailed) { + rc = 1; goto on_return; + } } - rc = features_tests(argc, argv); - if (rc) - goto on_return; + if (argc-1 > 0 && stat.nruns==argc-1) { + /* cmdline specifies test(s) to run, and the number of runs + * matches that. That means all requested tests have been run. + */ + } else { + rc = features_tests(argc, argv); + if (rc) + goto on_return; + } #if INCLUDE_ECHO_SERVER //echo_server(); diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 19c1b71917..1480769f0a 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -153,8 +153,8 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, break; } if (tc==&ut_app->suite.tests) { - PJ_LOG(1,(THIS_FILE, "Test \"%s\" is not found", argv[i])); - return PJ_ENOTFOUND; + PJ_LOG(2,(THIS_FILE, "Test \"%s\" is not found in %s", + argv[i], title)); } } } From 13db0c6fe8ce43833562a8a37118569f190c0116 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 21 Jun 2024 09:43:12 +0700 Subject: [PATCH 13/79] Replace PJ_TEST_PARALLEL with PJ_TEST_EXCLUSIVE --- pjlib-util/src/pjlib-util-test/test.c | 14 +++---- pjlib/include/pj/unittest.h | 9 ++-- pjlib/src/pj/unittest.c | 14 ++----- pjlib/src/pjlib-test/test.c | 42 +++++++++---------- pjlib/src/pjlib-test/test_util.h | 6 ++- pjlib/src/pjlib-test/unittest_test.c | 30 +++++++------- unittest.md | 59 +++++++-------------------- 7 files changed, 69 insertions(+), 105 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index 4c0ce91048..b652aaa736 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -57,30 +57,30 @@ static int test_inner(int argc, char *argv[]) pj_dump_config(); #if INCLUDE_XML_TEST - UT_ADD_TEST(&test_app.ut_app, xml_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, xml_test, 0); #endif #if INCLUDE_JSON_TEST - UT_ADD_TEST(&test_app.ut_app, json_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, json_test, 0); #endif #if INCLUDE_ENCRYPTION_TEST - UT_ADD_TEST(&test_app.ut_app, encryption_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, encryption_test, 0); # if WITH_BENCHMARK - UT_ADD_TEST(&test_app.ut_app, encryption_benchmark, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, encryption_benchmark, 0); # endif #endif #if INCLUDE_STUN_TEST - UT_ADD_TEST(&test_app.ut_app, stun_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, stun_test, 0); #endif #if INCLUDE_RESOLVER_TEST - UT_ADD_TEST(&test_app.ut_app, resolver_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, resolver_test, 0); #endif #if INCLUDE_HTTP_CLIENT_TEST - UT_ADD_TEST(&test_app.ut_app, http_client_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, http_client_test, 0); #endif if (ut_run_tests(&test_app.ut_app, "pjlib-util tests", argc, argv)) { diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 68f3471574..8d8669d52b 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -307,12 +307,11 @@ PJ_BEGIN_DECL typedef enum pj_test_case_flag { /** - * Allow next test case to run in parallel. If this flag is not specified, - * the test case will run exclusively without other test cases running. - * Note this only works for test runners that support worker threads. - * Basic runner will always run serially. + * Do not allow other test cases to run while this test case is running. + * Note this only makes sense for test runners that support worker + * threads. Basic runner will always run test cases serially. */ - PJ_TEST_PARALLEL = 1, + PJ_TEST_EXCLUSIVE = 1, /** * Specify that the test function must be called without argument. diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 06a66069b6..b56fdc961d 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -533,17 +533,11 @@ typedef struct text_runner_t } text_runner_t; /* This is called by thread(s) to get the next test case to run. - * The rule is: - * 1. if current test case (tc) doesn't have PJ_TEST_PARALLEL flag, then we - * cannot get next tc until that tc completes - * 2. if tc has PJ_TEST_PARALLEL flag, free to return next tc, until we - * meet tc with no PJ_TEST_PARALLEL flag, in this case that tc is - * returned and we'll have rule 1. * * Returns: - * - PJ_SUCCESS if we successfully returns next tc + * - PJ_SUCCESS if we successfully returns next test case (tc) * - PJ_EPENDING if there is tc left but we must wait for completion of - * non-parallel tc + * exclusive tc * - PJ_ENOTFOUND if there is no further tests, which in this case the * thread can exit. * - other error is not anticipated, and will cause thread to exit. @@ -581,8 +575,8 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, } } else { /* Test is still running. */ - if ((cur->flags & PJ_TEST_PARALLEL) && next != NULL && - (next->flags & PJ_TEST_PARALLEL)) + if ((cur->flags & PJ_TEST_EXCLUSIVE)==0 && next != NULL && + (next->flags & PJ_TEST_EXCLUSIVE)==0) { /* Allowed other test to run because test also allows * parallel test diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 394bd2ca95..3642b2420b 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -223,59 +223,59 @@ static int features_tests(int argc, char *argv[]) return 1; #if INCLUDE_RAND_TEST - UT_ADD_TEST(&test_app.ut_app, rand_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, rand_test, 0); #endif #if INCLUDE_POOL_PERF_TEST - UT_ADD_TEST(&test_app.ut_app, pool_perf_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, pool_perf_test, 0); #endif #if INCLUDE_RBTREE_TEST - UT_ADD_TEST(&test_app.ut_app, rbtree_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, rbtree_test, 0); #endif #if INCLUDE_HASH_TEST - UT_ADD_TEST(&test_app.ut_app, hash_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, hash_test, 0); #endif #if INCLUDE_TIMESTAMP_TEST - UT_ADD_TEST(&test_app.ut_app, timestamp_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, timestamp_test, 0); #endif #if INCLUDE_ATOMIC_TEST - UT_ADD_TEST(&test_app.ut_app, atomic_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, atomic_test, 0); #endif #if INCLUDE_TIMER_TEST - UT_ADD_TEST(&test_app.ut_app, timer_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, timer_test, 0); #endif #if INCLUDE_SLEEP_TEST - UT_ADD_TEST(&test_app.ut_app, sleep_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sleep_test, 0); #endif #if INCLUDE_FILE_TEST - UT_ADD_TEST(&test_app.ut_app, file_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, file_test, 0); #endif #if INCLUDE_SOCK_TEST - UT_ADD_TEST(&test_app.ut_app, sock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sock_test, 0); #endif #if INCLUDE_SOCK_PERF_TEST - UT_ADD_TEST(&test_app.ut_app, sock_perf_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, sock_perf_test, 0); #endif #if INCLUDE_SELECT_TEST - UT_ADD_TEST(&test_app.ut_app, select_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, select_test, 0); #endif #if INCLUDE_UDP_IOQUEUE_TEST - UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_test, 0); #endif #if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST - UT_ADD_TEST(&test_app.ut_app, tcp_ioqueue_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, tcp_ioqueue_test, 0); #endif /* Consistently encountered retcode 520 on Windows virtual machine @@ -295,27 +295,27 @@ static int features_tests(int argc, char *argv[]) */ #if INCLUDE_IOQUEUE_STRESS_TEST # if defined(PJ_WIN32) && PJ_WIN32!=0 - UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, 0); + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, PJ_TEST_EXCLUSIVE); # else - UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ioqueue_stress_test, 0); # endif #endif #if INCLUDE_IOQUEUE_UNREG_TEST - UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_unreg_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, udp_ioqueue_unreg_test, 0); #endif #if INCLUDE_IOQUEUE_PERF_TEST - UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test0, PJ_TEST_PARALLEL); - UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test1, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test0, 0); + UT_ADD_TEST(&test_app.ut_app, ioqueue_perf_test1, 0); #endif #if INCLUDE_ACTIVESOCK_TEST - UT_ADD_TEST(&test_app.ut_app, activesock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, activesock_test, 0); #endif #if INCLUDE_SSLSOCK_TEST - UT_ADD_TEST(&test_app.ut_app, ssl_sock_test, PJ_TEST_PARALLEL); + UT_ADD_TEST(&test_app.ut_app, ssl_sock_test, 0); #endif diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 1480769f0a..fc8f937958 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -115,14 +115,16 @@ static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void), static void ut_list_tests(ut_app_t *ut_app, const char *title) { unsigned d = pj_log_get_decor(); - const pj_test_case *tc; + const pj_test_case *tc, *prev=NULL; pj_log_set_decor(d ^ PJ_LOG_HAS_NEWLINE); PJ_LOG(3,(THIS_FILE, "%ld %s:", pj_list_size(&ut_app->suite.tests), title)); pj_log_set_decor(0); for (tc=ut_app->suite.tests.next; tc!=&ut_app->suite.tests; tc=tc->next) { - PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); + if (!prev || pj_ansi_strcmp(tc->obj_name, prev->obj_name)) + PJ_LOG(3,(THIS_FILE, " %s", tc->obj_name)); + prev = tc; } PJ_LOG(3,(THIS_FILE, "\n")); pj_log_set_decor(d); diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index 659c7acf44..ba9913fed9 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -310,7 +310,7 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, if (basic) flags = 0; else - flags = parallel? PJ_TEST_PARALLEL : 0; + flags = parallel? 0 : PJ_TEST_EXCLUSIVE; /* Add test case 0. This test case writes some logs and returns * success. @@ -502,7 +502,7 @@ static int parallel_func(void *arg) } /* - * Test that PJ_TEST_PARALLEL flag (or lack of) works. + * Test that PJ_TEST_EXCLUSIVE flag (or lack of) works. */ int unittest_parallel_test() { @@ -517,17 +517,17 @@ int unittest_parallel_test() pj_test_case test_cases[MAX_TESTS]; pj_test_runner_param prm; struct parallel_param_t parallel_params[MAX_TESTS] = { - {0, MS+2*MS, "a"}, - {0, MS+1*MS, "b"}, /* b have to wait for a */ - {PJ_TEST_PARALLEL, MS+7*MS, "c"}, /* c have to wait for b */ - {PJ_TEST_PARALLEL, MS+1*MS, "d"}, /* d have to wait for b */ - {PJ_TEST_PARALLEL, MS+4*MS, "e"}, /* e have to wait for b */ - {0, MS+2*MS, "f"}, /* f have to wait for c, d, e */ - {0, MS+0*MS, "g"}, /* g have to wait for f */ - {0, MS+5*MS, "h"}, /* h have to wait for g */ - {PJ_TEST_PARALLEL, MS+4*MS, "i"}, /* i will finish last */ - {PJ_TEST_PARALLEL, MS+2*MS, "j"}, /* i will finish second last */ - {PJ_TEST_PARALLEL, MS+0*MS, "k"}, /* i will finish third last */ + {PJ_TEST_EXCLUSIVE, MS+2*MS, "a"}, + {PJ_TEST_EXCLUSIVE, MS+1*MS, "b"}, /* b have to wait for a */ + {0, MS+7*MS, "c"}, /* c have to wait for b */ + {0, MS+1*MS, "d"}, /* d have to wait for b */ + {0, MS+4*MS, "e"}, /* e have to wait for b */ + {PJ_TEST_EXCLUSIVE, MS+2*MS, "f"}, /* f have to wait for c, d, e */ + {PJ_TEST_EXCLUSIVE, MS+0*MS, "g"}, /* g have to wait for f */ + {PJ_TEST_EXCLUSIVE, MS+5*MS, "h"}, /* h have to wait for g */ + {0, MS+4*MS, "i"}, /* i will finish last */ + {0, MS+2*MS, "j"}, /* i will finish second last */ + {0, MS+0*MS, "k"}, /* i will finish third last */ }; const char *correct_msg = "abdecfghkji"; pj_str_t stmp0, stmp1; @@ -541,8 +541,8 @@ int unittest_parallel_test() for (i=0; i` and `pj/unittest.c`. -2. largish modifications to `pjlib-test`, `pjlib-util-test`, `pjnath-test`, `pjmedia-test`, and `pjsip-test`. -3. minor developments: +2. Some modifications to `pjlib-test`, `pjlib-util-test`, `pjnath-test`, `pjmedia-test`, and `pjsip-test` to use the framework. +3. Other developments: - modifications to `pj/fifobuf.[hc]`, an old feature (part of pjsip initial commit!) that has never been used until now - new auxiliary feature: ``, header only utilities to parse command line arguments. @@ -85,11 +85,11 @@ The second part is the unit test framework. The framework uses common architectu Below are the features of the unit-test and also other enhancements done in this PR: -- Supports parallel execution on per test case basis, resulting in up to four times speed up in PJLIB-TEST. - Familiarity with common architecture as described in https://en.wikipedia.org/wiki/XUnit +- By default uses parallel execution unless it is disabled on per test case basis. - Easy to port existing test functions. In fact, no modification is needed to the test functions -- PJ_TEST_XXX() macros can be used in place of manual testing -- All (C based) PJ unit testing apps have nicer output: +- Convenient `PJ_TEST_XXX()` macros can be used in place of manual testing and error reporting +- Nicer output: ``` 07:34:32.878 Performing 20 features tests with 4 worker threads @@ -99,7 +99,7 @@ Below are the features of the unit-test and also other enhancements done in this ... ``` -- Even nicer output, logging emited during test is captured and by default only displayed if the test fails (or can be displayed all the time, configurable by cmdline option) +- Even nicer output, logging is captured and by default only displayed if the test fails (configurable by cmdline option) - All (C based) PJ unit testing apps can select test(s) to invoke from cmdline ### 2. Modifications to test apps @@ -135,35 +135,13 @@ Other test apps have been modified to produce similar look. #### Test body -The main test body (`test.c`) was modified to use the unit-test framework, resulting in nicer output: +The main modification in test body (`test.c`) is to use the unit-test framework. -``` -07:34:32.878 Performing 20 features tests with 4 worker threads -[ 1/20] hash_test [OK] [0.000s] -[ 2/20] atomic_test [OK] [0.000s] -[ 3/20] rand_test [OK] [0.002s] -[ 4/20] pool_perf_test [OK] [0.003s] -[ 5/20] sock_test [OK] [0.003s] -[ 6/20] rbtree_test [Err: -40] [0.007s] -[ 7/20] select_test [OK] [0.010s] -... -... -... -[19/20] ioqueue_perf_test1 [OK] [110.046s] -[20/20] ioqueue_stress_test [OK] [132.287s] -07:37:02.192 Unit test statistics for features tests: -07:37:02.192 Total number of tests: 20 -07:37:02.192 Number of test run: 20 -07:37:02.192 Number of failed test: 1 -07:37:02.192 Total duration: 2m29.313s -07:37:02.192 ------------ Displaying failed test logs: ------------ -07:37:02.192 Logs for rbtree_test [rc:-40]: -07:34:32.885 Error: ..... -07:37:02.192 -------------------------------------------------------- -07:37:02.192 -07:37:02.192 Stack max usage: 0, deepest: :0 -07:37:02.192 **Test completed with error(s)** -``` +#### Test modifications + +Some tests (mostly in pjlib-test) was modified, replacing manual checks with `PJ_TEST_XXX()` macros. This is done to test the usage of `PJ_TEST_XXX()` macros and to make the test nicer. But since it made the PR very big, I didn't continue the effort. + +Some tests were also split up to make them run in parallel. #### Speed result: PJLIB-TEST @@ -177,20 +155,11 @@ In PJLIB-TEST, test time is speed up by up to four times: - 6 worker threads: 2m28.450s - 8 worker threads: 2m19.779s -It looks like the sweet spot is with 3 worker threads. This is because some tests takes quite a long time t o finish, so using many threads won't help these tests: - -``` -[14/20] udp_ioqueue_unreg_test [OK] [92.759s] -[15/20] ioqueue_stress_test [OK] [111.061s] -[17/20] ioqueue_perf_test1 [OK] [110.149s] -[18/20] activesock_test [OK] [101.890s] -``` - -I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner). +It looks like the sweet spot is with 3 worker threads. Runing with more than this did not speed up the test considerably because some tests just take a long time to finish (more than 2 minutes). I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner), or because the tests shares global states with each other. #### Speed result: PJLIB-UTIL-TEST -In PJLIB-UTIL-TEST, there is about 2x speed up from 5m52.500s to 3m3.615s. We couldn't achieve more speed up due to long running tests such as resolver_test() and http_client_test() which couldn't be broken up due to the use of global states. +In PJLIB-UTIL-TEST, there is almost 2x speed up from 5m52.500s to 3m3.615s with 1 worker thread (the default). We couldn't speed up more because tests such as `resolver_test()` and `http_client_test()` takes about three minutes to complete and they couldn't be split up due to the use of global states. ### 3. Other developments From 6bfb5d215c627c4a120c9b6e485bba347ef8fea6 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 21 Jun 2024 11:54:30 +0700 Subject: [PATCH 14/79] Ported pjnath-test to use unit-testing framework, with limited speed-up from 45m originally to 15m using 10 worker threads --- pjnath/src/pjnath-test/concur_test.c | 90 +++++++------ pjnath/src/pjnath-test/ice_test.c | 164 ++++++++++-------------- pjnath/src/pjnath-test/main.c | 57 +++++--- pjnath/src/pjnath-test/server.c | 4 +- pjnath/src/pjnath-test/stun_sock_test.c | 43 ++----- pjnath/src/pjnath-test/test.c | 133 ++++++++++++------- pjnath/src/pjnath-test/test.h | 25 +++- pjnath/src/pjnath-test/turn_sock_test.c | 14 +- unittest.md | 4 + 9 files changed, 285 insertions(+), 249 deletions(-) diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c index 54ddb7b6c7..31d575f5f9 100644 --- a/pjnath/src/pjnath-test/concur_test.c +++ b/pjnath/src/pjnath-test/concur_test.c @@ -226,14 +226,14 @@ static int stun_destroy_test_session(struct stun_test_session *test_sess) static int stun_destroy_test(void) { - enum { LOOP = 500 }; +#define ERR(errval) { rc=errval; goto on_return; } + enum { LOOP = 10 }; struct stun_test_session test_sess; pj_sockaddr bind_addr; int addr_len; pj_caching_pool cp; pj_pool_t *pool; unsigned i; - pj_status_t status; int rc = 0; PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test")); @@ -241,53 +241,61 @@ static int stun_destroy_test(void) pj_bzero(&test_sess, sizeof(test_sess)); pj_caching_pool_init(&cp, NULL, 0); - pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL); + PJ_TEST_NOT_NULL((pool=pj_pool_create(&cp.factory, "testsess", + 512, 512, NULL)), NULL, ERR(-10)); pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL); - status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_timer_heap_create(pool, 1023, + &test_sess.stun_cfg.timer_heap), + NULL, ERR(-20)); - status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_lock_create_recursive_mutex(pool, NULL, + &test_sess.lock), + NULL, ERR(-30)); - pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE); - pj_assert(status == PJ_SUCCESS); + pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, + PJ_TRUE); - status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &test_sess.stun_cfg.ioqueue); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, + &test_sess.stun_cfg.ioqueue), + NULL, ERR(-40)); - pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock); + PJ_TEST_SUCCESS(pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, + &test_sess.server_sock), + NULL, ERR(-50)); pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0); - status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr)); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_sock_bind(test_sess.server_sock, &bind_addr, + pj_sockaddr_get_len(&bind_addr)), + NULL, ERR(-60)); /* Set socket to nonblocking to avoid stuck in recv/recvfrom() on concurrent events */ app_set_sock_nb(test_sess.server_sock); addr_len = sizeof(bind_addr); - status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_sock_getsockname(test_sess.server_sock, &bind_addr, + &addr_len), + NULL, ERR(-70)); test_sess.server_port = pj_sockaddr_get_port(&bind_addr); - status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, + &test_sess.server_event), + NULL, ERR(-80)); for (i=0; icfg.client_flag)?ept->cfg.client_flag:CLIENT_IPV4; - status = pj_gethostip(pj_AF_INET(), &hostip); - if (status != PJ_SUCCESS) - return -1030; + PJ_TEST_SUCCESS(pj_gethostip(pj_AF_INET(), &hostip), NULL, return -1030); pj_sockaddr_print(&hostip, serveripv4, sizeof(serveripv4), 0); if (flag & CLIENT_IPV6) { - status = pj_gethostip(pj_AF_INET6(), &hostip); - if (status != PJ_SUCCESS) - return -1031; + PJ_TEST_SUCCESS(pj_gethostip(pj_AF_INET6(), &hostip), + NULL, return -1031); pj_sockaddr_print(&hostip, serveripv6, sizeof(serveripv6), 0); } @@ -260,20 +256,16 @@ static int create_ice_strans(struct test_sess *test_sess, } /* Create ICE stream transport */ - status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, - (void*)ept, &ice_cb, - &ice); - if (status != PJ_SUCCESS) { - app_perror(INDENT "err: pj_ice_strans_create()", status); - return status; - } + PJ_TEST_SUCCESS(pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, + (void*)ept, &ice_cb, &ice), + NULL, return -1040); pj_create_unique_string(test_sess->pool, &ept->ufrag); pj_create_unique_string(test_sess->pool, &ept->pass); /* Looks alright */ *p_ice = ice; - return PJ_SUCCESS; + return 0; } /* Create test session */ @@ -289,10 +281,10 @@ static int create_sess(pj_stun_config *stun_cfg, pj_str_t ns_ip; pj_uint16_t ns_port; unsigned flags; - pj_status_t status = PJ_SUCCESS; + int rc; /* Create session structure */ - pool = pj_pool_create(mem, "testsess", 512, 512, NULL); + pool = pj_pool_create(stun_cfg->pf, "testsess", 512, 512, NULL); sess = PJ_POOL_ZALLOC_T(pool, struct test_sess); sess->pool = pool; sess->stun_cfg = stun_cfg; @@ -307,20 +299,17 @@ static int create_sess(pj_stun_config *stun_cfg, /* Create server */ flags = server_flag; if (flags & SERVER_IPV4) { - status = create_test_server(stun_cfg, (flags & ~SERVER_IPV6), - SRV_DOMAIN, &sess->server1); + PJ_TEST_SUCCESS(create_test_server(stun_cfg, (flags & ~SERVER_IPV6), + SRV_DOMAIN, &sess->server1), + NULL, { destroy_sess(sess, 500); return -10; }); } - if ((status == PJ_SUCCESS) && (flags & SERVER_IPV6)) { - status = create_test_server(stun_cfg, (flags & ~SERVER_IPV4), - SRV_DOMAIN, &sess->server2); + if (flags & SERVER_IPV6) { + PJ_TEST_SUCCESS(create_test_server(stun_cfg, (flags & ~SERVER_IPV4), + SRV_DOMAIN, &sess->server2), + NULL, { destroy_sess(sess, 500); return -11; }); } - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: create_test_server()", status); - destroy_sess(sess, 500); - return -10; - } if (flags & SERVER_IPV4) { sess->server1->turn_respond_allocate = sess->server1->turn_respond_refresh = PJ_TRUE; @@ -337,36 +326,31 @@ static int create_sess(pj_stun_config *stun_cfg, (sess->caller.cfg.enable_stun & SRV)==SRV || (sess->caller.cfg.enable_turn & SRV)==SRV) { - status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap, - stun_cfg->ioqueue, &sess->resolver); - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: pj_dns_resolver_create()", status); - destroy_sess(sess, 500); - return -20; - } + PJ_TEST_SUCCESS(pj_dns_resolver_create(stun_cfg->pf, NULL, 0, + stun_cfg->timer_heap, + stun_cfg->ioqueue, + &sess->resolver), + NULL, { destroy_sess(sess, 500); return -20; }); ns_ip = (flags & SERVER_IPV6)?pj_str("::1"):pj_str("127.0.0.1"); ns_port = (pj_uint16_t)DNS_SERVER_PORT; - status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port); - if (status != PJ_SUCCESS) { - app_perror(INDENT "error: pj_dns_resolver_set_ns()", status); - destroy_sess(sess, 500); - return -21; - } + PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, + &ns_port), + NULL, { destroy_sess(sess, 500); return -21; }); } /* Create caller ICE stream transport */ - status = create_ice_strans(sess, &sess->caller, &sess->caller.ice); - if (status != PJ_SUCCESS) { + rc = create_ice_strans(sess, &sess->caller, &sess->caller.ice); + if (rc != 0) { destroy_sess(sess, 500); - return -30; + return rc; } /* Create callee ICE stream transport */ - status = create_ice_strans(sess, &sess->callee, &sess->callee.ice); - if (status != PJ_SUCCESS) { + rc = create_ice_strans(sess, &sess->callee, &sess->callee.ice); + if (rc != 0) { destroy_sess(sess, 500); - return -40; + return rc; } *p_sess = sess; @@ -502,22 +486,18 @@ static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote) unsigned i; for (i=0; icfg.comp_cnt; ++i) { unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt; - status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt); - if (status != PJ_SUCCESS) { - app_perror(INDENT "err: pj_ice_strans_enum_cands()", status); - return status; - } + PJ_TEST_SUCCESS((status=pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, + rcand+rcand_cnt)), + NULL, return status); rcand_cnt += cnt; } } - status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass, - rcand_cnt, rcand); - - if (status != ept->cfg.expected.start_status) { - app_perror(INDENT "err: pj_ice_strans_start_ice()", status); - return status; - } + PJ_TEST_EQ((status=pj_ice_strans_start_ice(ept->ice, &remote->ufrag, + &remote->pass, + rcand_cnt, rcand)), + ept->cfg.expected.start_status, NULL, + {return status==PJ_SUCCESS? PJ_EINVALIDOP : status; }); return status; } @@ -688,7 +668,7 @@ static int perform_test2(const char *title, if (rc != PJ_SUCCESS) { app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); - return -100; + return -106; } /* Init ICE on callee */ @@ -904,8 +884,7 @@ static int perform_test(const char *title, int ice_test(void) { - pj_pool_t *pool; - pj_stun_config stun_cfg; + app_sess_t app_sess; unsigned i; int rc; struct sess_cfg_t { @@ -954,12 +933,9 @@ int ice_test(void) }, }; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); - if (rc != PJ_SUCCESS) { - pj_pool_release(pool); + rc = create_stun_config(&app_sess); + if (rc != PJ_SUCCESS) return -7; - } /* Simple test first with host candidate */ if (1) { @@ -972,7 +948,7 @@ int ice_test(void) {ROLE2, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -980,7 +956,7 @@ int ice_test(void) cfg.ua1.comp_cnt = 2; cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with host candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -997,7 +973,7 @@ int ice_test(void) {ROLE2, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1006,7 +982,7 @@ int ice_test(void) cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with srflx candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1023,7 +999,7 @@ int ice_test(void) {ROLE2, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1032,7 +1008,7 @@ int ice_test(void) cfg.ua2.comp_cnt = 2; rc = perform_test("Basic with relay candidates, 2 components", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1049,7 +1025,7 @@ int ice_test(void) {ROLE2, 2, NO, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1058,7 +1034,7 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("STUN resolution failure with destroy on callback", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1075,7 +1051,7 @@ int ice_test(void) {ROLE2, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_SUCCESS, PJ_STATUS_FROM_STUN_CODE(401), -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1084,7 +1060,7 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("TURN allocation failure with destroy on callback", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1102,7 +1078,7 @@ int ice_test(void) {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}} }; - rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, + rc = perform_test(cfg.title, &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1111,7 +1087,7 @@ int ice_test(void) cfg.ua2.client_flag |= DEL_ON_ERR; rc = perform_test("STUN failure, testing TURN deallocation (cb)", - &stun_cfg, cfg.server_flag, + &app_sess.stun_cfg, cfg.server_flag, &cfg.ua1, &cfg.ua2); if (rc != 0) goto on_return; @@ -1166,7 +1142,7 @@ int ice_test(void) delay[d], k1, k2); cfg->ua2.comp_cnt = k2; - rc = perform_test(title, &stun_cfg, cfg->server_flag, + rc = perform_test(title, &app_sess.stun_cfg, cfg->server_flag, &cfg->ua1, &cfg->ua2); if (rc != 0) goto on_return; @@ -1177,8 +1153,7 @@ int ice_test(void) } on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } @@ -1239,22 +1214,19 @@ int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit) int ice_conc_test(void) { - const unsigned LOOP = 100; - pj_pool_t *pool; - pj_stun_config stun_cfg; + const unsigned LOOP = 20; + app_sess_t app_sess; unsigned i; int rc; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); return -7; } for (i = 0; i < LOOP; i++) { PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP)); - rc = ice_one_conc_test(&stun_cfg, PJ_TRUE); + rc = ice_one_conc_test(&app_sess.stun_cfg, PJ_TRUE); if (rc) break; } @@ -1263,8 +1235,7 @@ int ice_conc_test(void) goto on_return; on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } @@ -1531,8 +1502,7 @@ static int perform_trickle_test(const char *title, /* Simple trickle ICE test */ int trickle_ice_test(void) { - pj_pool_t *pool; - pj_stun_config stun_cfg; + app_sess_t app_sess; struct sess_param test_param; unsigned i; int rc; @@ -1569,11 +1539,8 @@ int trickle_ice_test(void) PJ_LOG(3,(THIS_FILE, "Trickle ICE")); pj_log_push_indent(); - pool = pj_pool_create(mem, NULL, 512, 512, NULL); - - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); pj_log_pop_indent(); return -10; } @@ -1588,7 +1555,7 @@ int trickle_ice_test(void) cfg[i].ua1.comp_cnt = c1; cfg[i].ua2.comp_cnt = c2; rc = perform_trickle_test(cfg[i].title, - &stun_cfg, + &app_sess.stun_cfg, cfg[i].server_flag, &cfg[i].ua1, &cfg[i].ua2, @@ -1597,8 +1564,7 @@ int trickle_ice_test(void) } } - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); pj_log_pop_indent(); return rc; diff --git a/pjnath/src/pjnath-test/main.c b/pjnath/src/pjnath-test/main.c index 9ecc43e948..153f969a27 100644 --- a/pjnath/src/pjnath-test/main.c +++ b/pjnath/src/pjnath-test/main.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" +#include #if defined(PJ_SUNOS) && PJ_SUNOS!=0 @@ -33,19 +34,19 @@ static void init_signals() #elif (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -61,6 +62,21 @@ static void init_signals(void) #define boost() +static void usage() +{ + puts("Usage:"); + puts(" pjnath-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + int main(int argc, char *argv[]) { int rc; @@ -69,22 +85,25 @@ int main(int argc, char *argv[]) boost(); - while (argc > 1) { - char *arg = argv[--argc]; + if (pj_argparse_get("-h", &argc, argv) || + pj_argparse_get("--help", &argc, argv)) + { + usage(); + return 0; + } - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; + ut_app_init0(&test_app.ut_app); - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } - } + interractive = pj_argparse_get("-i", &argc, argv); + no_trap = pj_argparse_get("-n", &argc, argv); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c index 5b6699df4b..7bba5a688e 100644 --- a/pjnath/src/pjnath-test/server.c +++ b/pjnath/src/pjnath-test/server.c @@ -105,7 +105,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, RETURN_ERROR(status); } - pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL); + pool = pj_pool_create(stun_cfg->pf, THIS_FILE, 512, 512, NULL); test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server); test_srv->pool = pool; test_srv->flags = flags; @@ -118,7 +118,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key)); if (flags & CREATE_DNS_SERVER) { - status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue, + status = pj_dns_server_create(stun_cfg->pf, test_srv->stun_cfg->ioqueue, GET_AF(use_ipv6), DNS_SERVER_PORT, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c index f44988aee3..8244e22ec0 100644 --- a/pjnath/src/pjnath-test/stun_sock_test.c +++ b/pjnath/src/pjnath-test/stun_sock_test.c @@ -832,53 +832,34 @@ static int keep_alive_test(pj_stun_config *cfg, pj_bool_t use_ipv6) #define DO_TEST(expr) \ - capture_pjlib_state(&stun_cfg, &pjlib_state); \ + capture_pjlib_state(&app_sess.stun_cfg, &pjlib_state); \ ret = expr; \ if (ret != 0) goto on_return; \ - ret = check_pjlib_state(&stun_cfg, &pjlib_state); \ + ret = check_pjlib_state(&app_sess.stun_cfg, &pjlib_state); \ if (ret != 0) goto on_return; int stun_sock_test(void) { struct pjlib_state pjlib_state; - pj_stun_config stun_cfg; - pj_ioqueue_t *ioqueue = NULL; - pj_timer_heap_t *timer_heap = NULL; - pj_pool_t *pool = NULL; + app_sess_t app_sess; pj_status_t status; int ret = 0; - pool = pj_pool_create(mem, NULL, 512, 512, NULL); + status = create_stun_config(&app_sess); + if (status) + return -11; - status = pj_ioqueue_create(pool, 12, &ioqueue); - if (status != PJ_SUCCESS) { - app_perror(" pj_ioqueue_create()", status); - ret = -4; - goto on_return; - } - - status = pj_timer_heap_create(pool, 100, &timer_heap); - if (status != PJ_SUCCESS) { - app_perror(" pj_timer_heap_create()", status); - ret = -8; - goto on_return; - } - - pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap); - - DO_TEST(timeout_test(&stun_cfg, PJ_FALSE, USE_IPV6)); - DO_TEST(timeout_test(&stun_cfg, PJ_TRUE, USE_IPV6)); + DO_TEST(timeout_test(&app_sess.stun_cfg, PJ_FALSE, USE_IPV6)); + DO_TEST(timeout_test(&app_sess.stun_cfg, PJ_TRUE, USE_IPV6)); - DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE, USE_IPV6)); - DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE, USE_IPV6)); + DO_TEST(missing_attr_test(&app_sess.stun_cfg, PJ_FALSE, USE_IPV6)); + DO_TEST(missing_attr_test(&app_sess.stun_cfg, PJ_TRUE, USE_IPV6)); - DO_TEST(keep_alive_test(&stun_cfg, USE_IPV6)); + DO_TEST(keep_alive_test(&app_sess.stun_cfg, USE_IPV6)); on_return: - if (timer_heap) pj_timer_heap_destroy(timer_heap); - if (ioqueue) pj_ioqueue_destroy(ioqueue); - if (pool) pj_pool_release(pool); + destroy_stun_config(&app_sess); return ret; } diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 8626ac80d9..c97b40939a 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -20,6 +20,10 @@ #include #include +#define THIS_FILE "test.c" + +struct test_app_t test_app; + void app_perror_dbg(const char *msg, pj_status_t rc, const char *file, int line) { @@ -46,44 +50,63 @@ void app_set_sock_nb(pj_sock_t sock) #endif } -pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg) +pj_status_t create_stun_config(app_sess_t *app_sess) { - pj_ioqueue_t *ioqueue; - pj_timer_heap_t *timer_heap; - pj_lock_t *lock; + pj_ioqueue_t *ioqueue = NULL; + pj_timer_heap_t *timer_heap = NULL; + pj_lock_t *lock = NULL; pj_status_t status; - status = pj_ioqueue_create(pool, 64, &ioqueue); - if (status != PJ_SUCCESS) { - app_perror(" pj_ioqueue_create()", status); - return status; - } + pj_bzero(app_sess, sizeof(*app_sess)); + pj_caching_pool_init(&app_sess->cp, + &pj_pool_factory_default_policy, 0 ); + app_sess->pool = pj_pool_create(&app_sess->cp.factory, NULL, + 512, 512, NULL); + PJ_TEST_NOT_NULL(app_sess->pool, NULL, + { status=PJ_ENOMEM; goto on_error;}); - status = pj_timer_heap_create(pool, 256, &timer_heap); - if (status != PJ_SUCCESS) { - app_perror(" pj_timer_heap_create()", status); - pj_ioqueue_destroy(ioqueue); - return status; - } + PJ_TEST_SUCCESS(pj_ioqueue_create(app_sess->pool, 64, &ioqueue), NULL, + goto on_error); + PJ_TEST_SUCCESS(pj_timer_heap_create(app_sess->pool, 256, &timer_heap), + NULL, goto on_error); - pj_lock_create_recursive_mutex(pool, NULL, &lock); + pj_lock_create_recursive_mutex(app_sess->pool, NULL, &lock); pj_timer_heap_set_lock(timer_heap, lock, PJ_TRUE); + lock = NULL; - pj_stun_config_init(stun_cfg, mem, 0, ioqueue, timer_heap); + pj_stun_config_init(&app_sess->stun_cfg, app_sess->pool->factory, 0, + ioqueue, timer_heap); return PJ_SUCCESS; + +on_error: + if (ioqueue) + pj_ioqueue_destroy(ioqueue); + if (timer_heap) + pj_timer_heap_destroy(timer_heap); + if (lock) + pj_lock_destroy(lock); + if (app_sess->pool) + pj_pool_release(app_sess->pool); + pj_caching_pool_destroy(&app_sess->cp); + return status; } -void destroy_stun_config(pj_stun_config *stun_cfg) +void destroy_stun_config(app_sess_t *app_sess) { - if (stun_cfg->timer_heap) { - pj_timer_heap_destroy(stun_cfg->timer_heap); - stun_cfg->timer_heap = NULL; + if (app_sess->stun_cfg.timer_heap) { + pj_timer_heap_destroy(app_sess->stun_cfg.timer_heap); + app_sess->stun_cfg.timer_heap = NULL; } - if (stun_cfg->ioqueue) { - pj_ioqueue_destroy(stun_cfg->ioqueue); - stun_cfg->ioqueue = NULL; + if (app_sess->stun_cfg.ioqueue) { + pj_ioqueue_destroy(app_sess->stun_cfg.ioqueue); + app_sess->stun_cfg.ioqueue = NULL; } + if (app_sess->pool) { + pj_pool_release(app_sess->pool); + app_sess->pool = NULL; + } + pj_caching_pool_destroy(&app_sess->cp); } void poll_events(pj_stun_config *stun_cfg, unsigned msec, @@ -138,18 +161,18 @@ int check_pjlib_state(pj_stun_config *cfg, capture_pjlib_state(cfg, ¤t_state); if (current_state.timer_cnt > initial_st->timer_cnt) { - PJ_LOG(3,("", " error: possibly leaking timer")); rc |= ERR_TIMER_LEAK; #if PJ_TIMER_DEBUG pj_timer_heap_dump(cfg->timer_heap); #endif + PJ_LOG(3,("", " error: possibly leaking timer")); } if (current_state.pool_used_cnt > initial_st->pool_used_cnt) { - PJ_LOG(3,("", " error: possibly leaking memory")); PJ_LOG(3,("", " dumping memory pool:")); - pj_pool_factory_dump(mem, PJ_TRUE); + pj_pool_factory_dump(cfg->pf, PJ_TRUE); + PJ_LOG(3,("", " error: possibly leaking memory")); rc |= ERR_MEMORY_LEAK; } @@ -184,10 +207,10 @@ static void test_log_func(int level, const char *data, int len) orig_log_func(level, data, len); } -static int test_inner(void) +static int test_inner(int argc, char *argv[]) { pj_caching_pool caching_pool; - int rc = 0; + int i, rc = 0; mem = &caching_pool.factory; @@ -202,55 +225,69 @@ static int test_inner(void) pj_log_set_log_func(&test_log_func); #endif - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - goto on_return; - } + PJ_TEST_SUCCESS(pj_init(), NULL, + {if (log_file) fclose(log_file); return 1; }); pj_dump_config(); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); - pjlib_util_init(); - pjnath_init(); + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, {rc=2; goto on_return;}); + PJ_TEST_SUCCESS(pjnath_init(), NULL, {rc=3; goto on_return;}); + PJ_TEST_SUCCESS(ut_app_init1(&test_app.ut_app, mem), + NULL, {rc=4; goto on_return;}); #if INCLUDE_STUN_TEST - DO_TEST(stun_test()); - DO_TEST(sess_auth_test()); + UT_ADD_TEST(&test_app.ut_app, stun_test, 0); + UT_ADD_TEST(&test_app.ut_app, sess_auth_test, 0); #endif -#if INCLUDE_ICE_TEST - DO_TEST(ice_test()); +#if INCLUDE_STUN_SOCK_TEST + UT_ADD_TEST(&test_app.ut_app, stun_sock_test, 0); #endif -#if INCLUDE_TRICKLE_ICE_TEST - DO_TEST(trickle_ice_test()); +#if INCLUDE_ICE_TEST + UT_ADD_TEST(&test_app.ut_app, ice_test, PJ_TEST_EXCLUSIVE); #endif -#if INCLUDE_STUN_SOCK_TEST - DO_TEST(stun_sock_test()); +#if INCLUDE_TRICKLE_ICE_TEST + UT_ADD_TEST(&test_app.ut_app, trickle_ice_test, PJ_TEST_EXCLUSIVE); #endif #if INCLUDE_TURN_SOCK_TEST - DO_TEST(turn_sock_test()); + UT_ADD_TEST(&test_app.ut_app, turn_sock_test, PJ_TEST_EXCLUSIVE); #endif #if INCLUDE_CONCUR_TEST - DO_TEST(concur_test()); + UT_ADD_TEST(&test_app.ut_app, ice_conc_test, PJ_TEST_EXCLUSIVE); + + for (i=0; i<50; ++i) { + UT_ADD_TEST(&test_app.ut_app, concur_test, 0); + } +#else + PJ_UNUSED_ARG(i); #endif + if (ut_run_tests(&test_app.ut_app, "pjnath tests", argc, argv)) { + rc = 5; + } else { + rc = 0; + } + + ut_app_destroy(&test_app.ut_app); + on_return: if (log_file) fclose(log_file); + pj_caching_pool_destroy( &caching_pool ); return rc; } -int test_main(void) +int test_main(int argc, char *argv[]) { PJ_USE_EXCEPTION; PJ_TRY { - return test_inner(); + return test_inner(argc, argv); } PJ_CATCH_ANY { int id = PJ_GET_EXCEPTION(); diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h index 76bb9a0f7d..99c82e66cd 100644 --- a/pjnath/src/pjnath-test/test.h +++ b/pjnath/src/pjnath-test/test.h @@ -52,9 +52,10 @@ int sess_auth_test(void); int stun_sock_test(void); int turn_sock_test(void); int ice_test(void); +int ice_conc_test(void); int trickle_ice_test(void); int concur_test(void); -int test_main(void); +int test_main(int argc, char *argv[]); #define app_perror(msg, rc) app_perror_dbg(msg, rc, __FILE__, __LINE__) extern void app_perror_dbg(const char *msg, pj_status_t rc, @@ -64,12 +65,30 @@ extern pj_pool_factory *mem; int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit); +#define UT_MAX_TESTS 64 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; + int param_log_decor; +}; +extern struct test_app_t test_app; + + //////////////////////////////////// /* * Utilities */ -pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg); -void destroy_stun_config(pj_stun_config *stun_cfg); +typedef struct app_sess_t +{ + pj_caching_pool cp; + pj_pool_t *pool; + pj_stun_config stun_cfg; +} app_sess_t; + +pj_status_t create_stun_config(app_sess_t *stun_cfg); +void destroy_stun_config(app_sess_t *stun_cfg); void poll_events(pj_stun_config *stun_cfg, unsigned msec, pj_bool_t first_event_only); diff --git a/pjnath/src/pjnath-test/turn_sock_test.c b/pjnath/src/pjnath-test/turn_sock_test.c index 8b1b0067d5..7fbd8d650d 100644 --- a/pjnath/src/pjnath-test/turn_sock_test.c +++ b/pjnath/src/pjnath-test/turn_sock_test.c @@ -533,14 +533,11 @@ static int destroy_test(pj_stun_config *stun_cfg, int turn_sock_test(void) { - pj_pool_t *pool; - pj_stun_config stun_cfg; + app_sess_t app_sess; int n, i, rc = 0; - pool = pj_pool_create(mem, "turntest", 512, 512, NULL); - rc = create_stun_config(pool, &stun_cfg); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { - pj_pool_release(pool); return -2; } @@ -558,14 +555,14 @@ int turn_sock_test(void) tp_type = PJ_TURN_TP_TLS; } - rc = state_progression_test(&stun_cfg, USE_IPV6, tp_type); + rc = state_progression_test(&app_sess.stun_cfg, USE_IPV6, tp_type); if (rc != 0) goto on_return; for (i=0; i<=1; ++i) { int j; for (j=0; j<=1; ++j) { - rc = destroy_test(&stun_cfg, i, j, USE_IPV6, tp_type); + rc = destroy_test(&app_sess.stun_cfg, i, j, USE_IPV6, tp_type); if (rc != 0) goto on_return; } @@ -573,8 +570,7 @@ int turn_sock_test(void) } on_return: - destroy_stun_config(&stun_cfg); - pj_pool_release(pool); + destroy_stun_config(&app_sess); return rc; } diff --git a/unittest.md b/unittest.md index 6023d72cc2..69953f4976 100644 --- a/unittest.md +++ b/unittest.md @@ -161,6 +161,10 @@ It looks like the sweet spot is with 3 worker threads. Runing with more than thi In PJLIB-UTIL-TEST, there is almost 2x speed up from 5m52.500s to 3m3.615s with 1 worker thread (the default). We couldn't speed up more because tests such as `resolver_test()` and `http_client_test()` takes about three minutes to complete and they couldn't be split up due to the use of global states. +#### Speed result: PJNATH-TEST + +Original: 45m42.275s +10 threads: 15m10.862s ### 3. Other developments From 2b735d0633d0e5fe445a8b9ae6010881a7e2aed7 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 21 Jun 2024 18:10:19 +0700 Subject: [PATCH 15/79] Large modifications in pjnath-test to speed-up test. It is fast now (4:30 minutes with 10 worker threads, from 45:42m originally) --- pjlib-util/include/pjlib-util/dns_server.h | 11 ++++ pjlib-util/src/pjlib-util/dns_server.c | 13 ++++- pjlib/src/pj/unittest.c | 31 ++++++++++- pjlib/src/pjlib-test/test_util.h | 22 ++++++-- pjnath/src/pjnath-test/ice_test.c | 62 ++++++++++++++------- pjnath/src/pjnath-test/server.c | 37 +++++++++--- pjnath/src/pjnath-test/server.h | 7 +-- pjnath/src/pjnath-test/test.c | 11 ++-- pjnath/src/pjnath-test/test.h | 21 ++++++- pjnath/src/pjnath-test/turn_sock_test.c | 65 ++++++++++++---------- unittest.md | 17 +++++- 11 files changed, 219 insertions(+), 78 deletions(-) diff --git a/pjlib-util/include/pjlib-util/dns_server.h b/pjlib-util/include/pjlib-util/dns_server.h index 25d2b30e79..a56b68c60e 100644 --- a/pjlib-util/include/pjlib-util/dns_server.h +++ b/pjlib-util/include/pjlib-util/dns_server.h @@ -65,6 +65,17 @@ PJ_DECL(pj_status_t) pj_dns_server_create(pj_pool_factory *pf, unsigned flags, pj_dns_server **p_srv); +/** + * Get the DNS server address + * + * @param srv The DNS server instance. + * @param addr It will be filled with the server's bound address. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_dns_server_get_addr(pj_dns_server *srv, + pj_sockaddr *bound_addr); + /** * Destroy DNS server instance. * diff --git a/pjlib-util/src/pjlib-util/dns_server.c b/pjlib-util/src/pjlib-util/dns_server.c index 3231cb7d27..f3f7424d22 100644 --- a/pjlib-util/src/pjlib-util/dns_server.c +++ b/pjlib-util/src/pjlib-util/dns_server.c @@ -52,6 +52,7 @@ struct pj_dns_server pj_pool_t *pool; pj_pool_factory *pf; pj_activesock_t *asock; + pj_sockaddr bound_addr; pj_ioqueue_op_key_t send_key; struct rr rr_list; }; @@ -97,7 +98,8 @@ PJ_DEF(pj_status_t) pj_dns_server_create( pj_pool_factory *pf, pj_activesock_cfg_default(&sock_cfg); status = pj_activesock_create_udp(pool, &sock_addr, &sock_cfg, ioqueue, - &sock_cb, srv, &srv->asock, NULL); + &sock_cb, srv, &srv->asock, + &srv->bound_addr); if (status != PJ_SUCCESS) goto on_error; @@ -116,6 +118,15 @@ PJ_DEF(pj_status_t) pj_dns_server_create( pj_pool_factory *pf, } +PJ_DEF(pj_status_t) pj_dns_server_get_addr(pj_dns_server *srv, + pj_sockaddr *bound_addr) +{ + PJ_ASSERT_RETURN(srv && bound_addr, PJ_EINVAL); + pj_memcpy(bound_addr, &srv->bound_addr, sizeof(*bound_addr)); + return PJ_SUCCESS; +} + + PJ_DEF(pj_status_t) pj_dns_server_destroy(pj_dns_server *srv) { PJ_ASSERT_RETURN(srv, PJ_EINVAL); diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index b56fdc961d..2ff9e033ae 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -212,12 +212,36 @@ PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, (int)stat->duration.msec)); } +static const char *get_test_case_info(const pj_test_case *tc, + char *buf, unsigned size) +{ + char arg_info[64]; + if (tc->flags & PJ_TEST_FUNC_NO_ARG) { + arg_info[0] = '\0'; + } else { + char arg_val[40]; + /* treat argument as integer */ + pj_ansi_snprintf(arg_val, sizeof(arg_val), "%ld", (long)tc->arg); + + /* if arg value is too long (e.g. it's a pointer!), then just show + * a portion of it */ + if (pj_ansi_strlen(arg_val) > 6) { + pj_ansi_strxcat(arg_val+6, "...", sizeof(arg_val)); + } + + pj_ansi_snprintf(arg_info, sizeof(arg_info), " (arg: %s)", arg_val); + } + pj_ansi_snprintf(buf, size, "%s%s", tc->obj_name, arg_info); + return buf; +} + /* Dump previously saved log messages */ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, unsigned flags) { const pj_test_case *tc = suite->tests.next; pj_log_func *log_writer = pj_log_get_log_func(); + char tcname[64]; const char *title; if ((flags & PJ_TEST_ALL_TESTS)==PJ_TEST_ALL_TESTS) @@ -252,7 +276,8 @@ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, } PJ_LOG(3,(THIS_FILE, "Logs for %s [rc:%d]:", - tc->obj_name, tc->result)); + get_test_case_info(tc, tcname, sizeof(tcname)), + tc->result)); do { log_writer(log_item->level, log_item->msg, log_item->len); @@ -385,6 +410,7 @@ static void unittest_log_callback(int level, const char *data, int len) static int get_completion_line( const pj_test_case *tc, const char *end_line, char *log_buf, unsigned buf_size) { + char tcname[64]; char res_buf[64]; pj_time_val elapsed; int log_len; @@ -402,7 +428,8 @@ static int get_completion_line( const pj_test_case *tc, const char *end_line, } log_len = pj_ansi_snprintf(log_buf, buf_size, "%-32s %s%s\n", - tc->obj_name, res_buf, end_line); + get_test_case_info(tc, tcname, sizeof(tcname)), + res_buf, end_line); if (log_len < 1 || log_len >= sizeof(log_buf)) log_len = (int)pj_ansi_strlen(log_buf); diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index fc8f937958..621fbfa771 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -46,7 +46,7 @@ static void ut_app_init0(ut_app_t *ut_app) pj_bzero(ut_app, sizeof(*ut_app)); ut_app->prm_logging_policy = PJ_TEST_FAILED_TESTS; ut_app->prm_nthreads = -1; - ut_app->flags = PJ_TEST_FUNC_NO_ARG; + ut_app->flags = 0; } /* Call this in test.c before adding test cases */ @@ -65,8 +65,18 @@ static void ut_app_destroy(ut_app_t *ut_app) ut_app->pool = NULL; } +typedef int (*ut_func)(void*); + +/* This is for adding test func that has no arg */ #define UT_ADD_TEST(ut_app, test_func, flags) \ - ut_add_test(ut_app, test_func, #test_func, flags, argc, argv) + ut_add_test(ut_app, (ut_func)test_func, 0, \ + #test_func, flags | PJ_TEST_FUNC_NO_ARG, argc, argv) + + +/* This is for adding test func that HAS arg */ +#define UT_ADD_TEST1(ut_app, test_func, arg, flags) \ + ut_add_test(ut_app, test_func, arg, #test_func, flags, argc, argv) + /* Check if a test is specified/requested in cmdline */ static pj_bool_t ut_test_included(const char *name, int argc, char *argv[]) @@ -84,9 +94,9 @@ static pj_bool_t ut_test_included(const char *name, int argc, char *argv[]) } /* Add test case */ -static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void), - const char *test_name, unsigned flags, - int argc, char *argv[]) +static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void*), + void *arg, const char *test_name, + unsigned flags, int argc, char *argv[]) { char *log_buf; pj_test_case *tc; @@ -103,7 +113,7 @@ static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void), log_buf = (char*)pj_pool_alloc(ut_app->pool, UT_LOG_BUF_SIZE); tc = &ut_app->test_cases[ut_app->ntests]; flags |= ut_app->flags; - pj_test_case_init(tc, test_name, flags, (int (*)(void*))test_func, NULL, + pj_test_case_init(tc, test_name, flags, (int (*)(void*))test_func, arg, log_buf, UT_LOG_BUF_SIZE, NULL); pj_test_suite_add_case( &ut_app->suite, tc); diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c index fc03753a82..1189bfe7f0 100644 --- a/pjnath/src/pjnath-test/ice_test.c +++ b/pjnath/src/pjnath-test/ice_test.c @@ -30,6 +30,9 @@ enum #define SRV_DOMAIN "pjsip.lab.domain" #define MAX_THREADS 16 +#define STUN_RANDOM_PORT 43478 +#define TURN_RANDOM_PORT 43479 + #define THIS_FILE "ice_test.c" #define INDENT " " @@ -155,9 +158,11 @@ static pj_bool_t enable_ipv6_test() #endif static void set_stun_turn_cfg(struct ice_ept *ept, - pj_ice_strans_cfg *ice_cfg, - char *serverip, - pj_bool_t use_ipv6) + pj_ice_strans_cfg *ice_cfg, + char *serverip, + pj_uint16_t stun_server_port, + pj_uint16_t turn_server_port, + pj_bool_t use_ipv6) { if (ept->cfg.enable_stun & YES) { unsigned stun_idx = ice_cfg->stun_tp_cnt++; @@ -168,7 +173,7 @@ static void set_stun_turn_cfg(struct ice_ept *ept, } else { ice_cfg->stun_tp[stun_idx].server = pj_str(serverip); } - ice_cfg->stun_tp[stun_idx].port = STUN_SERVER_PORT; + ice_cfg->stun_tp[stun_idx].port = stun_server_port; ice_cfg->stun_tp[stun_idx].af = GET_AF(use_ipv6); } @@ -189,7 +194,7 @@ static void set_stun_turn_cfg(struct ice_ept *ept, } else { ice_cfg->turn_tp[turn_idx].server = pj_str(serverip); } - ice_cfg->turn_tp[turn_idx].port = TURN_SERVER_PORT; + ice_cfg->turn_tp[turn_idx].port = turn_server_port; ice_cfg->turn_tp[turn_idx].conn_type = PJ_TURN_TP_UDP; ice_cfg->turn_tp[turn_idx].auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.realm = @@ -247,13 +252,23 @@ static int create_ice_strans(struct test_sess *test_sess, if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV) ice_cfg.resolver = test_sess->resolver; + /* Assertion in pj_stun_sock_start() if default_port is zero*/ +#define OR(val1,val2) (val1? val1 : val2) + if (flag & CLIENT_IPV4) { - set_stun_turn_cfg(ept, &ice_cfg, serveripv4, PJ_FALSE); + set_stun_turn_cfg(ept, &ice_cfg, serveripv4, + OR(test_sess->server1->stun_server_port, STUN_RANDOM_PORT), + OR(test_sess->server1->turn_server_port, TURN_RANDOM_PORT), + PJ_FALSE); } if (flag & CLIENT_IPV6) { - set_stun_turn_cfg(ept, &ice_cfg, serveripv6, PJ_TRUE); + set_stun_turn_cfg(ept, &ice_cfg, serveripv6, + OR(test_sess->server2->stun_server_port, STUN_RANDOM_PORT), + OR(test_sess->server2->turn_server_port, TURN_RANDOM_PORT), + PJ_TRUE); } +#undef OR /* Create ICE stream transport */ PJ_TEST_SUCCESS(pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt, @@ -332,8 +347,14 @@ static int create_sess(pj_stun_config *stun_cfg, &sess->resolver), NULL, { destroy_sess(sess, 500); return -20; }); - ns_ip = (flags & SERVER_IPV6)?pj_str("::1"):pj_str("127.0.0.1"); - ns_port = (pj_uint16_t)DNS_SERVER_PORT; + if (flags & SERVER_IPV6) { + ns_ip = pj_str("::1"); + ns_port = sess->server2->dns_server_port; + } else { + ns_ip = pj_str("127.0.0.1"); + ns_port = sess->server1->dns_server_port; + } + PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port), NULL, { destroy_sess(sess, 500); return -21; }); @@ -882,8 +903,9 @@ static int perform_test(const char *title, #define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED #define ROLE2 PJ_ICE_SESS_ROLE_CONTROLLING -int ice_test(void) +int ice_test(void *p) { + unsigned test_id = (unsigned)(long)p; app_sess_t app_sess; unsigned i; int rc; @@ -892,7 +914,7 @@ int ice_test(void) unsigned server_flag; struct test_cfg ua1; struct test_cfg ua2; - } sess_cfg[] = + } sess_cfg[ICE_TEST_ARRAY_COUNT] = { /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */ { @@ -933,12 +955,14 @@ int ice_test(void) }, }; + assert(test_id < ICE_TEST_START_ARRAY+PJ_ARRAY_SIZE(sess_cfg)); + rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) return -7; /* Simple test first with host candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_HOST) { struct sess_cfg_t cfg = { "Basic with host candidates", @@ -963,7 +987,7 @@ int ice_test(void) } /* Simple test first with srflx candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_SRFLX) { struct sess_cfg_t cfg = { "Basic with srflx candidates", @@ -989,7 +1013,7 @@ int ice_test(void) } /* Simple test with relay candidate */ - if (1) { + if (test_id==ICE_TEST_BASIC_RELAY) { struct sess_cfg_t cfg = { "Basic with relay candidates", @@ -1015,7 +1039,7 @@ int ice_test(void) } /* Failure test with STUN resolution */ - if (1) { + if (test_id==ICE_TEST_STUN_RES_FAIL) { struct sess_cfg_t cfg = { "STUN resolution failure", @@ -1041,7 +1065,7 @@ int ice_test(void) } /* Failure test with TURN resolution */ - if (1) { + if (test_id==ICE_TEST_TURN_ALLOC_FAIL) { struct sess_cfg_t cfg = { "TURN allocation failure", @@ -1068,7 +1092,7 @@ int ice_test(void) /* STUN failure, testing TURN deallocation */ - if (1) { + if (test_id==ICE_TEST_STUN_FAIL_TURN_DEALLOC) { struct sess_cfg_t cfg = { "STUN failure, testing TURN deallocation", @@ -1094,8 +1118,8 @@ int ice_test(void) } rc = 0; - /* Iterate each test item */ - for (i=0; i= ICE_TEST_START_ARRAY) { + i = test_id - ICE_TEST_START_ARRAY; struct sess_cfg_t *cfg = &sess_cfg[i]; unsigned delay[] = { 50, 2000 }; unsigned d; diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c index 7bba5a688e..4fb996250d 100644 --- a/pjnath/src/pjnath-test/server.c +++ b/pjnath/src/pjnath-test/server.c @@ -86,7 +86,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, { pj_pool_t *pool; test_server *test_srv; - pj_sockaddr hostip; + pj_sockaddr hostip, bound_addr; char strbuf[100]; pj_status_t status = PJ_EINVAL; pj_bool_t use_ipv6 = flags & SERVER_IPV6; @@ -119,13 +119,16 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, if (flags & CREATE_DNS_SERVER) { status = pj_dns_server_create(stun_cfg->pf, test_srv->stun_cfg->ioqueue, - GET_AF(use_ipv6), DNS_SERVER_PORT, + GET_AF(use_ipv6), 0, 0, &test_srv->dns_server); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + pj_dns_server_get_addr(test_srv->dns_server, &bound_addr); + test_srv->dns_server_port = pj_sockaddr_get_port(&bound_addr); + /* Add DNS A record for the domain, for fallback */ if (flags & CREATE_A_RECORD_FOR_DOMAIN) { pj_dns_parsed_rr rr; @@ -154,17 +157,18 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom; pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, - NULL, STUN_SERVER_PORT); + NULL, 0); status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &stun_sock_cb, test_srv, - &test_srv->stun_sock, NULL); + &test_srv->stun_sock, &bound_addr); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + test_srv->stun_server_port = pj_sockaddr_get_port(&bound_addr); status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool, MAX_STUN_PKT, 0); if (status != PJ_SUCCESS) { @@ -187,7 +191,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, "stun.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, - STUN_SERVER_PORT, &target); + test_srv->stun_server_port, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; @@ -208,7 +212,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_sockaddr bound_addr; pj_turn_tp_type tp_type = get_turn_tp_type(flags); - pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, NULL, TURN_SERVER_PORT); + pj_sockaddr_init(GET_AF(use_ipv6), &bound_addr, NULL, 0); if (tp_type == PJ_TURN_TP_UDP) { pj_activesock_cb turn_sock_cb; @@ -219,18 +223,21 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, status = pj_activesock_create_udp(pool, &bound_addr, NULL, test_srv->stun_cfg->ioqueue, &turn_sock_cb, test_srv, - &test_srv->turn_sock, NULL); + &test_srv->turn_sock, + &bound_addr); if (status != PJ_SUCCESS) { destroy_test_server(test_srv); RETURN_ERROR(status); } + test_srv->turn_server_port = pj_sockaddr_get_port(&bound_addr); status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool, MAX_STUN_PKT, 0); } else if (tp_type == PJ_TURN_TP_TCP) { pj_sock_t sock_fd; pj_activesock_cb turn_sock_cb; + int name_len = sizeof(bound_addr); pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_accept_complete2 = &turn_tcp_on_accept_complete; @@ -271,6 +278,11 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, status = pj_activesock_start_accept(test_srv->turn_sock, pool); + + if (pj_sock_getsockname(sock_fd, &bound_addr, &name_len)==PJ_SUCCESS) { + test_srv->turn_server_port = pj_sockaddr_get_port(&bound_addr); + } + } #if USE_TLS else if (tp_type == PJ_TURN_TP_TLS) { @@ -281,6 +293,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_str_t cert_file = pj_str(CERT_FILE); pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE); pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS); + pj_ssl_sock_info ssock_info; pj_ssl_sock_param_default(&ssl_param); ssl_param.cb.on_accept_complete2 = &turn_tls_on_accept_complete2; @@ -310,8 +323,16 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_ssl_sock_close(ssock_serv); } test_srv->ssl_srv_sock = ssock_serv; + status = pj_ssl_sock_start_accept(ssock_serv, pool, &bound_addr, pj_sockaddr_get_len(&bound_addr)); + + /* Can only get local address after start_accept() */ + if (pj_ssl_sock_get_info(ssock_serv, &ssock_info)==PJ_SUCCESS) { + test_srv->turn_server_port = + pj_sockaddr_get_port(&ssock_info.local_addr); + } + } #endif if (status != PJ_SUCCESS) { @@ -346,7 +367,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, "turn.%s", domain); pj_strdup2(pool, &target, strbuf); pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, - TURN_SERVER_PORT, &target); + test_srv->turn_server_port, &target); pj_dns_server_add_rec(test_srv->dns_server, 1, &rr); res_name = target; diff --git a/pjnath/src/pjnath-test/server.h b/pjnath/src/pjnath-test/server.h index bb9a9baeb3..4cfb52c944 100644 --- a/pjnath/src/pjnath-test/server.h +++ b/pjnath/src/pjnath-test/server.h @@ -23,10 +23,6 @@ #include #include -#define DNS_SERVER_PORT 55533 -#define STUN_SERVER_PORT 33478 -#define TURN_SERVER_PORT 33479 - #define TURN_USERNAME "auser" #define TURN_PASSWD "apass" @@ -81,10 +77,13 @@ struct test_server pj_ioqueue_op_key_t send_key; pj_dns_server *dns_server; + pj_uint16_t dns_server_port; pj_activesock_t *stun_sock; + pj_uint16_t stun_server_port; pj_activesock_t *turn_sock; + pj_uint16_t turn_server_port; pj_ssl_sock_t *ssl_srv_sock; diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index c97b40939a..277028ac80 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -246,19 +246,22 @@ static int test_inner(int argc, char *argv[]) #endif #if INCLUDE_ICE_TEST - UT_ADD_TEST(&test_app.ut_app, ice_test, PJ_TEST_EXCLUSIVE); + for (i=0; itest_srv->dns_server_port; status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port); if (status != PJ_SUCCESS) { @@ -178,13 +179,15 @@ static int create_test_session(pj_stun_config *stun_cfg, if (cfg->client.enable_dns_srv) { /* Use DNS SRV to resolve server, may fallback to DNS A */ pj_str_t domain = pj_str(SRV_DOMAIN); - status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT, + status = pj_turn_sock_alloc(sess->turn_sock, &domain, + sess->test_srv->turn_server_port, sess->resolver, &cred, &alloc_param); } else { /* Explicitly specify server address */ pj_str_t host = use_ipv6?pj_str("::1") : pj_str("127.0.0.1"); - status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT, + status = pj_turn_sock_alloc(sess->turn_sock, &host, + sess->test_srv->turn_server_port, NULL, &cred, &alloc_param); } @@ -531,41 +534,45 @@ static int destroy_test(pj_stun_config *stun_cfg, ///////////////////////////////////////////////////////////////////// -int turn_sock_test(void) +int turn_sock_test(void *p) { + unsigned n = (int)(long)p; app_sess_t app_sess; - int n, i, rc = 0; + pj_turn_tp_type tp_type = PJ_TURN_TP_UDP; + int i, rc = 0; + + if ((n == 2) && !USE_TLS) + return 0; + + switch (n) { + case 0: + tp_type = PJ_TURN_TP_UDP; + break; + case 1: + tp_type = PJ_TURN_TP_TCP; + break; + case 2: + tp_type = PJ_TURN_TP_TLS; + break; + default: + PJ_TEST_LTE(n, 2, NULL, return -1); + } rc = create_stun_config(&app_sess); if (rc != PJ_SUCCESS) { return -2; } - for (n = 0; n <= 2; ++n) { - pj_turn_tp_type tp_type = PJ_TURN_TP_UDP; - - if ((n == 2) && !USE_TLS) - break; + rc = state_progression_test(&app_sess.stun_cfg, USE_IPV6, tp_type); + if (rc != 0) + goto on_return; - switch (n) { - case 1: - tp_type = PJ_TURN_TP_TCP; - break; - case 2: - tp_type = PJ_TURN_TP_TLS; - } - - rc = state_progression_test(&app_sess.stun_cfg, USE_IPV6, tp_type); - if (rc != 0) - goto on_return; - - for (i=0; i<=1; ++i) { - int j; - for (j=0; j<=1; ++j) { - rc = destroy_test(&app_sess.stun_cfg, i, j, USE_IPV6, tp_type); - if (rc != 0) - goto on_return; - } + for (i=0; i<=1; ++i) { + int j; + for (j=0; j<=1; ++j) { + rc = destroy_test(&app_sess.stun_cfg, i, j, USE_IPV6, tp_type); + if (rc != 0) + goto on_return; } } diff --git a/unittest.md b/unittest.md index 69953f4976..277f087359 100644 --- a/unittest.md +++ b/unittest.md @@ -163,8 +163,21 @@ In PJLIB-UTIL-TEST, there is almost 2x speed up from 5m52.500s to 3m3.615s with #### Speed result: PJNATH-TEST -Original: 45m42.275s -10 threads: 15m10.862s +The original version took 45m42.275s to complete, excluding `ice_conc_test()` which apparently is not called (this test alone takes 123s to complete). Parallelizing the test requires large modifications as follows: + +- remove global `mem` pool factory altogether, since the tests validate the memory leak in the pool factory, therefore having a single pool factory shared by multiple threads will not work +- remove static constants (such as server port number) in `server.c` so that server can be instantiated multiple times simultaneously. +- split tests with multiple configurations (such as `ice_test`, `turn_sock_test`, `concur_test`) into individual test for each configuration, making them parallelable. + +As the result, there are 70 test items in pjnath.test. The test durations are as follows: + +- 3 worker threads: 11m51.526s +- 4 worker threads: 9m44.503s +- 10 worker threads: 4m31.195s +- 40 worker threads: 2m9.205s + +Hence with 10 worker threads, we can save 40 minutes of test time! + ### 3. Other developments From b8594fe4b5f429e730b1421b851547b1ba005da9 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 22 Jun 2024 09:19:33 +0700 Subject: [PATCH 16/79] Ported pjmedia-test to use unit-test --- pjlib-util/include/pjlib-util/dns_server.h | 3 +- pjlib/src/pj/log.c | 9 ++- pjlib/src/pj/unittest.c | 3 +- pjmedia/src/test/codec_vectors.c | 6 +- pjmedia/src/test/jbuf_test.c | 82 ++++++++++++---------- pjmedia/src/test/main.c | 57 ++++++++++----- pjmedia/src/test/mips_test.c | 8 +-- pjmedia/src/test/test.c | 75 +++++++++++--------- pjmedia/src/test/test.h | 14 +++- pjnath/src/pjnath-test/test.c | 13 +--- pjnath/src/pjnath-test/test.h | 1 - unittest.md | 44 +++++++----- 12 files changed, 183 insertions(+), 132 deletions(-) diff --git a/pjlib-util/include/pjlib-util/dns_server.h b/pjlib-util/include/pjlib-util/dns_server.h index a56b68c60e..f1a8dfda08 100644 --- a/pjlib-util/include/pjlib-util/dns_server.h +++ b/pjlib-util/include/pjlib-util/dns_server.h @@ -50,7 +50,8 @@ typedef struct pj_dns_server pj_dns_server; * registered to. * @param af Address family of the server socket (valid values * are pj_AF_INET() for IPv4 and pj_AF_INET6() for IPv6). - * @param port The UDP port to listen. + * @param port The UDP port to listen. Specify zero to bind to any + * port. * @param flags Flags, currently must be zero. * @param p_srv Pointer to receive the DNS server instance. * diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c index db118118a0..a27cc78e69 100644 --- a/pjlib/src/pj/log.c +++ b/pjlib/src/pj/log.c @@ -468,7 +468,14 @@ PJ_DEF(void) pj_log( const char *sender, int level, print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, ""); } - if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) { + /* blp: + * The old "print_len < 1" check causes print_len to be very large when + * user specifies empty string, e.g. PJ_LOG(3,(THIS_FILE, "%s", "")). + * This breaks unit-test logging because the resulting log takes up all + * the unit-test logging buffer. + */ + //if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) { + if (print_len < 0 || print_len >= (int)(sizeof(log_buffer)-len)) { print_len = sizeof(log_buffer) - len - 1; } len = len + print_len; diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 2ff9e033ae..f16dd522e8 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -212,6 +212,7 @@ PJ_DEF(void) pj_test_display_stat(const pj_test_stat *stat, (int)stat->duration.msec)); } +/* Get name with argument info if any, e.g. "ice_test (arg: 1)" */ static const char *get_test_case_info(const pj_test_case *tc, char *buf, unsigned size) { @@ -275,7 +276,7 @@ PJ_DEF(void) pj_test_display_log_messages(const pj_test_suite *suite, title = NULL; } - PJ_LOG(3,(THIS_FILE, "Logs for %s [rc:%d]:", + PJ_LOG(3,(THIS_FILE, "------------ Logs for %s [rc:%d]: ------------", get_test_case_info(tc, tcname, sizeof(tcname)), tc->result)); diff --git a/pjmedia/src/test/codec_vectors.c b/pjmedia/src/test/codec_vectors.c index f561354d2d..f5d121f950 100644 --- a/pjmedia/src/test/codec_vectors.c +++ b/pjmedia/src/test/codec_vectors.c @@ -579,7 +579,7 @@ int codec_test_vectors(void) unsigned i; pj_status_t status; - status = pjmedia_endpt_create(mem, NULL, 0, &endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &endpt); if (status != PJ_SUCCESS) return -5; @@ -588,7 +588,7 @@ int codec_test_vectors(void) #if PJMEDIA_HAS_G7221_CODEC status = pjmedia_codec_g7221_init(endpt); if (status != PJ_SUCCESS) { - pjmedia_endpt_destroy(endpt); + pjmedia_endpt_destroy2(endpt); return -7; } @@ -635,7 +635,7 @@ int codec_test_vectors(void) if (pj_file_exists(TMP_OUT)) pj_file_delete(TMP_OUT); - pjmedia_endpt_destroy(endpt); + pjmedia_endpt_destroy2(endpt); return rc_final; } diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c index 82aabf79ad..c8c08e891a 100644 --- a/pjmedia/src/test/jbuf_test.c +++ b/pjmedia/src/test/jbuf_test.c @@ -26,10 +26,12 @@ #define JB_MAX_PREFETCH 10 #define JB_PTIME 20 #define JB_BUF_SIZE 50 +#define THIS_FILE "jbuf_test.c" //#define REPORT //#define PRINT_COMMENT + typedef struct test_param_t { pj_bool_t adaptive; unsigned init_prefetch; @@ -79,10 +81,16 @@ static pj_bool_t parse_test_headers(char *line, test_param_t *param, cond->lost = cond_val; } else if (*p == '=') { + char *newline_pos; + /* Test title. */ ++p; while (*p && isspace(*p)) ++p; - printf("%s", p); + + if ((newline_pos=strchr(p, '\n')) != NULL) + *newline_pos = '\0'; + + PJ_LOG(3, (THIS_FILE, "--- %s ---", p)); } else if (*p == '#') { /* Ignore comment line. */ @@ -121,15 +129,15 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, case 'L': /* Lost */ *last_seq = *seq; ++*seq; - printf("Lost\n"); + PJ_LOG(3,(THIS_FILE, "Lost")); break; case 'R': /* Sequence restarts */ *seq = 1; - printf("Sequence restarting, from %u to %u\n", *last_seq, *seq); + PJ_LOG(3,(THIS_FILE, "Sequence restarting, from %u to %u", *last_seq, *seq)); break; case 'J': /* Sequence jumps */ (*seq) += 20; - printf("Sequence jumping, from %u to %u\n", *last_seq, *seq); + PJ_LOG(3,(THIS_FILE, "Sequence jumping, from %u to %u", *last_seq, *seq)); break; case 'D': /* Frame duplicated */ pjmedia_jbuf_put_frame(jb, (void*)frame, 1, *seq - 1); @@ -142,7 +150,7 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, break; default: print_state = PJ_FALSE; - printf("Unknown test data '%c'\n", data); + PJ_LOG(3,(THIS_FILE, "Unknown test data '%c'", data)); break; } @@ -154,8 +162,8 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, pjmedia_jb_state state; pjmedia_jbuf_get_state(jb, &state); - printf("seq=%d\t%c\tsize=%d\tprefetch=%d\n", - *last_seq, toupper(data), state.size, state.prefetch); + PJ_LOG(3,(THIS_FILE, ("seq=%d\t%c\tsize=%d\tprefetch=%d", + *last_seq, toupper(data), state.size, state.prefetch)); } #else PJ_UNUSED_ARG(print_state); /* Warning about variable set but unused */ @@ -164,7 +172,7 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, return PJ_TRUE; } -int jbuf_main(void) +int jbuf_test(void) { FILE *input; pj_bool_t data_eof = PJ_FALSE; @@ -194,10 +202,8 @@ int jbuf_main(void) } /* Failed to open test data file. */ - if (input == NULL) { - printf("Failed to open test data file, Jbtest.dat\n"); - return -1; - } + PJ_TEST_NOT_NULL(input, "failed to open test data file, Jbtest.dat", + return -1); old_log_level = pj_log_get_level(); pj_log_set_level(5); @@ -226,7 +232,7 @@ int jbuf_main(void) cond.empty = -1; cond.lost = -1; - printf("\n\n"); + PJ_LOG(3,(THIS_FILE, "%s", "")); /* Parse test session title, param, and conditions */ do { @@ -237,8 +243,6 @@ int jbuf_main(void) if (p == NULL) break; - //printf("======================================================\n"); - /* Initialize test session */ pool = pj_pool_create(mem, "JBPOOL", 256*16, 256*16, NULL); pjmedia_jbuf_create(pool, &jb_name, 1, JB_PTIME, JB_BUF_SIZE, &jb); @@ -255,9 +259,9 @@ int jbuf_main(void) #ifdef REPORT pjmedia_jbuf_get_state(jb, &state); - printf("Initial\tsize=%d\tprefetch=%d\tmin.pftch=%d\tmax.pftch=%d\n", - state.size, state.prefetch, state.min_prefetch, - state.max_prefetch); + PJ_LOG(3,(THIS_FILE, (("Initial\tsize=%d\tprefetch=%d\tmin.pftch=%d\tmax.pftch=%d", + state.size, state.prefetch, state.min_prefetch, + state.max_prefetch)); #endif @@ -285,7 +289,7 @@ int jbuf_main(void) if (c == '#') { #ifdef PRINT_COMMENT while (*p && isspace(*p)) ++p; - if (*p) printf("..%s", p); + if (*p) printf(("..%s", p)); #endif *p = 0; continue; @@ -298,44 +302,44 @@ int jbuf_main(void) /* Print JB states */ pjmedia_jbuf_get_state(jb, &state); - printf("------------------------------------------------------\n"); - printf("Summary:\n"); - printf(" size=%d prefetch=%d\n", state.size, state.prefetch); - printf(" delay (min/max/avg/dev)=%d/%d/%d/%d ms\n", - state.min_delay, state.max_delay, state.avg_delay, - state.dev_delay); - printf(" lost=%d discard=%d empty=%d burst(avg)=%d\n", - state.lost, state.discard, state.empty, state.avg_burst); + //PJ_LOG(3,(THIS_FILE, "------------------------------------------------------")); + PJ_LOG(3,(THIS_FILE, "Summary:")); + PJ_LOG(3,(THIS_FILE, " size=%d prefetch=%d", state.size, state.prefetch)); + PJ_LOG(3,(THIS_FILE, " delay (min/max/avg/dev)=%d/%d/%d/%d ms", + state.min_delay, state.max_delay, state.avg_delay, + state.dev_delay)); + PJ_LOG(3,(THIS_FILE, " lost=%d discard=%d empty=%d burst(avg)=%d", + state.lost, state.discard, state.empty, state.avg_burst)); /* Evaluate test session */ if (cond.burst >= 0 && (int)state.avg_burst > cond.burst) { - printf("! 'Burst' should be %d, it is %d\n", - cond.burst, state.avg_burst); + PJ_LOG(1,(THIS_FILE, "! 'Burst' should be %d, it is %d", + cond.burst, state.avg_burst)); rc |= 1; } if (cond.delay >= 0 && (int)state.avg_delay/JB_PTIME > cond.delay) { - printf("! 'Delay' should be %d, it is %d\n", - cond.delay, state.avg_delay/JB_PTIME); + PJ_LOG(1,(THIS_FILE, "! 'Delay' should be %d, it is %d", + cond.delay, state.avg_delay/JB_PTIME)); rc |= 2; } if (cond.delay_min >= 0 && (int)state.min_delay/JB_PTIME > cond.delay_min) { - printf("! 'Minimum delay' should be %d, it is %d\n", - cond.delay_min, state.min_delay/JB_PTIME); + PJ_LOG(1,(THIS_FILE, "! 'Minimum delay' should be %d, it is %d", + cond.delay_min, state.min_delay/JB_PTIME)); rc |= 32; } if (cond.discard >= 0 && (int)state.discard > cond.discard) { - printf("! 'Discard' should be %d, it is %d\n", - cond.discard, state.discard); + PJ_LOG(3,(THIS_FILE, "! 'Discard' should be %d, it is %d", + cond.discard, state.discard)); rc |= 4; } if (cond.empty >= 0 && (int)state.empty > cond.empty) { - printf("! 'Empty' should be %d, it is %d\n", - cond.empty, state.empty); + PJ_LOG(3,(THIS_FILE, "! 'Empty' should be %d, it is %d", + cond.empty, state.empty)); rc |= 8; } if (cond.lost >= 0 && (int)state.lost > cond.lost) { - printf("! 'Lost' should be %d, it is %d\n", - cond.lost, state.lost); + PJ_LOG(3,(THIS_FILE, "! 'Lost' should be %d, it is %d", + cond.lost, state.lost)); rc |= 16; } diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index 77b49700cf..fae4dfec06 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include "test.h" @@ -33,19 +34,19 @@ #if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 -#include +#include #include -#include -#include -#include +#include +#include +#include static void print_stack(int sig) { - void *array[16]; - size_t size; - - size = backtrace(array, 16); - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); + void *array[16]; + size_t size; + + size = backtrace(array, 16); + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); } @@ -59,6 +60,21 @@ static void init_signals(void) #define init_signals() #endif +static void usage() +{ + puts("Usage:"); + puts(" pjmedia-test [OPTION] [test_to_run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h, --help Show this help screen"); + + ut_usage(); + + puts(" -i Ask ENTER before quitting"); + puts(" -n Do not trap signals"); +} + static int main_func(int argc, char *argv[]) { @@ -66,22 +82,25 @@ static int main_func(int argc, char *argv[]) int interractive = 0; int no_trap = 0; - while (argc > 1) { - char *arg = argv[--argc]; + if (pj_argparse_get("-h", &argc, argv) || + pj_argparse_get("--help", &argc, argv)) + { + usage(); + return 0; + } - if (*arg=='-' && *(arg+1)=='i') { - interractive = 1; + ut_app_init0(&test_app.ut_app); - } else if (*arg=='-' && *(arg+1)=='n') { - no_trap = 1; - } - } + interractive = pj_argparse_get("-i", &argc, argv); + no_trap = pj_argparse_get("-n", &argc, argv); + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; if (!no_trap) { init_signals(); } - rc = test_main(); + rc = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index d1518fc41d..9f865a1aea 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -731,7 +731,7 @@ static pj_status_t codec_on_destroy(struct pjmedia_port *this_port) pjmedia_codec_mgr_dealloc_codec(pjmedia_endpt_get_codec_mgr(cp->endpt), cp->codec); cp->codec_deinit(); - pjmedia_endpt_destroy(cp->endpt); + pjmedia_endpt_destroy2(cp->endpt); return PJ_SUCCESS; } @@ -764,7 +764,7 @@ static pjmedia_port* codec_encode_decode( pj_pool_t *pool, cp->base.on_destroy = &codec_on_destroy; cp->codec_deinit = codec_deinit; - status = pjmedia_endpt_create(mem, NULL, 0, &cp->endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &cp->endpt); if (status != PJ_SUCCESS) return NULL; @@ -1699,7 +1699,7 @@ static void stream_port_custom_deinit(struct test_entry *te) pjmedia_stream_destroy(sp->stream); pjmedia_transport_close(sp->transport); sp->codec_deinit(); - pjmedia_endpt_destroy(sp->endpt); + pjmedia_endpt_destroy2(sp->endpt); } @@ -1743,7 +1743,7 @@ static pjmedia_port* create_stream( pj_pool_t *pool, te->custom_deinit = &stream_port_custom_deinit; sp->codec_deinit = codec_deinit; - status = pjmedia_endpt_create(mem, NULL, 0, &sp->endpt); + status = pjmedia_endpt_create2(mem, NULL, 0, &sp->endpt); if (status != PJ_SUCCESS) return NULL; diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c index 5023bdf6f0..6eee9c34ca 100644 --- a/pjmedia/src/test/test.c +++ b/pjmedia/src/test/test.c @@ -20,17 +20,8 @@ #define THIS_FILE "test.c" -#define DO_TEST(test) do { \ - PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; +struct test_app_t test_app; void app_perror(pj_status_t status, const char *msg) @@ -53,15 +44,17 @@ void *dummy() } #endif -int test_main(void) +int test_main(int argc, char *argv[]) { int rc = 0; pj_caching_pool caching_pool; - pj_pool_t *pool; + pj_pool_t *pool = NULL; - pj_init(); + PJ_TEST_SUCCESS(pj_init(), NULL, return 1); pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); - pool = pj_pool_create(&caching_pool.factory, "test", 1000, 512, NULL); + PJ_TEST_NOT_NULL(pool=pj_pool_create(&caching_pool.factory, "test", + 1000, 512, NULL), + NULL, {rc=10; goto on_return;}); pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT); @@ -69,43 +62,59 @@ int test_main(void) mem = &caching_pool.factory; - pjmedia_event_mgr_create(pool, 0, NULL); + PJ_TEST_SUCCESS(pjmedia_event_mgr_create(pool, 0, NULL), + NULL, {rc=30; goto on_return;}); #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) - pjmedia_video_format_mgr_create(pool, 64, 0, NULL); - pjmedia_converter_mgr_create(pool, NULL); - pjmedia_vid_codec_mgr_create(pool, NULL); + PJ_TEST_SUCCESS(pjmedia_video_format_mgr_create(pool, 64, 0, NULL), + NULL, {rc=50; goto on_return;}); + PJ_TEST_SUCCESS(pjmedia_converter_mgr_create(pool, NULL), + NULL, {rc=60; goto on_return;}); + PJ_TEST_SUCCESS(pjmedia_vid_codec_mgr_create(pool, NULL), + NULL, {rc=70; goto on_return;}); #endif -#if HAS_VID_PORT_TEST - DO_TEST(vid_port_test()); -#endif + PJ_TEST_SUCCESS(ut_app_init1(&test_app.ut_app, mem), + NULL, {rc=40; goto on_return;}); -#if HAS_VID_DEV_TEST - DO_TEST(vid_dev_test()); +#if HAS_MIPS_TEST + /* Run in exclusive mode to get the best performance */ + UT_ADD_TEST(&test_app.ut_app, mips_test, PJ_TEST_EXCLUSIVE); #endif #if HAS_VID_CODEC_TEST - DO_TEST(vid_codec_test()); + /* Run in exclusive mode due to device sharing error? */ + UT_ADD_TEST(&test_app.ut_app, vid_codec_test, PJ_TEST_EXCLUSIVE); +#endif + +#if HAS_VID_PORT_TEST + UT_ADD_TEST(&test_app.ut_app, vid_port_test, 0); +#endif + +#if HAS_VID_DEV_TEST + UT_ADD_TEST(&test_app.ut_app, vid_dev_test, 0); #endif #if HAS_SDP_NEG_TEST - DO_TEST(sdp_neg_test()); + UT_ADD_TEST(&test_app.ut_app, sdp_neg_test, 0); #endif //DO_TEST(sdp_test (&caching_pool.factory)); //DO_TEST(rtp_test(&caching_pool.factory)); //DO_TEST(session_test (&caching_pool.factory)); #if HAS_JBUF_TEST - DO_TEST(jbuf_main()); -#endif -#if HAS_MIPS_TEST - DO_TEST(mips_test()); + UT_ADD_TEST(&test_app.ut_app, jbuf_test, 0); #endif #if HAS_CODEC_VECTOR_TEST - DO_TEST(codec_test_vectors()); + UT_ADD_TEST(&test_app.ut_app, codec_test_vectors, 0); #endif - PJ_LOG(3,(THIS_FILE," ")); + if (ut_run_tests(&test_app.ut_app, "pjmedia tests", argc, argv)) { + rc = 99; + } else { + rc = 0; + } + + ut_app_destroy(&test_app.ut_app); on_return: if (rc != 0) { @@ -121,8 +130,8 @@ int test_main(void) #endif pjmedia_event_mgr_destroy(pjmedia_event_mgr_instance()); - - pj_pool_release(pool); + if (pool) + pj_pool_release(pool); pj_caching_pool_destroy(&caching_pool); return rc; diff --git a/pjmedia/src/test/test.h b/pjmedia/src/test/test.h index a4d2b48dcc..848ae97eda 100644 --- a/pjmedia/src/test/test.h +++ b/pjmedia/src/test/test.h @@ -41,7 +41,7 @@ int session_test(void); int rtp_test(void); int sdp_test(void); -int jbuf_main(void); +int jbuf_test(void); int sdp_neg_test(void); int mips_test(void); int codec_test_vectors(void); @@ -52,6 +52,16 @@ int vid_port_test(void); extern pj_pool_factory *mem; void app_perror(pj_status_t status, const char *title); -int test_main(void); +int test_main(int argc, char *argv[]); + +#define UT_MAX_TESTS 80 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; +}; +extern struct test_app_t test_app; + #endif /* __PJMEDIA_TEST_H__ */ diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 277028ac80..15fb33516f 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -22,8 +22,6 @@ #define THIS_FILE "test.c" -struct test_app_t test_app; - void app_perror_dbg(const char *msg, pj_status_t rc, const char *file, int line) { @@ -180,17 +178,8 @@ int check_pjlib_state(pj_stun_config *cfg, } -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (char*)(rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - pj_pool_factory *mem; +struct test_app_t test_app; int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_MICRO_SEC; diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h index 6a40729c5a..60ae0b21b3 100644 --- a/pjnath/src/pjnath-test/test.h +++ b/pjnath/src/pjnath-test/test.h @@ -86,7 +86,6 @@ int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit); struct test_app_t { ut_app_t ut_app; - int param_log_decor; }; extern struct test_app_t test_app; diff --git a/unittest.md b/unittest.md index 277f087359..0b96f34994 100644 --- a/unittest.md +++ b/unittest.md @@ -104,9 +104,11 @@ Below are the features of the unit-test and also other enhancements done in this ### 2. Modifications to test apps -#### Console front-end +#### Common modifications -The main front-end (`main.c`) was modified to be more nice as command line apps, now it can be invoked with arguments: +There is a new utility file in `pjlib/src/pjlib-test/test_util.h` which is shared by all test apps, to parse command line arguments, show usage, register tests, and control the unit testing process. + +The main front-end files (`main.c`) were modified to be more nice as command line apps, now it can be invoked with arguments, which are uniform in all unit-test apps: ``` Usage: @@ -130,20 +132,15 @@ where OPTIONS: -t ucp,tcp Set echo socket type to UDP or TCP ``` -Other test apps have been modified to produce similar look. - - -#### Test body - The main modification in test body (`test.c`) is to use the unit-test framework. -#### Test modifications - -Some tests (mostly in pjlib-test) was modified, replacing manual checks with `PJ_TEST_XXX()` macros. This is done to test the usage of `PJ_TEST_XXX()` macros and to make the test nicer. But since it made the PR very big, I didn't continue the effort. +Some test codes were changed, replacing manual checks with `PJ_TEST_XXX()` macros, mainly to test the usage of these macros and to make the test nicer. But since it made the PR very big, I didn't continue the effort, unless when it was necessary for debugging some problems. Some tests were also split up to make them run in parallel. -#### Speed result: PJLIB-TEST +More specific changes are discussed below. + +#### Changes to PJLIB-TEST In PJLIB-TEST, test time is speed up by up to four times: @@ -157,19 +154,19 @@ In PJLIB-TEST, test time is speed up by up to four times: It looks like the sweet spot is with 3 worker threads. Runing with more than this did not speed up the test considerably because some tests just take a long time to finish (more than 2 minutes). I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner), or because the tests shares global states with each other. -#### Speed result: PJLIB-UTIL-TEST +#### Changes to PJLIB-UTIL-TEST In PJLIB-UTIL-TEST, there is almost 2x speed up from 5m52.500s to 3m3.615s with 1 worker thread (the default). We couldn't speed up more because tests such as `resolver_test()` and `http_client_test()` takes about three minutes to complete and they couldn't be split up due to the use of global states. -#### Speed result: PJNATH-TEST +#### Changes to PJNATH-TEST -The original version took 45m42.275s to complete, excluding `ice_conc_test()` which apparently is not called (this test alone takes 123s to complete). Parallelizing the test requires large modifications as follows: +PJNATH-TEST is the one with biggest modifications. The original version took 45m42.275s to complete, excluding `ice_conc_test()` which apparently is not called (this test alone takes 123s to complete). Parallelizing the test requires large modifications as follows: - remove global `mem` pool factory altogether, since the tests validate the memory leak in the pool factory, therefore having a single pool factory shared by multiple threads will not work -- remove static constants (such as server port number) in `server.c` so that server can be instantiated multiple times simultaneously. +- remove constant server port numbers in `server.c` so that server can be instantiated multiple times simultaneously. - split tests with multiple configurations (such as `ice_test`, `turn_sock_test`, `concur_test`) into individual test for each configuration, making them parallelable. -As the result, there are 70 test items in pjnath.test. The test durations are as follows: +As the result, there are 70 smaller test items in pjnath-test. The test durations are as follows: - 3 worker threads: 11m51.526s - 4 worker threads: 9m44.503s @@ -178,6 +175,14 @@ As the result, there are 70 test items in pjnath.test. The test durations are as Hence with 10 worker threads, we can save 40 minutes of test time! +#### Changes to PJMEDIA-TEST + +PJMEDIA-TEST has the least modifications because it has very few tests. The original duration was 4m18.691s, and has come down a little to 2m8.363s with 1 worker thread. + +Having said that, some minor modifications were done: + +- replace `pjmedia_endpt_create()` with `pjmedia_endpt_create2()` (similarly `..destroy()` with `..destroy2()` in `mips_test()` and `codec_test_vectors()`, to avoid inadvertently initializing `pjmedia_aud_subsys`. +- replace `printf` with log in jbuf test to make the output tidy, and renamed `jbuf_main` function name to `jbuf_test`. ### 3. Other developments @@ -189,6 +194,13 @@ This is header only feature to parse command line options. We have `pj_getopt()` The `fifobuf` feature has been there for the longest time (it was part of pjlib first ever commit) and finally has got some use. +#### Minor fixes in `pj/log.c` + +Fixed bug with writing empty string i.e. `PJ_LOG(3,(THIS_FILE, "%s", ""))` causing message length to be set to the size of the buffer (i.e. too large). This causes the buffer to take all the available space in unit-testing log buffer. + +#### Minor enhancements in `pjlib-util/dns_server.[hc]` + +Add `pj_dns_server_get_addr()` API to allow app to get the bound port when null port is specified for the server. ### 4. Known issues and considerations From af408ea1a63c2724b037d9cd6f9fa40d7535267d Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 26 Jun 2024 06:23:42 +0700 Subject: [PATCH 17/79] Porting of pjsip test to unit testing framework. Not much of speed improvements due to exclusive tests --- pjlib/include/pj/unittest.h | 9 +- pjlib/src/pj/unittest.c | 87 +++----- pjlib/src/pjlib-test/test_util.h | 9 +- pjmedia/src/test/main.c | 2 +- pjsip/src/test/inv_offer_answer_test.c | 57 +++-- pjsip/src/test/main.c | 99 ++++----- pjsip/src/test/test.c | 285 +++++++------------------ pjsip/src/test/test.h | 23 +- pjsip/src/test/transport_test.c | 26 +-- pjsip/src/test/transport_udp_test.c | 90 ++++---- pjsip/src/test/tsx_basic_test.c | 5 +- pjsip/src/test/tsx_bench.c | 6 +- pjsip/src/test/tsx_uac_test.c | 72 +++---- pjsip/src/test/tsx_uas_test.c | 5 +- pjsip/src/test/txdata_test.c | 4 +- unittest.md | 19 +- 16 files changed, 341 insertions(+), 457 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 8d8669d52b..5f77d1036f 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -477,10 +477,17 @@ typedef struct pj_test_runner_param pj_bool_t stop_on_error; /** Number of worker threads. Set to zero to disable parallel testings. - * Only applicable to test text runner. + * Only applicable to test text runner. Default is 1 if multithreading + * is available. */ unsigned nthreads; + /** + * 0: only display test name and result after test completion (default) + * 1: display test name test when starting and finishing a test + */ + unsigned verbosity; + } pj_test_runner_param; diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index f16dd522e8..7751262551 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -26,29 +26,9 @@ #define INVALID_TLS_ID -1 static long tls_id = INVALID_TLS_ID; -static pj_test_case *tc_main_thread; -#define TRACE 0 -#if TRACE -# define TC_TRACE(tc__, msg__) \ - {\ - pj_time_val tv = pj_elapsed_time(&tc__->runner->suite->start_time, \ - &tc__->start_time); \ - printf("%02ld:%02ld %s %s\n", tv.sec/60, tv.sec%60, \ - tc__->obj_name, msg__); \ - } -# define RUNNER_TRACE(runner__, msg__) \ - { \ - pj_timestamp now; \ - pj_time_val tv; \ - pj_get_timestamp(&now); \ - tv = pj_elapsed_time(&((pj_test_runner*)runner__)->suite->start_time, &now); \ - printf("%02ld:%02ld %s\n", tv.sec/60, tv.sec%60, msg__); \ - } -#else -# define TC_TRACE(tc__, msg__) -# define RUNNER_TRACE(runner__, msg__) -#endif +/* When basic runner is used, current test is saved in this global var */ +static pj_test_case *tc_main_thread; /* Forward decls. */ static void unittest_log_callback(int level, const char *data, int len); @@ -442,14 +422,23 @@ static int get_completion_line( const pj_test_case *tc, const char *end_line, * be used by the basic runner, which has no threads (=no TLS), * no fifobuf, no pool, or by multiple threads. */ -static void run_test_case(pj_test_runner *runner, pj_test_case *tc) +static void run_test_case(pj_test_runner *runner, int tid, pj_test_case *tc) { + char tcname[64]; + + if (runner->prm.verbosity >= 1) { + const char *exclusivity = (tc->flags & PJ_TEST_EXCLUSIVE) ? + " (exclusive)" : ""; + get_test_case_info(tc, tcname, sizeof(tcname)); + PJ_LOG(3,(THIS_FILE, "Thread %d starts running %s%s", + tid, tcname, exclusivity)); + } + /* Set the test case being worked on by this thread */ set_current_test_case(tc); tc->runner = runner; pj_get_timestamp(&tc->start_time); - TC_TRACE(tc, "starting"); /* Call the test case's function */ if (tc->flags & PJ_TEST_FUNC_NO_ARG) { @@ -467,12 +456,16 @@ static void run_test_case(pj_test_runner *runner, pj_test_case *tc) if (tc->result && runner->prm.stop_on_error) runner->stopping = PJ_TRUE; - TC_TRACE(tc, "done"); pj_get_timestamp(&tc->end_time); runner->on_test_complete(runner, tc); /* Reset the test case being worked on by this thread */ set_current_test_case(NULL); + + if (runner->prm.verbosity >= 1) { + PJ_LOG(3,(THIS_FILE, "Thread %d done running %s (rc: %d)", + tid, tcname, tc->result)); + } } static pj_test_case *get_first_running(pj_test_case *tests) @@ -509,7 +502,7 @@ static void basic_runner_main(pj_test_runner *runner) tc != &runner->suite->tests && !runner->stopping; tc = tc->next) { - run_test_case(runner, tc); + run_test_case(runner, 0, tc); } } @@ -632,16 +625,18 @@ static pj_status_t text_runner_get_next_test_case(text_runner_t *runner, return status; } +typedef struct thread_param_t +{ + text_runner_t *runner; + unsigned tid; +} thread_param_t; + /* Thread loop */ static int text_runner_thread_proc(void *arg) { - #if TRACE - static int global_tid = 0; - int tid = global_tid++; - char tmp[80]; - #endif - - text_runner_t *runner = (text_runner_t*)arg; + thread_param_t *prm = (thread_param_t*)arg; + text_runner_t *runner = prm->runner; + unsigned tid = prm->tid; for (;;) { pj_test_case *tc; @@ -649,33 +644,17 @@ static int text_runner_thread_proc(void *arg) status = text_runner_get_next_test_case(runner, &tc); if (status==PJ_SUCCESS) { - #if TRACE - snprintf(tmp, sizeof(tmp), "thread %d running %s", tid, tc->obj_name); - RUNNER_TRACE(runner, tmp); - #endif - - run_test_case(&runner->base, tc); - - #if TRACE - snprintf(tmp, sizeof(tmp), "thread %d done running %s", tid, tc->obj_name); - RUNNER_TRACE(runner, tmp); - #endif + run_test_case(&runner->base, tid, tc); } else if (status==PJ_EPENDING) { /* Yeah sleep, but the "correct" solution is probably an order of * magnitute more complicated, so this is good I think. */ - #if TRACE - snprintf(tmp, sizeof(tmp), "thread %d waiting", tid); - RUNNER_TRACE(runner, tmp); - #endif - pj_thread_sleep(PJ_TEST_THREAD_WAIT_MSEC); } else { break; } } - RUNNER_TRACE(runner, "thread exiting"); return 0; } @@ -683,6 +662,7 @@ static int text_runner_thread_proc(void *arg) static void text_runner_main(pj_test_runner *base) { text_runner_t *runner = (text_runner_t*)base; + thread_param_t tprm = { runner, 0 }; unsigned i; for (i=0; iprm.nthreads; ++i) { @@ -690,7 +670,7 @@ static void text_runner_main(pj_test_runner *base) } /* The main thread behaves like another worker thread */ - text_runner_thread_proc(base); + text_runner_thread_proc(&tprm); for (i=0; iprm.nthreads; ++i) { pj_thread_join(runner->threads[i]); @@ -756,8 +736,11 @@ PJ_DEF(pj_status_t) pj_test_create_text_runner( runner->threads = (pj_thread_t**) pj_pool_calloc(pool, prm->nthreads, sizeof(pj_thread_t*)); for (i=0; inthreads; ++i) { + thread_param_t *tprm = PJ_POOL_ZALLOC_T(pool, thread_param_t); + tprm->runner = runner; + tprm->tid = i+1; status = pj_thread_create(pool, "unittest%p", - text_runner_thread_proc, runner, + text_runner_thread_proc, tprm, 0, PJ_THREAD_SUSPENDED, &runner->threads[i]); if (status != PJ_SUCCESS) diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 621fbfa771..251b9c5133 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -31,6 +31,7 @@ typedef struct ut_app_t int prm_list_test; pj_bool_t prm_stop_on_error; unsigned flags; + unsigned verbosity; pj_pool_t *pool; pj_test_suite suite; @@ -75,7 +76,7 @@ typedef int (*ut_func)(void*); /* This is for adding test func that HAS arg */ #define UT_ADD_TEST1(ut_app, test_func, arg, flags) \ - ut_add_test(ut_app, test_func, arg, #test_func, flags, argc, argv) + ut_add_test(ut_app, (ut_func)test_func, arg, #test_func, flags, argc, argv) /* Check if a test is specified/requested in cmdline */ @@ -178,6 +179,7 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, runner_prm.stop_on_error = ut_app->prm_stop_on_error; if (ut_app->prm_nthreads >= 0) runner_prm.nthreads = ut_app->prm_nthreads; + runner_prm.verbosity = ut_app->verbosity; status = pj_test_create_text_runner(ut_app->pool, &runner_prm, &runner); PJ_TEST_SUCCESS(status, "error creating text runner", return status); @@ -204,6 +206,7 @@ static void ut_usage() printf(" -w N Set N worker threads (0: disable. Default: %d)\n", PJ_HAS_THREADS); puts(" -L, --list List the tests and exit"); puts(" --stop-err Stop testing on error"); + puts(" -v, --verbose Show info when starting/stopping tests"); } @@ -239,6 +242,10 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) puts("Error: invalid/missing value for -w option"); return PJ_EINVAL; } + + ut_app->verbosity = pj_argparse_get("-v", argc, argv) || + pj_argparse_get("--verbose", argc, argv); + return PJ_SUCCESS; } diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index fae4dfec06..92fb4f4cb7 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -63,7 +63,7 @@ static void init_signals(void) static void usage() { puts("Usage:"); - puts(" pjmedia-test [OPTION] [test_to_run] [..]"); + puts(" pjmedia-test [OPTIONS] [test_to_run] [test to run] [..]"); puts(""); puts("where OPTIONS:"); puts(""); diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c index 2fc0d22c7e..1b05ebc6cc 100644 --- a/pjsip/src/test/inv_offer_answer_test.c +++ b/pjsip/src/test/inv_offer_answer_test.c @@ -24,7 +24,7 @@ #define THIS_FILE "inv_offer_answer_test.c" #define PORT 5068 -#define CONTACT "sip:127.0.0.1:5068" +#define CONTACT "sip:inv_offer_answer_test@127.0.0.1:5068" #define TRACE_(x) PJ_LOG(3,x) static struct oa_sdp_t @@ -189,6 +189,16 @@ static pjmedia_sdp_session *create_sdp(pj_pool_t *pool, const char *body) return sdp; } +/* Check that the message is part of our test. Multiple tests sharing the + * same SIP endpoint may run simultaneously + */ +static pj_bool_t is_our_message(const pjsip_from_hdr *from_hdr) +{ + const pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) + pjsip_uri_get_uri(from_hdr->uri); + return pj_strcmp2(&sip_uri->user, "inv_offer_answer_test")==0; +} + /**************** INVITE SESSION CALLBACKS ******************/ static void on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer) @@ -314,15 +324,17 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) pjmedia_sdp_session *sdp = NULL; pj_str_t uri; pjsip_tx_data *tdata; - pj_status_t status; + + if (!is_our_message(rdata->msg_info.from)) + return PJ_FALSE; /* * Create UAS */ uri = pj_str(CONTACT); - status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, - &uri, &dlg); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, + &uri, &dlg), + NULL, { pj_assert(0); return PJ_FALSE; }); if (inv_test.param.oa[0] == OFFERER_UAC) sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].answer); @@ -331,8 +343,10 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) else pj_assert(!"Invalid offerer type"); - status = pjsip_inv_create_uas(dlg, rdata, sdp, inv_test.param.inv_option, &inv_test.uas); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_create_uas(dlg, rdata, sdp, + inv_test.param.inv_option, + &inv_test.uas), + NULL, { pj_assert(0); return PJ_FALSE; }); pjsip_dlg_dec_lock(dlg); TRACE_((THIS_FILE, " Sending 183 with SDP")); @@ -340,23 +354,23 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) /* * Answer with 183 */ - status = pjsip_inv_initial_answer(inv_test.uas, rdata, 183, NULL, - NULL, &tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_initial_answer(inv_test.uas, rdata, 183, + NULL, NULL, &tdata), + NULL, { pj_assert(0); return PJ_FALSE; }); /* Use multipart body, if configured */ if (sdp && inv_test.param.multipart_body) { - status = pjsip_create_multipart_sdp_body( + PJ_TEST_SUCCESS(pjsip_create_multipart_sdp_body( tdata->pool, pjmedia_sdp_session_clone(tdata->pool, sdp), - &tdata->msg->body); + &tdata->msg->body), + NULL, { pj_assert(0); return PJ_FALSE; }); } - pj_assert(status == PJ_SUCCESS); - status = pjsip_inv_send_msg(inv_test.uas, tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uas, tdata), + NULL, { pj_assert(0); return PJ_FALSE; }); - return (status == PJ_SUCCESS); + return PJ_TRUE; } return PJ_FALSE; @@ -466,8 +480,8 @@ static int perform_test(inv_test_param_t *param) } PJ_ASSERT_RETURN(status==PJ_SUCCESS, -40); - status = pjsip_inv_send_msg(inv_test.uac, tdata); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, -50); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uac, tdata), + NULL, PJ_ASSERT_RETURN(0, -50)); /* * Wait until test completes @@ -498,8 +512,8 @@ static int perform_test(inv_test_param_t *param) pj_assert(status == PJ_SUCCESS); if (tdata) { - status = pjsip_inv_send_msg(inv_test.uas, tdata); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pjsip_inv_send_msg(inv_test.uas, tdata), + NULL, pj_assert(0)); } flush_events(500); @@ -513,6 +527,9 @@ static pj_bool_t log_on_rx_msg(pjsip_rx_data *rdata) pjsip_msg *msg = rdata->msg_info.msg; char info[80]; + if (!is_our_message(rdata->msg_info.from)) + return PJ_FALSE; + if (msg->type == PJSIP_REQUEST_MSG) pj_ansi_snprintf(info, sizeof(info), "%.*s", (int)msg->line.req.method.name.slen, diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index 18dc2e266c..e6dc770595 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -20,19 +20,25 @@ #include #include #include +#include extern const char *system_name; static void usage(void) { - puts("Usage: test-pjsip"); - puts("Options:"); - puts(" -i,--interractive Key input at the end."); - puts(" -h,--help Show this screen"); - puts(" -l,--log-level N Set log level (0-6)"); - puts(" -n,--no-trap Let signals be handled by the OS"); - puts(" -t,--tests testname,... Comma separated list of test to run"); - list_tests(); + puts("Usage:"); + puts(" pjsip-test [OPTIONS] [test_to_run] [test to run] [..]"); + puts(""); + puts("where OPTIONS:"); + puts(""); + puts(" -h,--help Show this screen"); + + ut_usage(); + + puts(" -i,--interractive Key input at the end."); + puts(" -n,--no-trap Let signals be handled by the OS"); + puts(" --log-level N Set log level (0-6)"); + puts(" -s,--system NAME Set system name to NAME"); } #if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 @@ -67,68 +73,47 @@ int main(int argc, char *argv[]) { int interractive = 0; int retval; - char **opt_arg; int no_trap = 0; - char *testlist = NULL; - - PJ_UNUSED_ARG(argc); - - /* Parse arguments. */ - opt_arg = argv+1; - while (*opt_arg) { - if (strcmp(*opt_arg, "-i") == 0 || - strcmp(*opt_arg, "--interractive") == 0) - { - interractive = 1; - } else if (strcmp(*opt_arg, "-n") == 0 || - strcmp(*opt_arg, "--no-trap") == 0) - { - no_trap = 1; - } else if (strcmp(*opt_arg, "-h") == 0 || - strcmp(*opt_arg, "--help") == 0) - { + + if (pj_argparse_get("-h", &argc, argv) || + pj_argparse_get("--help", &argc, argv)) + { + usage(); + return 0; + } + + ut_app_init0(&test_app.ut_app); + + interractive = pj_argparse_get("-i", &argc, argv); + no_trap = pj_argparse_get("-n", &argc, argv); + if (pj_argparse_exists("-s", argv)) { + if (pj_argparse_get_str("-s", &argc, argv, (char**)&system_name)==PJ_EINVAL) { + puts("Error: value required for -s argument"); usage(); return 1; - } else if (strcmp(*opt_arg, "-l") == 0 || - strcmp(*opt_arg, "--log-level") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - log_level = atoi(*opt_arg); - } else if (strcmp(*opt_arg, "-s") == 0 || - strcmp(*opt_arg, "--system") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - system_name = *opt_arg; - } else if (strcmp(*opt_arg, "-t") == 0 || - strcmp(*opt_arg, "--tests") == 0) - { - ++opt_arg; - if (!*opt_arg) { - usage(); - return 1; - } - testlist = *opt_arg; - } else { + } + } else if (pj_argparse_exists("--system", argv)) { + if (pj_argparse_get_str("--system", &argc, argv, (char**)&system_name)==PJ_EINVAL) { + puts("Error: value required for --system argument"); usage(); return 1; } + } - ++opt_arg; + if (pj_argparse_get_int("--log-level", &argc, argv, &log_level)==PJ_EINVAL) { + puts("Error: invalid/missing value for --log-level argument"); + usage(); + return 1; } + if (ut_parse_args(&test_app.ut_app, &argc, argv)) + return 1; + if (!no_trap) { init_signals(); } - retval = test_main(testlist); + retval = test_main(argc, argv); if (interractive) { char s[10]; diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 37c4dabec0..4f75c24e6e 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -25,29 +25,6 @@ #define THIS_FILE "test.c" -#define DO_TEST(test) do { \ - PJ_LOG(3, (THIS_FILE, "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - -#define DO_TSX_TEST(test, param) \ - do { \ - PJ_LOG(3, (THIS_FILE, "Running %s(%s)...", #test, (param)->tp_type)); \ - rc = test(param); \ - PJ_LOG(3, (THIS_FILE, \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - -#if defined(_MSC_VER) || defined(__MINGW32__) -# define strtok_r strtok_s -#endif - pjsip_endpoint *endpt; pj_caching_pool caching_pool; int log_level = 3; @@ -57,85 +34,8 @@ int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_SENDER | static pj_oshandle_t fd_report; const char *system_name = "Unknown"; static char buf[1024]; - -static struct { - const char *name; - int run_test; -} test_list[] = { - { "uri", 0}, - { "msg", 0}, - { "multipart", 0}, - { "txdata", 0}, - { "tsx_bench", 0}, - { "udp", 0}, - { "loop", 0}, - { "tcp", 0}, - { "resolve", 0}, - { "tsx", 0}, - { "tsx_destroy", 0}, - { "inv_oa", 0}, - { "regc", 0}, -}; -enum tests_to_run { - include_uri_test = 0, - include_msg_test, - include_multipart_test, - include_txdata_test, - include_tsx_bench, - include_udp_test, - include_loop_test, - include_tcp_test, - include_resolve_test, - include_tsx_test, - include_tsx_destroy_test, - include_inv_oa_test, - include_regc_test, -}; -static int run_all_tests = 1; - -static pj_status_t select_tests(char *testlist) -{ - char *token; - char *saveptr; - int maxtok = PJ_ARRAY_SIZE(test_list); - int j; - - if (!testlist) { - return PJ_SUCCESS; - } - run_all_tests = 0; - - for (token = strtok_r(testlist, ",", &saveptr); token != NULL; - token = strtok_r(NULL, ",", &saveptr)) { - - int found = 0; - for (j = 0; j < maxtok; j++) { - if (strcmp(token, test_list[j].name) == 0) { - test_list[j].run_test = 1; - found = 1; - } - } - if (!found) { - fprintf(stderr, "Test '%s' is not valid\n", token); - return PJ_ENOTFOUND; - } - } - - return PJ_SUCCESS; -} - -void list_tests(void) { - int maxtok = PJ_ARRAY_SIZE(test_list); - int j; - - fprintf(stderr, "Valid tests:\n"); - - for (j = 0; j < maxtok; j++) { - fprintf(stderr, " %s\n", test_list[j].name); - } -} - -#define SHOULD_RUN_TEST(ix) (run_all_tests || test_list[ix].run_test) +struct test_app_t test_app; +struct tsx_test_param tsx_test[10]; void app_perror(const char *msg, pj_status_t rc) { @@ -309,13 +209,11 @@ static void close_report(void) } -int test_main(char *testlist) +int test_main(int argc, char *argv[]) { pj_status_t rc; const char *filename; unsigned tsx_test_cnt=0; - struct tsx_test_param tsx_test[10]; - pj_status_t status; #if INCLUDE_TSX_TEST unsigned i; pjsip_transport *tp; @@ -325,40 +223,23 @@ int test_main(char *testlist) #endif /* INCLUDE_TSX_TEST */ int line; - rc = select_tests(testlist); - if (rc != PJ_SUCCESS) { - list_tests(); - return rc; - } - pj_log_set_level(log_level); pj_log_set_decor(param_log_decor); - if ((rc=pj_init()) != PJ_SUCCESS) { - app_perror("pj_init", rc); - return rc; - } - - if ((rc=pjlib_util_init()) != PJ_SUCCESS) { - app_perror("pj_init", rc); - return rc; - } - - status = init_report(); - if (status != PJ_SUCCESS) - return status; + PJ_TEST_SUCCESS(pj_init(), NULL, return 10); + PJ_TEST_SUCCESS(pjlib_util_init(), NULL, return 20); + PJ_TEST_SUCCESS(init_report(), NULL, return 30); pj_dump_config(); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, PJSIP_TEST_MEM_SIZE ); - rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt); - if (rc != PJ_SUCCESS) { - app_perror("pjsip_endpt_create", rc); - pj_caching_pool_destroy(&caching_pool); - return rc; - } + PJ_TEST_SUCCESS(pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt), + NULL, { + pj_caching_pool_destroy(&caching_pool); + return 40; + }); PJ_LOG(3,(THIS_FILE," ")); @@ -367,122 +248,95 @@ int test_main(char *testlist) msg_logger_set_enabled(1); /* Start transaction layer module. */ - rc = pjsip_tsx_layer_init_module(endpt); - if (rc != PJ_SUCCESS) { - app_perror(" Error initializing transaction module", rc); - goto on_return; - } + PJ_TEST_SUCCESS(rc = pjsip_tsx_layer_init_module(endpt), + NULL, goto on_return); /* Create loop transport. */ - rc = pjsip_loop_start(endpt, NULL); - if (rc != PJ_SUCCESS) { - app_perror(" error: unable to create datagram loop transport", - rc); - goto on_return; - } + PJ_TEST_SUCCESS(rc = pjsip_loop_start(endpt, NULL), + NULL, goto on_return); + tsx_test[tsx_test_cnt].port = 5060; tsx_test[tsx_test_cnt].tp_type = "loop-dgram"; tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_LOOP_DGRAM; ++tsx_test_cnt; + PJ_TEST_SUCCESS(rc=ut_app_init1(&test_app.ut_app, &caching_pool.factory), + NULL, goto on_return); #if INCLUDE_URI_TEST - if (SHOULD_RUN_TEST(include_uri_test)) { - DO_TEST(uri_test()); - } + UT_ADD_TEST(&test_app.ut_app, uri_test, 0); #endif #if INCLUDE_MSG_TEST - if (SHOULD_RUN_TEST(include_msg_test)) { - DO_TEST(msg_test()); - DO_TEST(msg_err_test()); - } + UT_ADD_TEST(&test_app.ut_app, msg_test, 0); + UT_ADD_TEST(&test_app.ut_app, msg_err_test, 0); #endif #if INCLUDE_MULTIPART_TEST - if (SHOULD_RUN_TEST(include_multipart_test)) { - DO_TEST(multipart_test()); - } + UT_ADD_TEST(&test_app.ut_app, multipart_test, 0); #endif #if INCLUDE_TXDATA_TEST - if (SHOULD_RUN_TEST(include_txdata_test)) { - DO_TEST(txdata_test()); - } + UT_ADD_TEST(&test_app.ut_app, txdata_test, 0); #endif #if INCLUDE_TSX_BENCH - if (SHOULD_RUN_TEST(include_tsx_bench)) { - DO_TEST(tsx_bench()); - } + UT_ADD_TEST(&test_app.ut_app, tsx_bench, 0); +#endif + +#if INCLUDE_RESOLVE_TEST + UT_ADD_TEST(&test_app.ut_app, resolve_test, 0); +#endif + +#if INCLUDE_INV_OA_TEST + UT_ADD_TEST(&test_app.ut_app, inv_offer_answer_test, 0); +#endif + +#if INCLUDE_REGC_TEST + UT_ADD_TEST(&test_app.ut_app, regc_test, 0); #endif + /* Note: put exclusive tests last */ + #if INCLUDE_UDP_TEST - if (SHOULD_RUN_TEST(include_udp_test)) { - DO_TEST(transport_udp_test()); - } + /* Transport tests share same testing codes which are not reentrant */ + UT_ADD_TEST(&test_app.ut_app, transport_udp_test, PJ_TEST_EXCLUSIVE); #endif #if INCLUDE_LOOP_TEST - if (SHOULD_RUN_TEST(include_loop_test)) { - DO_TEST(transport_loop_test()); - } + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, PJ_TEST_EXCLUSIVE); #endif #if INCLUDE_TCP_TEST - if (SHOULD_RUN_TEST(include_tcp_test)) { - DO_TEST(transport_tcp_test()); - } -#endif - -#if INCLUDE_RESOLVE_TEST - if (SHOULD_RUN_TEST(include_resolve_test)) { - DO_TEST(resolve_test()); - } + UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, PJ_TEST_EXCLUSIVE); #endif #if INCLUDE_TSX_TEST - if (SHOULD_RUN_TEST(include_tsx_test)) { - status = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp); - if (status == PJ_SUCCESS) { - tsx_test[tsx_test_cnt].port = tp->local_name.port; - tsx_test[tsx_test_cnt].tp_type = "udp"; - tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP; - ++tsx_test_cnt; - } + PJ_TEST_SUCCESS(rc=pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp), + NULL, goto on_return); + tsx_test[tsx_test_cnt].port = tp->local_name.port; + tsx_test[tsx_test_cnt].tp_type = "udp"; + tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_UDP; + ++tsx_test_cnt; #if PJ_HAS_TCP - status = pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory); - if (status == PJ_SUCCESS) { - tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port; - tsx_test[tsx_test_cnt].tp_type = "tcp"; - tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP; - ++tsx_test_cnt; - } else { - app_perror("Unable to create TCP", status); - rc = -4; - goto on_return; - } -#endif - - for (i = 0; i < tsx_test_cnt; ++i) { - DO_TSX_TEST(tsx_basic_test, &tsx_test[i]); - DO_TSX_TEST(tsx_uac_test, &tsx_test[i]); - DO_TSX_TEST(tsx_uas_test, &tsx_test[i]); - } - } -#endif - -#if INCLUDE_INV_OA_TEST - if (SHOULD_RUN_TEST(include_inv_oa_test)) { - DO_TEST(inv_offer_answer_test()); - } + PJ_TEST_SUCCESS(rc=pjsip_tcp_transport_start(endpt, NULL, 1, &tpfactory), + NULL, goto on_return); + tsx_test[tsx_test_cnt].port = tpfactory->addr_name.port; + tsx_test[tsx_test_cnt].tp_type = "tcp"; + tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_TCP; + ++tsx_test_cnt; #endif -#if INCLUDE_REGC_TEST - if (SHOULD_RUN_TEST(include_regc_test)) { - DO_TEST(regc_test()); + /* each of these tests registers the same module hence must be exclusive */ + for (i = 0; i < tsx_test_cnt; ++i) { + UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, + PJ_TEST_EXCLUSIVE); + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, + PJ_TEST_EXCLUSIVE); + UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, + PJ_TEST_EXCLUSIVE); } #endif @@ -490,16 +344,23 @@ int test_main(char *testlist) * Better be last because it recreates the endpt */ #if INCLUDE_TSX_DESTROY_TEST - if (SHOULD_RUN_TEST(include_tsx_destroy_test)) { - DO_TEST(tsx_destroy_test()); - } + UT_ADD_TEST(&test_app.ut_app, tsx_destroy_test, PJ_TEST_EXCLUSIVE); #endif + if (ut_run_tests(&test_app.ut_app, "pjsip tests", argc, argv)) { + rc = 99; + } else { + rc = 0; + } + + ut_app_destroy(&test_app.ut_app); + on_return: flush_events(500); /* Show additional info on the log. e.g: not released memory pool. */ - pj_log_set_level(4); + // Commented out to make the output tidy :D (blp) + //pj_log_set_level(4); /* Dumping memory pool usage */ /* Field peak_used_size is deprecated by #3897 */ diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 4fd8793c70..11b18ac8a4 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -86,6 +86,7 @@ int transport_loop_test(void); int transport_tcp_test(void); int resolve_test(void); int regc_test(void); +int inv_offer_answer_test(void); struct tsx_test_param { @@ -93,10 +94,11 @@ struct tsx_test_param int port; char *tp_type; }; +extern struct tsx_test_param tsx_test[10]; -int tsx_basic_test(struct tsx_test_param *param); -int tsx_uac_test(struct tsx_test_param *param); -int tsx_uas_test(struct tsx_test_param *param); +int tsx_basic_test(unsigned index); +int tsx_uac_test(unsigned index); +int tsx_uas_test(unsigned index); /* Transport test helpers (transport_test.c). */ int generic_transport_test(pjsip_transport *tp); @@ -110,14 +112,10 @@ int transport_rt_test( pjsip_transport_type_e tp_type, int *pkt_lost); int transport_load_test(char *target_url); -/* Invite session */ -int inv_offer_answer_test(void); - /* Test main entry */ -int test_main(char *testlist); +int test_main(int argc, char *argv[]); /* Test utilities. */ -void list_tests(void); void app_perror(const char *msg, pj_status_t status); int init_msg_logger(void); int msg_logger_set_enabled(pj_bool_t enabled); @@ -131,4 +129,13 @@ void report_sval(const char *name, const char* value, const char *valname, const /* Settings. */ extern int log_level; +#define UT_MAX_TESTS 32 +#include "../../../pjlib/src/pjlib-test/test_util.h" + +struct test_app_t +{ + ut_app_t ut_app; +}; +extern struct test_app_t test_app; + #endif /* __TEST_H__ */ diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index f834d1dec9..450e0f8daa 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -39,10 +39,8 @@ int generic_transport_test(pjsip_transport *tp) if (pj_inet_pton(pj_AF_INET(), &tp->local_name.host, &addr) == PJ_SUCCESS) { - if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) { - PJ_LOG(3,(THIS_FILE, " Error: invalid address name")); - return -420; - } + PJ_TEST_TRUE(addr.s_addr!=PJ_INADDR_ANY && addr.s_addr!=PJ_INADDR_NONE, + "invalid address name", return -420); } else { /* It's okay. local_name.host may be a hostname instead of * IP address. @@ -51,17 +49,13 @@ int generic_transport_test(pjsip_transport *tp) } /* Check that port is valid. */ - if (tp->local_name.port <= 0) { - return -430; - } + PJ_TEST_GT(tp->local_name.port, 0, NULL, return -430); /* Check length of address (for now we only check against sockaddr_in). */ - if (tp->addr_len != sizeof(pj_sockaddr_in)) - return -440; + PJ_TEST_EQ(tp->addr_len, sizeof(pj_sockaddr_in), NULL, return -440); /* Check type. */ - if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED) - return -450; + PJ_TEST_NEQ(tp->key.type, PJSIP_TRANSPORT_UNSPECIFIED, NULL, return -450); /* That's it. */ return PJ_SUCCESS; @@ -77,8 +71,8 @@ int generic_transport_test(pjsip_transport *tp) * The main purpose is to test that the basic transport functionalities works, * before we continue with more complicated tests. */ -#define FROM_HDR "Bob " -#define CONTACT_HDR "Bob " +#define FROM_HDR "Bob " +#define CONTACT_HDR "Bob " #define CALL_ID_HDR "SendRecv-Test" #define CSEQ_VALUE 100 #define BODY "Hello World!" @@ -687,7 +681,7 @@ static struct mod_load_test static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata) { if (rdata->msg_info.cseq->cseq != mod_load.next_seq) { - PJ_LOG(1,("THIS_FILE", " err: expecting cseq %u, got %u", + PJ_LOG(1,(THIS_FILE, " err: expecting cseq %u, got %u", mod_load.next_seq, rdata->msg_info.cseq->cseq)); mod_load.err = PJ_TRUE; mod_load.next_seq = rdata->msg_info.cseq->cseq + 1; @@ -726,7 +720,7 @@ int transport_load_test(char *target_url) pjsip_tx_data *tdata; target = pj_str(target_url); - from = pj_str(""); + from = pj_str(""); call_id = pj_str("thecallid"); status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, @@ -751,7 +745,7 @@ int transport_load_test(char *target_url) } while (i != 0); if (mod_load.next_seq != COUNT) { - PJ_LOG(1,("THIS_FILE", " err: expecting %u msg, got only %u", + PJ_LOG(1,(THIS_FILE, " err: expecting %u msg, got only %u", COUNT, mod_load.next_seq)); status = -2; goto on_return; diff --git a/pjsip/src/test/transport_udp_test.c b/pjsip/src/test/transport_udp_test.c index 2c063d4846..c34fd644d1 100644 --- a/pjsip/src/test/transport_udp_test.c +++ b/pjsip/src/test/transport_udp_test.c @@ -23,87 +23,99 @@ #define THIS_FILE "transport_udp_test.c" -static pj_status_t multi_transport_test(pjsip_transport *tp[], unsigned num_tp) +static pj_status_t multi_transport_test(pjsip_transport *tp[], unsigned max_tp) { - pj_status_t status; - unsigned i = 0; +#define ERR(rc__) { rc=rc__; goto on_return; } + int rc; + unsigned i, num_tp=0; pj_str_t s; - pjsip_transport *udp_tp; + pjsip_transport *other_udp_tp = NULL, *udp_tp; pj_sockaddr_in rem_addr; pjsip_tpselector tp_sel; - for (;iref_cnt) != 1) - return -120; + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-120)); - /* Test basic transport attributes */ - status = generic_transport_test(udp_tp); - if (status != PJ_SUCCESS) - return status; - - tp[i] = udp_tp; + tp[num_tp++] = udp_tp; } for (i = 0; i < num_tp; ++i) { - udp_tp = tp[i]; - if (pj_atomic_get(udp_tp->ref_cnt) != 1) - return -130; + PJ_TEST_EQ(pj_atomic_get(tp[i]->ref_cnt), 1, NULL, ERR(-130)); + + /* Test basic transport attributes */ + rc = generic_transport_test(udp_tp); + if (rc != 0) + goto on_return; } /* Acquire transport test without selector. */ pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, + PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &rem_addr, sizeof(rem_addr), - NULL, &udp_tp); - if (status != PJ_SUCCESS) - return -140; + NULL, &udp_tp), + NULL, ERR(-140)); for (i = 0; i < num_tp; ++i) { if (udp_tp == tp[i]) { break; } } - if (i == num_tp) - return -150; + PJ_TEST_TRUE(iref_cnt) != 1) - return -160; + if (udp_tp == other_udp_tp) { + PJ_TEST_GT(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-160)); + } else { + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-165)); + } /* Acquire transport test with selector. */ pj_bzero(&tp_sel, sizeof(tp_sel)); tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; tp_sel.u.transport = tp[num_tp-1]; pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "1.1.1.1"), 80); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, + PJ_TEST_SUCCESS( pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &rem_addr, sizeof(rem_addr), - &tp_sel, &udp_tp); - if (status != PJ_SUCCESS) - return -170; + &tp_sel, &udp_tp), + NULL, ERR(-170)); - if (udp_tp != tp[num_tp-1]) - return -180; + PJ_TEST_EQ(udp_tp, tp[num_tp-1], NULL, ERR(-180)); pjsip_transport_dec_ref(udp_tp); - if (pj_atomic_get(udp_tp->ref_cnt) != 1) - return -190; + PJ_TEST_EQ(pj_atomic_get(udp_tp->ref_cnt), 1, NULL, ERR(-190)); + + rc = 0; + +on_return: + if (other_udp_tp) { + pjsip_transport_dec_ref(other_udp_tp); + } + if (rc != 0) { + for (i=0; iport, param->tp_type); pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), - "sip:alice@127.0.0.1:%d;transport=%s", + "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); status = tsx_layer_test(); diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c index 30b2aa2209..3af3ee080b 100644 --- a/pjsip/src/test/tsx_bench.c +++ b/pjsip/src/test/tsx_bench.c @@ -20,7 +20,7 @@ #include #include -#define THIS_FILE "tsx_uas_test.c" +#define THIS_FILE "tsx_bench.c" static pjsip_module mod_tsx_user; @@ -36,7 +36,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; @@ -108,7 +108,7 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c index 3709a5a5cd..b71b65c0ff 100644 --- a/pjsip/src/test/tsx_uac_test.c +++ b/pjsip/src/test/tsx_uac_test.c @@ -1353,10 +1353,12 @@ static int perform_generic_test( const char *title, ** ***************************************************************************** */ -int tsx_uac_test(struct tsx_test_param *param) +int tsx_uac_test(unsigned index) { +#define ERR(rc__) { status=rc__; goto on_return; } + struct tsx_test_param *param = &tsx_test[index]; pj_sockaddr_in addr; - pj_status_t status; + int status; timer.tsx_key.ptr = timer.key_buf; @@ -1367,43 +1369,36 @@ int tsx_uac_test(struct tsx_test_param *param) pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:alice@127.0.0.1:%d;transport=%s", + pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:tsx_uac_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); /* Check if loop transport is configured. */ - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop); - if (status != PJ_SUCCESS) { - PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!")); - return -10; - } + PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, + &addr, sizeof(addr), NULL, &loop), + NULL, ERR(-10)); /* Register modules. */ - status = pjsip_endpt_register_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -30; + if (tsx_user.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, ERR(-30)); } - status = pjsip_endpt_register_module(endpt, &msg_receiver); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -40; + if (msg_receiver.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, ERR(-40)); } /* TEST1_BRANCH_ID: Basic retransmit and timeout test. */ status = tsx_uac_retransmit_test(); if (status != 0) - return status; + goto on_return; /* TEST2_BRANCH_ID: Resolve error test. */ status = tsx_resolve_error_test(); if (status != 0) - return status; + goto on_return; /* TEST3_BRANCH_ID: UAC terminate while resolving test. */ status = tsx_terminate_resolving_test(); if (status != 0) - return status; + goto on_return; /* TEST4_BRANCH_ID: Transport failed after several retransmissions. * Only applies to loop transport. @@ -1411,7 +1406,7 @@ int tsx_uac_test(struct tsx_test_param *param) if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { status = tsx_retransmit_fail_test(); if (status != 0) - return status; + goto on_return; } /* TEST5_BRANCH_ID: Terminate transaction after several retransmissions @@ -1420,50 +1415,55 @@ int tsx_uac_test(struct tsx_test_param *param) if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { status = tsx_terminate_after_retransmit_test(); if (status != 0) - return status; + goto on_return; } /* TEST6_BRANCH_ID: Successfull non-invite transaction */ status = perform_generic_test("test6: successfull non-invite transaction", TEST6_BRANCH_ID, &pjsip_options_method); if (status != 0) - return status; + goto on_return; /* TEST7_BRANCH_ID: Successfull non-invite transaction */ status = perform_generic_test("test7: successfull non-invite transaction " "with provisional response", TEST7_BRANCH_ID, &pjsip_options_method); if (status != 0) - return status; + goto on_return; /* TEST8_BRANCH_ID: Failed invite transaction */ status = perform_generic_test("test8: failed invite transaction", TEST8_BRANCH_ID, &pjsip_invite_method); if (status != 0) - return status; + goto on_return; /* TEST9_BRANCH_ID: Failed invite transaction with provisional response */ status = perform_generic_test("test9: failed invite transaction with " "provisional response", TEST9_BRANCH_ID, &pjsip_invite_method); if (status != 0) - return status; + goto on_return; pjsip_transport_dec_ref(loop); flush_events(500); +on_return: /* Unregister modules. */ - status = pjsip_endpt_unregister_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -31; + if (tsx_user.id != -1) { + status = pjsip_endpt_unregister_module(endpt, &tsx_user); + if (status != PJ_SUCCESS) { + app_perror(" Error: unable to unregister module", status); + return -31; + } } - status = pjsip_endpt_unregister_module(endpt, &msg_receiver); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -41; + if (msg_receiver.id != -1) { + status = pjsip_endpt_unregister_module(endpt, &msg_receiver); + if (status != PJ_SUCCESS) { + app_perror(" Error: unable to unregister module", status); + return -41; + } } - - return 0; + return status; +#undef ERR } diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 815cbe069e..beeb8b9036 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -1564,8 +1564,9 @@ int tsx_transport_failure_test(void) ** ***************************************************************************** */ -int tsx_uas_test(struct tsx_test_param *param) +int tsx_uas_test(unsigned index) { + struct tsx_test_param *param = &tsx_test[index]; pj_sockaddr_in addr; pj_status_t status; @@ -1574,7 +1575,7 @@ int tsx_uas_test(struct tsx_test_param *param) pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:alice@127.0.0.1:%d;transport=%s", + pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:tsx_uas_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); /* Check if loop transport is configured. */ diff --git a/pjsip/src/test/txdata_test.c b/pjsip/src/test/txdata_test.c index ee0ce4d47f..b06f9b8615 100644 --- a/pjsip/src/test/txdata_test.c +++ b/pjsip/src/test/txdata_test.c @@ -636,7 +636,7 @@ static int create_request_bench(pj_timestamp *p_elapsed) pj_status_t status; pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; @@ -695,7 +695,7 @@ static int create_response_bench(pj_timestamp *p_elapsed) /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); - pj_str_t str_from = pj_str("\"Local User\" "); + pj_str_t str_from = pj_str("\"Local User\" "); pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; diff --git a/unittest.md b/unittest.md index 0b96f34994..a5339faf0f 100644 --- a/unittest.md +++ b/unittest.md @@ -184,6 +184,13 @@ Having said that, some minor modifications were done: - replace `pjmedia_endpt_create()` with `pjmedia_endpt_create2()` (similarly `..destroy()` with `..destroy2()` in `mips_test()` and `codec_test_vectors()`, to avoid inadvertently initializing `pjmedia_aud_subsys`. - replace `printf` with log in jbuf test to make the output tidy, and renamed `jbuf_main` function name to `jbuf_test`. +#### Changes to PJSIP-TEST + +Original: 28m22.744s +First porting: 27m1.894s + +Changed `tsx_basic_test`, `tsx_uac_test`, `tsx_uas_test` to take the index to parameters rather than the parameter itself to make the test output more informative. + ### 3. Other developments #### `` @@ -194,15 +201,17 @@ This is header only feature to parse command line options. We have `pj_getopt()` The `fifobuf` feature has been there for the longest time (it was part of pjlib first ever commit) and finally has got some use. -#### Minor fixes in `pj/log.c` +### 4. Tips, known issues and considerations + +### Basic runner limitations -Fixed bug with writing empty string i.e. `PJ_LOG(3,(THIS_FILE, "%s", ""))` causing message length to be set to the size of the buffer (i.e. too large). This causes the buffer to take all the available space in unit-testing log buffer. +There can only be one basic runner running at any single time, because it stores current test in a global variable. -#### Minor enhancements in `pjlib-util/dns_server.[hc]` +Note: this limitation is because the unit test needs to access current test case inside logging callback, and we want the basic runner to be as simple as possible without the need to use pool or OS features such as thread local storage. -Add `pj_dns_server_get_addr()` API to allow app to get the bound port when null port is specified for the server. +### Multithreaded debugging tips -### 4. Known issues and considerations +Set `verbosity` to 1 in runner's param to display what tests are running to help finding out what's going on when error happens during test which suspected to be caused by concurrency issue. ### Cluttered logging From 2e5fa0ef76820211a888190fd9395f0b6f7dbf1d Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 26 Jun 2024 08:54:51 +0700 Subject: [PATCH 18/79] Modifications in pj_argparse API (changed get() to get_bool() and add automatic error reporting) hopefully make it easier to use --- pjlib-util/src/pjlib-util-test/main.c | 8 ++--- pjlib/include/pj/argparse.h | 42 ++++++++++++++++------- pjlib/src/pjlib-test/main.c | 48 ++++++++++++--------------- pjlib/src/pjlib-test/test_util.h | 43 ++++++++++++------------ pjmedia/src/test/main.c | 8 ++--- pjnath/src/pjnath-test/main.c | 8 ++--- pjsip/src/test/main.c | 28 ++++++---------- 7 files changed, 94 insertions(+), 91 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c index 7a25d859e4..f3699b61ff 100644 --- a/pjlib-util/src/pjlib-util-test/main.c +++ b/pjlib-util/src/pjlib-util-test/main.c @@ -90,8 +90,8 @@ int main(int argc, char *argv[]) boost(); - if (pj_argparse_get("-h", &argc, argv) || - pj_argparse_get("--help", &argc, argv)) + if (pj_argparse_get_bool("-h", &argc, argv) || + pj_argparse_get_bool("--help", &argc, argv)) { usage(); return 0; @@ -99,8 +99,8 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get("-i", &argc, argv); - no_trap = pj_argparse_get("-n", &argc, argv); + interractive = pj_argparse_get_bool("-i", &argc, argv); + no_trap = pj_argparse_get_bool("-n", &argc, argv); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjlib/include/pj/argparse.h b/pjlib/include/pj/argparse.h index 92673f0fc1..192e873e03 100644 --- a/pjlib/include/pj/argparse.h +++ b/pjlib/include/pj/argparse.h @@ -28,6 +28,15 @@ PJ_BEGIN_DECL +/** + * Define function to display parsing error. + */ +#ifndef PJ_ARGPARSE_ERROR +# include +# define PJ_ARGPARSE_ERROR(fmt, arg) printf(fmt "\n", arg) +#endif + + /** * @defgroup PJ_ARGPARSE Command line argument parser * @ingroup PJ_MISC @@ -42,7 +51,7 @@ PJ_BEGIN_DECL /** * Peek the next possible option from argv. An argument is considered an * option if it starts with "-" and followed by at least another letter that - * is not digit. + * is not digit or starts with "--" and followed by a letter. * * @param argv The argv, which must be null terminated. * @@ -51,8 +60,12 @@ PJ_BEGIN_DECL static char* pj_argparse_peek_next_option(char *const argv[]) { while (*argv) { - if (**argv=='-' && **argv && !pj_isdigit(**argv) ) + const char *arg = *argv; + if ((*arg=='-' && *(arg+1) && !pj_isdigit(*(arg+1))) || + (*arg=='-' && *(arg+1)=='-' && *(arg+2))) + { return *argv; + } ++argv; } return NULL; @@ -86,7 +99,7 @@ static pj_bool_t pj_argparse_exists(const char *opt, char *const argv[]) * * @return PJ_TRUE if the option exists, else PJ_FALSE. */ -static pj_bool_t pj_argparse_get(const char *opt, int *argc, char *argv[]) +static pj_bool_t pj_argparse_get_bool(const char *opt, int *argc, char *argv[]) { int i; for (i=1; argv[i]; ++i) { @@ -110,9 +123,9 @@ static pj_bool_t pj_argparse_get(const char *opt, int *argc, char *argv[]) * @param argv Null terminated argv. * @param ptr_value Pointer to receive the value. * - * @return PJ_SUCCESS if the option exists and value is found, + * @return PJ_SUCCESS if the option exists and value is found or if the + * option does not exist * PJ_EINVAL if the option exits but value is not found, - * PJ_ENOTFOUND if the option does not exist. */ static pj_status_t pj_argparse_get_str( const char *opt, int *argc, char *argv[], char **ptr_value) @@ -130,11 +143,13 @@ static pj_status_t pj_argparse_get_str( const char *opt, int *argc, *ptr_value = val; return PJ_SUCCESS; } else { + PJ_ARGPARSE_ERROR("Error: missing value for %s argument", + opt); return PJ_EINVAL; } } } - return PJ_ENOTFOUND; + return PJ_SUCCESS; } /** @@ -148,22 +163,25 @@ static pj_status_t pj_argparse_get_str( const char *opt, int *argc, * @param argv Null terminated argv. * @param ptr_value Pointer to receive the value. * - * @return PJ_SUCCESS if the option exists and value is found, - * PJ_EINVAL if the option exits but value is not found or invalid, - * PJ_ENOTFOUND if the option does not exist. + * @return PJ_SUCCESS if the option exists and value is found or if the + * option does not exist + * PJ_EINVAL if the option exits but value is not found, */ static pj_status_t pj_argparse_get_int( char *opt, int *argc, char *argv[], int *ptr_value) { - char *endptr, *sval; + char *endptr, *sval=NULL; long val; pj_status_t status = pj_argparse_get_str(opt, argc, argv, &sval); - if (status != PJ_SUCCESS) + if (status!=PJ_SUCCESS || !sval) return status; val = strtol(sval, &endptr, 10); - if (*endptr) + if (*endptr) { + PJ_ARGPARSE_ERROR("Error: invalid value for %s argument", + opt); return PJ_EINVAL; + } *ptr_value = (int)val; return PJ_SUCCESS; diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index 6d516edd2f..e369871700 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -103,8 +103,6 @@ int main(int argc, char *argv[]) int rc; int interractive = 0; int no_trap = 0; - pj_status_t status; - char *s; boost(); ut_app_init0(&test_app.ut_app); @@ -112,51 +110,47 @@ int main(int argc, char *argv[]) /* * Parse arguments */ - if (pj_argparse_get("-h", &argc, argv) || - pj_argparse_get("--help", &argc, argv)) + if (pj_argparse_get_bool("-h", &argc, argv) || + pj_argparse_get_bool("--help", &argc, argv)) { usage(); return 0; } - interractive = pj_argparse_get("-i", &argc, argv); - no_trap = pj_argparse_get("-n", &argc, argv); - status = pj_argparse_get_int("-p", &argc, argv, &test_app.param_echo_port); - if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { - puts("Error: invalid/missing value for -p option"); + interractive = pj_argparse_get_bool("-i", &argc, argv); + no_trap = pj_argparse_get_bool("-n", &argc, argv); + if (pj_argparse_get_int("-p", &argc, argv, &test_app.param_echo_port)) { usage(); return 1; } - status = pj_argparse_get_str("-s", &argc, argv, - (char**)&test_app.param_echo_server); - if (status!=PJ_SUCCESS && status!=PJ_ENOTFOUND) { - puts("Error: value is required for -s option"); + if (pj_argparse_get_str("-s", &argc, argv, (char**)&test_app.param_echo_server)) { usage(); return 1; } - status = pj_argparse_get_str("-t", &argc, argv, &s); - if (status==PJ_SUCCESS) { - if (pj_ansi_stricmp(s, "tcp")==0) - test_app.param_echo_sock_type = pj_SOCK_STREAM(); - else if (pj_ansi_stricmp(s, "udp")==0) - test_app.param_echo_sock_type = pj_SOCK_DGRAM(); - else { - printf("Error: unknown socket type %s for -t option\n", s); + if (pj_argparse_exists("-t", argv)) { + char *sock_type; + if (pj_argparse_get_str("-t", &argc, argv, &sock_type)==PJ_SUCCESS) { + if (pj_ansi_stricmp(sock_type, "tcp")==0) + test_app.param_echo_sock_type = pj_SOCK_STREAM(); + else if (pj_ansi_stricmp(sock_type, "udp")==0) + test_app.param_echo_sock_type = pj_SOCK_DGRAM(); + else { + printf("Error: unknown socket type %s for -t option\n", sock_type); + usage(); + return 1; + } + } else { usage(); return 1; } - } else if (status!=PJ_ENOTFOUND) { - puts("Error: value is required for -t option"); - usage(); - return 1; } if (ut_parse_args(&test_app.ut_app, &argc, argv)) { usage(); return 1; } - test_app.param_skip_essentials = pj_argparse_get("--skip-e", &argc, argv); - test_app.param_ci_mode = pj_argparse_get("--ci-mode", &argc, argv); + test_app.param_skip_essentials = pj_argparse_get_bool("--skip-e", &argc, argv); + test_app.param_ci_mode = pj_argparse_get_bool("--ci-mode", &argc, argv); if (!no_trap) { diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 251b9c5133..363cc0dcf2 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -212,39 +212,38 @@ static void ut_usage() static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) { - int itmp; + int itmp = -1; pj_status_t status; - ut_app->prm_list_test = pj_argparse_get("-L", argc, argv) || - pj_argparse_get("--list", argc, argv); - ut_app->prm_stop_on_error = pj_argparse_get("--stop-err", argc, argv); - if (pj_argparse_get("--log-no-cache", argc, argv)) { + ut_app->prm_list_test = pj_argparse_get_bool("-L", argc, argv) || + pj_argparse_get_bool("--list", argc, argv); + ut_app->prm_stop_on_error = pj_argparse_get_bool("--stop-err", argc, argv); + if (pj_argparse_get_bool("--log-no-cache", argc, argv)) { ut_app->flags |= PJ_TEST_LOG_NO_CACHE; } - status = pj_argparse_get_int("-l", argc, argv, &itmp); - if (status==PJ_SUCCESS && itmp>=0 && itmp<=3) { - ut_app->prm_logging_policy = (pj_test_select_tests)itmp; - } else if (status!=PJ_ENOTFOUND) { - puts("Error: invalid/missing value for -l option"); - return PJ_EINVAL; + if (pj_argparse_exists("-l", argv)) { + status = pj_argparse_get_int("-l", argc, argv, &itmp); + if (status==PJ_SUCCESS && itmp>=0 && itmp<=3) { + ut_app->prm_logging_policy = (pj_test_select_tests)itmp; + } else { + puts("Error: invalid value for -l option"); + return PJ_EINVAL; + } } - status = pj_argparse_get_int("-w", argc, argv, - (int*)&ut_app->prm_nthreads); - if (status==PJ_SUCCESS) { - if (ut_app->prm_nthreads > 50 || ut_app->prm_nthreads < 0) { - printf("Error: value %d is not valid for -w option\n", - ut_app->prm_nthreads); + if (pj_argparse_exists("-w", argv)) { + status = pj_argparse_get_int("-w", argc, argv, &itmp); + if (status==PJ_SUCCESS && itmp>=0 && itmp<50) { + ut_app->prm_nthreads = itmp; + } else { + puts("Error: invalid/missing value for -w option"); return PJ_EINVAL; } - } else if (status!=PJ_ENOTFOUND) { - puts("Error: invalid/missing value for -w option"); - return PJ_EINVAL; } - ut_app->verbosity = pj_argparse_get("-v", argc, argv) || - pj_argparse_get("--verbose", argc, argv); + ut_app->verbosity = pj_argparse_get_bool("-v", argc, argv) || + pj_argparse_get_bool("--verbose", argc, argv); return PJ_SUCCESS; } diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index 92fb4f4cb7..24ad0876d4 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -82,8 +82,8 @@ static int main_func(int argc, char *argv[]) int interractive = 0; int no_trap = 0; - if (pj_argparse_get("-h", &argc, argv) || - pj_argparse_get("--help", &argc, argv)) + if (pj_argparse_get_bool("-h", &argc, argv) || + pj_argparse_get_bool("--help", &argc, argv)) { usage(); return 0; @@ -91,8 +91,8 @@ static int main_func(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get("-i", &argc, argv); - no_trap = pj_argparse_get("-n", &argc, argv); + interractive = pj_argparse_get_bool("-i", &argc, argv); + no_trap = pj_argparse_get_bool("-n", &argc, argv); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjnath/src/pjnath-test/main.c b/pjnath/src/pjnath-test/main.c index 153f969a27..c73c971a1e 100644 --- a/pjnath/src/pjnath-test/main.c +++ b/pjnath/src/pjnath-test/main.c @@ -85,8 +85,8 @@ int main(int argc, char *argv[]) boost(); - if (pj_argparse_get("-h", &argc, argv) || - pj_argparse_get("--help", &argc, argv)) + if (pj_argparse_get_bool("-h", &argc, argv) || + pj_argparse_get_bool("--help", &argc, argv)) { usage(); return 0; @@ -94,8 +94,8 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get("-i", &argc, argv); - no_trap = pj_argparse_get("-n", &argc, argv); + interractive = pj_argparse_get_bool("-i", &argc, argv); + no_trap = pj_argparse_get_bool("-n", &argc, argv); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index e6dc770595..fb5ffbd218 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -75,8 +75,8 @@ int main(int argc, char *argv[]) int retval; int no_trap = 0; - if (pj_argparse_get("-h", &argc, argv) || - pj_argparse_get("--help", &argc, argv)) + if (pj_argparse_get_bool("-h", &argc, argv) || + pj_argparse_get_bool("--help", &argc, argv)) { usage(); return 0; @@ -84,24 +84,16 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get("-i", &argc, argv); - no_trap = pj_argparse_get("-n", &argc, argv); - if (pj_argparse_exists("-s", argv)) { - if (pj_argparse_get_str("-s", &argc, argv, (char**)&system_name)==PJ_EINVAL) { - puts("Error: value required for -s argument"); - usage(); - return 1; - } - } else if (pj_argparse_exists("--system", argv)) { - if (pj_argparse_get_str("--system", &argc, argv, (char**)&system_name)==PJ_EINVAL) { - puts("Error: value required for --system argument"); - usage(); - return 1; - } + interractive = pj_argparse_get_bool("-i", &argc, argv); + no_trap = pj_argparse_get_bool("-n", &argc, argv); + if (pj_argparse_get_str("-s", &argc, argv, (char**)&system_name) || + pj_argparse_get_str("--system", &argc, argv, (char**)&system_name)) + { + usage(); + return 1; } - if (pj_argparse_get_int("--log-level", &argc, argv, &log_level)==PJ_EINVAL) { - puts("Error: invalid/missing value for --log-level argument"); + if (pj_argparse_get_int("--log-level", &argc, argv, &log_level)) { usage(); return 1; } From d4b267ef2d5c0f978646cafaa0e955a227c00e6d Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 27 Jun 2024 06:49:06 +0700 Subject: [PATCH 19/79] Refactor tsx_uas_test() to allow parallel testing --- pjsip/src/test/test.c | 2 +- pjsip/src/test/test.h | 4 +- pjsip/src/test/tsx_uas_test.c | 539 ++++++++++++++++++++-------------- 3 files changed, 322 insertions(+), 223 deletions(-) diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 4f75c24e6e..99b7765429 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -35,7 +35,7 @@ static pj_oshandle_t fd_report; const char *system_name = "Unknown"; static char buf[1024]; struct test_app_t test_app; -struct tsx_test_param tsx_test[10]; +struct tsx_test_param tsx_test[MAX_TSX_TESTS]; void app_perror(const char *msg, pj_status_t rc) { diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 11b18ac8a4..641e14f2e5 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -88,13 +88,15 @@ int resolve_test(void); int regc_test(void); int inv_offer_answer_test(void); +#define MAX_TSX_TESTS 10 + struct tsx_test_param { int type; int port; char *tp_type; }; -extern struct tsx_test_param tsx_test[10]; +extern struct tsx_test_param tsx_test[MAX_TSX_TESTS]; int tsx_basic_test(unsigned index); int tsx_uac_test(unsigned index); diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index beeb8b9036..ffcfab0dcb 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -131,12 +131,34 @@ #define TEST5_TITLE "test5: retransmit last response in PROCEEDING state" #define TEST6_TITLE "test6: retransmit last response in COMPLETED state" +/* Since several tsx_uas_test() may run concurrently, keep the global vars + * in array indexed according to the test index (tid) + */ +static struct tsx_uas_test_global_t +{ + char TARGET_URI[128]; + char FROM_URI[128]; + struct tsx_test_param *test_param; + unsigned tp_flag; + + /* Static vars, which will be reset on each test. */ + int recv_count; + pj_time_val recv_last; + pj_bool_t test_complete; + + /* Loop transport instance. */ + pjsip_transport *loop; + + /* UAS transaction key. */ + char key_buf[64]; + pj_str_t tsx_key; -static char TARGET_URI[128]; -static char FROM_URI[128]; -static struct tsx_test_param *test_param; -static unsigned tp_flag; + /* General timer entry to be used by tests. */ + //pj_timer_entry timer; + pj_bool_t modules_registered; + +} g[MAX_TSX_TESTS]; #define TEST_TIMEOUT_ERROR -30 @@ -182,22 +204,6 @@ static pjsip_module msg_sender = NULL, /* on_tsx_state() */ }; -/* Static vars, which will be reset on each test. */ -static int recv_count; -static pj_time_val recv_last; -static pj_bool_t test_complete; - -/* Loop transport instance. */ -static pjsip_transport *loop; - -/* UAS transaction key. */ -static char key_buf[64]; -static pj_str_t tsx_key = { key_buf, 0 }; - - -/* General timer entry to be used by tests. */ -//static pj_timer_entry timer; - /* Timer to send response via transaction. */ struct response { @@ -205,6 +211,93 @@ struct response pjsip_tx_data *tdata; }; +/* Get test ID from transaction instance */ +static unsigned get_tsx_tid(const pjsip_transaction *tsx) +{ + pj_assert(tsx_user.id >= 0); + return (unsigned)(long)tsx->mod_data[tsx_user.id]; +} + +/* Set test ID to transaction instance */ +static void set_tsx_tid(pjsip_transaction *tsx, unsigned tid) +{ + pj_assert(tsx_user.id >= 0); + tsx->mod_data[tsx_user.id] = (void*)(long)tid; +} + +static int modules_reg_cnt; + +/* Register modules, taking care of multiple re-registration attempts */ +static pj_status_t register_modules(unsigned tid) +{ + int old_reg_cnt; + pj_status_t status; + + pj_enter_critical_section(); + old_reg_cnt = modules_reg_cnt++; + pj_leave_critical_section(); + + if (old_reg_cnt==0) { + PJ_TEST_SUCCESS(status=pjsip_endpt_register_module(endpt, &tsx_user), + NULL, goto on_error); + PJ_TEST_SUCCESS(status=pjsip_endpt_register_module(endpt, &msg_sender), + NULL, { + pjsip_endpt_unregister_module(endpt, &tsx_user); + goto on_error; + }); + } else { + unsigned i; + /* Make sure modules are registered, wait if necessary */ + for (i=0; i<20 && (tsx_user.id<0 || msg_sender.id<0); ++i) + pj_thread_sleep(50); + + if (tsx_user.id<0 || msg_sender.id<0) { + PJ_TEST_SUCCESS(status = PJSIP_ENOTINITIALIZED, + "other thread failed to register module", + goto on_error); + } + } + + g[tid].modules_registered = 1; + return PJ_SUCCESS; + +on_error: + pj_enter_critical_section(); + modules_reg_cnt--; + pj_leave_critical_section(); + + return status; +} + +/* Unregister modules, taking care of premature unregistration attempt */ +static void unregister_modules(unsigned tid) +{ + int new_reg_cnt; + + if (!g[tid].modules_registered) + return; + + g[tid].modules_registered = 0; + + // Note: + // on_tsx_state() can be called much later during pjsip shutdown + // i.e. when transaction layer is being destroyed. If we unregister + // the modules, get_tsx_tid() will fail with assertion. + // So just let the module registered. + return; + + pj_enter_critical_section(); + new_reg_cnt = --modules_reg_cnt; + pj_leave_critical_section(); + + if (new_reg_cnt == 0) { + PJ_TEST_SUCCESS(pjsip_endpt_unregister_module(endpt, &tsx_user), + "error ignored", {}); + PJ_TEST_SUCCESS(pjsip_endpt_unregister_module(endpt, &msg_sender), + "error ignored", {}); + } +} + /* Timer callback to send response. */ static void send_response_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) @@ -241,12 +334,13 @@ static void send_response( pjsip_rx_data *rdata, { pj_status_t status; pjsip_tx_data *tdata; + unsigned tid = get_tsx_tid(tsx); status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create response", status); - test_complete = -196; + g[tid].test_complete = -196; return; } @@ -255,13 +349,14 @@ static void send_response( pjsip_rx_data *rdata, pjsip_tx_data_dec_ref(tdata); // Some tests do expect failure! //app_perror(" error: unable to send response", status); - //test_complete = -197; + //g[tid].test_complete = -197; return; } } /* Schedule timer to send response for the specified UAS transaction */ -static void schedule_send_response( pjsip_rx_data *rdata, +static void schedule_send_response( unsigned tid, + pjsip_rx_data *rdata, const pj_str_t *tsx_key_, int status_code, int msec_delay ) @@ -276,7 +371,7 @@ static void schedule_send_response( pjsip_rx_data *rdata, &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create response", status); - test_complete = -198; + g[tid].test_complete = -198; return; } @@ -296,18 +391,18 @@ static void schedule_send_response( pjsip_rx_data *rdata, if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); app_perror(" error: unable to schedule timer", status); - test_complete = -199; + g[tid].test_complete = -199; return; } } /* Find and terminate tsx with the specified key. */ -static void terminate_our_tsx(int status_code) +static void terminate_our_tsx(unsigned tid, int status_code) { pjsip_transaction *tsx; - tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); + tsx = pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_TRUE); if (!tsx) { PJ_LOG(3,(THIS_FILE," error: timer unable to find transaction")); return; @@ -337,7 +432,7 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx, delay.msec = msec_delay; pj_time_val_normalize(&delay); - pj_assert(pj_strcmp(&tsx->transaction_key, &tsx_key)==0); + pj_assert(pj_strcmp(&tsx->transaction_key, &g[tid].tsx_key)==0); timer.user_data = NULL; timer.id = status_code; timer.cb = &terminate_tsx_timer; @@ -353,6 +448,8 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx, */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { + unsigned tid = get_tsx_tid(tsx); + if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 || pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { @@ -368,18 +465,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Check that status code is status_code. */ if (tsx->status_code != status_code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -100; + g[tid].test_complete = -100; } /* Previous state must be completed. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -101; + g[tid].test_complete = -101; } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -387,7 +484,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be TRYING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -102; + g[tid].test_complete = -102; } } @@ -399,18 +496,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Check that status code is status_code. */ if (tsx->status_code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -110; + g[tid].test_complete = -110; } /* Previous state must be completed. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -111; + g[tid].test_complete = -111; } } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) { @@ -418,19 +515,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be TRYING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -112; + g[tid].test_complete = -112; } /* Check that status code is status_code. */ if (tsx->status_code != TEST3_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -113; + g[tid].test_complete = -113; } /* Check that event must be TX_MSG */ if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) { PJ_LOG(3,(THIS_FILE, " error: incorrect event")); - test_complete = -114; + g[tid].test_complete = -114; } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -438,19 +535,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be PROCEEDING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -115; + g[tid].test_complete = -115; } /* Check that status code is status_code. */ if (tsx->status_code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -116; + g[tid].test_complete = -116; } /* Check that event must be TX_MSG */ if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) { PJ_LOG(3,(THIS_FILE, " error: incorrect event")); - test_complete = -117; + g[tid].test_complete = -117; } } @@ -470,20 +567,20 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: incorrect status code %d " "(expecting %d)", tsx->status_code, TEST4_STATUS_CODE)); - test_complete = -120; + g[tid].test_complete = -120; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -121; + g[tid].test_complete = -121; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (122)", pjsip_tsx_state_str(tsx->state))); - test_complete = -122; + g[tid].test_complete = -122; } @@ -501,13 +598,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != TEST5_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -130; + g[tid].test_complete = -130; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -131; + g[tid].test_complete = -131; } } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) { @@ -515,13 +612,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check status code. */ if (tsx->status_code != TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -132; + g[tid].test_complete = -132; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (133)", pjsip_tsx_state_str(tsx->state))); - test_complete = -133; + g[tid].test_complete = -133; } @@ -540,13 +637,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: incorrect status code %d " "(expecting %d)", tsx->status_code, TEST6_STATUS_CODE)); - test_complete = -140; + g[tid].test_complete = -140; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -141; + g[tid].test_complete = -141; } } else if (tsx->state != PJSIP_TSX_STATE_PROCEEDING && @@ -555,7 +652,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: unexpected state %s (142)", pjsip_tsx_state_str(tsx->state))); - test_complete = -142; + g[tid].test_complete = -142; } @@ -580,27 +677,27 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Check status code. */ if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -150; + g[tid].test_complete = -150; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -151; + g[tid].test_complete = -151; } /* Check the number of retransmissions */ - if (tp_flag & PJSIP_TRANSPORT_RELIABLE) { + if (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) { if (tsx->retransmit_count != 0) { PJ_LOG(3,(THIS_FILE, " error: should not retransmit")); - test_complete = -1510; + g[tid].test_complete = -1510; } } else { @@ -610,7 +707,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: incorrect retransmit count %d " "(expecting 10)", tsx->retransmit_count)); - test_complete = -1510; + g[tid].test_complete = -1510; } } @@ -620,19 +717,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -152; + g[tid].test_complete = -152; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -153; + g[tid].test_complete = -153; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state (154)")); - test_complete = -154; + g[tid].test_complete = -154; } @@ -649,19 +746,20 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Check status code. */ if (tsx->status_code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -160; + g[tid].test_complete = -160; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_CONFIRMED) { - PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -161; + PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state %d", + e->body.tsx_state.prev_state)); + g[tid].test_complete = -161; } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -669,13 +767,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -162; + g[tid].test_complete = -162; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -163; + g[tid].test_complete = -163; } @@ -684,19 +782,19 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check that status code is status_code. */ if (tsx->status_code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -164; + g[tid].test_complete = -164; } /* Previous state. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state")); - test_complete = -165; + g[tid].test_complete = -165; } } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) { PJ_LOG(3,(THIS_FILE, " error: unexpected state (166)")); - test_complete = -166; + g[tid].test_complete = -166; } @@ -707,8 +805,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (!test_complete) - test_complete = 1; + if (!g[tid].test_complete) + g[tid].test_complete = 1; if (tsx->status_code != PJSIP_SC_REQUEST_TIMEOUT) { PJ_LOG(3,(THIS_FILE," error: incorrect status code" @@ -716,7 +814,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJSIP_SC_REQUEST_TIMEOUT, // PJSIP_SC_TSX_TRANSPORT_ERROR, tsx->status_code)); - test_complete = -170; + g[tid].test_complete = -170; } } } else @@ -724,8 +822,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (!test_complete) - test_complete = 1; + if (!g[tid].test_complete) + g[tid].test_complete = 1; if (tsx->status_code != PJSIP_SC_REQUEST_TIMEOUT && tsx->status_code != PJSIP_SC_OK) @@ -735,7 +833,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJSIP_SC_REQUEST_TIMEOUT, // PJSIP_SC_TSX_TRANSPORT_ERROR, tsx->status_code)); - test_complete = -170; + g[tid].test_complete = -170; } } } @@ -745,9 +843,12 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) static void save_key(pjsip_transaction *tsx) { pj_str_t key; + unsigned tid = get_tsx_tid(tsx); + g[tid].tsx_key.ptr = g[tid].key_buf; + g[tid].tsx_key.slen = 0; pj_strdup(tsx->pool, &key, &tsx->transaction_key); - pj_strcpy(&tsx_key, &key); + pj_strcpy(&g[tid].tsx_key, &key); } #define DIFF(a,b) ((amsg_info.msg; pj_str_t branch_param = rdata->msg_info.via->branch_param; pj_status_t status; + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid = (unsigned)pj_strtol(&target->user); if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 || pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0) @@ -783,9 +887,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -110; + g[tid].test_complete = -110; return PJ_TRUE; } + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -794,18 +899,18 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { /* Verify the response received. */ - ++recv_count; + ++g[tid].recv_count; /* Verify status code. */ if (msg->line.status.code != status_code) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -113; + g[tid].test_complete = -113; } /* Verify that no retransmissions is received. */ - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE, " error: retransmission received")); - test_complete = -114; + g[tid].test_complete = -114; } } @@ -824,37 +929,38 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -116; + g[tid].test_complete = -116; return PJ_TRUE; } + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST3_PROVISIONAL_CODE); - schedule_send_response(rdata, &tsx->transaction_key, + schedule_send_response(tid, rdata, &tsx->transaction_key, TEST3_STATUS_CODE, 2000); } else { /* Verify the response received. */ - ++recv_count; + ++g[tid].recv_count; - if (recv_count == 1) { + if (g[tid].recv_count == 1) { /* Verify status code. */ if (msg->line.status.code != TEST3_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -123; + g[tid].test_complete = -123; } - } else if (recv_count == 2) { + } else if (g[tid].recv_count == 2) { /* Verify status code. */ if (msg->line.status.code != TEST3_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - test_complete = -124; + g[tid].test_complete = -124; } } else { PJ_LOG(3,(THIS_FILE, " error: retransmission received")); - test_complete = -125; + g[tid].test_complete = -125; } } @@ -878,10 +984,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -130; + g[tid].test_complete = -130; return PJ_TRUE; } - + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -900,35 +1006,35 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { /* Verify the response received. */ - PJ_LOG(4,(THIS_FILE, " received response number %d", recv_count)); + PJ_LOG(4,(THIS_FILE, " received response number %d", g[tid].recv_count)); - ++recv_count; + ++g[tid].recv_count; if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); - test_complete = -132; + g[tid].test_complete = -132; } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code!")); - test_complete = -133; + g[tid].test_complete = -133; } - if (recv_count > TEST5_RESPONSE_COUNT) { + if (g[tid].recv_count > TEST5_RESPONSE_COUNT) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); - test_complete = -134; + g[tid].test_complete = -134; } } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { int code = rdata->msg_info.msg->line.status.code; - switch (recv_count) { + switch (g[tid].recv_count) { case 1: if (code != TEST6_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: invalid code!")); - test_complete = -135; + g[tid].test_complete = -135; } break; case 2: @@ -936,12 +1042,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) if (code != TEST6_STATUS_CODE) { PJ_LOG(3,(THIS_FILE, " error: invalid code %d " "(expecting %d)", code, TEST6_STATUS_CODE)); - test_complete = -136; + g[tid].test_complete = -136; } break; default: PJ_LOG(3,(THIS_FILE, " error: not expecting response")); - test_complete = -137; + g[tid].test_complete = -137; break; } } @@ -965,10 +1071,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -140; + g[tid].test_complete = -140; return PJ_TRUE; } - + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -985,21 +1091,21 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { int code; - ++recv_count; + ++g[tid].recv_count; if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; - if (recv_count==1) { + if (g[tid].recv_count==1) { if (rdata->msg_info.msg->line.status.code != code) { PJ_LOG(3,(THIS_FILE," error: invalid status code")); - test_complete = -141; + g[tid].test_complete = -141; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } else { @@ -1008,10 +1114,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec = now.sec*1000 + now.msec; - msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1; + msec_expected = (1 << (g[tid].recv_count-2)) * pjsip_cfg()->tsx.t1; if (msec_expected > pjsip_cfg()->tsx.t2) msec_expected = pjsip_cfg()->tsx.t2; @@ -1020,16 +1126,16 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) " error: incorrect retransmission " "time (%d ms expected, %d ms received", msec_expected, msec)); - test_complete = -142; + g[tid].test_complete = -142; } - if (recv_count > 11) { + if (g[tid].recv_count > 11) { PJ_LOG(3,(THIS_FILE," error: too many responses (%d)", - recv_count)); - test_complete = -143; + g[tid].recv_count)); + g[tid].test_complete = -143; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } } @@ -1050,10 +1156,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -150; + g[tid].test_complete = -150; return PJ_TRUE; } - + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST9_STATUS_CODE); @@ -1061,18 +1167,18 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else { - ++recv_count; + ++g[tid].recv_count; if (rdata->msg_info.msg->line.status.code != TEST9_STATUS_CODE) { PJ_LOG(3,(THIS_FILE," error: invalid status code")); - test_complete = -151; + g[tid].test_complete = -151; } - if (recv_count==1) { + if (g[tid].recv_count==1) { - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; - } else if (recv_count < 5) { + } else if (g[tid].recv_count < 5) { /* Let UAS retransmit some messages before we send ACK. */ pj_time_val now; @@ -1080,10 +1186,10 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec = now.sec*1000 + now.msec; - msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1; + msec_expected = (1 << (g[tid].recv_count-2)) * pjsip_cfg()->tsx.t1; if (msec_expected > pjsip_cfg()->tsx.t2) msec_expected = pjsip_cfg()->tsx.t2; @@ -1092,12 +1198,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) " error: incorrect retransmission " "time (%d ms expected, %d ms received", msec_expected, msec)); - test_complete = -152; + g[tid].test_complete = -152; } - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; - } else if (recv_count == 5) { + } else if (g[tid].recv_count == 5) { pjsip_tx_data *tdata; pjsip_sip_uri *uri; pjsip_via_hdr *via; @@ -1114,12 +1220,14 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) &tdata); if (status != PJ_SUCCESS) { app_perror(" error: unable to create ACK", status); - test_complete = -153; + g[tid].test_complete = -153; return PJ_TRUE; } uri=(pjsip_sip_uri*)pjsip_uri_get_uri(tdata->msg->line.req.uri); - uri->transport_param = pj_str("loop-dgram"); + pj_strdup2(tdata->pool, &uri->transport_param, + g[tid].test_param->tp_type); + uri->port = g[tid].test_param->port; via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(TEST9_BRANCH_ID); @@ -1128,13 +1236,13 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send ACK", status); - test_complete = -154; + g[tid].test_complete = -154; } } else { PJ_LOG(3,(THIS_FILE," error: too many responses (%d)", - recv_count)); - test_complete = -155; + g[tid].recv_count)); + g[tid].test_complete = -155; } } @@ -1163,17 +1271,17 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { app_perror(" error: unable to create transaction", status); - test_complete = -150; + g[tid].test_complete = -150; return PJ_TRUE; } - + set_tsx_tid(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - schedule_send_response(rdata, &tsx_key, code1, 1000); + schedule_send_response(tid, rdata, &g[tid].tsx_key, code1, 1000); if (code2) - schedule_send_response(rdata, &tsx_key, code2, 2000); + schedule_send_response(tid, rdata, &g[tid].tsx_key, code2, 2000); } else { @@ -1188,7 +1296,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) /* * The generic test framework, used by most of the tests. */ -static int perform_test( char *target_uri, char *from_uri, +static int perform_test( unsigned tid, + char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method, int request_cnt, int request_interval_msec, @@ -1208,9 +1317,9 @@ static int perform_test( char *target_uri, char *from_uri, } /* Reset test. */ - recv_count = 0; - test_complete = 0; - tsx_key.slen = 0; + g[tid].recv_count = 0; + g[tid].test_complete = 0; + g[tid].tsx_key.slen = 0; /* Init headers. */ target = pj_str(target_uri); @@ -1239,7 +1348,7 @@ static int perform_test( char *target_uri, char *from_uri, timeout.sec += test_time; /* Wait until test complete. */ - while (!test_complete) { + while (!g[tid].test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); @@ -1279,24 +1388,24 @@ static int perform_test( char *target_uri, char *from_uri, } } - if (test_complete < 0) { + if (g[tid].test_complete < 0) { pjsip_transaction *tsx; - tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); + tsx = pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_grp_lock_release(tsx->grp_lock); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); - return test_complete; + return g[tid].test_complete; } /* Allow transaction to destroy itself */ flush_events(500); /* Make sure transaction has been destroyed. */ - if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { + if (pjsip_tsx_layer_find_tsx(&g[tid].tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -40; @@ -1325,7 +1434,7 @@ static int perform_test( char *target_uri, char *from_uri, ** ***************************************************************************** */ -static int tsx_basic_final_response_test(void) +static int tsx_basic_final_response_test(unsigned tid) { unsigned duration; int status; @@ -1335,16 +1444,16 @@ static int tsx_basic_final_response_test(void) /* Test duration must be greater than 32 secs if unreliable transport * is used. */ - duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; + duration = (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; - status = perform_test(TARGET_URI, FROM_URI, TEST1_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST1_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); if (status != 0) return status; PJ_LOG(3,(THIS_FILE," test2: basic sending non-2xx final response")); - status = perform_test(TARGET_URI, FROM_URI, TEST2_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST2_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); if (status != 0) return status; @@ -1359,17 +1468,17 @@ static int tsx_basic_final_response_test(void) ** ***************************************************************************** */ -static int tsx_basic_provisional_response_test(void) +static int tsx_basic_provisional_response_test(unsigned tid) { unsigned duration; int status; PJ_LOG(3,(THIS_FILE," test3: basic sending 2xx final response")); - duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; + duration = (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33; duration += 2; - status = perform_test(TARGET_URI, FROM_URI, TEST3_BRANCH_ID, duration, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST3_BRANCH_ID, duration, &pjsip_options_method, 1, 0, 0); return status; @@ -1384,7 +1493,8 @@ static int tsx_basic_provisional_response_test(void) ** ***************************************************************************** */ -static int tsx_retransmit_last_response_test(const char *title, +static int tsx_retransmit_last_response_test(unsigned tid, + const char *title, char *branch_id, int request_cnt, int status_code) @@ -1393,7 +1503,7 @@ static int tsx_retransmit_last_response_test(const char *title, PJ_LOG(3,(THIS_FILE," %s", title)); - status = perform_test(TARGET_URI, FROM_URI, branch_id, 5, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, branch_id, 5, &pjsip_options_method, request_cnt, 1000, 1); if (status && status != TEST_TIMEOUT_ERROR) @@ -1403,11 +1513,11 @@ static int tsx_retransmit_last_response_test(const char *title, return -31; } - terminate_our_tsx(status_code); + terminate_our_tsx(tid, status_code); flush_events(100); - if (test_complete != 1) - return test_complete; + if (g[tid].test_complete != 1) + return g[tid].test_complete; flush_events(100); return 0; @@ -1420,14 +1530,14 @@ static int tsx_retransmit_last_response_test(const char *title, ** ***************************************************************************** */ -static int tsx_final_response_retransmission_test(void) +static int tsx_final_response_retransmission_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test7: INVITE non-2xx final response retransmission")); - status = perform_test(TARGET_URI, FROM_URI, TEST7_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST7_BRANCH_ID, 33, /* Test duration must be greater than 32 secs */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1436,7 +1546,7 @@ static int tsx_final_response_retransmission_test(void) PJ_LOG(3,(THIS_FILE, " test8: INVITE 2xx final response retransmission")); - status = perform_test(TARGET_URI, FROM_URI, TEST8_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST8_BRANCH_ID, 33, /* Test duration must be greater than 32 secs */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1453,14 +1563,14 @@ static int tsx_final_response_retransmission_test(void) ** ***************************************************************************** */ -static int tsx_ack_test(void) +static int tsx_ack_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test9: receiving ACK for non-2xx final response")); - status = perform_test(TARGET_URI, FROM_URI, TEST9_BRANCH_ID, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, TEST9_BRANCH_ID, 20, /* allow 5 retransmissions */ &pjsip_invite_method, 1, 0, 0); if (status != 0) @@ -1481,7 +1591,7 @@ static int tsx_ack_test(void) ** ***************************************************************************** */ -int tsx_transport_failure_test(void) +int tsx_transport_failure_test(unsigned tid) { struct test_desc { @@ -1515,10 +1625,10 @@ int tsx_transport_failure_test(void) pj_time_val fail_time, end_test, now; PJ_LOG(3,(THIS_FILE, " %s", tests[i].title)); - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_delay(loop, tests[i].transport_delay); + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_delay(g[tid].loop, tests[i].transport_delay); - status = perform_test(TARGET_URI, FROM_URI, tests[i].branch_id, + status = perform_test(tid, g[tid].TARGET_URI, g[tid].FROM_URI, tests[i].branch_id, 0, &pjsip_invite_method, 1, 0, 1); if (status && status != TEST_TIMEOUT_ERROR) return status; @@ -1537,7 +1647,7 @@ int tsx_transport_failure_test(void) pjsip_endpt_handle_events(endpt, &interval); } while (PJ_TIME_VAL_LT(now, fail_time)); - pjsip_loop_set_failure(loop, 1, NULL); + pjsip_loop_set_failure(g[tid].loop, 1, NULL); PJ_LOG(5,(THIS_FILE, " transport loop fail mode set")); end_test = now; @@ -1547,9 +1657,9 @@ int tsx_transport_failure_test(void) pj_time_val interval = { 0, 1 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); - } while (!test_complete && PJ_TIME_VAL_LT(now, end_test)); + } while (!g[tid].test_complete && PJ_TIME_VAL_LT(now, end_test)); - if (test_complete != tests[i].result) { + if (g[tid].test_complete != tests[i].result) { PJ_LOG(3,(THIS_FILE, " error: expecting timeout")); return -41; } @@ -1564,99 +1674,95 @@ int tsx_transport_failure_test(void) ** ***************************************************************************** */ -int tsx_uas_test(unsigned index) +int tsx_uas_test(unsigned tid) { - struct tsx_test_param *param = &tsx_test[index]; + struct tsx_test_param *param = &tsx_test[tid]; pj_sockaddr_in addr; pj_status_t status; - test_param = param; - tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type); + g[tid].test_param = param; + g[tid].tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type); - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:tsx_uas_test@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:%d@127.0.0.1:%d;transport=%s", + tid, param->port, param->tp_type); + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), + "sip:tsx_uas_test@127.0.0.1:%d;transport=%s", + param->port, param->tp_type); /* Check if loop transport is configured. */ status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop); + &addr, sizeof(addr), NULL, &g[tid].loop); if (status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!")); return -10; } /* Register modules. */ - status = pjsip_endpt_register_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -3; - } - status = pjsip_endpt_register_module(endpt, &msg_sender); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to register module", status); - return -4; + if ((status=register_modules(tid)) != PJ_SUCCESS) { + pjsip_transport_dec_ref(g[tid].loop); + return -20; } /* TEST1_BRANCH_ID: Basic 2xx final response. * TEST2_BRANCH_ID: Basic non-2xx final response. */ - status = tsx_basic_final_response_test(); + status = tsx_basic_final_response_test(tid); if (status != 0) - return status; + goto on_return; /* TEST3_BRANCH_ID: with provisional response */ - status = tsx_basic_provisional_response_test(); + status = tsx_basic_provisional_response_test(tid); if (status != 0) - return status; + goto on_return; /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state */ - status = tsx_retransmit_last_response_test(TEST4_TITLE, + status = tsx_retransmit_last_response_test(tid, TEST4_TITLE, TEST4_BRANCH_ID, TEST4_REQUEST_COUNT, TEST4_STATUS_CODE); if (status != 0) - return status; + goto on_return; /* TEST5_BRANCH_ID: retransmit last response in PROCEEDING state */ - status = tsx_retransmit_last_response_test(TEST5_TITLE, + status = tsx_retransmit_last_response_test(tid, TEST5_TITLE, TEST5_BRANCH_ID, TEST5_REQUEST_COUNT, TEST5_STATUS_CODE); if (status != 0) - return status; + goto on_return; /* TEST6_BRANCH_ID: retransmit last response in COMPLETED state * This only applies to non-reliable transports, * since UAS transaction is destroyed as soon * as final response is sent for reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_retransmit_last_response_test(TEST6_TITLE, + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_retransmit_last_response_test(tid, TEST6_TITLE, TEST6_BRANCH_ID, TEST6_REQUEST_COUNT, TEST6_STATUS_CODE); if (status != 0) - return status; + goto on_return; } /* TEST7_BRANCH_ID: INVITE non-2xx final response retransmission test * TEST8_BRANCH_ID: INVITE 2xx final response retransmission test */ - status = tsx_final_response_retransmission_test(); + status = tsx_final_response_retransmission_test(tid); if (status != 0) - return status; + goto on_return; /* TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must * cease when ACK is received * Only applicable for non-reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_ack_test(); + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_ack_test(tid); if (status != 0) - return status; + goto on_return; } /* TEST10_BRANCH_ID: test transport failure in TRYING state. @@ -1666,27 +1772,18 @@ int tsx_uas_test(unsigned index) */ /* Only valid for loop-dgram */ if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { - status = tsx_transport_failure_test(); + status = tsx_transport_failure_test(tid); if (status != 0) - return status; + goto on_return; } + status = 0; - /* Register modules. */ - status = pjsip_endpt_unregister_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -8; - } - status = pjsip_endpt_unregister_module(endpt, &msg_sender); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -9; - } - +on_return: - if (loop) - pjsip_transport_dec_ref(loop); + if (g[tid].loop) + pjsip_transport_dec_ref(g[tid].loop); - return 0; + unregister_modules(tid); + return status; } From f7f73925d3137b77ceab672f5d07e43426f115b4 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 27 Jun 2024 14:08:53 +0700 Subject: [PATCH 20/79] Parallelize tsx_uac_test() --- pjsip/src/test/test.c | 41 ++- pjsip/src/test/tsx_uac_test.c | 547 +++++++++++++++++++--------------- pjsip/src/test/tsx_uas_test.c | 122 ++++---- 3 files changed, 392 insertions(+), 318 deletions(-) diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 99b7765429..b34d8a2e75 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -296,22 +296,6 @@ int test_main(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, regc_test, 0); #endif - /* Note: put exclusive tests last */ - -#if INCLUDE_UDP_TEST - /* Transport tests share same testing codes which are not reentrant */ - UT_ADD_TEST(&test_app.ut_app, transport_udp_test, PJ_TEST_EXCLUSIVE); -#endif - -#if INCLUDE_LOOP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_loop_test, PJ_TEST_EXCLUSIVE); -#endif - -#if INCLUDE_TCP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, PJ_TEST_EXCLUSIVE); -#endif - - #if INCLUDE_TSX_TEST PJ_TEST_SUCCESS(rc=pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp), NULL, goto on_return); @@ -329,17 +313,28 @@ int test_main(int argc, char *argv[]) ++tsx_test_cnt; #endif - /* each of these tests registers the same module hence must be exclusive */ for (i = 0; i < tsx_test_cnt; ++i) { - UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, - PJ_TEST_EXCLUSIVE); - UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, - PJ_TEST_EXCLUSIVE); - UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, - PJ_TEST_EXCLUSIVE); + UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, 0); + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); + UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, 0); } #endif + /* Note: put exclusive tests last */ + +#if INCLUDE_UDP_TEST + /* Transport tests share same testing codes which are not reentrant */ + UT_ADD_TEST(&test_app.ut_app, transport_udp_test, PJ_TEST_EXCLUSIVE); +#endif + +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, PJ_TEST_EXCLUSIVE); +#endif + +#if INCLUDE_TCP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, PJ_TEST_EXCLUSIVE); +#endif + /* * Better be last because it recreates the endpt */ diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c index b71b65c0ff..c56ef80ec9 100644 --- a/pjsip/src/test/tsx_uac_test.c +++ b/pjsip/src/test/tsx_uac_test.c @@ -77,25 +77,49 @@ ***************************************************************************** */ -static char *TEST1_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test1"; -static char *TEST2_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test2"; -static char *TEST3_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test3"; -static char *TEST4_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test4"; -static char *TEST5_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test5"; -static char *TEST6_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test6"; -static char *TEST7_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test7"; -static char *TEST8_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test8"; -static char *TEST9_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test9"; +static char *TEST1_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test01"; +static char *TEST2_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test02"; +static char *TEST3_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test03"; +static char *TEST4_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test04"; +static char *TEST5_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test05"; +static char *TEST6_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test06"; +static char *TEST7_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test07"; +static char *TEST8_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test08"; +static char *TEST9_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAC-Test09"; + +#define BRANCH_LEN (7+11) // An effort to accommodate CPU load spike on some test machines. #define TEST1_ALLOWED_DIFF 500 //(150) #define TEST4_RETRANSMIT_CNT 3 #define TEST5_RETRANSMIT_CNT 3 -static char TARGET_URI[128]; -static char FROM_URI[128]; -static unsigned tp_flag; -static struct tsx_test_param *test_param; +struct my_timer +{ + pj_timer_entry entry; + char key_buf[1024]; + pj_str_t tsx_key; +}; + + +static struct tsx_uac_test_global_t +{ + char TARGET_URI[128]; + char FROM_URI[128]; + unsigned tp_flag; + struct tsx_test_param *test_param; + + /* Static vars, which will be reset on each test. */ + int recv_count; + pj_time_val recv_last; + pj_bool_t test_complete; + + /* Loop transport instance. */ + pjsip_transport *loop; + + struct my_timer timer; + +} g[MAX_TSX_TESTS]; static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e); static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata); @@ -136,21 +160,19 @@ static pjsip_module msg_receiver = NULL, /* on_tsx_state() */ }; -/* Static vars, which will be reset on each test. */ -static int recv_count; -static pj_time_val recv_last; -static pj_bool_t test_complete; - -/* Loop transport instance. */ -static pjsip_transport *loop; +/* Get test ID from transaction instance */ +static unsigned get_tsx_tid(const pjsip_transaction *tsx) +{ + pj_assert(tsx_user.id >= 0); + return (unsigned)(long)tsx->mod_data[tsx_user.id]; +} -/* General timer entry to be used by tests. */ -static struct my_timer +/* Set test ID to transaction instance */ +static void set_tsx_tid(pjsip_transaction *tsx, unsigned tid) { - pj_timer_entry entry; - char key_buf[1024]; - pj_str_t tsx_key; -} timer; + pj_assert(tsx_user.id >= 0); + tsx->mod_data[tsx_user.id] = (void*)(long)tid; +} /* * This is the handler to receive state changed notification from the @@ -159,34 +181,36 @@ static struct my_timer */ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { - if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) { + unsigned tid = get_tsx_tid(tsx); + + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) { /* * Transaction with TEST1_BRANCH_ID should terminate with transaction * timeout status. */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; /* Test the status code. */ if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) { PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_TSX_TIMEOUT)); - test_complete = -710; + g[tid].test_complete = -710; } /* If transport is reliable, then there must not be any * retransmissions. */ - if (tp_flag & PJSIP_TRANSPORT_RELIABLE) { - if (recv_count != 1) { + if (g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) { + if (g[tid].recv_count != 1) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -715; + g[tid].recv_count)); + g[tid].test_complete = -715; } } else { /* Check the number of (re)transmissions, which must be @@ -197,29 +221,29 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) * fires first which causes recv_count fall short (by one). */ //if (tsx->method.id==PJSIP_INVITE_METHOD && recv_count != 7) { - if (tsx->method.id==PJSIP_INVITE_METHOD && recv_count < 6) { + if (tsx->method.id==PJSIP_INVITE_METHOD && g[tid].recv_count < 6) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -716; + g[tid].recv_count)); + g[tid].test_complete = -716; } else //if (tsx->method.id==PJSIP_OPTIONS_METHOD && recv_count != 11) { - if (tsx->method.id==PJSIP_OPTIONS_METHOD && recv_count < 10) { + if (tsx->method.id==PJSIP_OPTIONS_METHOD && g[tid].recv_count < 10) { PJ_LOG(3,(THIS_FILE, " error: there were %d (re)transmissions", - recv_count)); - test_complete = -717; + g[tid].recv_count)); + g[tid].test_complete = -717; } else if (tsx->method.id!=PJSIP_INVITE_METHOD && tsx->method.id!=PJSIP_OPTIONS_METHOD) { PJ_LOG(3,(THIS_FILE, " error: unexpected method")); - test_complete = -718; + g[tid].test_complete = -718; } } } - } else if (pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST2_BRANCH_ID, BRANCH_LEN)==0) { /* * Transaction with TEST2_BRANCH_ID should terminate with transport error. */ @@ -233,14 +257,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: status code is %d instead of %d or %d", tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR, PJSIP_SC_BAD_GATEWAY)); - test_complete = -720; + g[tid].test_complete = -720; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST3_BRANCH_ID, BRANCH_LEN)==0) { /* * This test terminates the transaction while resolver is still * running. @@ -257,15 +281,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_REQUEST_TERMINATED)); - test_complete = -730; + g[tid].test_complete = -730; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST4_BRANCH_ID, BRANCH_LEN)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -277,7 +301,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR)); - test_complete = -730; + g[tid].test_complete = -730; } /* Must have correct retransmission count. */ @@ -285,15 +309,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, TEST4_RETRANSMIT_CNT)); - test_complete = -731; + g[tid].test_complete = -731; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST5_BRANCH_ID, BRANCH_LEN)==0) { /* * This test simulates transport failure after several * retransmissions. @@ -305,7 +329,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, PJSIP_SC_REQUEST_TERMINATED)); - test_complete = -733; + g[tid].test_complete = -733; } /* Must have correct retransmission count. */ @@ -313,15 +337,15 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, TEST5_RETRANSMIT_CNT)); - test_complete = -734; + g[tid].test_complete = -734; } - if (test_complete == 0) - test_complete = 1; + if (g[tid].test_complete == 0) + g[tid].test_complete = 1; } - } else if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST6_BRANCH_ID, BRANCH_LEN)==0) { /* * Successfull non-INVITE transaction. */ @@ -332,7 +356,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 202)); - test_complete = -736; + g[tid].test_complete = -736; } /* Must have correct retransmission count. */ @@ -340,18 +364,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -737; + g[tid].test_complete = -737; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -738; + g[tid].test_complete = -738; } - if (test_complete == 0) { - test_complete = 1; + if (g[tid].test_complete == 0) { + g[tid].test_complete = 1; pjsip_tsx_terminate(tsx, 202); } @@ -359,12 +383,12 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -7381; + g[tid].test_complete = -7381; } } - } else if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN)==0) { /* * Successfull non-INVITE transaction. */ @@ -376,7 +400,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) " error: prev state is %s instead of %s", pjsip_tsx_state_str((pjsip_tsx_state_e)e->body.tsx_state.prev_state), pjsip_tsx_state_str(PJSIP_TSX_STATE_PROCEEDING))); - test_complete = -739; + g[tid].test_complete = -739; } /* Status code must be 202. */ @@ -384,7 +408,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 202)); - test_complete = -740; + g[tid].test_complete = -740; } /* Must have correct retransmission count. */ @@ -392,18 +416,18 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -741; + g[tid].test_complete = -741; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -741; + g[tid].test_complete = -741; } - if (test_complete == 0) { - test_complete = 1; + if (g[tid].test_complete == 0) { + g[tid].test_complete = 1; pjsip_tsx_terminate(tsx, 202); } @@ -411,13 +435,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -742; + g[tid].test_complete = -742; } } - } else if (pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST8_BRANCH_ID, BRANCH_LEN)==0) { /* * Failed INVITE transaction. */ @@ -428,7 +452,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 301)); - test_complete = -745; + g[tid].test_complete = -745; } /* Must have correct retransmission count. */ @@ -436,14 +460,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -746; + g[tid].test_complete = -746; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -747; + g[tid].test_complete = -747; } /* last_tx MUST be the INVITE request @@ -454,16 +478,16 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: last_tx is not INVITE")); - test_complete = -748; + g[tid].test_complete = -748; } } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -750; + g[tid].test_complete = -750; } /* Status code must be 301. */ @@ -471,13 +495,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 301)); - test_complete = -751; + g[tid].test_complete = -751; } } - } else if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + } else if (pj_strnicmp2(&tsx->branch, TEST9_BRANCH_ID, BRANCH_LEN)==0) { /* * Failed INVITE transaction with provisional response. */ @@ -485,7 +509,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be PJSIP_TSX_STATE_PROCEEDING. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) { - test_complete = -760; + g[tid].test_complete = -760; } /* Status code must be 302. */ @@ -493,7 +517,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 302)); - test_complete = -761; + g[tid].test_complete = -761; } /* Must have correct retransmission count. */ @@ -501,14 +525,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: retransmit cnt is %d instead of %d", tsx->retransmit_count, 0)); - test_complete = -762; + g[tid].test_complete = -762; } /* Must still keep last_tx */ if (tsx->last_tx == NULL) { PJ_LOG(3,(THIS_FILE, " error: transaction lost last_tx")); - test_complete = -763; + g[tid].test_complete = -763; } /* last_tx MUST be INVITE. @@ -519,17 +543,17 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { PJ_LOG(3,(THIS_FILE, " error: last_tx is not INVITE")); - test_complete = -764; + g[tid].test_complete = -764; } } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - test_complete = 1; + g[tid].test_complete = 1; /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { - test_complete = -767; + g[tid].test_complete = -767; } /* Status code must be 302. */ @@ -537,7 +561,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) PJ_LOG(3,(THIS_FILE, " error: status code is %d instead of %d", tsx->status_code, 302)); - test_complete = -768; + g[tid].test_complete = -768; } } @@ -594,7 +618,21 @@ static void terminate_tsx_callback( pj_timer_heap_t *timer_heap, */ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) { - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + pjsip_to_hdr *from_hdr = rdata->msg_info.from; + pjsip_sip_uri *from_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri); + unsigned tid; + + if (pj_strcmp2(&from_uri->user, "tsx_uac_test")) { + /* Not our message */ + return PJ_FALSE; + } + + tid = (unsigned)pj_strtol(&target->user); + + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST1_BRANCH_ID test performs the verifications for transaction * retransmission mechanism. It will not answer the incoming request @@ -611,14 +649,14 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJ_LOG(3,(THIS_FILE, " error: received unexpected method %.*s", (int)msg->line.req.method.name.slen, msg->line.req.method.name.ptr)); - test_complete = -600; + g[tid].test_complete = -600; return PJ_TRUE; } - if (recv_count == 0) { - recv_count++; + if (g[tid].recv_count == 0) { + g[tid].recv_count++; //pj_gettimeofday(&recv_last); - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } else { pj_time_val now; unsigned msec_expected, msec_elapsed; @@ -626,11 +664,11 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) //pj_gettimeofday(&now); now = rdata->pkt_info.timestamp; - PJ_TIME_VAL_SUB(now, recv_last); + PJ_TIME_VAL_SUB(now, g[tid].recv_last); msec_elapsed = now.sec*1000 + now.msec; - ++recv_count; - msec_expected = (1<<(recv_count-2))*pjsip_cfg()->tsx.t1; + ++g[tid].recv_count; + msec_expected = (1<<(g[tid].recv_count-2))*pjsip_cfg()->tsx.t1; if (msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (msec_expected > pjsip_cfg()->tsx.t2) @@ -644,53 +682,55 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJ_LOG(3,(THIS_FILE, " error: expecting retransmission no. %d in %d " "ms, received in %d ms", - recv_count-1, msec_expected, msec_elapsed)); - test_complete = -610; + g[tid].recv_count-1, msec_expected, msec_elapsed)); + g[tid].test_complete = -610; } - if (recv_count > max_received) { + if (g[tid].recv_count > max_received) { PJ_LOG(3,(THIS_FILE, " error: too many messages (%d) received", - recv_count)); - test_complete = -620; + g[tid].recv_count)); + g[tid].test_complete = -620; } //pj_gettimeofday(&recv_last); - recv_last = rdata->pkt_info.timestamp; + g[tid].recv_last = rdata->pkt_info.timestamp; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST4_BRANCH_ID test simulates transport failure after several * retransmissions. */ - recv_count++; + g[tid].recv_count++; - if (recv_count == TEST4_RETRANSMIT_CNT) { + if (g[tid].recv_count == TEST4_RETRANSMIT_CNT) { /* Simulate transport failure. */ - pjsip_loop_set_failure(loop, 2, NULL); + pjsip_loop_set_failure(g[tid].loop, 2, NULL); - } else if (recv_count > TEST4_RETRANSMIT_CNT) { + } else if (g[tid].recv_count > TEST4_RETRANSMIT_CNT) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -631; + g[tid].recv_count)); + g[tid].test_complete = -631; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST5_BRANCH_ID test simulates user terminating the transaction * after several retransmissions. */ - recv_count++; + g[tid].recv_count++; - if (recv_count == TEST5_RETRANSMIT_CNT+1) { + if (g[tid].recv_count == TEST5_RETRANSMIT_CNT+1) { pj_str_t key; pjsip_transaction *tsx; @@ -702,44 +742,46 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pj_grp_lock_release(tsx->grp_lock); } else { PJ_LOG(3,(THIS_FILE, " error: uac transaction not found!")); - test_complete = -633; + g[tid].test_complete = -633; } - } else if (recv_count > TEST5_RETRANSMIT_CNT+1) { + } else if (g[tid].recv_count > TEST5_RETRANSMIT_CNT+1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -634; + g[tid].recv_count)); + g[tid].test_complete = -634; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST6_BRANCH_ID test successfull non-INVITE transaction. */ pj_status_t status; - recv_count++; + g[tid].recv_count++; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -635; + g[tid].recv_count)); + g[tid].test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 202, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); - test_complete = -636; + g[tid].test_complete = -636; } return PJ_TRUE; } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST7_BRANCH_ID test successfull non-INVITE transaction * with provisional response. @@ -750,12 +792,12 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; - recv_count++; + g[tid].recv_count++; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -640; + g[tid].recv_count)); + g[tid].test_complete = -640; return PJ_TRUE; } @@ -781,14 +823,15 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); - timer.entry.cb = &send_response_callback; - timer.entry.user_data = r; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + g[tid].timer.entry.cb = &send_response_callback; + g[tid].timer.entry.user_data = r; + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); return (status == PJ_SUCCESS); } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST8_BRANCH_ID test failed INVITE transaction. */ @@ -797,26 +840,26 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) method = &rdata->msg_info.msg->line.req.method; - recv_count++; + g[tid].recv_count++; if (method->id == PJSIP_INVITE_METHOD) { - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -635; + g[tid].recv_count)); + g[tid].test_complete = -635; } status = pjsip_endpt_respond_stateless(endpt, rdata, 301, NULL, NULL, NULL); if (status != PJ_SUCCESS) { app_perror(" error: unable to send response", status); - test_complete = -636; + g[tid].test_complete = -636; } } else if (method->id == PJSIP_ACK_METHOD) { - if (recv_count == 2) { + if (g[tid].recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; @@ -828,30 +871,31 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); - pj_strcpy(&timer.tsx_key, &key); - timer.entry.id = 301; - timer.entry.cb = &terminate_tsx_callback; + pj_strcpy(&g[tid].timer.tsx_key, &key); + g[tid].timer.entry.id = 301; + g[tid].timer.entry.cb = &terminate_tsx_callback; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } - if (recv_count > 2) { + if (g[tid].recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -638; + g[tid].recv_count)); + g[tid].test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); - test_complete = -639; + g[tid].test_complete = -639; } } else - if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) { + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID, + BRANCH_LEN) == 0) { /* * The TEST9_BRANCH_ID test failed INVITE transaction with * provisional response. @@ -861,7 +905,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) method = &rdata->msg_info.msg->line.req.method; - recv_count++; + g[tid].recv_count++; if (method->id == PJSIP_INVITE_METHOD) { @@ -870,10 +914,10 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pjsip_tx_data *tdata; pj_time_val delay = { 2, 0 }; - if (recv_count > 1) { + if (g[tid].recv_count > 1) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -650; + g[tid].recv_count)); + g[tid].test_complete = -650; return PJ_TRUE; } @@ -901,13 +945,13 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) if (r->res_addr.transport) pjsip_transport_add_ref(r->res_addr.transport); - timer.entry.cb = &send_response_callback; - timer.entry.user_data = r; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + g[tid].timer.entry.cb = &send_response_callback; + g[tid].timer.entry.user_data = r; + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } else if (method->id == PJSIP_ACK_METHOD) { - if (recv_count == 2) { + if (g[tid].recv_count == 2) { pj_str_t key; pj_time_val delay = { 5, 0 }; @@ -919,24 +963,24 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) PJSIP_ROLE_UAC, &pjsip_invite_method, rdata); - pj_strcpy(&timer.tsx_key, &key); - timer.entry.id = 302; - timer.entry.cb = &terminate_tsx_callback; + pj_strcpy(&g[tid].timer.tsx_key, &key); + g[tid].timer.entry.id = 302; + g[tid].timer.entry.cb = &terminate_tsx_callback; - pjsip_endpt_schedule_timer(endpt, &timer.entry, &delay); + pjsip_endpt_schedule_timer(endpt, &g[tid].timer.entry, &delay); } - if (recv_count > 2) { + if (g[tid].recv_count > 2) { PJ_LOG(3,(THIS_FILE," error: not expecting %d-th packet!", - recv_count)); - test_complete = -638; + g[tid].recv_count)); + g[tid].test_complete = -638; } } else { PJ_LOG(3,(THIS_FILE," error: not expecting %s", pjsip_rx_data_get_info(rdata))); - test_complete = -639; + g[tid].test_complete = -639; } @@ -950,26 +994,28 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) /* * The generic test framework, used by most of the tests. */ -static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, - char *branch_param, int test_time, +static int perform_tsx_test(unsigned tid, int dummy, char *target_uri, + char *from_uri, char *branch_param, int test_time, const pjsip_method *method) { pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_str_t target, from, tsx_key; pjsip_via_hdr *via; + char branch_buf[BRANCH_LEN+20]; pj_time_val timeout; pj_status_t status; PJ_UNUSED_ARG(dummy); + PJ_TEST_EQ(strlen(branch_param), BRANCH_LEN, NULL, return -99); PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ - recv_count = 0; - test_complete = 0; + g[tid].recv_count = 0; + g[tid].test_complete = 0; /* Init headers. */ target = pj_str(target_uri); @@ -981,12 +1027,16 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); - return -100; + return -105; } - /* Set the branch param for test 1. */ + /* Set the branch param. Note that other tsx_uac_test() instances may + * be running simultaneously, thus the branch ID needs to be made unique + * by adding tid */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(branch_param); + pj_ansi_snprintf(branch_buf, sizeof(branch_buf), + "%s-%02d", branch_param, tid); + pj_strdup2(tdata->pool, &via->branch_param, branch_buf); /* Add additional reference to tdata to prevent transaction from * deleting it. @@ -1000,6 +1050,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, pjsip_tx_data_dec_ref(tdata); return -110; } + set_tsx_tid(tsx, tid); /* Get transaction key. */ pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); @@ -1020,7 +1071,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, timeout.sec += test_time; /* Wait until test complete. */ - while (!test_complete) { + while (!g[tid].test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); @@ -1033,7 +1084,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, } } - if (test_complete < 0) { + if (g[tid].test_complete < 0) { tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); @@ -1041,7 +1092,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, flush_events(1000); } pjsip_tx_data_dec_ref(tdata); - return test_complete; + return g[tid].test_complete; } else { pj_time_val now; @@ -1091,7 +1142,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, ** ***************************************************************************** */ -static int tsx_uac_retransmit_test(void) +static int tsx_uac_retransmit_test(unsigned tid) { int status = 0, enabled; int i; @@ -1126,19 +1177,19 @@ static int tsx_uac_retransmit_test(void) sub_test[i].delay)); /* Configure transport */ - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_recv_delay(loop, sub_test[i].delay, NULL); + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_recv_delay(g[tid].loop, sub_test[i].delay, NULL); /* Do the test. */ - status = perform_tsx_test(-500, TARGET_URI, FROM_URI, - TEST1_BRANCH_ID, + status = perform_tsx_test(tid, -500, g[tid].TARGET_URI, + g[tid].FROM_URI, TEST1_BRANCH_ID, 35, sub_test[i].method); if (status != 0) break; } /* Restore transport. */ - pjsip_loop_set_recv_delay(loop, 0, NULL); + pjsip_loop_set_recv_delay(g[tid].loop, 0, NULL); /* Restore msg logger. */ msg_logger_set_enabled(enabled); @@ -1158,7 +1209,7 @@ static int tsx_uac_retransmit_test(void) ** ***************************************************************************** */ -static int tsx_resolve_error_test(void) +static int tsx_resolve_error_test(unsigned tid) { int status = 0; @@ -1169,9 +1220,9 @@ static int tsx_resolve_error_test(void) */ PJ_LOG(3,(THIS_FILE, " variant a: immediate resolving error")); - status = perform_tsx_test(-800, + status = perform_tsx_test(tid, -800, "sip:bob@unresolved-host", - FROM_URI, TEST2_BRANCH_ID, 20, + g[tid].FROM_URI, TEST2_BRANCH_ID, 20, &pjsip_options_method); if (status != 0) return status; @@ -1182,20 +1233,20 @@ static int tsx_resolve_error_test(void) PJ_LOG(3,(THIS_FILE, " variant b: error via callback")); /* This only applies to "loop-dgram" transport */ - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { /* Set loop transport to return delayed error. */ - pjsip_loop_set_failure(loop, 2, NULL); - pjsip_loop_set_send_callback_delay(loop, 10, NULL); + pjsip_loop_set_failure(g[tid].loop, 2, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, 10, NULL); - status = perform_tsx_test(-800, TARGET_URI, FROM_URI, - TEST2_BRANCH_ID, 2, + status = perform_tsx_test(tid, -800, g[tid].TARGET_URI, + g[tid].FROM_URI, TEST2_BRANCH_ID, 2, &pjsip_options_method); if (status != 0) return status; /* Restore loop transport settings. */ - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_send_callback_delay(loop, 0, NULL); + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, 0, NULL); } return status; @@ -1210,7 +1261,7 @@ static int tsx_resolve_error_test(void) ** ***************************************************************************** */ -static int tsx_terminate_resolving_test(void) +static int tsx_terminate_resolving_test(unsigned tid) { unsigned prev_delay; pj_status_t status; @@ -1218,14 +1269,14 @@ static int tsx_terminate_resolving_test(void) PJ_LOG(3,(THIS_FILE, " test3: terminate while resolving test")); /* Configure transport delay. */ - pjsip_loop_set_send_callback_delay(loop, 100, &prev_delay); + pjsip_loop_set_send_callback_delay(g[tid].loop, 100, &prev_delay); /* Start the test. */ - status = perform_tsx_test(-900, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -900, g[tid].TARGET_URI, g[tid].FROM_URI, TEST3_BRANCH_ID, 2, &pjsip_options_method); /* Restore delay. */ - pjsip_loop_set_send_callback_delay(loop, prev_delay, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); return status; } @@ -1241,7 +1292,7 @@ static int tsx_terminate_resolving_test(void) ** ***************************************************************************** */ -static int tsx_retransmit_fail_test(void) +static int tsx_retransmit_fail_test(unsigned tid) { int i; unsigned delay[] = {0, 10}; @@ -1257,13 +1308,13 @@ static int tsx_retransmit_fail_test(void) " variant %c: transport delay %d ms", ('a'+i), delay[i])); /* Configure transport delay. */ - pjsip_loop_set_send_callback_delay(loop, delay[i], NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, delay[i], NULL); /* Restore transport failure mode. */ - pjsip_loop_set_failure(loop, 0, 0); + pjsip_loop_set_failure(g[tid].loop, 0, 0); /* Start the test. */ - status = perform_tsx_test(-1000, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1000, g[tid].TARGET_URI, g[tid].FROM_URI, TEST4_BRANCH_ID, 6, &pjsip_options_method); if (status != 0) @@ -1272,10 +1323,10 @@ static int tsx_retransmit_fail_test(void) } /* Restore delay. */ - pjsip_loop_set_send_callback_delay(loop, 0, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, 0, NULL); /* Restore transport failure mode. */ - pjsip_loop_set_failure(loop, 0, 0); + pjsip_loop_set_failure(g[tid].loop, 0, 0); return status; } @@ -1287,14 +1338,14 @@ static int tsx_retransmit_fail_test(void) ** ***************************************************************************** */ -static int tsx_terminate_after_retransmit_test(void) +static int tsx_terminate_after_retransmit_test(unsigned tid) { int status; PJ_LOG(3,(THIS_FILE, " test5: terminate after retransmissions")); /* Do the test. */ - status = perform_tsx_test(-1100, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1100, g[tid].TARGET_URI, g[tid].FROM_URI, TEST5_BRANCH_ID, 6, &pjsip_options_method); @@ -1312,7 +1363,8 @@ static int tsx_terminate_after_retransmit_test(void) ** ***************************************************************************** */ -static int perform_generic_test( const char *title, +static int perform_generic_test( unsigned tid, + const char *title, char *branch_id, const pjsip_method *method) { @@ -1324,23 +1376,23 @@ static int perform_generic_test( const char *title, /* Do the test. */ for (i=0; i<(int)PJ_ARRAY_SIZE(delay); ++i) { - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { PJ_LOG(3,(THIS_FILE, " variant %c: with %d ms transport delay", ('a'+i), delay[i])); - pjsip_loop_set_delay(loop, delay[i]); + pjsip_loop_set_delay(g[tid].loop, delay[i]); } - status = perform_tsx_test(-1200, TARGET_URI, FROM_URI, + status = perform_tsx_test(tid, -1200, g[tid].TARGET_URI, g[tid].FROM_URI, branch_id, 10, method); if (status != 0) return status; - if (test_param->type != PJSIP_TRANSPORT_LOOP_DGRAM) + if (g[tid].test_param->type != PJSIP_TRANSPORT_LOOP_DGRAM) break; } - pjsip_loop_set_delay(loop, 0); + pjsip_loop_set_delay(g[tid].loop, 0); /* Done. */ return status; @@ -1353,58 +1405,66 @@ static int perform_generic_test( const char *title, ** ***************************************************************************** */ -int tsx_uac_test(unsigned index) +int tsx_uac_test(unsigned tid) { #define ERR(rc__) { status=rc__; goto on_return; } - struct tsx_test_param *param = &tsx_test[index]; + struct tsx_test_param *param = &tsx_test[tid]; pj_sockaddr_in addr; int status; - timer.tsx_key.ptr = timer.key_buf; + g[tid].timer.tsx_key.ptr = g[tid].timer.key_buf; - test_param = param; + g[tid].test_param = param; /* Get transport flag */ - tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)test_param->type); - - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), "sip:bob@127.0.0.1:%d;transport=%s", - param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), "sip:tsx_uac_test@127.0.0.1:%d;transport=%s", + g[tid].tp_flag = pjsip_transport_get_flag_from_type( + (pjsip_transport_type_e)g[tid].test_param->type); + + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:%d@127.0.0.1:%d;transport=%s", + tid, param->port, param->tp_type); + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), + "sip:tsx_uac_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); /* Check if loop transport is configured. */ PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &loop), + &addr, sizeof(addr), NULL, &g[tid].loop), NULL, ERR(-10)); /* Register modules. */ + pj_enter_critical_section(); if (tsx_user.id == -1) { - PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, ERR(-30)); + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, + { status=-30; pj_leave_critical_section(); goto on_return; }); } + if (msg_receiver.id == -1) { - PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, ERR(-40)); + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, + { status=-40; pj_leave_critical_section(); goto on_return; }); } + pj_leave_critical_section(); /* TEST1_BRANCH_ID: Basic retransmit and timeout test. */ - status = tsx_uac_retransmit_test(); + status = tsx_uac_retransmit_test(tid); if (status != 0) goto on_return; /* TEST2_BRANCH_ID: Resolve error test. */ - status = tsx_resolve_error_test(); + status = tsx_resolve_error_test(tid); if (status != 0) goto on_return; /* TEST3_BRANCH_ID: UAC terminate while resolving test. */ - status = tsx_terminate_resolving_test(); + status = tsx_terminate_resolving_test(tid); if (status != 0) goto on_return; /* TEST4_BRANCH_ID: Transport failed after several retransmissions. * Only applies to loop transport. */ - if (test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { - status = tsx_retransmit_fail_test(); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + status = tsx_retransmit_fail_test(tid); if (status != 0) goto on_return; } @@ -1412,56 +1472,55 @@ int tsx_uac_test(unsigned index) /* TEST5_BRANCH_ID: Terminate transaction after several retransmissions * Only applicable to non-reliable transports. */ - if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { - status = tsx_terminate_after_retransmit_test(); + if ((g[tid].tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) { + status = tsx_terminate_after_retransmit_test(tid); if (status != 0) goto on_return; } /* TEST6_BRANCH_ID: Successfull non-invite transaction */ - status = perform_generic_test("test6: successfull non-invite transaction", + status = perform_generic_test(tid, + "test6: successfull non-invite transaction", TEST6_BRANCH_ID, &pjsip_options_method); if (status != 0) goto on_return; /* TEST7_BRANCH_ID: Successfull non-invite transaction */ - status = perform_generic_test("test7: successfull non-invite transaction " + status = perform_generic_test(tid, + "test7: successfull non-invite transaction " "with provisional response", TEST7_BRANCH_ID, &pjsip_options_method); if (status != 0) goto on_return; /* TEST8_BRANCH_ID: Failed invite transaction */ - status = perform_generic_test("test8: failed invite transaction", + status = perform_generic_test(tid, + "test8: failed invite transaction", TEST8_BRANCH_ID, &pjsip_invite_method); if (status != 0) goto on_return; /* TEST9_BRANCH_ID: Failed invite transaction with provisional response */ - status = perform_generic_test("test9: failed invite transaction with " + status = perform_generic_test(tid, + "test9: failed invite transaction with " "provisional response", TEST9_BRANCH_ID, &pjsip_invite_method); if (status != 0) goto on_return; - pjsip_transport_dec_ref(loop); + pjsip_transport_dec_ref(g[tid].loop); flush_events(500); on_return: - /* Unregister modules. */ + /* Note: don't unregister modules on_tsx_state() may be called when + * transaction layer is shutdown later, which will cause + * get_tsx_tid() to be called and it needs the module id. + */ if (tsx_user.id != -1) { - status = pjsip_endpt_unregister_module(endpt, &tsx_user); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -31; - } + //status = pjsip_endpt_unregister_module(endpt, &tsx_user); } if (msg_receiver.id != -1) { - status = pjsip_endpt_unregister_module(endpt, &msg_receiver); - if (status != PJ_SUCCESS) { - app_perror(" Error: unable to unregister module", status); - return -41; - } + //status = pjsip_endpt_unregister_module(endpt, &msg_receiver); } return status; #undef ERR diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index ffcfab0dcb..30f2100595 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -94,20 +94,22 @@ ** **/ -#define TEST1_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test1") -#define TEST2_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test2") -#define TEST3_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test3") -#define TEST4_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test4") -#define TEST5_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test5") -#define TEST6_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test6") -#define TEST7_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test7") -#define TEST8_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test8") -#define TEST9_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test9") +#define TEST1_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test01") +#define TEST2_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test02") +#define TEST3_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test03") +#define TEST4_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test04") +#define TEST5_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test05") +#define TEST6_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test06") +#define TEST7_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test07") +#define TEST8_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test08") +#define TEST9_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test09") #define TEST10_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test10") #define TEST11_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test11") #define TEST12_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test12") //#define TEST13_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test13") +#define BRANCH_LEN (7+11) + #define TEST1_STATUS_CODE 200 #define TEST2_STATUS_CODE 301 #define TEST3_PROVISIONAL_CODE PJSIP_SC_QUEUED @@ -124,7 +126,7 @@ #define TEST6_RESPONSE_COUNT 3 #define TEST7_STATUS_CODE 301 #define TEST8_STATUS_CODE 302 -#define TEST9_STATUS_CODE 301 +#define TEST9_STATUS_CODE 301 /* Must be non-2xx */ #define TEST4_TITLE "test4: absorbing request retransmission" @@ -450,8 +452,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { unsigned tid = get_tsx_tid(tsx); - if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST2_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits final @@ -460,7 +462,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) * * TEST2_BRANCH_ID does similar test for non-2xx final response. */ - int status_code = (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ? + int status_code = (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { @@ -490,7 +492,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST3_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST3_BRANCH_ID tests sending provisional response. */ @@ -553,7 +555,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } else - if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST4_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST4_BRANCH_ID tests receiving retransmissions in TRYING state. */ @@ -586,7 +588,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST5_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST5_BRANCH_ID tests receiving retransmissions in PROCEEDING state */ @@ -623,7 +625,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } else - if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST6_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST6_BRANCH_ID tests receiving retransmissions in COMPLETED state */ @@ -658,8 +660,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST8_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST7_BRANCH_ID and TEST8_BRANCH_ID test retransmission of @@ -667,7 +669,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) */ int code; - if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID) == 0) + if (pj_strnicmp2(&tsx->branch, TEST7_BRANCH_ID, BRANCH_LEN) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; @@ -735,7 +737,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) { + if (pj_strnicmp2(&tsx->branch, TEST9_BRANCH_ID, BRANCH_LEN)==0) { /* * TEST9_BRANCH_ID tests that retransmission of INVITE final response * must cease when ACK is received. @@ -800,8 +802,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } else - if (pj_stricmp2(&tsx->branch, TEST10_BRANCH_ID)==0 || - pj_stricmp2(&tsx->branch, TEST12_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST10_BRANCH_ID, BRANCH_LEN)==0 || + pj_strnicmp2(&tsx->branch, TEST12_BRANCH_ID, BRANCH_LEN)==0) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { @@ -818,7 +820,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) } } } else - if (pj_stricmp2(&tsx->branch, TEST11_BRANCH_ID)==0) + if (pj_strnicmp2(&tsx->branch, TEST11_BRANCH_ID, BRANCH_LEN)==0) { if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { @@ -863,10 +865,19 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pj_status_t status; pjsip_to_hdr *to_hdr = rdata->msg_info.to; pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); - unsigned tid = (unsigned)pj_strtol(&target->user); + pjsip_to_hdr *from_hdr = rdata->msg_info.from; + pjsip_sip_uri *from_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri); + unsigned tid; + + if (pj_strcmp2(&from_uri->user, "tsx_uas_test")) { + /* Not our message */ + return PJ_FALSE; + } - if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0) + tid = (unsigned)pj_strtol(&target->user); + + if (pj_strnicmp2(&branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST2_BRANCH_ID, BRANCH_LEN) == 0) { /* * TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx @@ -875,7 +886,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) * * TEST2_BRANCH_ID performs similar test for non-2xx final response. */ - int status_code = (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0) ? + int status_code = (pj_strnicmp2(&branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0) ? TEST1_STATUS_CODE : TEST2_STATUS_CODE; if (msg->type == PJSIP_REQUEST_MSG) { @@ -916,7 +927,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST3_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST3_BRANCH_ID, + BRANCH_LEN) == 0) { /* TEST3_BRANCH_ID tests provisional response. */ @@ -966,9 +978,9 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state. */ @@ -991,12 +1003,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0) { - } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0) { send_response(rdata, tsx, TEST5_PROVISIONAL_CODE); - } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { PJ_LOG(4,(THIS_FILE, " sending provisional response")); send_response(rdata, tsx, TEST6_PROVISIONAL_CODE); PJ_LOG(4,(THIS_FILE, " sending final response")); @@ -1010,11 +1022,11 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) ++g[tid].recv_count; - if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST4_BRANCH_ID, BRANCH_LEN) == 0) { PJ_LOG(3,(THIS_FILE, " error: not expecting response!")); g[tid].test_complete = -132; - } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST5_BRANCH_ID, BRANCH_LEN) == 0) { if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) { PJ_LOG(3,(THIS_FILE, " error: incorrect status code!")); @@ -1026,7 +1038,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -134; } - } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST6_BRANCH_ID, BRANCH_LEN) == 0) { int code = rdata->msg_info.msg->line.status.code; @@ -1055,8 +1067,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST8_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST8_BRANCH_ID, BRANCH_LEN) == 0) { /* @@ -1078,7 +1090,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); - if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) { + if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0) { send_response(rdata, tsx, TEST7_STATUS_CODE); @@ -1093,7 +1105,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) ++g[tid].recv_count; - if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) + if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0) code = TEST7_STATUS_CODE; else code = TEST8_STATUS_CODE; @@ -1141,7 +1153,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST9_BRANCH_ID) == 0) { + } else if (pj_strnicmp2(&branch_param, TEST9_BRANCH_ID, BRANCH_LEN) == 0) { /* * TEST9_BRANCH_ID tests that the retransmission of INVITE final @@ -1230,7 +1242,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) uri->port = g[tid].test_param->port; via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(TEST9_BRANCH_ID); + pj_strdup(tdata->pool, &via->branch_param, + &rdata->msg_info.via->branch_param); status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL); @@ -1248,15 +1261,15 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } return PJ_TRUE; - } else if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0 || - pj_stricmp2(&branch_param, TEST12_BRANCH_ID) == 0) + } else if (pj_strnicmp2(&branch_param, TEST10_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST11_BRANCH_ID, BRANCH_LEN) == 0 || + pj_strnicmp2(&branch_param, TEST12_BRANCH_ID, BRANCH_LEN) == 0) { int test_num, code1, code2; - if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0) + if (pj_strnicmp2(&branch_param, TEST10_BRANCH_ID, BRANCH_LEN) == 0) test_num=10, code1 = 100, code2 = 0; - else if (pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0) + else if (pj_strnicmp2(&branch_param, TEST11_BRANCH_ID, BRANCH_LEN) == 0) test_num=11, code1 = 100, code2 = 200; else test_num=12, code1 = 200, code2 = 0; @@ -1306,10 +1319,13 @@ static int perform_test( unsigned tid, pjsip_tx_data *tdata; pj_str_t target, from; pjsip_via_hdr *via; + char branch_buf[BRANCH_LEN+20]; pj_time_val timeout, next_send; int sent_cnt; pj_status_t status; + PJ_TEST_EQ(strlen(branch_param), BRANCH_LEN, NULL, return -99); + if (test_time > 0) { PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", @@ -1334,9 +1350,13 @@ static int perform_test( unsigned tid, return -10; } - /* Set the branch param for test 1. */ + /* Set the branch param. Note that other tsx_uas_test() instances may + * be running simultaneously, thus the branch ID needs to be made unique + * by adding tid */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param = pj_str(branch_param); + pj_ansi_snprintf(branch_buf, sizeof(branch_buf), + "%s-%02d", branch_param, tid); + pj_strdup2(tdata->pool, &via->branch_param, branch_buf); /* Schedule first send. */ sent_cnt = 0; From 64d5a619defd7bafa304be2b19f49a1abaa65d0d Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 27 Jun 2024 18:50:09 +0700 Subject: [PATCH 21/79] Further effort to parallize tests in pjsip-test. Discoverd major issue with unit-test logging (see unittest.md) --- pjlib/src/pjlib-test/test_util.h | 2 +- pjsip/src/test/inv_offer_answer_test.c | 14 +------ pjsip/src/test/regc_test.c | 14 +++++-- pjsip/src/test/test.h | 13 +++++++ pjsip/src/test/transport_test.c | 44 +++++++++++++++------- pjsip/src/test/tsx_basic_test.c | 33 +++++++++-------- pjsip/src/test/tsx_uas_test.c | 2 +- unittest.md | 51 ++++++++++++++++++++++++++ 8 files changed, 126 insertions(+), 47 deletions(-) diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 363cc0dcf2..e1cf206b3b 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -189,9 +189,9 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, runner_prm.nthreads>1?"s":"")); pj_test_run(runner, &ut_app->suite); pj_test_runner_destroy(runner); + pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); pj_test_get_stat(&ut_app->suite, &stat); pj_test_display_stat(&stat, title, THIS_FILE); - pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); return stat.nfailed ? PJ_EBUG : PJ_SUCCESS; } diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c index 1b05ebc6cc..a1b3ba99f8 100644 --- a/pjsip/src/test/inv_offer_answer_test.c +++ b/pjsip/src/test/inv_offer_answer_test.c @@ -189,16 +189,6 @@ static pjmedia_sdp_session *create_sdp(pj_pool_t *pool, const char *body) return sdp; } -/* Check that the message is part of our test. Multiple tests sharing the - * same SIP endpoint may run simultaneously - */ -static pj_bool_t is_our_message(const pjsip_from_hdr *from_hdr) -{ - const pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) - pjsip_uri_get_uri(from_hdr->uri); - return pj_strcmp2(&sip_uri->user, "inv_offer_answer_test")==0; -} - /**************** INVITE SESSION CALLBACKS ******************/ static void on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer) @@ -325,7 +315,7 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) pj_str_t uri; pjsip_tx_data *tdata; - if (!is_our_message(rdata->msg_info.from)) + if (!is_user_equal(rdata->msg_info.from, "inv_offer_answer_test")) return PJ_FALSE; /* @@ -527,7 +517,7 @@ static pj_bool_t log_on_rx_msg(pjsip_rx_data *rdata) pjsip_msg *msg = rdata->msg_info.msg; char info[80]; - if (!is_our_message(rdata->msg_info.from)) + if (!is_user_equal(rdata->msg_info.from, "inv_offer_answer_test")) return PJ_FALSE; if (msg->type == PJSIP_REQUEST_MSG) diff --git a/pjsip/src/test/regc_test.c b/pjsip/src/test/regc_test.c index 9708208bd2..357b9e0427 100644 --- a/pjsip/src/test/regc_test.c +++ b/pjsip/src/test/regc_test.c @@ -39,14 +39,14 @@ static struct NULL, NULL, /* prev, next. */ { "mod-send", 8 }, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ - &mod_send_on_tx_request, /* on_tx_request. */ + &mod_send_on_tx_request, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }, @@ -57,7 +57,12 @@ static struct static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata) { - PJ_UNUSED_ARG(tdata); + const pjsip_from_hdr *from_hdr = (const pjsip_from_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + + if ((tdata->msg->line.req.method.id != PJSIP_REGISTER_METHOD) || + !is_user_equal(from_hdr, "regc-test")) + return PJ_FALSE; if (++send_mod.count > send_mod.count_before_reject) return PJ_ECANCELLED; @@ -120,7 +125,8 @@ static pj_bool_t regs_rx_request(pjsip_rx_data *rdata) int code; pj_status_t status; - if (msg->line.req.method.id != PJSIP_REGISTER_METHOD) + if ((rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) || + !is_user_equal(rdata->msg_info.from, "regc-test")) return PJ_FALSE; if (!registrar.cfg.respond) diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 641e14f2e5..0727fdb0d2 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -20,6 +20,8 @@ #define __TEST_H__ #include +#include +#include extern pjsip_endpoint *endpt; extern pj_caching_pool caching_pool; @@ -127,6 +129,17 @@ void flush_events(unsigned duration); void report_ival(const char *name, int value, const char *valname, const char *desc); void report_sval(const char *name, const char* value, const char *valname, const char *desc); +/* Utility to check if the user part of From/To is equal to the string */ +PJ_INLINE(pj_bool_t) is_user_equal(const pjsip_fromto_hdr *hdr, const char *user) +{ + const pjsip_sip_uri *sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(hdr->uri); + const pj_str_t *scheme = pjsip_uri_get_scheme(sip_uri); + + if (pj_stricmp2(scheme, "sip") && pj_stricmp2(scheme, "sips")) + return PJ_FALSE; + + return pj_strcmp2(&sip_uri->user, user)==0; +} /* Settings. */ extern int log_level; diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index 450e0f8daa..7f314e21c5 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -71,11 +71,12 @@ int generic_transport_test(pjsip_transport *tp) * The main purpose is to test that the basic transport functionalities works, * before we continue with more complicated tests. */ -#define FROM_HDR "Bob " -#define CONTACT_HDR "Bob " -#define CALL_ID_HDR "SendRecv-Test" -#define CSEQ_VALUE 100 -#define BODY "Hello World!" +#define SEND_RECV_FROM_HDR "Bob " +#define SEND_RECV_CALL_ID_HDR "Transport-SendRecv-Test" +#define RT_FROM_HDR "Bob " +#define CONTACT_HDR "Bob " +#define CSEQ_VALUE 100 +#define BODY "Hello World!" static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata); @@ -89,7 +90,7 @@ static int recv_status = NO_STATUS; static pj_timestamp my_send_time, my_recv_time; /* Module to receive messages for this test. */ -static pjsip_module my_module = +static pjsip_module send_recv_module = { NULL, NULL, /* prev and next */ { "Transport-Test", 14}, /* Name. */ @@ -107,8 +108,11 @@ static pjsip_module my_module = static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) { + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) + return PJ_FALSE; + /* Check that this is our request. */ - if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) { + if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { /* It is! */ /* Send response. */ pjsip_tx_data *tdata; @@ -141,7 +145,10 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata) { - if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) { + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) + return PJ_FALSE; + + if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { pj_get_timestamp(&my_recv_time); recv_status = PJ_SUCCESS; return PJ_TRUE; @@ -186,8 +193,8 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ - if (my_module.id == -1) { - status = pjsip_endpt_register_module( endpt, &my_module ); + if (send_recv_module.id == -1) { + status = pjsip_endpt_register_module( endpt, &send_recv_module ); if (status != PJ_SUCCESS) { app_perror(" error: unable to register module", status); return -500; @@ -199,10 +206,10 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, /* Create a request message. */ target = pj_str(target_url); - from = pj_str(FROM_HDR); + from = pj_str(SEND_RECV_FROM_HDR); to = pj_str(target_url); contact = pj_str(CONTACT_HDR); - call_id = pj_str(CALL_ID_HDR); + call_id = pj_str(SEND_RECV_CALL_ID_HDR); body = pj_str(BODY); pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); @@ -334,6 +341,9 @@ static pj_str_t rt_call_id; static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) { + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) + return PJ_FALSE; + if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { pjsip_tx_data *tdata; pjsip_response_addr res_addr; @@ -373,7 +383,7 @@ static pj_status_t rt_send_request(int thread_id) /* Create a request message. */ target = pj_str(rt_target_uri); - from = pj_str(FROM_HDR); + from = pj_str(RT_FROM_HDR); to = pj_str(rt_target_uri); contact = pj_str(CONTACT_HDR); call_id = rt_test_data[thread_id].call_id; @@ -419,6 +429,9 @@ static pj_status_t rt_send_request(int thread_id) static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata) { + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) + return PJ_FALSE; + if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1; int thread_id = (*pos - '0'); @@ -680,6 +693,9 @@ static struct mod_load_test static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata) { + if (!is_user_equal(rdata->msg_info.from, "transport_load_test")) + return PJ_FALSE; + if (rdata->msg_info.cseq->cseq != mod_load.next_seq) { PJ_LOG(1,(THIS_FILE, " err: expecting cseq %u, got %u", mod_load.next_seq, rdata->msg_info.cseq->cseq)); @@ -720,7 +736,7 @@ int transport_load_test(char *target_url) pjsip_tx_data *tdata; target = pj_str(target_url); - from = pj_str(""); + from = pj_str(""); call_id = pj_str("thecallid"); status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c index 0019172e67..fb3b2590a5 100644 --- a/pjsip/src/test/tsx_basic_test.c +++ b/pjsip/src/test/tsx_basic_test.c @@ -23,12 +23,15 @@ #define THIS_FILE "tsx_basic_test.c" -static char TARGET_URI[PJSIP_MAX_URL_SIZE]; -static char FROM_URI[PJSIP_MAX_URL_SIZE]; +static struct tsx_basic_test_global_t +{ + char TARGET_URI[PJSIP_MAX_URL_SIZE]; + char FROM_URI[PJSIP_MAX_URL_SIZE]; +} g[MAX_TSX_TESTS]; /* Test transaction layer. */ -static int tsx_layer_test(void) +static int tsx_layer_test(unsigned tid) { pj_str_t target, from, tsx_key; pjsip_tx_data *tdata; @@ -37,8 +40,8 @@ static int tsx_layer_test(void) PJ_LOG(3,(THIS_FILE, " transaction layer test")); - target = pj_str(TARGET_URI); - from = pj_str(FROM_URI); + target = pj_str(g[tid].TARGET_URI); + from = pj_str(g[tid].FROM_URI); status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, &target, NULL, NULL, -1, NULL, @@ -72,7 +75,7 @@ static int tsx_layer_test(void) } /* Double terminate test. */ -static int double_terminate(void) +static int double_terminate(unsigned tid) { pj_str_t target, from, tsx_key; pjsip_tx_data *tdata; @@ -81,8 +84,8 @@ static int double_terminate(void) PJ_LOG(3,(THIS_FILE, " double terminate test")); - target = pj_str(TARGET_URI); - from = pj_str(FROM_URI); + target = pj_str(g[tid].TARGET_URI); + from = pj_str(g[tid].FROM_URI); /* Create request. */ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, @@ -135,23 +138,23 @@ static int double_terminate(void) return PJ_SUCCESS; } -int tsx_basic_test(unsigned index) +int tsx_basic_test(unsigned tid) { - struct tsx_test_param *param = &tsx_test[index]; + struct tsx_test_param *param = &tsx_test[tid]; int status; - pj_ansi_snprintf(TARGET_URI, sizeof(TARGET_URI), - "sip:bob@127.0.0.1:%d;transport=%s", + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), + "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - pj_ansi_snprintf(FROM_URI, sizeof(FROM_URI), + pj_ansi_snprintf(g[tid].FROM_URI, sizeof(g[tid].FROM_URI), "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - status = tsx_layer_test(); + status = tsx_layer_test(tid); if (status != 0) return status; - status = double_terminate(); + status = double_terminate(tid); if (status != 0) return status; diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 30f2100595..f5d8373830 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -865,7 +865,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pj_status_t status; pjsip_to_hdr *to_hdr = rdata->msg_info.to; pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); - pjsip_to_hdr *from_hdr = rdata->msg_info.from; + pjsip_from_hdr *from_hdr = rdata->msg_info.from; pjsip_sip_uri *from_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(from_hdr->uri); unsigned tid; diff --git a/unittest.md b/unittest.md index a5339faf0f..ecb5009c10 100644 --- a/unittest.md +++ b/unittest.md @@ -203,6 +203,57 @@ The `fifobuf` feature has been there for the longest time (it was part of pjlib ### 4. Tips, known issues and considerations +### [MAJOR] Wrong logging report with multithreading + +Consider two test cases running on two threads: + +test0() is running on thread 0: + +``` +/* simplified on_rx_request() module callback */ +pj_bool_t on_rx_message0(pjsip_rx_data *rdata) +{ + if (pj_strcmp2(&rdata->msg_info.from->user, "test0")) { + PJ_LOG(1,(THIS_FILE, "test0 got message")); + return PJ_TRUE; + } + return PJ_FALSE; +} + +/* test0() is running on thread 0 */ +int test0() +{ + send_request( .., from_uri="sip:test0@127.0.0.1" ); + while (1) + pjsip_endpt_handle_events(..); +} +``` + +test1() is running on thread 1: + +``` +/* simplified on_rx_request() module callback */ +pj_bool_t on_rx_message1(pjsip_rx_data *rdata) +{ + if (pj_strcmp2(&rdata->msg_info.from->user, "test1")) { + PJ_LOG(1,(THIS_FILE, "test1 got message")); + return PJ_TRUE; + } + return PJ_FALSE; +} + +/* test1() is running on thread 1 */ +int test1() +{ + send_request( .., from_uri="sip:test1@127.0.0.1" ); + while (1) + pjsip_endpt_handle_events(..); +} +``` + +The tests above should run correctly, in the sense that `on_rx_message0()` and `on_rx_message1()` will work correctly. However, logging messages `"test0 got message"` or `"test1 got message"` may appear in the wrong test, because we don't know which thread will get the event. If thread 1 happens to get message for test 0, then `"test0 got message"` will appear in test1 log. + + ### Basic runner limitations There can only be one basic runner running at any single time, because it stores current test in a global variable. From 2b90407d320bc72eb3b1dcc6abfc4ace66ddd367 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sun, 30 Jun 2024 09:46:40 +0700 Subject: [PATCH 22/79] Showing PJLIB config is optional with cmd line option --- pjlib-util/src/pjlib-util-test/test.c | 3 ++- pjlib/src/pjlib-test/test.c | 5 +++-- pjlib/src/pjlib-test/test_util.h | 4 ++++ pjmedia/src/test/test.c | 4 ++++ pjnath/src/pjnath-test/test.c | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c index b652aaa736..cc519c949e 100644 --- a/pjlib-util/src/pjlib-util-test/test.c +++ b/pjlib-util/src/pjlib-util-test/test.c @@ -54,7 +54,8 @@ static int test_inner(int argc, char *argv[]) if (ut_app_init1(&test_app.ut_app, mem) != PJ_SUCCESS) return 1; - pj_dump_config(); + if (test_app.ut_app.prm_config) + pj_dump_config(); #if INCLUDE_XML_TEST UT_ADD_TEST(&test_app.ut_app, xml_test, 0); diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 3642b2420b..8256675869 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -104,12 +104,13 @@ static pj_test_stat essential_tests(int argc, char *argv[]) pj_bzero(&stat, sizeof(stat)); + if (test_app.ut_app.prm_config) + pj_dump_config(); + /* Test the unit-testing framework first, outside unit-test! * Only perform the test if user is not requesting specific test. */ if (argc==1 && !test_app.ut_app.prm_list_test) { - pj_dump_config(); - PJ_LOG(3,(THIS_FILE, "Testing the unit-test framework (basic)")); if (unittest_basic_test()) { stat.nfailed = 1; diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index e1cf206b3b..5052a2ec43 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -26,6 +26,7 @@ /* Unit testing app */ typedef struct ut_app_t { + pj_bool_t prm_config; pj_test_select_tests prm_logging_policy; int prm_nthreads; int prm_list_test; @@ -198,6 +199,7 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, static void ut_usage() { + puts(" -c, --config Show configuration macros"); puts(" -l 0,1,2,3 0: Don't show logging after tests"); puts(" 1: Show logs of only failed tests (default)"); puts(" 2: Show logs of only successful tests"); @@ -215,6 +217,8 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) int itmp = -1; pj_status_t status; + ut_app->prm_config = pj_argparse_get_bool("-c", argc, argv) || + pj_argparse_get_bool("--config", argc, argv); ut_app->prm_list_test = pj_argparse_get_bool("-L", argc, argv) || pj_argparse_get_bool("--list", argc, argv); ut_app->prm_stop_on_error = pj_argparse_get_bool("--stop-err", argc, argv); diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c index 6eee9c34ca..a7eb075851 100644 --- a/pjmedia/src/test/test.c +++ b/pjmedia/src/test/test.c @@ -51,6 +51,10 @@ int test_main(int argc, char *argv[]) pj_pool_t *pool = NULL; PJ_TEST_SUCCESS(pj_init(), NULL, return 1); + + if (test_app.ut_app.prm_config) + pj_dump_config(); + pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); PJ_TEST_NOT_NULL(pool=pj_pool_create(&caching_pool.factory, "test", 1000, 512, NULL), diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 15fb33516f..41130524fb 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -217,7 +217,8 @@ static int test_inner(int argc, char *argv[]) PJ_TEST_SUCCESS(pj_init(), NULL, {if (log_file) fclose(log_file); return 1; }); - pj_dump_config(); + if (test_app.ut_app.prm_config) + pj_dump_config(); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); PJ_TEST_SUCCESS(pjlib_util_init(), NULL, {rc=2; goto on_return;}); From 9caadb2e49a69848ffbb552d8920be2578867cc8 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sun, 30 Jun 2024 12:38:17 +0700 Subject: [PATCH 23/79] Bug fixing failed pjsip tests when running in parallel mode --- pjsip/src/test/main.c | 10 +- pjsip/src/test/test.c | 28 +++++- pjsip/src/test/test.h | 7 +- pjsip/src/test/transport_loop_test.c | 2 + pjsip/src/test/transport_test.c | 4 + pjsip/src/test/tsx_uac_test.c | 140 ++++++++++++++++++--------- pjsip/src/test/tsx_uas_test.c | 15 ++- unittest.md | 2 + 8 files changed, 143 insertions(+), 65 deletions(-) diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index fb5ffbd218..fbefd16b52 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -31,14 +31,14 @@ static void usage(void) puts(""); puts("where OPTIONS:"); puts(""); - puts(" -h,--help Show this screen"); + puts(" -h,--help Show this screen"); ut_usage(); - puts(" -i,--interractive Key input at the end."); - puts(" -n,--no-trap Let signals be handled by the OS"); - puts(" --log-level N Set log level (0-6)"); - puts(" -s,--system NAME Set system name to NAME"); + puts(" -i,--interractive Key input at the end."); + puts(" -n,--no-trap Let signals be handled by the OS"); + puts(" --log-level N Set log level (0-6)"); + puts(" -s,--system NAME Set system name to NAME"); } #if (PJ_LINUX || PJ_DARWINOS) && defined(PJ_HAS_EXECINFO_H) && PJ_HAS_EXECINFO_H != 0 diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index b34d8a2e75..ba59012ce3 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -230,7 +230,9 @@ int test_main(int argc, char *argv[]) PJ_TEST_SUCCESS(pjlib_util_init(), NULL, return 20); PJ_TEST_SUCCESS(init_report(), NULL, return 30); - pj_dump_config(); + if (test_app.ut_app.prm_config) { + pj_dump_config(); + } pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, PJSIP_TEST_MEM_SIZE ); @@ -241,8 +243,6 @@ int test_main(int argc, char *argv[]) return 40; }); - PJ_LOG(3,(THIS_FILE," ")); - /* Init logger module. */ init_msg_logger(); msg_logger_set_enabled(1); @@ -313,15 +313,33 @@ int test_main(int argc, char *argv[]) ++tsx_test_cnt; #endif + pj_assert(tsx_test[0].type == PJSIP_TRANSPORT_LOOP_DGRAM); + for (i = 0; i < tsx_test_cnt; ++i) { UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, 0); - UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); + /* tsx_uac_test for loop dgram will be added later because it's exclusive */ + if (i!=0) + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, 0); } + + if (tsx_test_cnt) { + /* tsx_uac_test for loop dgram sets various delay to the loop dgram. + * This will mess up other tests that are also uses loop-dgram + * such as tsx_uas_test(0). So run it exclusively. */ + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)0, + PJ_TEST_EXCLUSIVE); + + /* tsx_transport_failure_test() also sets transport loop delay, hence + * it must run exclusively. */ + UT_ADD_TEST1(&test_app.ut_app, tsx_transport_failure_test, + (void*)(long)0, PJ_TEST_EXCLUSIVE); + + } #endif /* Note: put exclusive tests last */ - + #if INCLUDE_UDP_TEST /* Transport tests share same testing codes which are not reentrant */ UT_ADD_TEST(&test_app.ut_app, transport_udp_test, PJ_TEST_EXCLUSIVE); diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 0727fdb0d2..ec77884ee3 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -100,9 +100,10 @@ struct tsx_test_param }; extern struct tsx_test_param tsx_test[MAX_TSX_TESTS]; -int tsx_basic_test(unsigned index); -int tsx_uac_test(unsigned index); -int tsx_uas_test(unsigned index); +int tsx_basic_test(unsigned tid); +int tsx_uac_test(unsigned tid); +int tsx_uas_test(unsigned tid); +int tsx_transport_failure_test(unsigned tid); /* Transport test helpers (transport_test.c). */ int generic_transport_test(pjsip_transport *tp); diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 63b346439a..8bd791ab77 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -44,6 +44,8 @@ static int datagram_loop_test() return -20; } + pjsip_loop_set_failure(loop, 0, 0); + /* Get initial reference counter */ ref_cnt = pj_atomic_get(loop->ref_cnt); diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index 7f314e21c5..28ec703c1a 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -164,6 +164,8 @@ static void send_msg_callback(pjsip_send_state *stateless_data, if (sent < 1) { /* Obtain the error code. */ + PJ_LOG(3,(THIS_FILE, " Sending %s got callback error %ld", + stateless_data->tdata->info, -sent)); send_status = (int)-sent; } else { send_status = PJ_SUCCESS; @@ -234,6 +236,8 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ + PJ_LOG(3,(THIS_FILE, " Sending %s to %.*s got immediate error %d", + tdata->info, (int)target.slen, target.ptr, status)); pjsip_tx_data_dec_ref(tdata); send_status = status; } diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c index c56ef80ec9..95d5bf6cd6 100644 --- a/pjsip/src/test/tsx_uac_test.c +++ b/pjsip/src/test/tsx_uac_test.c @@ -142,13 +142,17 @@ static pjsip_module tsx_user = &tsx_user_on_tsx_state, /* on_tsx_state() */ }; -/* Module to receive the loop-backed request. */ +/* Module to receive the loop-backed request and also process tx msgs. */ static pjsip_module msg_receiver = { NULL, NULL, /* prev and next */ { "Msg-Receiver", 12}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + /* Note: + * Priority needs to be more important than UA layer, because UA layer + * silently absorbs ACK with To tag. + */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -160,6 +164,44 @@ static pjsip_module msg_receiver = NULL, /* on_tsx_state() */ }; +/* Init uac test */ +static int init_test(unsigned tid) +{ + pj_sockaddr_in addr; + + pj_enter_critical_section(); + if (tsx_user.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, + { pj_leave_critical_section(); return -30; }); + } + + if (msg_receiver.id == -1) { + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, + { pj_leave_critical_section(); return -40; }); + } + pj_leave_critical_section(); + + pj_assert(g[tid].loop==NULL); + PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, + &addr, sizeof(addr), NULL, &g[tid].loop), + NULL, return -50); + + return PJ_SUCCESS; +} + +/* Finish test */ +static void finish_test(unsigned tid) +{ + /* Note: don't unregister modules on_tsx_state() may be called when + * transaction layer is shutdown later, which will cause + * get_tsx_tid() to be called and it needs the module id. + */ + if (g[tid].loop) { + pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = 0; + } +} + /* Get test ID from transaction instance */ static unsigned get_tsx_tid(const pjsip_transaction *tsx) { @@ -183,6 +225,13 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { unsigned tid = get_tsx_tid(tsx); + PJ_LOG(3,(THIS_FILE, + " on_tsx_state state: %s, event: %s (%s)", + pjsip_tsx_state_str(tsx->state), + pjsip_event_str(e->type), + pjsip_event_str(e->body.tsx_state.type) + )); + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) { /* * Transaction with TEST1_BRANCH_ID should terminate with transaction @@ -487,6 +536,9 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Previous state must be COMPLETED. */ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { + PJ_LOG(3,(THIS_FILE, + " error: expecting last state=COMLETED instead of %d", + e->body.tsx_state.prev_state)); g[tid].test_complete = -750; } @@ -631,6 +683,12 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) tid = (unsigned)pj_strtol(&target->user); + PJ_LOG(3,(THIS_FILE, " on_rx_request %s (recv_count: %d) on %s, branch: %.*s", + pjsip_rx_data_get_info(rdata), + g[tid].recv_count, rdata->tp_info.transport->info, + (int)rdata->msg_info.via->branch_param.slen, + rdata->msg_info.via->branch_param.ptr)); + if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0) { /* @@ -838,8 +896,8 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) pjsip_method *method; pj_status_t status; + method = &rdata->msg_info.msg->line.req.method; - g[tid].recv_count++; if (method->id == PJSIP_INVITE_METHOD) { @@ -1162,7 +1220,6 @@ static int tsx_uac_retransmit_test(unsigned tid) PJ_LOG(3,(THIS_FILE, " test1: basic uac retransmit and timeout test")); - /* For this test. message printing shound be disabled because it makes * incorrect timing. */ @@ -1177,8 +1234,10 @@ static int tsx_uac_retransmit_test(unsigned tid) sub_test[i].delay)); /* Configure transport */ - pjsip_loop_set_failure(g[tid].loop, 0, NULL); - pjsip_loop_set_recv_delay(g[tid].loop, sub_test[i].delay, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_failure(g[tid].loop, 0, NULL); + pjsip_loop_set_recv_delay(g[tid].loop, sub_test[i].delay, NULL); + } /* Do the test. */ status = perform_tsx_test(tid, -500, g[tid].TARGET_URI, @@ -1189,7 +1248,9 @@ static int tsx_uac_retransmit_test(unsigned tid) } /* Restore transport. */ - pjsip_loop_set_recv_delay(g[tid].loop, 0, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_recv_delay(g[tid].loop, 0, NULL); + } /* Restore msg logger. */ msg_logger_set_enabled(enabled); @@ -1234,19 +1295,23 @@ static int tsx_resolve_error_test(unsigned tid) /* This only applies to "loop-dgram" transport */ if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + int prev_fail = 0; + unsigned prev_delay = 0; + /* Set loop transport to return delayed error. */ - pjsip_loop_set_failure(g[tid].loop, 2, NULL); - pjsip_loop_set_send_callback_delay(g[tid].loop, 10, NULL); + pjsip_loop_set_failure(g[tid].loop, 2, &prev_fail); + pjsip_loop_set_send_callback_delay(g[tid].loop, 10, &prev_delay); status = perform_tsx_test(tid, -800, g[tid].TARGET_URI, g[tid].FROM_URI, TEST2_BRANCH_ID, 2, &pjsip_options_method); - if (status != 0) - return status; /* Restore loop transport settings. */ - pjsip_loop_set_failure(g[tid].loop, 0, NULL); - pjsip_loop_set_send_callback_delay(g[tid].loop, 0, NULL); + pjsip_loop_set_failure(g[tid].loop, prev_fail, NULL); + pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); + + if (status != 0) + return status; } return status; @@ -1269,14 +1334,18 @@ static int tsx_terminate_resolving_test(unsigned tid) PJ_LOG(3,(THIS_FILE, " test3: terminate while resolving test")); /* Configure transport delay. */ - pjsip_loop_set_send_callback_delay(g[tid].loop, 100, &prev_delay); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_send_callback_delay(g[tid].loop, 100, &prev_delay); + } /* Start the test. */ status = perform_tsx_test(tid, -900, g[tid].TARGET_URI, g[tid].FROM_URI, TEST3_BRANCH_ID, 2, &pjsip_options_method); /* Restore delay. */ - pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_send_callback_delay(g[tid].loop, prev_delay, NULL); + } return status; } @@ -1372,7 +1441,7 @@ static int perform_generic_test( unsigned tid, unsigned delay[] = { 1, 200 }; PJ_LOG(3,(THIS_FILE, " %s", title)); - + /* Do the test. */ for (i=0; i<(int)PJ_ARRAY_SIZE(delay); ++i) { @@ -1380,6 +1449,7 @@ static int perform_generic_test( unsigned tid, PJ_LOG(3,(THIS_FILE, " variant %c: with %d ms transport delay", ('a'+i), delay[i])); + pjsip_loop_set_failure(g[tid].loop, 0, 0); pjsip_loop_set_delay(g[tid].loop, delay[i]); } @@ -1392,7 +1462,9 @@ static int perform_generic_test( unsigned tid, break; } - pjsip_loop_set_delay(g[tid].loop, 0); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_loop_set_delay(g[tid].loop, 0); + } /* Done. */ return status; @@ -1409,7 +1481,6 @@ int tsx_uac_test(unsigned tid) { #define ERR(rc__) { status=rc__; goto on_return; } struct tsx_test_param *param = &tsx_test[tid]; - pj_sockaddr_in addr; int status; g[tid].timer.tsx_key.ptr = g[tid].timer.key_buf; @@ -1427,25 +1498,9 @@ int tsx_uac_test(unsigned tid) "sip:tsx_uac_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - /* Check if loop transport is configured. */ - PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &g[tid].loop), - NULL, ERR(-10)); - - /* Register modules. */ - pj_enter_critical_section(); - if (tsx_user.id == -1) { - PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, - { status=-30; pj_leave_critical_section(); goto on_return; }); - } - - if (msg_receiver.id == -1) { - PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &msg_receiver), NULL, - { status=-40; pj_leave_critical_section(); goto on_return; }); - } - pj_leave_critical_section(); + if ((status=init_test(tid)) != 0) + return status; - /* TEST1_BRANCH_ID: Basic retransmit and timeout test. */ status = tsx_uac_retransmit_test(tid); if (status != 0) goto on_return; @@ -1497,6 +1552,7 @@ int tsx_uac_test(unsigned tid) status = perform_generic_test(tid, "test8: failed invite transaction", TEST8_BRANCH_ID, &pjsip_invite_method); + if (status != 0) goto on_return; @@ -1508,20 +1564,10 @@ int tsx_uac_test(unsigned tid) if (status != 0) goto on_return; - pjsip_transport_dec_ref(g[tid].loop); flush_events(500); on_return: - /* Note: don't unregister modules on_tsx_state() may be called when - * transaction layer is shutdown later, which will cause - * get_tsx_tid() to be called and it needs the module id. - */ - if (tsx_user.id != -1) { - //status = pjsip_endpt_unregister_module(endpt, &tsx_user); - } - if (msg_receiver.id != -1) { - //status = pjsip_endpt_unregister_module(endpt, &msg_receiver); - } + finish_test(tid); return status; #undef ERR } diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index f5d8373830..2be54de1cc 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -684,7 +684,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) /* Check status code. */ if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) { - PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); + PJ_LOG(3,(THIS_FILE, " error: incorrect status code %d", + tsx->status_code)); g[tid].test_complete = -150; } @@ -1070,7 +1071,6 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) } else if (pj_strnicmp2(&branch_param, TEST7_BRANCH_ID, BRANCH_LEN) == 0 || pj_strnicmp2(&branch_param, TEST8_BRANCH_ID, BRANCH_LEN) == 0) { - /* * TEST7_BRANCH_ID and TEST8_BRANCH_ID test the retransmission * of INVITE final response @@ -1113,7 +1113,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) if (g[tid].recv_count==1) { if (rdata->msg_info.msg->line.status.code != code) { - PJ_LOG(3,(THIS_FILE," error: invalid status code")); + PJ_LOG(3,(THIS_FILE," error: invalid status code %d", + rdata->msg_info.msg->line.status.code)); g[tid].test_complete = -141; } @@ -1662,7 +1663,7 @@ int tsx_transport_failure_test(unsigned tid) pj_time_val_normalize(&fail_time); do { - pj_time_val interval = { 0, 1 }; + pj_time_val interval = { 0, 10 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); } while (PJ_TIME_VAL_LT(now, fail_time)); @@ -1674,7 +1675,7 @@ int tsx_transport_failure_test(unsigned tid) end_test.sec += 33; do { - pj_time_val interval = { 0, 1 }; + pj_time_val interval = { 0, 10 }; pj_gettimeofday(&now); pjsip_endpt_handle_events(endpt, &interval); } while (!g[tid].test_complete && PJ_TIME_VAL_LT(now, end_test)); @@ -1791,11 +1792,15 @@ int tsx_uas_test(unsigned tid) * TEST13_BRANCH_ID: test transport failure in CONFIRMED state. */ /* Only valid for loop-dgram */ + /* 2024-06-28: + * tsx_transport_failure_test() is now run directly from test.c because + * it sets transport loop delay, hence it must run exclusively. if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { status = tsx_transport_failure_test(tid); if (status != 0) goto on_return; } + */ status = 0; diff --git a/unittest.md b/unittest.md index ecb5009c10..1c2943a6e7 100644 --- a/unittest.md +++ b/unittest.md @@ -188,6 +188,8 @@ Having said that, some minor modifications were done: Original: 28m22.744s First porting: 27m1.894s +4 worker threads: 14m12.509s +10 worker threads: 13m2.482s Changed `tsx_basic_test`, `tsx_uac_test`, `tsx_uas_test` to take the index to parameters rather than the parameter itself to make the test output more informative. From e8864880d77351f81d3bc9221b122a502408bcbb Mon Sep 17 00:00:00 2001 From: bennylp Date: Mon, 1 Jul 2024 20:28:35 +0700 Subject: [PATCH 24/79] Finished paralleizing all pjsip tests except one --- pjsip/src/pjsip/sip_resolve.c | 3 +- pjsip/src/test/inv_offer_answer_test.c | 3 +- pjsip/src/test/main.c | 16 ++ pjsip/src/test/test.c | 32 +-- pjsip/src/test/test.h | 9 +- pjsip/src/test/transport_loop_test.c | 188 +++++++++++-- pjsip/src/test/transport_tcp_test.c | 19 +- pjsip/src/test/transport_test.c | 376 ++++++++++++++----------- pjsip/src/test/transport_udp_test.c | 4 +- pjsip/src/test/tsx_uac_test.c | 57 +++- pjsip/src/test/tsx_uas_test.c | 63 ++++- unittest.md | 11 +- 12 files changed, 532 insertions(+), 249 deletions(-) diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index eebf47e45a..b58e67ac6c 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -440,10 +440,11 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP || type == PJSIP_TRANSPORT_UDP6) query->naptr[0].res_type = pj_str("_sip._udp."); + else if (type == PJSIP_TRANSPORT_LOOP_DGRAM) + query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); - } } else { diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c index a1b3ba99f8..5ffdc70d5b 100644 --- a/pjsip/src/test/inv_offer_answer_test.c +++ b/pjsip/src/test/inv_offer_answer_test.c @@ -25,7 +25,7 @@ #define THIS_FILE "inv_offer_answer_test.c" #define PORT 5068 #define CONTACT "sip:inv_offer_answer_test@127.0.0.1:5068" -#define TRACE_(x) PJ_LOG(3,x) +#define TRACE_(x) //PJ_LOG(3,x) static struct oa_sdp_t { @@ -277,6 +277,7 @@ static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) const char *who = NULL; PJ_UNUSED_ARG(e); + PJ_UNUSED_ARG(who); if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { TRACE_((THIS_FILE, " %s call disconnected", diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index fbefd16b52..e9b6ffd4dc 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -24,6 +24,20 @@ extern const char *system_name; +static void warn(void) +{ + puts("*******************************************************************"); + puts("** W A R N I N G **"); + puts("*******************************************************************"); + puts("** Due to centralized event processing in PJSIP, events may be **"); + puts("** read by different thread than the test's thread. This may **"); + puts("** cause logging to be sent to the wrong test when multithreaded **"); + puts("** testing is used. The test results are correct, but do not **"); + puts("** trust the log. **"); + puts("** For debugging with correct logging, use \"-w 0 --log-no-cache\" **"); + puts("*******************************************************************"); +} + static void usage(void) { puts("Usage:"); @@ -75,6 +89,8 @@ int main(int argc, char *argv[]) int retval; int no_trap = 0; + warn(); + if (pj_argparse_get_bool("-h", &argc, argv) || pj_argparse_get_bool("--help", &argc, argv)) { diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index ba59012ce3..92e6e491f6 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -251,10 +251,6 @@ int test_main(int argc, char *argv[]) PJ_TEST_SUCCESS(rc = pjsip_tsx_layer_init_module(endpt), NULL, goto on_return); - /* Create loop transport. */ - PJ_TEST_SUCCESS(rc = pjsip_loop_start(endpt, NULL), - NULL, goto on_return); - tsx_test[tsx_test_cnt].port = 5060; tsx_test[tsx_test_cnt].tp_type = "loop-dgram"; tsx_test[tsx_test_cnt].type = PJSIP_TRANSPORT_LOOP_DGRAM; @@ -284,6 +280,10 @@ int test_main(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, tsx_bench, 0); #endif +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_multi_test, 0); +#endif + #if INCLUDE_RESOLVE_TEST UT_ADD_TEST(&test_app.ut_app, resolve_test, 0); #endif @@ -317,40 +317,24 @@ int test_main(int argc, char *argv[]) for (i = 0; i < tsx_test_cnt; ++i) { UT_ADD_TEST1(&test_app.ut_app, tsx_basic_test, (void*)(long)i, 0); - /* tsx_uac_test for loop dgram will be added later because it's exclusive */ - if (i!=0) - UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); + UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)i, 0); UT_ADD_TEST1(&test_app.ut_app, tsx_uas_test, (void*)(long)i, 0); } - - if (tsx_test_cnt) { - /* tsx_uac_test for loop dgram sets various delay to the loop dgram. - * This will mess up other tests that are also uses loop-dgram - * such as tsx_uas_test(0). So run it exclusively. */ - UT_ADD_TEST1(&test_app.ut_app, tsx_uac_test, (void*)(long)0, - PJ_TEST_EXCLUSIVE); - - /* tsx_transport_failure_test() also sets transport loop delay, hence - * it must run exclusively. */ - UT_ADD_TEST1(&test_app.ut_app, tsx_transport_failure_test, - (void*)(long)0, PJ_TEST_EXCLUSIVE); - - } #endif /* Note: put exclusive tests last */ #if INCLUDE_UDP_TEST /* Transport tests share same testing codes which are not reentrant */ - UT_ADD_TEST(&test_app.ut_app, transport_udp_test, PJ_TEST_EXCLUSIVE); + UT_ADD_TEST(&test_app.ut_app, transport_udp_test, 0); #endif #if INCLUDE_LOOP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_loop_test, PJ_TEST_EXCLUSIVE); + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, 0); #endif #if INCLUDE_TCP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, PJ_TEST_EXCLUSIVE); + UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, 0); #endif /* diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index ec77884ee3..7bd686e612 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -85,6 +85,7 @@ int tsx_bench(void); int tsx_destroy_test(void); int transport_udp_test(void); int transport_loop_test(void); +int transport_loop_multi_test(void); int transport_tcp_test(void); int resolve_test(void); int regc_test(void); @@ -103,19 +104,19 @@ extern struct tsx_test_param tsx_test[MAX_TSX_TESTS]; int tsx_basic_test(unsigned tid); int tsx_uac_test(unsigned tid); int tsx_uas_test(unsigned tid); -int tsx_transport_failure_test(unsigned tid); /* Transport test helpers (transport_test.c). */ int generic_transport_test(pjsip_transport *tp); int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *p_usec_rtt); int transport_rt_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *pkt_lost); -int transport_load_test(char *target_url); +int transport_load_test(pjsip_transport_type_e tp_type, + const char *host_port_transport); /* Test main entry */ int test_main(int argc, char *argv[]); diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 8bd791ab77..86a0c6d7da 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -23,26 +23,166 @@ #define THIS_FILE "transport_loop_test.c" +static pjsip_transport *cur_loop; +static int loop_test_status; + +static pj_bool_t on_rx_request(pjsip_rx_data *rdata); +static pj_bool_t on_rx_response(pjsip_rx_data *rdata); +static pj_status_t on_tx_message(pjsip_tx_data *tdata); + +static pjsip_module loop_tester_mod = +{ + NULL, NULL, /* prev and next */ + { "transport_loop_test", 19}, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &on_rx_request, /* on_rx_request() */ + &on_rx_response, /* on_rx_response() */ + &on_tx_message, /* on_tx_request() */ + &on_tx_message, /* on_tx_response() */ + NULL, /* on_tsx_state() */ +}; + + +static pj_bool_t on_rx_request(pjsip_rx_data *rdata) +{ +#define ERR(rc__) {loop_test_status=rc__; return PJ_TRUE; } + if (!is_user_equal(rdata->msg_info.from, "transport_loop_multi_test")) + return PJ_FALSE; + + PJ_TEST_EQ(rdata->tp_info.transport, cur_loop, NULL, ERR(-100)); + PJ_TEST_SUCCESS(pjsip_endpt_respond_stateless(endpt, rdata, PJSIP_SC_ACCEPTED, + NULL, NULL, NULL), + NULL, ERR(-100)); + return PJ_TRUE; +#undef ERR +} + +static pj_bool_t on_rx_response(pjsip_rx_data *rdata) +{ +#define ERR(rc__) {loop_test_status=rc__; return PJ_TRUE; } + if (!is_user_equal(rdata->msg_info.from, "transport_loop_multi_test")) + return PJ_FALSE; + + PJ_TEST_EQ(rdata->tp_info.transport, cur_loop, NULL, ERR(-150)); + PJ_TEST_EQ(rdata->msg_info.msg->line.status.code, PJSIP_SC_ACCEPTED, NULL, + ERR(-160)); + + loop_test_status = 0; + return PJ_TRUE; +#undef ERR +} + +static pj_status_t on_tx_message(pjsip_tx_data *tdata) +{ + pjsip_from_hdr *from_hdr = (pjsip_from_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + + if (!is_user_equal(from_hdr, "transport_loop_multi_test")) + return PJ_SUCCESS; + + PJ_TEST_EQ(tdata->tp_info.transport, cur_loop, + tdata->msg->type==PJSIP_REQUEST_MSG? + "request transport mismatch" : "response transport mismatch", + loop_test_status=-200); + + return PJ_SUCCESS; +} + +/* Test that request and responses are sent/received on the correct loop + * instance when multiple instances of transport loop are created + */ +int transport_loop_multi_test(void) +{ +#define ERR(rc__) { rc=rc__; goto on_return; } + enum { N = 4, START_PORT=5000, TIMEOUT=1000 }; + pjsip_transport *loops[N]; + int i, rc; + + PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &loop_tester_mod), + NULL, ERR(-5)); + + pj_bzero(loops, sizeof(loops)); + for (i=0; iref_cnt) != ref_cnt) { PJ_LOG(3,(THIS_FILE, " error: ref counter is not %ld (%ld)", ref_cnt, pj_atomic_get(loop->ref_cnt))); - return -51; + status = -51; + goto on_return; } + status = 0; + +on_return: /* Decrement reference. */ pjsip_transport_dec_ref(loop); - - return 0; + return status; } int transport_loop_test(void) diff --git a/pjsip/src/test/transport_tcp_test.c b/pjsip/src/test/transport_tcp_test.c index c3da6bd319..42f0b5c16c 100644 --- a/pjsip/src/test/transport_tcp_test.c +++ b/pjsip/src/test/transport_tcp_test.c @@ -156,7 +156,7 @@ int transport_tcp_test(void) pjsip_transport *tcp[NUM_TP]; pj_sockaddr_in rem_addr; pj_status_t status; - char url[PJSIP_MAX_URL_SIZE]; + char host_port_param[PJSIP_MAX_URL_SIZE]; char addr[PJ_INET_ADDRSTRLEN]; int rtt[SEND_RECV_LOOP], min_rtt; int pkt_lost; @@ -172,19 +172,23 @@ int transport_tcp_test(void) status = pj_sockaddr_in_init(&rem_addr, &tpfactory[0]->addr_name.host, (pj_uint16_t)tpfactory[0]->addr_name.port); - pj_ansi_snprintf(url, sizeof(url), "sip:alice@%s:%d;transport=tcp", + pj_ansi_snprintf(host_port_param, sizeof(host_port_param), + "%s:%d;transport=tcp", pj_inet_ntop2(pj_AF_INET(), &rem_addr.sin_addr, addr, sizeof(addr)), pj_ntohs(rem_addr.sin_port)); /* Load test */ - if (transport_load_test(url) != 0) - return -60; + if ((status=transport_load_test(PJSIP_TRANSPORT_TCP, + host_port_param)) != 0) + { + return status; + } /* Basic transport's send/receive loopback test. */ for (i=0; imsg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) return PJ_FALSE; + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + /* Check that this is our request. */ if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { /* It is! */ @@ -121,18 +134,18 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; return PJ_TRUE; } status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL); if (status != PJ_SUCCESS) { - recv_status = status; + sr_g[tid].recv_status = status; pjsip_tx_data_dec_ref(tdata); return PJ_TRUE; } @@ -145,12 +158,19 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata) static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + if (!is_user_equal(rdata->msg_info.from, "transport_send_recv_test")) return PJ_FALSE; + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + if (pj_strcmp2(&rdata->msg_info.cid->id, SEND_RECV_CALL_ID_HDR) == 0) { - pj_get_timestamp(&my_recv_time); - recv_status = PJ_SUCCESS; + pj_get_timestamp(&sr_g[tid].my_recv_time); + sr_g[tid].recv_status = PJ_SUCCESS; return PJ_TRUE; } return PJ_FALSE; @@ -160,15 +180,16 @@ static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata) static void send_msg_callback(pjsip_send_state *stateless_data, pj_ssize_t sent, pj_bool_t *cont) { - PJ_UNUSED_ARG(stateless_data); + unsigned tid = (unsigned)(long)stateless_data->token; + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); if (sent < 1) { /* Obtain the error code. */ PJ_LOG(3,(THIS_FILE, " Sending %s got callback error %ld", stateless_data->tdata->info, -sent)); - send_status = (int)-sent; + sr_g[tid].send_status = (int)-sent; } else { - send_status = PJ_SUCCESS; + sr_g[tid].send_status = PJ_SUCCESS; } /* Don't want to continue. */ @@ -179,10 +200,12 @@ static void send_msg_callback(pjsip_send_state *stateless_data, /* Test that we receive loopback message. */ int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *p_usec_rtt) { + unsigned tid = tp_type; pj_bool_t msg_log_enabled; + char target_url[64]; pj_status_t status; pj_str_t target, from, to, contact, call_id, body; pjsip_method method; @@ -206,6 +229,9 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); + /* Create a request message. */ target = pj_str(target_url); from = pj_str(SEND_RECV_FROM_HDR); @@ -224,22 +250,22 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, } /* Reset statuses */ - send_status = recv_status = NO_STATUS; + sr_g[tid].send_status = sr_g[tid].recv_status = NO_STATUS; /* Start time. */ - pj_get_timestamp(&my_send_time); + pj_get_timestamp(&sr_g[tid].my_send_time); /* Send the message (statelessly). */ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); - status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, + status = pjsip_endpt_send_request_stateless( endpt, tdata, (void*)(long)tid, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ PJ_LOG(3,(THIS_FILE, " Sending %s to %.*s got immediate error %d", tdata->info, (int)target.slen, target.ptr, status)); pjsip_tx_data_dec_ref(tdata); - send_status = status; + sr_g[tid].send_status = status; } /* Set the timeout (2 seconds from now) */ @@ -258,19 +284,19 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, goto on_return; } - if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) { - app_perror(" error sending message", send_status); + if (sr_g[tid].send_status!=NO_STATUS && sr_g[tid].send_status!=PJ_SUCCESS) { + app_perror(" error sending message", sr_g[tid].send_status); status = -550; goto on_return; } - if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) { - app_perror(" error receiving message", recv_status); + if (sr_g[tid].recv_status!=NO_STATUS && sr_g[tid].recv_status!=PJ_SUCCESS) { + app_perror(" error receiving message", sr_g[tid].recv_status); status = -560; goto on_return; } - if (send_status!=NO_STATUS && recv_status!=NO_STATUS) { + if (sr_g[tid].send_status!=NO_STATUS && sr_g[tid].recv_status!=NO_STATUS) { /* Success! */ break; } @@ -281,7 +307,7 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, if (status == PJ_SUCCESS) { unsigned usec_rt; - usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time); + usec_rt = pj_elapsed_usec(&sr_g[tid].my_send_time, &sr_g[tid].my_recv_time); PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt)); @@ -327,28 +353,39 @@ static pjsip_module rt_module = NULL, /* tsx_handler() */ }; -static struct -{ - pj_thread_t *thread; - pj_timestamp send_time; - pj_timestamp total_rt_time; - int sent_request_count, recv_response_count; - pj_str_t call_id; - pj_timer_entry timeout_timer; - pj_timer_entry tx_timer; - pj_mutex_t *mutex; -} rt_test_data[16]; - -static char rt_target_uri[64]; -static pj_bool_t rt_stop; -static pj_str_t rt_call_id; +static struct rt_global_t { + struct { + pj_thread_t *thread; + pj_timestamp send_time; + pj_timestamp total_rt_time; + int sent_request_count, recv_response_count; + pj_str_t call_id; + pj_timer_entry timeout_timer; + pj_timer_entry tx_timer; + pj_mutex_t *mutex; + } rt_test_data[16]; + + char rt_target_uri[64]; + pj_bool_t rt_stop; + pj_str_t rt_call_id; + +} g_rt[PJSIP_TRANSPORT_START_OTHER]; static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) return PJ_FALSE; - if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (!pj_strncmp(&rdata->msg_info.cid->id, &g_rt[tid].rt_call_id, + g_rt[tid].rt_call_id.slen)) + { pjsip_tx_data *tdata; pjsip_response_addr res_addr; pj_status_t status; @@ -376,21 +413,21 @@ static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata) return PJ_FALSE; } -static pj_status_t rt_send_request(int thread_id) +static pj_status_t rt_send_request(unsigned tid, int thread_id) { pj_status_t status; pj_str_t target, from, to, contact, call_id; pjsip_tx_data *tdata; pj_time_val timeout_delay; - pj_mutex_lock(rt_test_data[thread_id].mutex); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); /* Create a request message. */ - target = pj_str(rt_target_uri); + target = pj_str(g_rt[tid].rt_target_uri); from = pj_str(RT_FROM_HDR); - to = pj_str(rt_target_uri); + to = pj_str(g_rt[tid].rt_target_uri); contact = pj_str(CONTACT_HDR); - call_id = rt_test_data[thread_id].call_id; + call_id = g_rt[tid].rt_test_data[thread_id].call_id; status = pjsip_endpt_create_request( endpt, &pjsip_options_method, &target, &from, &to, @@ -398,12 +435,12 @@ static pj_status_t rt_send_request(int thread_id) NULL, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return -610; } /* Start time. */ - pj_get_timestamp(&rt_test_data[thread_id].send_time); + pj_get_timestamp(&g_rt[tid].rt_test_data[thread_id].send_time); /* Send the message (statelessly). */ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL); @@ -411,57 +448,66 @@ static pj_status_t rt_send_request(int thread_id) /* Immediate error! */ app_perror(" error: send request", status); pjsip_tx_data_dec_ref(tdata); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return -620; } /* Update counter. */ - rt_test_data[thread_id].sent_request_count++; + g_rt[tid].rt_test_data[thread_id].sent_request_count++; /* Set timeout timer. */ - if (rt_test_data[thread_id].timeout_timer.user_data != NULL) { - pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer); + if (g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data != NULL) { + pjsip_endpt_cancel_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer); } timeout_delay.sec = 100; timeout_delay.msec = 0; - rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer, + g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer, &timeout_delay); - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return PJ_SUCCESS; } static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata) { + pjsip_to_hdr *to_hdr = rdata->msg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + if (!is_user_equal(rdata->msg_info.from, "transport_rt_test")) return PJ_FALSE; - if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) { + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (!pj_strncmp(&rdata->msg_info.cid->id, &g_rt[tid].rt_call_id, + g_rt[tid].rt_call_id.slen)) + { char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1; int thread_id = (*pos - '0'); pj_timestamp recv_time; - pj_mutex_lock(rt_test_data[thread_id].mutex); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); /* Stop timer. */ - pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer); + pjsip_endpt_cancel_timer(endpt, &g_rt[tid].rt_test_data[thread_id].timeout_timer); /* Update counter and end-time. */ - rt_test_data[thread_id].recv_response_count++; + g_rt[tid].rt_test_data[thread_id].recv_response_count++; pj_get_timestamp(&recv_time); - pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time); - pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time); + pj_sub_timestamp(&recv_time, &g_rt[tid].rt_test_data[thread_id].send_time); + pj_add_timestamp(&g_rt[tid].rt_test_data[thread_id].total_rt_time, &recv_time); - if (!rt_stop) { + if (!g_rt[tid].rt_stop) { pj_time_val tx_delay = { 0, 0 }; - pj_assert(rt_test_data[thread_id].tx_timer.user_data == NULL); - rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].tx_timer, + pj_assert(g_rt[tid].rt_test_data[thread_id].tx_timer.user_data == NULL); + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].tx_timer, &tx_delay); } - pj_mutex_unlock(rt_test_data[thread_id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); return PJ_TRUE; } @@ -471,47 +517,57 @@ static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata) static void rt_timeout_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry ) { - pj_mutex_lock(rt_test_data[entry->id].mutex); + unsigned tid = entry->id >> 16; + unsigned thread_id = entry->id & 0xFFFF; + + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); PJ_UNUSED_ARG(timer_heap); PJ_LOG(3,(THIS_FILE, " timeout waiting for response")); - rt_test_data[entry->id].timeout_timer.user_data = NULL; + g_rt[tid].rt_test_data[thread_id].timeout_timer.user_data = NULL; - if (rt_test_data[entry->id].tx_timer.user_data == NULL) { + if (g_rt[tid].rt_test_data[thread_id].tx_timer.user_data == NULL) { pj_time_val delay = { 0, 0 }; - rt_test_data[entry->id].tx_timer.user_data = (void*)(pj_ssize_t)1; - pjsip_endpt_schedule_timer(endpt, &rt_test_data[entry->id].tx_timer, + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1; + pjsip_endpt_schedule_timer(endpt, &g_rt[tid].rt_test_data[thread_id].tx_timer, &delay); } - pj_mutex_unlock(rt_test_data[entry->id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); } static void rt_tx_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry ) { - pj_mutex_lock(rt_test_data[entry->id].mutex); + unsigned tid = entry->id >> 16; + unsigned thread_id = entry->id & 0xFFFF; + + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + pj_mutex_lock(g_rt[tid].rt_test_data[thread_id].mutex); PJ_UNUSED_ARG(timer_heap); - pj_assert(rt_test_data[entry->id].tx_timer.user_data != NULL); - rt_test_data[entry->id].tx_timer.user_data = NULL; - rt_send_request(entry->id); + pj_assert(g_rt[tid].rt_test_data[thread_id].tx_timer.user_data != NULL); + g_rt[tid].rt_test_data[thread_id].tx_timer.user_data = NULL; + rt_send_request(tid, thread_id); - pj_mutex_unlock(rt_test_data[entry->id].mutex); + pj_mutex_unlock(g_rt[tid].rt_test_data[thread_id].mutex); } static int rt_worker_thread(void *arg) { + unsigned tid; int i; pj_time_val poll_delay = { 0, 10 }; - PJ_UNUSED_ARG(arg); + tid = (unsigned)(long)arg; + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); /* Sleep to allow main threads to run. */ pj_thread_sleep(10); - while (!rt_stop) { + while (!g_rt[tid].rt_stop) { pjsip_endpt_handle_events(endpt, &poll_delay); } @@ -524,11 +580,12 @@ static int rt_worker_thread(void *arg) int transport_rt_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, - char *target_url, + const char *host_port_transport, int *lost) { enum { THREADS = 4, INTERVAL = 10 }; - int i; + int i, tid=tp_type; + char target_url[80]; pj_status_t status; pj_pool_t *pool; pj_bool_t logger_enabled; @@ -564,9 +621,12 @@ int transport_rt_test( pjsip_transport_type_e tp_type, return -610; /* Initialize static test data. */ - pj_ansi_strxcpy(rt_target_uri, target_url, sizeof(rt_target_uri)); - rt_call_id = pj_str("RT-Call-Id/"); - rt_stop = PJ_FALSE; + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); + pj_ansi_strxcpy(g_rt[tid].rt_target_uri, target_url, + sizeof(g_rt[tid].rt_target_uri)); + g_rt[tid].rt_call_id = pj_str("RT-Call-Id/"); + g_rt[tid].rt_stop = PJ_FALSE; /* Initialize thread data. */ for (i=0; imsg_info.to; + pjsip_sip_uri *target = (pjsip_sip_uri*)pjsip_uri_get_uri(to_hdr->uri); + unsigned tid; + if (!is_user_equal(rdata->msg_info.from, "transport_load_test")) return PJ_FALSE; - if (rdata->msg_info.cseq->cseq != mod_load.next_seq) { + tid = (unsigned)pj_strtoul(&target->user); + pj_assert(tid < PJSIP_TRANSPORT_START_OTHER); + + if (rdata->msg_info.cseq->cseq != g_lt[tid].next_seq) { PJ_LOG(1,(THIS_FILE, " err: expecting cseq %u, got %u", - mod_load.next_seq, rdata->msg_info.cseq->cseq)); - mod_load.err = PJ_TRUE; - mod_load.next_seq = rdata->msg_info.cseq->cseq + 1; + g_lt[tid].next_seq, rdata->msg_info.cseq->cseq)); + g_lt[tid].err = PJ_TRUE; + g_lt[tid].next_seq = rdata->msg_info.cseq->cseq + 1; } else - mod_load.next_seq++; + g_lt[tid].next_seq++; return PJ_TRUE; } -int transport_load_test(char *target_url) +int transport_load_test(pjsip_transport_type_e tp_type, + const char *host_port_transport) { +#define ERR(rc__) { rc=rc__; goto on_return; } enum { COUNT = 2000 }; - unsigned i; - pj_status_t status = PJ_SUCCESS; + char target_url[64]; + unsigned i, tid=tp_type; + int rc; + + pj_ansi_snprintf(target_url, sizeof(target_url), "sip:%d@%s", + tp_type, host_port_transport); /* exhaust packets */ - do { - pj_time_val delay = {1, 0}; - i = 0; - pjsip_endpt_handle_events2(endpt, &delay, &i); - } while (i != 0); + flush_events(500); PJ_LOG(3,(THIS_FILE, " transport load test...")); - if (mod_load.mod.id == -1) { - status = pjsip_endpt_register_module( endpt, &mod_load.mod); - if (status != PJ_SUCCESS) { - app_perror("error registering module", status); - return -1; + if (mod_load_test.id == -1) { + rc = pjsip_endpt_register_module( endpt, &mod_load_test); + if (rc != PJ_SUCCESS) { + app_perror("error registering module", rc); + return -610; } } - mod_load.err = PJ_FALSE; - mod_load.next_seq = 0; + g_lt[tid].err = PJ_FALSE; + g_lt[tid].next_seq = 0; - for (i=0; i"); call_id = pj_str("thecallid"); - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS(pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, &from, &target, &from, &call_id, - i, NULL, &tdata ); - if (status != PJ_SUCCESS) { - app_perror("error creating request", status); - goto on_return; - } + i, NULL, &tdata ), + NULL, ERR(-620)); - status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL); - if (status != PJ_SUCCESS) { - app_perror("error sending request", status); - goto on_return; - } + PJ_TEST_SUCCESS(pjsip_endpt_send_request_stateless(endpt, tdata, + NULL, NULL), + NULL, ERR(-630)); } - do { - pj_time_val delay = {1, 0}; - i = 0; - pjsip_endpt_handle_events2(endpt, &delay, &i); - } while (i != 0); - - if (mod_load.next_seq != COUNT) { - PJ_LOG(1,(THIS_FILE, " err: expecting %u msg, got only %u", - COUNT, mod_load.next_seq)); - status = -2; - goto on_return; - } + flush_events(1000); + + PJ_TEST_EQ(g_lt[tid].next_seq, COUNT, "message count mismatch", + ERR(-640)); + + rc = 0; on_return: - if (mod_load.mod.id != -1) { - pjsip_endpt_unregister_module( endpt, &mod_load.mod); - mod_load.mod.id = -1; + if (mod_load_test.id != -1) { + /* Don't unregister if there are multiple transport_load_test() */ + pjsip_endpt_unregister_module( endpt, &mod_load_test); + mod_load_test.id = -1; } - if (status != PJ_SUCCESS || mod_load.err) { - return -2; - } - PJ_LOG(3,(THIS_FILE, " success")); - return 0; + return rc; } diff --git a/pjsip/src/test/transport_udp_test.c b/pjsip/src/test/transport_udp_test.c index c34fd644d1..958e954e15 100644 --- a/pjsip/src/test/transport_udp_test.c +++ b/pjsip/src/test/transport_udp_test.c @@ -140,7 +140,7 @@ int transport_udp_test(void) pj_sockaddr_in_init(&rem_addr, pj_cstr(&s, "127.0.0.1"), TEST_UDP_PORT); for (i=0; itp_type is loop. */ pjsip_transport *loop; struct my_timer timer; @@ -167,8 +167,6 @@ static pjsip_module msg_receiver = /* Init uac test */ static int init_test(unsigned tid) { - pj_sockaddr_in addr; - pj_enter_critical_section(); if (tsx_user.id == -1) { PJ_TEST_SUCCESS(pjsip_endpt_register_module(endpt, &tsx_user), NULL, @@ -182,10 +180,11 @@ static int init_test(unsigned tid) pj_leave_critical_section(); pj_assert(g[tid].loop==NULL); - PJ_TEST_SUCCESS(pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &g[tid].loop), - NULL, return -50); - + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &g[tid].loop), + NULL, return -50); + } return PJ_SUCCESS; } @@ -197,7 +196,7 @@ static void finish_test(unsigned tid) * get_tsx_tid() to be called and it needs the module id. */ if (g[tid].loop) { - pjsip_transport_dec_ref(g[tid].loop); + pjsip_transport_shutdown(g[tid].loop); g[tid].loop = 0; } } @@ -225,12 +224,32 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { unsigned tid = get_tsx_tid(tsx); + /* PJ_LOG(3,(THIS_FILE, " on_tsx_state state: %s, event: %s (%s)", pjsip_tsx_state_str(tsx->state), pjsip_event_str(e->type), pjsip_event_str(e->body.tsx_state.type) )); + */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM && + e->type==PJSIP_EVENT_TSX_STATE) + { + if (e->body.tsx_state.type==PJSIP_EVENT_RX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.rdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -704; + return; + }); + } else if (e->body.tsx_state.type==PJSIP_EVENT_TX_MSG && + e->body.tsx_state.src.tdata->dest_info.addr.count) { + PJ_TEST_EQ( e->body.tsx_state.src.tdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -706; + return; + }); + } + } if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0) { /* @@ -681,13 +700,20 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata) return PJ_FALSE; } - tid = (unsigned)pj_strtol(&target->user); + tid = (unsigned)pj_strtoul(&target->user); + /* PJ_LOG(3,(THIS_FILE, " on_rx_request %s (recv_count: %d) on %s, branch: %.*s", pjsip_rx_data_get_info(rdata), g[tid].recv_count, rdata->tp_info.transport->info, (int)rdata->msg_info.via->branch_param.slen, rdata->msg_info.via->branch_param.ptr)); + */ + + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_EQ(rdata->tp_info.transport, g[tid].loop, NULL, + {g[tid].test_complete = -602; return PJ_TRUE;}); + } if (pj_strnicmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0) { @@ -1110,6 +1136,16 @@ static int perform_tsx_test(unsigned tid, int dummy, char *target_uri, } set_tsx_tid(tsx, tid); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tsx_set_transport(tsx, &tp_sel); + } + /* Get transaction key. */ pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); @@ -1564,10 +1600,9 @@ int tsx_uac_test(unsigned tid) if (status != 0) goto on_return; - flush_events(500); - on_return: finish_test(tid); + flush_events(500); return status; #undef ERR } diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 2be54de1cc..86b5311d13 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -452,6 +452,24 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { unsigned tid = get_tsx_tid(tsx); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM && + e->type==PJSIP_EVENT_TSX_STATE) + { + if (e->body.tsx_state.type==PJSIP_EVENT_RX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.rdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -704; + return; + }); + } else if (e->body.tsx_state.type==PJSIP_EVENT_TX_MSG) { + PJ_TEST_EQ( e->body.tsx_state.src.tdata->tp_info.transport, + g[tid].loop, NULL, { + g[tid].test_complete = -706; + return; + }); + } + } + if (pj_strnicmp2(&tsx->branch, TEST1_BRANCH_ID, BRANCH_LEN)==0 || pj_strnicmp2(&tsx->branch, TEST2_BRANCH_ID, BRANCH_LEN)==0) { @@ -877,6 +895,11 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) tid = (unsigned)pj_strtol(&target->user); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_EQ(rdata->tp_info.transport, g[tid].loop, NULL, + {g[tid].test_complete = -602; return PJ_TRUE;}); + } + if (pj_strnicmp2(&branch_param, TEST1_BRANCH_ID, BRANCH_LEN) == 0 || pj_strnicmp2(&branch_param, TEST2_BRANCH_ID, BRANCH_LEN) == 0) { @@ -1323,6 +1346,7 @@ static int perform_test( unsigned tid, char branch_buf[BRANCH_LEN+20]; pj_time_val timeout, next_send; int sent_cnt; + pjsip_tpselector tp_sel; pj_status_t status; PJ_TEST_EQ(strlen(branch_param), BRANCH_LEN, NULL, return -99); @@ -1359,6 +1383,15 @@ static int perform_test( unsigned tid, "%s-%02d", branch_param, tid); pj_strdup2(tdata->pool, &via->branch_param, branch_buf); + /* Must select specific transport to use */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tx_data_set_transport(tdata, &tp_sel); + } + /* Schedule first send. */ sent_cnt = 0; pj_gettimeofday(&next_send); @@ -1612,7 +1645,7 @@ static int tsx_ack_test(unsigned tid) ** ***************************************************************************** */ -int tsx_transport_failure_test(unsigned tid) +static int tsx_transport_failure_test(unsigned tid) { struct test_desc { @@ -1697,13 +1730,21 @@ int tsx_transport_failure_test(unsigned tid) */ int tsx_uas_test(unsigned tid) { +#define ERR(rc__) { status=rc__; goto on_return; } struct tsx_test_param *param = &tsx_test[tid]; - pj_sockaddr_in addr; - pj_status_t status; + int status; g[tid].test_param = param; g[tid].tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type); + /* Create loop transport */ + g[tid].loop = NULL; + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &g[tid].loop), + NULL, return -50); + pjsip_transport_add_ref(g[tid].loop); + } + pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), "sip:%d@127.0.0.1:%d;transport=%s", tid, param->port, param->tp_type); @@ -1711,16 +1752,12 @@ int tsx_uas_test(unsigned tid) "sip:tsx_uas_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); - /* Check if loop transport is configured. */ - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &addr, sizeof(addr), NULL, &g[tid].loop); - if (status != PJ_SUCCESS) { - PJ_LOG(3,(THIS_FILE, " Error: loop transport is not configured!")); - return -10; - } /* Register modules. */ if ((status=register_modules(tid)) != PJ_SUCCESS) { - pjsip_transport_dec_ref(g[tid].loop); + if (g[tid].loop) { + pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = NULL; + } return -20; } @@ -1792,15 +1829,11 @@ int tsx_uas_test(unsigned tid) * TEST13_BRANCH_ID: test transport failure in CONFIRMED state. */ /* Only valid for loop-dgram */ - /* 2024-06-28: - * tsx_transport_failure_test() is now run directly from test.c because - * it sets transport loop delay, hence it must run exclusively. if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { status = tsx_transport_failure_test(tid); if (status != 0) goto on_return; } - */ status = 0; diff --git a/unittest.md b/unittest.md index 1c2943a6e7..f702f61f91 100644 --- a/unittest.md +++ b/unittest.md @@ -186,10 +186,18 @@ Having said that, some minor modifications were done: #### Changes to PJSIP-TEST +PJSIP-TEST has also gone through one of the biggest modifications to make the tests parallelable, which involves: + +- changing tests to mark and uniquely identify its own message and skip messages belonging to other tests +- remove global loop transport and replace with individual loop transport for each test +- bug fixing to the test code as some test flows have changed (for example, `tsx_uac_test` failed because UA layer has now been registered before the test) + Original: 28m22.744s First porting: 27m1.894s +3 worker threads: 16m20.369s 4 worker threads: 14m12.509s -10 worker threads: 13m2.482s +5 worker threads: 13m12.218s +10 worker threads: 13m2.482s --> 7m3.533s Changed `tsx_basic_test`, `tsx_uac_test`, `tsx_uas_test` to take the index to parameters rather than the parameter itself to make the test output more informative. @@ -255,6 +263,7 @@ int test1() The tests above should run correctly, in the sense that `on_rx_message0()` and `on_rx_message1()` will work correctly. However, logging messages `"test0 got message"` or `"test1 got message"` may appear in the wrong test, because we don't know which thread will get the event. If thread 1 happens to get message for test 0, then `"test0 got message"` will appear in test1 log. +Other issues: pjsip_loop_transport uses a worker thread, thus the log will not be captured. ### Basic runner limitations From 47d94bb27e86a8422fb7dcde35e5ba0b6efb67d1 Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 2 Jul 2024 07:57:36 +0700 Subject: [PATCH 25/79] Continuing correcting errors --- pjsip/src/test/main.c | 20 ++++----- pjsip/src/test/transport_loop_test.c | 11 ++++- pjsip/src/test/transport_tcp_test.c | 14 +++--- pjsip/src/test/transport_test.c | 4 +- pjsip/src/test/tsx_basic_test.c | 16 +++++-- pjsip/src/test/tsx_bench.c | 64 ++++++++++++---------------- pjsip/src/test/tsx_uas_test.c | 6 +-- 7 files changed, 71 insertions(+), 64 deletions(-) diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index e9b6ffd4dc..c26dd6a1ca 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -26,16 +26,16 @@ extern const char *system_name; static void warn(void) { - puts("*******************************************************************"); - puts("** W A R N I N G **"); - puts("*******************************************************************"); - puts("** Due to centralized event processing in PJSIP, events may be **"); - puts("** read by different thread than the test's thread. This may **"); - puts("** cause logging to be sent to the wrong test when multithreaded **"); - puts("** testing is used. The test results are correct, but do not **"); - puts("** trust the log. **"); - puts("** For debugging with correct logging, use \"-w 0 --log-no-cache\" **"); - puts("*******************************************************************"); + puts("********************************************************************"); + puts("** W A R N I N G **"); + puts("********************************************************************"); + puts("** Due to centralized event processing in PJSIP, events may be **"); + puts("** read by different thread than the test's thread. This may **"); + puts("** cause logs to be saved by the wrong test when multithreaded **"); + puts("** testing is used. The test results are correct, but the log **"); + puts("** may not be accurate. **"); + puts("** For debugging with correct logging, use \"-w 0 --log-no-cache\" **"); + puts("********************************************************************"); } static void usage(void) diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 86a0c6d7da..cfad4cfbfb 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -118,6 +118,7 @@ int transport_loop_multi_test(void) pj_time_val timeout, now; loop_test_status = PJ_EPENDING; + cur_loop = loops[i]; pj_bzero(&tp_sel, sizeof(tp_sel)); tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; @@ -173,6 +174,7 @@ static int datagram_loop_test() { enum { LOOP = 8 }; pjsip_transport *loop = NULL; + char host_port_transport[64]; int i, pkt_lost; int status; long ref_cnt; @@ -186,6 +188,12 @@ static int datagram_loop_test() pjsip_loop_set_failure(loop, 0, 0); + pj_ansi_snprintf(host_port_transport, sizeof(host_port_transport), + "%.*s:%d;transport=loop-dgram", + (int)loop->local_name.host.slen, + loop->local_name.host.ptr, + loop->local_name.port); + /* Get initial reference counter */ ref_cnt = pj_atomic_get(loop->ref_cnt); @@ -197,8 +205,7 @@ static int datagram_loop_test() /* Basic transport's send/receive loopback test. */ for (i=0; iref_cnt) != 1) diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index fb9fef84db..04cbea85da 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -256,7 +256,7 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, pj_get_timestamp(&sr_g[tid].my_send_time); /* Send the message (statelessly). */ - PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", + PJ_LOG(3,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); status = pjsip_endpt_send_request_stateless( endpt, tdata, (void*)(long)tid, &send_msg_callback); @@ -819,6 +819,8 @@ int transport_load_test(pjsip_transport_type_e tp_type, PJ_TEST_SUCCESS(pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL), NULL, ERR(-630)); + + flush_events(20); } flush_events(1000); diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c index fb3b2590a5..cb78bedb70 100644 --- a/pjsip/src/test/tsx_basic_test.c +++ b/pjsip/src/test/tsx_basic_test.c @@ -141,8 +141,13 @@ static int double_terminate(unsigned tid) int tsx_basic_test(unsigned tid) { struct tsx_test_param *param = &tsx_test[tid]; + pjsip_transport *loop = NULL; int status; + if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -10); + pjsip_transport_add_ref(loop); + } pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", param->port, param->tp_type); @@ -152,13 +157,18 @@ int tsx_basic_test(unsigned tid) status = tsx_layer_test(tid); if (status != 0) - return status; + goto on_return; status = double_terminate(tid); if (status != 0) - return status; + goto on_return; - return 0; + status = 0; + +on_return: + if (loop) + pjsip_transport_dec_ref(loop); + return status; } /**************************************************************************/ diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c index 3af3ee080b..d445cbd90e 100644 --- a/pjsip/src/test/tsx_bench.c +++ b/pjsip/src/test/tsx_bench.c @@ -32,7 +32,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pjsip_transaction **tsx; pj_timestamp t1, t2, elapsed; pjsip_via_hdr *via; - pj_status_t status; + int rc; /* Create the request first. */ pj_str_t str_target = pj_str("sip:someuser@someprovider.com"); @@ -40,14 +40,11 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pj_str_t str_to = pj_str("\"Remote User\" "); pj_str_t str_contact = str_from; - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS(pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, - &request); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to create request", status); - return status; - } + &request), + NULL, return -110); via = (pjsip_via_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_VIA, NULL); @@ -62,9 +59,9 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) elapsed.u64 = 0; pj_get_timestamp(&t1); for (i=0; ibranch_param.slen = 0; } @@ -73,7 +70,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; - status = PJ_SUCCESS; + rc = 0; on_error: for (i=0; i"); pj_str_t str_contact = str_from; - status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + PJ_TEST_SUCCESS( pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, - &request); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to create request", status); - return status; - } + &request), + NULL, return -210); /* Create Via */ via = pjsip_via_hdr_create(request->pool); @@ -141,15 +135,10 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CALL_ID, NULL); rdata.msg_info.via = via; - pj_sockaddr_in_init(&remote, 0, 0); - status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, - &remote, sizeof(pj_sockaddr_in), - NULL, &rdata.tp_info.transport); - if (status != PJ_SUCCESS) { - app_perror(" error: unable to get loop transport", status); - return status; - } - + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, + { pjsip_tx_data_dec_ref(request); return -220; }); + pjsip_transport_add_ref(loop); + rdata.tp_info.transport = loop; /* Create transaction array */ tsx = (pjsip_transaction**) pj_pool_zalloc(request->pool, working_set * sizeof(pjsip_transaction*)); @@ -167,17 +156,15 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) pj_ansi_snprintf(branch_buf+PJSIP_RFC3261_BRANCH_LEN, sizeof(branch_buf)-PJSIP_RFC3261_BRANCH_LEN, "-%d", i); - status = pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]); - if (status != PJ_SUCCESS) - goto on_error; - + PJ_TEST_SUCCESS(pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]), + NULL, { rc=-230; goto on_error; }); } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; - status = PJ_SUCCESS; + rc = 0; on_error: for (i=0; imsg_info.msg->line.status.code != TEST9_STATUS_CODE) { - PJ_LOG(3,(THIS_FILE," error: invalid status code")); - g[tid].test_complete = -151; - } + PJ_TEST_EQ(rdata->msg_info.msg->line.status.code, TEST9_STATUS_CODE, + NULL, g[tid].test_complete = -151); if (g[tid].recv_count==1) { From ed3ac4600593ed4519f5f10d3807be746ee55ce9 Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 2 Jul 2024 17:39:25 +0700 Subject: [PATCH 26/79] Add test shuffle feature --- pjlib/include/pj/unittest.h | 22 ++++++++ pjlib/src/pj/unittest.c | 63 +++++++++++++++++++++ pjlib/src/pjlib-test/test.c | 7 ++- pjlib/src/pjlib-test/test.h | 1 - pjlib/src/pjlib-test/test_util.h | 26 +++++++++ pjlib/src/pjlib-test/unittest_test.c | 83 +++++++++++++++++++++++++--- pjsip/src/test/test.c | 3 +- pjsip/src/test/tsx_uas_test.c | 53 +++++++++++------- unittest.md | 3 +- 9 files changed, 229 insertions(+), 32 deletions(-) diff --git a/pjlib/include/pj/unittest.h b/pjlib/include/pj/unittest.h index 5f77d1036f..739a9c6098 100644 --- a/pjlib/include/pj/unittest.h +++ b/pjlib/include/pj/unittest.h @@ -326,6 +326,18 @@ typedef enum pj_test_case_flag */ PJ_TEST_LOG_NO_CACHE = 4, + /** + * Keep this test case in front of the list when shuffling the test + * cases. + */ + PJ_TEST_KEEP_FIRST = 8, + + /** + * Keep this test case in last in the list when shuffling the test + * cases. + */ + PJ_TEST_KEEP_LAST = 16, + } pj_test_case_flag; @@ -600,6 +612,16 @@ PJ_DECL(void) pj_test_case_init(pj_test_case *tc, */ PJ_DECL(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc); +/** + * Shuffle the tests. + * + * @param suite The test suite + * @param seed Optional random seed to use, only if the value is + * greater than or equal to zero. It is recommended + * to set this value to make the test reproducible. + */ +PJ_DECL(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed); + /** * Initialize parameters with reasonable default values. This usually means * using one worker thread if threading is enabled, and zero worker thread diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index 7751262551..916bd72097 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #define THIS_FILE "unittest.c" @@ -79,6 +80,8 @@ PJ_DEF(void) pj_test_case_init( pj_test_case *tc, unsigned buf_size, const pj_test_case_param *prm) { + PJ_ASSERT_ON_FAIL( ((flags & (PJ_TEST_KEEP_FIRST|PJ_TEST_KEEP_LAST)) != + (PJ_TEST_KEEP_FIRST|PJ_TEST_KEEP_LAST)), {} ); pj_bzero(tc, sizeof(*tc)); /* Parameters */ @@ -111,6 +114,66 @@ PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) pj_list_push_back(&suite->tests, tc); } +/* Shuffle */ +PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) +{ + pj_test_case src, *tc; + unsigned total, movable; + + if (seed >= 0) + pj_srand(seed); + + /* Move tests to new list */ + pj_list_init(&src); + pj_list_merge_last(&src, &suite->tests); + + /* Move KEEP_FIRST tests first */ + for (tc=src.next; tc!=&src; ) { + pj_test_case *next = tc->next; + if (tc->flags & PJ_TEST_KEEP_FIRST) { + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + } + + tc = next; + } + + /* Count non-KEEP_LAST tests */ + for (total=0, movable=0, tc=src.next; tc!=&src; tc=tc->next) { + ++total; + if ((tc->flags & PJ_TEST_KEEP_LAST)==0) + ++movable; + } + + /* Shuffle non KEEP_LAST tests */ + while (movable > 0) { + int step = pj_rand() % total; + if (step < 0) + continue; + + for (tc=src.next; step>0; tc=tc->next, --step) + ; + + pj_assert(tc!=&src); + if (tc->flags & PJ_TEST_KEEP_LAST) + continue; + + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + --movable; + --total; + } + + /* Move KEEP_LAST tests */ + for (tc=src.next; tc!=&src; ) { + pj_test_case *next = tc->next; + pj_assert(tc->flags & PJ_TEST_KEEP_LAST); + pj_list_erase(tc); + pj_list_push_back(&suite->tests, tc); + tc = next; + } +} + /* Initialize text runner param with default values */ PJ_DEF(void) pj_test_runner_param_default(pj_test_runner_param *prm) { diff --git a/pjlib/src/pjlib-test/test.c b/pjlib/src/pjlib-test/test.c index 8256675869..055f8a2c9b 100644 --- a/pjlib/src/pjlib-test/test.c +++ b/pjlib/src/pjlib-test/test.c @@ -103,6 +103,9 @@ static pj_test_stat essential_tests(int argc, char *argv[]) int ntests = 0; pj_bzero(&stat, sizeof(stat)); + /* Not necessary but to prevent unused-var warning when no test */ + pj_bzero(test_cases, sizeof(test_cases)); + pj_bzero(log_bufs, sizeof(log_bufs)); if (test_app.ut_app.prm_config) pj_dump_config(); @@ -189,10 +192,10 @@ static pj_test_stat essential_tests(int argc, char *argv[]) PJ_LOG(3,(THIS_FILE, "Performing %d essential tests", ntests)); pj_test_init_basic_runner(&runner, &runner_prm); pj_test_run(&runner, &suite); - pj_test_get_stat(&suite, &stat); - pj_test_display_stat(&stat, "essential tests", THIS_FILE); pj_test_display_log_messages(&suite, test_app.ut_app.prm_logging_policy); + pj_test_get_stat(&suite, &stat); + pj_test_display_stat(&stat, "essential tests", THIS_FILE); if (stat.nfailed) return stat; diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 185098898e..b2b809004f 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -69,7 +69,6 @@ #define INCLUDE_IOQUEUE_PERF_TEST (PJ_HAS_THREADS && GROUP_NETWORK && WITH_BENCHMARK) #define INCLUDE_IOQUEUE_UNREG_TEST (PJ_HAS_THREADS && GROUP_NETWORK) #define INCLUDE_FILE_TEST GROUP_FILE -#define INCLUDE_UNITTEST_TEST GROUP_DATA_STRUCTURE #define INCLUDE_ECHO_SERVER 0 #define INCLUDE_ECHO_CLIENT 0 diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 5052a2ec43..91ab0e32d9 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,8 @@ typedef struct ut_app_t int prm_nthreads; int prm_list_test; pj_bool_t prm_stop_on_error; + pj_bool_t prm_shuffle; + int prm_seed; unsigned flags; unsigned verbosity; @@ -188,6 +191,13 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, "Performing %d %s with %d worker thread%s", ut_app->ntests, title, runner_prm.nthreads, runner_prm.nthreads>1?"s":"")); + + if (ut_app->prm_shuffle) { + PJ_LOG(3,(THIS_FILE, "Shuffling tests, random seed=%d", + ut_app->prm_seed)); + pj_test_suite_shuffle(&ut_app->suite, ut_app->prm_seed); + } + pj_test_run(runner, &ut_app->suite); pj_test_runner_destroy(runner); pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); @@ -208,6 +218,8 @@ static void ut_usage() printf(" -w N Set N worker threads (0: disable. Default: %d)\n", PJ_HAS_THREADS); puts(" -L, --list List the tests and exit"); puts(" --stop-err Stop testing on error"); + puts(" --shuffle Shuffle the test order"); + puts(" --seed N Set shuffle random seed (must be >= 0)"); puts(" -v, --verbose Show info when starting/stopping tests"); } @@ -222,6 +234,7 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) ut_app->prm_list_test = pj_argparse_get_bool("-L", argc, argv) || pj_argparse_get_bool("--list", argc, argv); ut_app->prm_stop_on_error = pj_argparse_get_bool("--stop-err", argc, argv); + ut_app->prm_shuffle = pj_argparse_get_bool("--shuffle", argc, argv); if (pj_argparse_get_bool("--log-no-cache", argc, argv)) { ut_app->flags |= PJ_TEST_LOG_NO_CACHE; } @@ -246,6 +259,19 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) } } + if (ut_app->prm_shuffle) { + pj_time_val tv; + + status = pj_gettimeofday(&tv); + if (status != PJ_SUCCESS) + return status; + + ut_app->prm_seed = (int)(tv.msec); + status = pj_argparse_get_int("--seed", argc, argv, &ut_app->prm_seed); + if (status != PJ_SUCCESS) + return status; + } + ut_app->verbosity = pj_argparse_get_bool("-v", argc, argv) || pj_argparse_get_bool("--verbose", argc, argv); diff --git a/pjlib/src/pjlib-test/unittest_test.c b/pjlib/src/pjlib-test/unittest_test.c index ba9913fed9..f4653a0862 100644 --- a/pjlib/src/pjlib-test/unittest_test.c +++ b/pjlib/src/pjlib-test/unittest_test.c @@ -19,13 +19,6 @@ #include -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_unittest_test; - -#if INCLUDE_UNITTEST_TEST - #define THIS_FILE "unittest_test.c" static char log_buffer[1024], *log_buffer_ptr = log_buffer; @@ -446,6 +439,77 @@ static int usage_test(pj_pool_t *pool, pj_bool_t basic, pj_bool_t parallel, return 0; } +static int shuffle_test() +{ + enum { N=16, REPEAT=16 }; + pj_test_suite suite; + char test_names[N][16]; + pj_test_case tcs[N], *tc; + unsigned i, repeat, flags[N]; + + for (i=0; iarg, 2, seed_info, return -40); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 5, seed_info, return -41); + tc = tc->next; + PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + return -42); + tc = tc->next; + PJ_TEST_TRUE((long)tc->arg==0 || (long)tc->arg==3, seed_info, + return -43); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 1, seed_info, return -44); + tc = tc->next; + PJ_TEST_EQ((long)tc->arg, 4, seed_info, return -45); + } + + return 0; +} + static int log_msg_sizes[] = { 1*MSG_LEN, /* log buffer enough for 1 message */ 2*MSG_LEN, /* log buffer enough for 2 messages */ @@ -480,6 +544,10 @@ int unittest_basic_test(void) } } + ret = shuffle_test(); + if (ret) + goto on_return; + on_return: pj_log_set_level(log_level); return ret; @@ -597,4 +665,3 @@ int unittest_test(void) return ret; } -#endif /* INCLUDE_UNITTEST_TEST */ diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 92e6e491f6..7a1a0af058 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -341,7 +341,8 @@ int test_main(int argc, char *argv[]) * Better be last because it recreates the endpt */ #if INCLUDE_TSX_DESTROY_TEST - UT_ADD_TEST(&test_app.ut_app, tsx_destroy_test, PJ_TEST_EXCLUSIVE); + UT_ADD_TEST(&test_app.ut_app, tsx_destroy_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); #endif if (ut_run_tests(&test_app.ut_app, "pjsip tests", argc, argv)) { diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 9e6d1c8cd6..50c13fe874 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -176,7 +176,7 @@ static pjsip_module tsx_user = NULL, NULL, /* prev and next */ { "Tsx-UAS-User", 12}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -194,7 +194,7 @@ static pjsip_module msg_sender = NULL, NULL, /* prev and next */ { "Msg-Sender", 10}, /* Name. */ -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER-1,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -220,11 +220,22 @@ static unsigned get_tsx_tid(const pjsip_transaction *tsx) return (unsigned)(long)tsx->mod_data[tsx_user.id]; } -/* Set test ID to transaction instance */ -static void set_tsx_tid(pjsip_transaction *tsx, unsigned tid) + +static void init_tsx(pjsip_transaction *tsx, unsigned tid) { pj_assert(tsx_user.id >= 0); tsx->mod_data[tsx_user.id] = (void*)(long)tid; + + /* Must select specific transport to use for loop */ + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + + pj_assert(g[tid].loop); + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tsx_set_transport(tsx, &tp_sel); + } } static int modules_reg_cnt; @@ -771,17 +782,12 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) g[tid].test_complete = 1; /* Check status code. */ - if (tsx->status_code != TEST9_STATUS_CODE) { - PJ_LOG(3,(THIS_FILE, " error: incorrect status code")); - g[tid].test_complete = -160; - } + PJ_TEST_EQ(tsx->status_code, TEST9_STATUS_CODE, NULL, + g[tid].test_complete = -160); /* Previous state. */ - if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_CONFIRMED) { - PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state %d", - e->body.tsx_state.prev_state)); - g[tid].test_complete = -161; - } + PJ_TEST_EQ(e->body.tsx_state.prev_state, PJSIP_TSX_STATE_CONFIRMED, + NULL, g[tid].test_complete = -161); } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) { @@ -925,7 +931,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -110; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -968,7 +974,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -116; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -1023,7 +1029,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -130; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -1109,7 +1115,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -140; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -1195,7 +1201,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -150; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST9_STATUS_CODE); @@ -1267,6 +1273,15 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) pj_strdup(tdata->pool, &via->branch_param, &rdata->msg_info.via->branch_param); + if (g[tid].test_param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { + pjsip_tpselector tp_sel; + + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].loop; + pjsip_tx_data_set_transport(tdata, &tp_sel); + } + status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL); if (status != PJ_SUCCESS) { @@ -1309,7 +1324,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) g[tid].test_complete = -150; return PJ_TRUE; } - set_tsx_tid(tsx, tid); + init_tsx(tsx, tid); pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); diff --git a/unittest.md b/unittest.md index f702f61f91..f48c76c85b 100644 --- a/unittest.md +++ b/unittest.md @@ -101,6 +101,7 @@ Below are the features of the unit-test and also other enhancements done in this - Even nicer output, logging is captured and by default only displayed if the test fails (configurable by cmdline option) - All (C based) PJ unit testing apps can select test(s) to invoke from cmdline +- Test shuffle feature for all testing apps ### 2. Modifications to test apps @@ -189,7 +190,7 @@ Having said that, some minor modifications were done: PJSIP-TEST has also gone through one of the biggest modifications to make the tests parallelable, which involves: - changing tests to mark and uniquely identify its own message and skip messages belonging to other tests -- remove global loop transport and replace with individual loop transport for each test +- remove global loop transport and replace with individual loop transport for each test. Fix tests that assume there is only one global loop transport (for example, with one global loop transport, there is no failover when sending messages fails). - bug fixing to the test code as some test flows have changed (for example, `tsx_uac_test` failed because UA layer has now been registered before the test) Original: 28m22.744s From 46ef3e608b037455c3ad618c683ca3f7144fc176 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 3 Jul 2024 07:37:38 +0700 Subject: [PATCH 27/79] Modify CI workflows to use standard arguments: -w 3 --shuffle. These args are set in GitHub action variables --- .github/workflows/ci-linux.yml | 4 ++-- .github/workflows/ci-mac.yml | 4 ++-- .github/workflows/ci-win.yml | 14 +++++++------- Makefile | 13 +++++-------- unittest.md | 7 ++++++- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index faaca2cb59..0d18402152 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -37,7 +37,7 @@ jobs: with: python-version: '3.10' - name: unit tests - run: make pjlib-test-ci pjlib-util-test pjmedia-test pjsua-test + run: make pjlib-test pjlib-util-test pjmedia-test pjsua-test ubuntu-default-full-bundle-2: # full bundle 2: running pjnath test @@ -127,7 +127,7 @@ jobs: with: python-version: '3.10' - name: unit tests - run: make pjlib-test-ci pjlib-util-test pjmedia-test pjsua-test + run: make pjlib-test pjlib-util-test pjmedia-test pjsua-test ubuntu-video-openh264-2: # video 2: running pjnath test diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index d664932558..f1f055f4f4 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -39,7 +39,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjlib-test-ci pjmedia-test pjlib-util-test pjsua-test + run: make pjlib-test pjmedia-test pjlib-util-test pjsua-test mac-default-full-bundle-2: # full bundle 2: running pjnath test @@ -139,7 +139,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjlib-test-ci pjmedia-test pjlib-util-test pjsua-test + run: make pjlib-test pjmedia-test pjlib-util-test pjsua-test mac-video-openh264-2: # video 2: running pjnath test diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index bff96d083c..877bac9cab 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -79,9 +79,9 @@ jobs: run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe --ci-mode - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe + cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe %CI_ARGS% %CI_MODE% + cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe %CI_ARGS% + cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe %CI_ARGS% shell: powershell windows-with-openssl-unit-test-2: @@ -137,7 +137,7 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd tests/pjsua; python runall.py - cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe + cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe %CI_ARGS% shell: powershell build-windows-gnu-tls: @@ -258,9 +258,9 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd tests/pjsua; python runall.py - cd ../../pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe --ci-mode - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe + cd ../../pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe %CI_ARGS% --ci-mode + cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe %CI_ARGS% + cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe %CI_ARGS% shell: powershell build-windows-video-ffmpeg: diff --git a/Makefile b/Makefile index 2d5c31c909..d7edd3b6a4 100644 --- a/Makefile +++ b/Makefile @@ -104,22 +104,19 @@ xhdrid: selftest: pjlib-test pjlib-util-test pjnath-test pjmedia-test pjsip-test pjsua-test pjlib-test: pjlib/bin/pjlib-test-$(TARGET_NAME) - cd pjlib/build && ../bin/pjlib-test-$(TARGET_NAME) - -pjlib-test-ci: pjlib/bin/pjlib-test-$(TARGET_NAME) - cd pjlib/build && ../bin/pjlib-test-$(TARGET_NAME) --ci-mode + cd pjlib/build && ../bin/pjlib-test-$(TARGET_NAME) $(CI_ARGS) $(CI_MODE) pjlib-util-test: pjlib-util/bin/pjlib-util-test-$(TARGET_NAME) - cd pjlib-util/build && ../bin/pjlib-util-test-$(TARGET_NAME) + cd pjlib-util/build && ../bin/pjlib-util-test-$(TARGET_NAME) $(CI_ARGS) pjnath-test: pjnath/bin/pjnath-test-$(TARGET_NAME) - cd pjnath/build && ../bin/pjnath-test-$(TARGET_NAME) + cd pjnath/build && ../bin/pjnath-test-$(TARGET_NAME) $(CI_ARGS) pjmedia-test: pjmedia/bin/pjmedia-test-$(TARGET_NAME) - cd pjmedia/build && ../bin/pjmedia-test-$(TARGET_NAME) + cd pjmedia/build && ../bin/pjmedia-test-$(TARGET_NAME) $(CI_ARGS) pjsip-test: pjsip/bin/pjsip-test-$(TARGET_NAME) - cd pjsip/build && ../bin/pjsip-test-$(TARGET_NAME) + cd pjsip/build && ../bin/pjsip-test-$(TARGET_NAME) $(CI_ARGS) pjsua-test: cmp_wav cd tests/pjsua && python runall.py -t 2 diff --git a/unittest.md b/unittest.md index f48c76c85b..e61e6d1324 100644 --- a/unittest.md +++ b/unittest.md @@ -2,6 +2,10 @@ This PR is part one of two to speed up testing in PJSIP. This part attempts to speed up the C/C++ based tests and part two will address Python based testing framework. +## Tasks + +### Task 1. Unit testing framework + The plan is to implement unit testing framework in PJLIB, that that is what the bulk of this PR does. More specifically, this PR contains several work: 1. A (new) unit testing framework, in `` and `pj/unittest.c`. @@ -10,6 +14,7 @@ The plan is to implement unit testing framework in PJLIB, that that is what the - modifications to `pj/fifobuf.[hc]`, an old feature (part of pjsip initial commit!) that has never been used until now - new auxiliary feature: ``, header only utilities to parse command line arguments. + - modify CI workflows Let's discuss the work in more detail. @@ -103,7 +108,7 @@ Below are the features of the unit-test and also other enhancements done in this - All (C based) PJ unit testing apps can select test(s) to invoke from cmdline - Test shuffle feature for all testing apps -### 2. Modifications to test apps +### Task 2. Modifications to test apps #### Common modifications From b012957ce49da5f3c1c3aa479a93e4e59e443c3e Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 11 Jul 2024 05:58:00 +0700 Subject: [PATCH 28/79] Updated due to change in argparse API signature (swap arg order) --- pjlib-util/src/pjlib-util-test/main.c | 8 ++-- pjlib/src/pjlib-test/main.c | 26 +++++++------ pjlib/src/pjlib-test/ssl_sock.c | 2 + pjlib/src/pjlib-test/test_util.h | 55 ++++++++++++++------------- pjmedia/src/test/main.c | 8 ++-- pjnath/src/pjnath-test/main.c | 8 ++-- pjsip/src/test/main.c | 14 +++---- 7 files changed, 64 insertions(+), 57 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c index f3699b61ff..7c4ec7a049 100644 --- a/pjlib-util/src/pjlib-util-test/main.c +++ b/pjlib-util/src/pjlib-util-test/main.c @@ -90,8 +90,8 @@ int main(int argc, char *argv[]) boost(); - if (pj_argparse_get_bool("-h", &argc, argv) || - pj_argparse_get_bool("--help", &argc, argv)) + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) { usage(); return 0; @@ -99,8 +99,8 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get_bool("-i", &argc, argv); - no_trap = pj_argparse_get_bool("-n", &argc, argv); + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjlib/src/pjlib-test/main.c b/pjlib/src/pjlib-test/main.c index e369871700..68cd44a6ef 100644 --- a/pjlib/src/pjlib-test/main.c +++ b/pjlib/src/pjlib-test/main.c @@ -110,32 +110,35 @@ int main(int argc, char *argv[]) /* * Parse arguments */ - if (pj_argparse_get_bool("-h", &argc, argv) || - pj_argparse_get_bool("--help", &argc, argv)) + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) { usage(); return 0; } - interractive = pj_argparse_get_bool("-i", &argc, argv); - no_trap = pj_argparse_get_bool("-n", &argc, argv); - if (pj_argparse_get_int("-p", &argc, argv, &test_app.param_echo_port)) { + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (pj_argparse_get_int(&argc, argv, "-p", &test_app.param_echo_port)) { usage(); return 1; } - if (pj_argparse_get_str("-s", &argc, argv, (char**)&test_app.param_echo_server)) { + if (pj_argparse_get_str(&argc, argv, "-s", + (char**)&test_app.param_echo_server)) + { usage(); return 1; } - if (pj_argparse_exists("-t", argv)) { + if (pj_argparse_exists(argv, "-t")) { char *sock_type; - if (pj_argparse_get_str("-t", &argc, argv, &sock_type)==PJ_SUCCESS) { + if (pj_argparse_get_str(&argc, argv, "-t", &sock_type)==PJ_SUCCESS) { if (pj_ansi_stricmp(sock_type, "tcp")==0) test_app.param_echo_sock_type = pj_SOCK_STREAM(); else if (pj_ansi_stricmp(sock_type, "udp")==0) test_app.param_echo_sock_type = pj_SOCK_DGRAM(); else { - printf("Error: unknown socket type %s for -t option\n", sock_type); + printf("Error: unknown socket type %s for -t option\n", + sock_type); usage(); return 1; } @@ -149,8 +152,9 @@ int main(int argc, char *argv[]) usage(); return 1; } - test_app.param_skip_essentials = pj_argparse_get_bool("--skip-e", &argc, argv); - test_app.param_ci_mode = pj_argparse_get_bool("--ci-mode", &argc, argv); + test_app.param_skip_essentials = pj_argparse_get_bool(&argc, argv, + "--skip-e"); + test_app.param_ci_mode = pj_argparse_get_bool(&argc, argv, "--ci-mode"); if (!no_trap) { diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index a4e8570fea..313c91522e 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -654,6 +654,8 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE); pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS); + PJ_UNUSED_ARG(load_cert_from_store); + #if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1) status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file, &privkey_file, &privkey_pass, diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 91ab0e32d9..9c7a67620a 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -46,7 +46,7 @@ typedef struct ut_app_t } ut_app_t; /* Call this in main.c before parsing arguments */ -static void ut_app_init0(ut_app_t *ut_app) +PJ_INLINE(void) ut_app_init0(ut_app_t *ut_app) { pj_bzero(ut_app, sizeof(*ut_app)); ut_app->prm_logging_policy = PJ_TEST_FAILED_TESTS; @@ -55,7 +55,7 @@ static void ut_app_init0(ut_app_t *ut_app) } /* Call this in test.c before adding test cases */ -static pj_status_t ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) +PJ_INLINE(pj_status_t) ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) { ut_app->pool = pj_pool_create(mem, THIS_FILE, 4000, 4000, NULL); PJ_TEST_NOT_NULL(ut_app->pool, NULL, return PJ_ENOMEM); @@ -64,7 +64,7 @@ static pj_status_t ut_app_init1(ut_app_t *ut_app, pj_pool_factory *mem) } /* Don't forget to call this */ -static void ut_app_destroy(ut_app_t *ut_app) +PJ_INLINE(void) ut_app_destroy(ut_app_t *ut_app) { pj_pool_release(ut_app->pool); ut_app->pool = NULL; @@ -84,7 +84,7 @@ typedef int (*ut_func)(void*); /* Check if a test is specified/requested in cmdline */ -static pj_bool_t ut_test_included(const char *name, int argc, char *argv[]) +PJ_INLINE(pj_bool_t) ut_test_included(const char *name, int argc, char *argv[]) { if (argc <= 1) return PJ_TRUE; @@ -99,9 +99,9 @@ static pj_bool_t ut_test_included(const char *name, int argc, char *argv[]) } /* Add test case */ -static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void*), - void *arg, const char *test_name, - unsigned flags, int argc, char *argv[]) +PJ_INLINE(pj_status_t) ut_add_test(ut_app_t *ut_app, int (*test_func)(void*), + void *arg, const char *test_name, + unsigned flags, int argc, char *argv[]) { char *log_buf; pj_test_case *tc; @@ -127,7 +127,7 @@ static pj_status_t ut_add_test(ut_app_t *ut_app, int (*test_func)(void*), return PJ_SUCCESS; } -static void ut_list_tests(ut_app_t *ut_app, const char *title) +PJ_INLINE(void) ut_list_tests(ut_app_t *ut_app, const char *title) { unsigned d = pj_log_get_decor(); const pj_test_case *tc, *prev=NULL; @@ -145,8 +145,8 @@ static void ut_list_tests(ut_app_t *ut_app, const char *title) pj_log_set_decor(d); } -static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, - int argc, char *argv[]) +PJ_INLINE(pj_status_t) ut_run_tests(ut_app_t *ut_app, const char *title, + int argc, char *argv[]) { pj_test_runner_param runner_prm; pj_test_runner_param_default(&runner_prm); @@ -207,7 +207,7 @@ static pj_status_t ut_run_tests(ut_app_t *ut_app, const char *title, return stat.nfailed ? PJ_EBUG : PJ_SUCCESS; } -static void ut_usage() +PJ_INLINE(void) ut_usage() { puts(" -c, --config Show configuration macros"); puts(" -l 0,1,2,3 0: Don't show logging after tests"); @@ -215,7 +215,8 @@ static void ut_usage() puts(" 2: Show logs of only successful tests"); puts(" 3: Show logs of all tests"); puts(" --log-no-cache Do not cache logging"); - printf(" -w N Set N worker threads (0: disable. Default: %d)\n", PJ_HAS_THREADS); + printf(" -w N Set N worker threads (0: disable. Default: %d)\n", + PJ_HAS_THREADS); puts(" -L, --list List the tests and exit"); puts(" --stop-err Stop testing on error"); puts(" --shuffle Shuffle the test order"); @@ -224,23 +225,23 @@ static void ut_usage() } -static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) +PJ_INLINE(pj_status_t) ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) { int itmp = -1; pj_status_t status; - ut_app->prm_config = pj_argparse_get_bool("-c", argc, argv) || - pj_argparse_get_bool("--config", argc, argv); - ut_app->prm_list_test = pj_argparse_get_bool("-L", argc, argv) || - pj_argparse_get_bool("--list", argc, argv); - ut_app->prm_stop_on_error = pj_argparse_get_bool("--stop-err", argc, argv); - ut_app->prm_shuffle = pj_argparse_get_bool("--shuffle", argc, argv); - if (pj_argparse_get_bool("--log-no-cache", argc, argv)) { + ut_app->prm_config = pj_argparse_get_bool(argc, argv, "-c") || + pj_argparse_get_bool(argc, argv, "--config"); + ut_app->prm_list_test = pj_argparse_get_bool(argc, argv, "-L") || + pj_argparse_get_bool(argc, argv, "--list"); + ut_app->prm_stop_on_error = pj_argparse_get_bool(argc, argv, "--stop-err"); + ut_app->prm_shuffle = pj_argparse_get_bool(argc, argv, "--shuffle"); + if (pj_argparse_get_bool(argc, argv, "--log-no-cache")) { ut_app->flags |= PJ_TEST_LOG_NO_CACHE; } - if (pj_argparse_exists("-l", argv)) { - status = pj_argparse_get_int("-l", argc, argv, &itmp); + if (pj_argparse_exists(argv, "-l")) { + status = pj_argparse_get_int(argc, argv, "-l", &itmp); if (status==PJ_SUCCESS && itmp>=0 && itmp<=3) { ut_app->prm_logging_policy = (pj_test_select_tests)itmp; } else { @@ -249,8 +250,8 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) } } - if (pj_argparse_exists("-w", argv)) { - status = pj_argparse_get_int("-w", argc, argv, &itmp); + if (pj_argparse_exists(argv, "-w")) { + status = pj_argparse_get_int(argc, argv, "-w", &itmp); if (status==PJ_SUCCESS && itmp>=0 && itmp<50) { ut_app->prm_nthreads = itmp; } else { @@ -267,13 +268,13 @@ static pj_status_t ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) return status; ut_app->prm_seed = (int)(tv.msec); - status = pj_argparse_get_int("--seed", argc, argv, &ut_app->prm_seed); + status = pj_argparse_get_int(argc, argv, "--seed", &ut_app->prm_seed); if (status != PJ_SUCCESS) return status; } - ut_app->verbosity = pj_argparse_get_bool("-v", argc, argv) || - pj_argparse_get_bool("--verbose", argc, argv); + ut_app->verbosity = pj_argparse_get_bool(argc, argv, "-v") || + pj_argparse_get_bool(argc, argv, "--verbose"); return PJ_SUCCESS; } diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index 24ad0876d4..30415351d0 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -82,8 +82,8 @@ static int main_func(int argc, char *argv[]) int interractive = 0; int no_trap = 0; - if (pj_argparse_get_bool("-h", &argc, argv) || - pj_argparse_get_bool("--help", &argc, argv)) + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) { usage(); return 0; @@ -91,8 +91,8 @@ static int main_func(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get_bool("-i", &argc, argv); - no_trap = pj_argparse_get_bool("-n", &argc, argv); + interractive = pj_argparse_get_bool&argc, argv, ("-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjnath/src/pjnath-test/main.c b/pjnath/src/pjnath-test/main.c index c73c971a1e..7f184dd568 100644 --- a/pjnath/src/pjnath-test/main.c +++ b/pjnath/src/pjnath-test/main.c @@ -85,8 +85,8 @@ int main(int argc, char *argv[]) boost(); - if (pj_argparse_get_bool("-h", &argc, argv) || - pj_argparse_get_bool("--help", &argc, argv)) + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) { usage(); return 0; @@ -94,8 +94,8 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get_bool("-i", &argc, argv); - no_trap = pj_argparse_get_bool("-n", &argc, argv); + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); if (ut_parse_args(&test_app.ut_app, &argc, argv)) return 1; diff --git a/pjsip/src/test/main.c b/pjsip/src/test/main.c index c26dd6a1ca..cf17b5e177 100644 --- a/pjsip/src/test/main.c +++ b/pjsip/src/test/main.c @@ -91,8 +91,8 @@ int main(int argc, char *argv[]) warn(); - if (pj_argparse_get_bool("-h", &argc, argv) || - pj_argparse_get_bool("--help", &argc, argv)) + if (pj_argparse_get_bool(&argc, argv, "-h") || + pj_argparse_get_bool(&argc, argv, "--help")) { usage(); return 0; @@ -100,16 +100,16 @@ int main(int argc, char *argv[]) ut_app_init0(&test_app.ut_app); - interractive = pj_argparse_get_bool("-i", &argc, argv); - no_trap = pj_argparse_get_bool("-n", &argc, argv); - if (pj_argparse_get_str("-s", &argc, argv, (char**)&system_name) || - pj_argparse_get_str("--system", &argc, argv, (char**)&system_name)) + interractive = pj_argparse_get_bool(&argc, argv, "-i"); + no_trap = pj_argparse_get_bool(&argc, argv, "-n"); + if (pj_argparse_get_str(&argc, argv, "-s", (char**)&system_name) || + pj_argparse_get_str(&argc, argv, "--system", (char**)&system_name)) { usage(); return 1; } - if (pj_argparse_get_int("--log-level", &argc, argv, &log_level)) { + if (pj_argparse_get_int(&argc, argv, "--log-level", &log_level)) { usage(); return 1; } From 7117775ee65863d3b44b521d7e470f1546b7f356 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 11 Jul 2024 09:03:59 +0700 Subject: [PATCH 29/79] Removed unittest.md (it was draft for the PR) --- unittest.md | 295 ---------------------------------------------------- 1 file changed, 295 deletions(-) delete mode 100644 unittest.md diff --git a/unittest.md b/unittest.md deleted file mode 100644 index e61e6d1324..0000000000 --- a/unittest.md +++ /dev/null @@ -1,295 +0,0 @@ -(Work in progress, do not review etc) - -This PR is part one of two to speed up testing in PJSIP. This part attempts to speed up the C/C++ based tests and part two will address Python based testing framework. - -## Tasks - -### Task 1. Unit testing framework - -The plan is to implement unit testing framework in PJLIB, that that is what the bulk of this PR does. More specifically, this PR contains several work: - -1. A (new) unit testing framework, in `` and `pj/unittest.c`. -2. Some modifications to `pjlib-test`, `pjlib-util-test`, `pjnath-test`, `pjmedia-test`, and `pjsip-test` to use the framework. -3. Other developments: - - - modifications to `pj/fifobuf.[hc]`, an old feature (part of pjsip initial commit!) that has never been used until now - - new auxiliary feature: ``, header only utilities to parse command line arguments. - - modify CI workflows - -Let's discuss the work in more detail. - -### 1. Unit testing framework - -#### Overview - -The unit testing framework `` provides two parts. - -First part is test macros that can be used to replace manual `if`s in the test codes. This can be used anywhere (in the test code though, not in library code, due to size concern of the expansion macro) without having to use the unit test framework (apart from including the header file of course). Example: - -``` - PJ_TEST_NON_ZERO(pool, "pool allocation failed", {rc=-1; goto on_error;}); - PJ_TEST_EQ(count, expected, "invalid read count", return -100); - PJ_TEST_SUCCESS(pj_ioqueue_read(...), NULL, {rc=-80; goto on_return; }); -``` - -On failure, the macros above will display the expressions being checked, the value of the expressions, and the status message (for PJ_TEST_SUCCESS), saving the developer from having to write all these things. For example: - -``` -15:57:24.365 Test pj_strcmp("abdefcghkji", "abdecfghkji")==0 fails (pj_strcmp result=3) in \ - unittest_test.c:563 (wrong test scheduling) -``` - -A list of the test macros currently implemented are as follows: - -- `PJ_TEST_SUCCESS(expr, err_reason, err_action)` -- `PJ_TEST_NON_ZERO(expr, err_reason, err_action)` -- `PJ_TEST_TRUE(expr, err_reason, err_action)` -- `PJ_TEST_NOT_NULL(expr, err_reason, err_action)` -- `PJ_TEST_EQ(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_NEQ(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_LT(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_LTE(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_GT(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_GTE(expr0, expr1, err_reason, err_action)` -- `PJ_TEST_STRCMP(ps0, ps1, res_op, exp_result, err_reason, err_action)` -- `PJ_TEST_STRICMP(ps0, ps1, res_op, exp_result, err_reason, err_action)` -- `PJ_TEST_STREQ(ps0, ps1, err_reason, err_action)` -- `PJ_TEST_STRNEQ(ps0, ps1, err_reason, err_action)` - - -The second part is the unit test framework. The framework uses common architecture as described in https://en.wikipedia.org/wiki/XUnit. The global workflow to create/run unit test is something like this: - -``` - pj_test_case tc0, tc1; - pj_test_suite suite; - pj_test_runner *runner; - pj_test_stat stat; - - /* Init test suite */ - pj_test_suite_init(&suite); - - /* Init test cases */ - pj_test_case_init(&tc0, ..., &func_to_test0, arg0, ... ); - pj_test_suite_add_case(&suite, &tc0); - - pj_test_case_init(&tc1, ..., &func_to_test1, arg1, ... ); - pj_test_suite_add_case(&suite, &tc1); - - /* Create runner and run the test suite */ - pj_test_create_text_runner(pool, .., &runner); - pj_test_run(runner, &suite); - pj_test_runner_destroy(runner); - - /* Get/show stats, display logs for failed tests */ - pj_test_get_stat(&suite, &stat); - pj_test_display_stat(&stat, ..); - pj_test_display_log_messages(&suite, PJ_TEST_FAILED_TESTS); -``` - -#### Features - -Below are the features of the unit-test and also other enhancements done in this PR: - -- Familiarity with common architecture as described in https://en.wikipedia.org/wiki/XUnit -- By default uses parallel execution unless it is disabled on per test case basis. -- Easy to port existing test functions. In fact, no modification is needed to the test functions -- Convenient `PJ_TEST_XXX()` macros can be used in place of manual testing and error reporting -- Nicer output: - -``` -07:34:32.878 Performing 20 features tests with 4 worker threads -[ 1/20] hash_test [OK] [0.000s] -[ 2/20] atomic_test [OK] [0.000s] -[ 3/20] rand_test [OK] [0.002s] -... -``` - -- Even nicer output, logging is captured and by default only displayed if the test fails (configurable by cmdline option) -- All (C based) PJ unit testing apps can select test(s) to invoke from cmdline -- Test shuffle feature for all testing apps - -### Task 2. Modifications to test apps - -#### Common modifications - -There is a new utility file in `pjlib/src/pjlib-test/test_util.h` which is shared by all test apps, to parse command line arguments, show usage, register tests, and control the unit testing process. - -The main front-end files (`main.c`) were modified to be more nice as command line apps, now it can be invoked with arguments, which are uniform in all unit-test apps: - -``` -Usage: - pjlib-test [OPTION] [test_to_run] [..] - -where OPTIONS: - - -h, --help Show this help screen - -l 0,1,2 0: Don't show logging after tests - 1: Show logs of failed tests (default) - 2: Show logs of all tests - -w N Set N worker threads (0: disable. Default: 1) - -L, --list List the tests and exit - --stop-err Stop testing on error - --skip-e Skip essential tests - --ci-mode Running in slow CI mode - -i Ask ENTER before quitting - -n Do not trap signals - -p PORT Use port PORT for echo port - -s SERVER Use SERVER as ech oserver - -t ucp,tcp Set echo socket type to UDP or TCP -``` - -The main modification in test body (`test.c`) is to use the unit-test framework. - -Some test codes were changed, replacing manual checks with `PJ_TEST_XXX()` macros, mainly to test the usage of these macros and to make the test nicer. But since it made the PR very big, I didn't continue the effort, unless when it was necessary for debugging some problems. - -Some tests were also split up to make them run in parallel. - -More specific changes are discussed below. - -#### Changes to PJLIB-TEST - -In PJLIB-TEST, test time is speed up by up to four times: - -- 0 worker thread: 9m22.228s (resembles the original file) -- 1 worker thread: 4m51.940s -- 2 worker threads: 4m9.322s -- 3 worker threads: 2m52.350s -- 4 worker threads: 2m31.399s -- 6 worker threads: 2m28.450s -- 8 worker threads: 2m19.779s - -It looks like the sweet spot is with 3 worker threads. Runing with more than this did not speed up the test considerably because some tests just take a long time to finish (more than 2 minutes). I've tried to split up those tests into smaller tests, but mostly that's not feasible because the tests are benchmarking tests (that need to gather all results to determine the overall winner), or because the tests shares global states with each other. - -#### Changes to PJLIB-UTIL-TEST - -In PJLIB-UTIL-TEST, there is almost 2x speed up from 5m52.500s to 3m3.615s with 1 worker thread (the default). We couldn't speed up more because tests such as `resolver_test()` and `http_client_test()` takes about three minutes to complete and they couldn't be split up due to the use of global states. - -#### Changes to PJNATH-TEST - -PJNATH-TEST is the one with biggest modifications. The original version took 45m42.275s to complete, excluding `ice_conc_test()` which apparently is not called (this test alone takes 123s to complete). Parallelizing the test requires large modifications as follows: - -- remove global `mem` pool factory altogether, since the tests validate the memory leak in the pool factory, therefore having a single pool factory shared by multiple threads will not work -- remove constant server port numbers in `server.c` so that server can be instantiated multiple times simultaneously. -- split tests with multiple configurations (such as `ice_test`, `turn_sock_test`, `concur_test`) into individual test for each configuration, making them parallelable. - -As the result, there are 70 smaller test items in pjnath-test. The test durations are as follows: - -- 3 worker threads: 11m51.526s -- 4 worker threads: 9m44.503s -- 10 worker threads: 4m31.195s -- 40 worker threads: 2m9.205s - -Hence with 10 worker threads, we can save 40 minutes of test time! - -#### Changes to PJMEDIA-TEST - -PJMEDIA-TEST has the least modifications because it has very few tests. The original duration was 4m18.691s, and has come down a little to 2m8.363s with 1 worker thread. - -Having said that, some minor modifications were done: - -- replace `pjmedia_endpt_create()` with `pjmedia_endpt_create2()` (similarly `..destroy()` with `..destroy2()` in `mips_test()` and `codec_test_vectors()`, to avoid inadvertently initializing `pjmedia_aud_subsys`. -- replace `printf` with log in jbuf test to make the output tidy, and renamed `jbuf_main` function name to `jbuf_test`. - -#### Changes to PJSIP-TEST - -PJSIP-TEST has also gone through one of the biggest modifications to make the tests parallelable, which involves: - -- changing tests to mark and uniquely identify its own message and skip messages belonging to other tests -- remove global loop transport and replace with individual loop transport for each test. Fix tests that assume there is only one global loop transport (for example, with one global loop transport, there is no failover when sending messages fails). -- bug fixing to the test code as some test flows have changed (for example, `tsx_uac_test` failed because UA layer has now been registered before the test) - -Original: 28m22.744s -First porting: 27m1.894s -3 worker threads: 16m20.369s -4 worker threads: 14m12.509s -5 worker threads: 13m12.218s -10 worker threads: 13m2.482s --> 7m3.533s - -Changed `tsx_basic_test`, `tsx_uac_test`, `tsx_uas_test` to take the index to parameters rather than the parameter itself to make the test output more informative. - -### 3. Other developments - -#### `` - -This is header only feature to parse command line options. We have `pj_getopt()`, but unfortunately this is in `pjlib-util` thus can't be used by PJLIB. - -#### Working `fifobuf` - -The `fifobuf` feature has been there for the longest time (it was part of pjlib first ever commit) and finally has got some use. - -### 4. Tips, known issues and considerations - -### [MAJOR] Wrong logging report with multithreading - -Consider two test cases running on two threads: - -test0() is running on thread 0: - -``` -/* simplified on_rx_request() module callback */ -pj_bool_t on_rx_message0(pjsip_rx_data *rdata) -{ - if (pj_strcmp2(&rdata->msg_info.from->user, "test0")) { - PJ_LOG(1,(THIS_FILE, "test0 got message")); - return PJ_TRUE; - } - return PJ_FALSE; -} - -/* test0() is running on thread 0 */ -int test0() -{ - send_request( .., from_uri="sip:test0@127.0.0.1" ); - while (1) - pjsip_endpt_handle_events(..); -} -``` - -test1() is running on thread 1: - -``` -/* simplified on_rx_request() module callback */ -pj_bool_t on_rx_message1(pjsip_rx_data *rdata) -{ - if (pj_strcmp2(&rdata->msg_info.from->user, "test1")) { - PJ_LOG(1,(THIS_FILE, "test1 got message")); - return PJ_TRUE; - } - return PJ_FALSE; -} - -/* test1() is running on thread 1 */ -int test1() -{ - send_request( .., from_uri="sip:test1@127.0.0.1" ); - while (1) - pjsip_endpt_handle_events(..); -} -``` - -The tests above should run correctly, in the sense that `on_rx_message0()` and `on_rx_message1()` will work correctly. However, logging messages `"test0 got message"` or `"test1 got message"` may appear in the wrong test, because we don't know which thread will get the event. If thread 1 happens to get message for test 0, then `"test0 got message"` will appear in test1 log. - -Other issues: pjsip_loop_transport uses a worker thread, thus the log will not be captured. - -### Basic runner limitations - -There can only be one basic runner running at any single time, because it stores current test in a global variable. - -Note: this limitation is because the unit test needs to access current test case inside logging callback, and we want the basic runner to be as simple as possible without the need to use pool or OS features such as thread local storage. - -### Multithreaded debugging tips - -Set `verbosity` to 1 in runner's param to display what tests are running to help finding out what's going on when error happens during test which suspected to be caused by concurrency issue. - -### Cluttered logging - -When a unit-test is run (in any thread), it will change (globally) the log writer function to an internal unittest log writer function (in order to capture the log). This has some implications: - -1. If apps change the log writer before running the unittest, this should be okay. The unittest will restore the log writer to any writer prior to it being run, and will use that writer to display the logs after it is run. -2. If apps change the log writer somewhere inside a test function, I think this should be okay as long as it restores back the writer. -3. if another thread (that is not aware about testing) is writing to the log, then the unittest log writer will pass that log message to pj_log_write() (i.e. the "official" log writer). I think this is okay and it is the desired behavior, but it will clutter the test output. - -### Other - -1. Just rationale: in `PJ_TEST_XXX()` macros, we use `{ }` instead of `do {} while (0)` block to support user putting "`break`" as action. - From cfe3a3abbace6ee038bfc9c8fa58087f5491b08c Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 11 Jul 2024 13:23:58 +0700 Subject: [PATCH 30/79] Fix the use of GitHub repository vars --- .github/workflows/ci-linux.yml | 12 ++++++------ .github/workflows/ci-mac.yml | 12 ++++++------ .github/workflows/ci-win.yml | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 0d18402152..6253e9293c 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -37,7 +37,7 @@ jobs: with: python-version: '3.10' - name: unit tests - run: make pjlib-test pjlib-util-test pjmedia-test pjsua-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjlib-util-test pjmedia-test pjsua-test ubuntu-default-full-bundle-2: # full bundle 2: running pjnath test @@ -53,7 +53,7 @@ jobs: - name: make run: make - name: unit tests - run: make pjnath-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test ubuntu-default-full-bundle-3: # full bundle 3: running pjsip test @@ -69,7 +69,7 @@ jobs: - name: make run: make - name: unit tests - run: make pjsip-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test build-ubuntu-no-tls: # no TLS @@ -127,7 +127,7 @@ jobs: with: python-version: '3.10' - name: unit tests - run: make pjlib-test pjlib-util-test pjmedia-test pjsua-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjlib-util-test pjmedia-test pjsua-test ubuntu-video-openh264-2: # video 2: running pjnath test @@ -147,7 +147,7 @@ jobs: - name: make run: make - name: unit tests - run: make pjnath-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test ubuntu-video-openh264-3: # video: 3: running pjsip test @@ -167,7 +167,7 @@ jobs: - name: make run: make - name: unit tests - run: make pjsip-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test build-ubuntu-video-ffmpeg: # video enabled with vpx and ffmpeg and x264 diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index f1f055f4f4..d9babf731d 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -39,7 +39,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjlib-test pjmedia-test pjlib-util-test pjsua-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjmedia-test pjlib-util-test pjsua-test mac-default-full-bundle-2: # full bundle 2: running pjnath test @@ -57,7 +57,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjnath-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test mac-default-full-bundle-3: # full bundle 3: running pjsip test @@ -75,7 +75,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjsip-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test # build-ubuntu-no-tls: # no TLS (same as build-mac-default) @@ -139,7 +139,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjlib-test pjmedia-test pjlib-util-test pjsua-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjmedia-test pjlib-util-test pjsua-test mac-video-openh264-2: # video 2: running pjnath test @@ -157,7 +157,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjnath-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test mac-video-openh264-3: # video 3: running pjsip test @@ -175,7 +175,7 @@ jobs: - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: unit tests - run: make pjsip-test + run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test build-mac-video-ffmpeg: # video enabled with vpx and ffmpeg and x264 diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 877bac9cab..dc635e8651 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -79,9 +79,9 @@ jobs: run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe %CI_ARGS% %CI_MODE% - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe %CI_ARGS% - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe %CI_ARGS% + cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} ${{ vars.CI_MODE }} + cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} + cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} shell: powershell windows-with-openssl-unit-test-2: @@ -137,7 +137,7 @@ jobs: $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" cd tests/pjsua; python runall.py - cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe %CI_ARGS% + cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} shell: powershell build-windows-gnu-tls: @@ -258,9 +258,9 @@ jobs: $env:SDL_DIR = Get-Content .\sdl_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" cd tests/pjsua; python runall.py - cd ../../pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe %CI_ARGS% --ci-mode - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe %CI_ARGS% - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe %CI_ARGS% + cd ../../pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} ${{ vars.CI_MODE }} + cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} + cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} shell: powershell build-windows-video-ffmpeg: From e1b1e457b596dc6a55ce37a2687bec214e8c632d Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 12 Jul 2024 11:38:17 +0700 Subject: [PATCH 31/79] Tidying up minor conflicting merge in VC projects and pjlib.h --- pjlib/build/pjlib.vcproj | 4 ---- pjlib/build/pjlib.vcxproj.filters | 3 --- pjlib/include/pjlib.h | 3 --- 3 files changed, 10 deletions(-) diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index 85a9b28dcb..bc4e2b2c75 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -12637,10 +12637,6 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl" > - - diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index 7b57c8f466..edfa1069ac 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -445,8 +445,5 @@ Header Files - - Header Files - \ No newline at end of file diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index f102bd8f86..eb9e757842 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -25,9 +25,6 @@ * @brief Include all PJLIB header files. */ -/* argparse is a utility for test apps, not really a feature of pjlib. */ -/*#include */ - #include #include #include From 0246ac99a931ec81613992301b8c13cf79e5a585 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 13 Jul 2024 20:48:43 +0700 Subject: [PATCH 32/79] Attempt to fix assertion failure when tdata is being accessed after reference counter goes to zero in regc_test --- pjsip/src/test/regc_test.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pjsip/src/test/regc_test.c b/pjsip/src/test/regc_test.c index 357b9e0427..5f978f8acf 100644 --- a/pjsip/src/test/regc_test.c +++ b/pjsip/src/test/regc_test.c @@ -328,6 +328,9 @@ static int do_test(const char *title, pjsip_regc_destroy(regc); return -120; } + /* Add additional ref counter to prevent premature destroy */ + pjsip_tx_data_add_ref(tdata); + status = pjsip_regc_send(regc, tdata); /* That's it, wait until the callback is sent */ @@ -338,6 +341,7 @@ static int do_test(const char *title, if (!client_result.done) { PJ_LOG(3,(THIS_FILE, " error: test has timed out")); pjsip_regc_destroy(regc); + pjsip_tx_data_dec_ref(tdata); return -200; } @@ -352,6 +356,7 @@ static int do_test(const char *title, if (client_result.error != client_cfg->error) { PJ_LOG(3,(THIS_FILE, " error: expecting err=%d, got err=%d", client_cfg->error, client_result.error)); + pjsip_tx_data_dec_ref(tdata); return -210; } if (client_result.code != client_cfg->code && @@ -360,31 +365,37 @@ static int do_test(const char *title, { PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d", client_cfg->code, client_result.code)); + pjsip_tx_data_dec_ref(tdata); return -220; } if (client_result.expiration != client_cfg->expiration) { PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d", client_cfg->expiration, client_result.expiration)); + pjsip_tx_data_dec_ref(tdata); return -240; } if (client_result.contact_cnt != client_cfg->contact_cnt) { PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d", client_cfg->contact_cnt, client_result.contact_cnt)); + pjsip_tx_data_dec_ref(tdata); return -250; } if (client_result.have_reg != client_cfg->have_reg) { PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d", client_cfg->have_reg, client_result.have_reg)); + pjsip_tx_data_dec_ref(tdata); return -260; } if (client_result.have_reg && client_result.interval != client_result.expiration) { PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)", client_result.interval, client_result.expiration)); + pjsip_tx_data_dec_ref(tdata); return -270; } if (client_result.interval > 0 && client_result.next_reg < 1) { PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because interval is %d", client_result.next_reg, client_result.interval)); + pjsip_tx_data_dec_ref(tdata); return -280; } @@ -393,6 +404,7 @@ static int do_test(const char *title, *p_regc = regc; } + pjsip_tx_data_dec_ref(tdata); return 0; } From d6b892f46efb820cc9bb44343f58bc395d9c5764 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 13 Jul 2024 20:55:57 +0700 Subject: [PATCH 33/79] Fixing failure in transport_loop_test(), first in loop_resolve_error(), presumably because there are other loop transport around when the test is run. Solution are: 1) make the test exclusive for now, 2) fix the cleanup of loop transport in other tests. Then there is failure in transport_rt_test(), because it gets loop transport from other test that is being shutdown. Sneakily add pjsip_tpmgr_get_transport_count_by_type() that's useful for debugging. --- pjlib/src/pjlib-test/fifobuf.c | 4 -- pjsip/include/pjsip/sip_transport.h | 13 ++++ pjsip/src/pjsip/sip_transport.c | 16 ++++- pjsip/src/test/dns_test.c | 12 ++++ pjsip/src/test/test.c | 47 ++++++++++++-- pjsip/src/test/test.h | 2 +- pjsip/src/test/transport_loop_test.c | 97 +++++++++++++++++----------- pjsip/src/test/transport_test.c | 47 +++++++++++--- pjsip/src/test/tsx_basic_test.c | 6 +- pjsip/src/test/tsx_bench.c | 5 +- pjsip/src/test/tsx_uac_test.c | 16 +++-- pjsip/src/test/tsx_uas_test.c | 6 +- 12 files changed, 205 insertions(+), 66 deletions(-) diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index e3a3640536..684d0ecc60 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -137,10 +137,6 @@ static int fifobuf_misc_test() void *buffer; int i; - i = fifobuf_size_test(); - if (i != 0) - return i; - pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); PJ_TEST_NOT_NULL(pool, NULL, return -10); diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h index 75fcfad034..14c2e5c522 100644 --- a/pjsip/include/pjsip/sip_transport.h +++ b/pjsip/include/pjsip/sip_transport.h @@ -1302,6 +1302,19 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr); +/** + * Return number of transports of the specified type currently registered to + * the transport manager. + * + * @param mgr The transport manager. + * @param type Transport type (can be from pjsip_transport_type_e enum) + * + * @return Number of transports. + */ +PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count_by_type(pjsip_tpmgr *mgr, + int type); + + /** * Destroy a transport manager. Normally application doesn't need to call * this function directly, since a transport manager will be created and diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 27c8176093..3d4ba30460 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1897,6 +1897,12 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, * manager. */ PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr) +{ + return pjsip_tpmgr_get_transport_count_by_type(mgr, -1); +} + +PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count_by_type(pjsip_tpmgr *mgr, + int type) { pj_hash_iterator_t itr_val; pj_hash_iterator_t *itr; @@ -1907,7 +1913,15 @@ PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr) itr = pj_hash_first(mgr->table, &itr_val); while (itr) { transport *tp_entry = (transport *)pj_hash_this(mgr->table, itr); - nr_of_transports += (int)pj_list_size(tp_entry); + if (type<0) { + nr_of_transports += (int)pj_list_size(tp_entry); + } else { + transport *node = tp_entry->next; + for (; node!=tp_entry; node=node->next) { + if (tp_entry->tp->key.type==type) + ++nr_of_transports; + } + } itr = pj_hash_next(mgr->table, itr); } diff --git a/pjsip/src/test/dns_test.c b/pjsip/src/test/dns_test.c index 29dc762223..86ed8cd53e 100644 --- a/pjsip/src/test/dns_test.c +++ b/pjsip/src/test/dns_test.c @@ -331,7 +331,11 @@ static int test_resolve(const char *title, int port, pjsip_server_addresses *ref) { + enum { + TIMEOUT_SECS = 60 + }; pjsip_host_info dest; + pj_time_val timeout; struct result result; PJ_LOG(3,(THIS_FILE, " test_resolve(): %s", title)); @@ -343,14 +347,22 @@ static int test_resolve(const char *title, result.status = 0x12345678; + pj_gettimeofday(&timeout); + timeout.sec += TIMEOUT_SECS; + pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb); while (result.status == 0x12345678) { int i = 0; pj_time_val timeout = { 1, 0 }; + pj_time_val now; + pjsip_endpt_handle_events(endpt, &timeout); if (i == 1) pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE); + + pj_gettimeofday(&now); + PJ_TEST_TRUE(PJ_TIME_VAL_LT(now, timeout), NULL, return 12345678); } if (result.status != PJ_SUCCESS) { diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index 7a1a0af058..f8d54cc21b 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -68,6 +68,40 @@ void flush_events(unsigned duration) } } +/* Wait until there is no loop transport instance */ +pjsip_transport *wait_loop_transport_clear(int secs) +{ + pj_time_val timeout; + + pj_gettimeofday(&timeout); + timeout.sec += secs; + + for (;;) { + pj_sockaddr_in addr; + pjsip_transport *loop = NULL; + pj_time_val now; + pj_status_t status; + + loop = NULL; + pj_bzero(&addr, sizeof(addr)); + status = pjsip_endpt_acquire_transport(endpt, + PJSIP_TRANSPORT_LOOP_DGRAM, + &addr, sizeof(addr), NULL, + &loop); + if (status!=PJ_SUCCESS) + return NULL; + + pj_gettimeofday(&now); + if (PJ_TIME_VAL_GTE(now, timeout)) { + return loop; + } + + pjsip_transport_dec_ref(loop); + loop = NULL; + flush_events(500); + } +} + pj_status_t register_static_modules(pj_size_t *count, pjsip_module **modules) { PJ_UNUSED_ARG(modules); @@ -329,14 +363,19 @@ int test_main(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, transport_udp_test, 0); #endif -#if INCLUDE_LOOP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_loop_test, 0); -#endif - #if INCLUDE_TCP_TEST UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, 0); #endif + /* Loop test needs to be exclusive, because there must NOT be any other + * loop transport otherwise some test will fail (e.g. sending will + * fallback to that transport) + */ +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, + PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); +#endif + /* * Better be last because it recreates the endpt */ diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 7bd686e612..51526a112b 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -126,7 +126,7 @@ void app_perror(const char *msg, pj_status_t status); int init_msg_logger(void); int msg_logger_set_enabled(pj_bool_t enabled); void flush_events(unsigned duration); - +pjsip_transport *wait_loop_transport_clear(int secs); void report_ival(const char *name, int value, const char *valname, const char *desc); void report_sval(const char *name, const char* value, const char *valname, const char *desc); diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 3d28785016..4434f0a6e9 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -109,6 +109,7 @@ int transport_loop_multi_test(void) pj_bzero(loops, sizeof(loops)); for (i=0; iref_cnt); + /* Resolve error */ + status = loop_resolve_error(disable_no_selector); + if (status) + goto on_return; + /* Test basic transport attributes */ status = generic_transport_test(loop); if (status != PJ_SUCCESS) @@ -295,11 +335,6 @@ static int datagram_loop_test() goto on_return; } - /* Resolve error */ - status = loop_resolve_error(); - if (status) - goto on_return; - min_rtt = 0xFFFFFFF; for (i=0; itype == PJSIP_TRANSPORT_LOOP_DGRAM) { PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &g[tid].loop), NULL, return -50); + pjsip_transport_add_ref(g[tid].loop); } return PJ_SUCCESS; } @@ -196,7 +197,9 @@ static void finish_test(unsigned tid) * get_tsx_tid() to be called and it needs the module id. */ if (g[tid].loop) { + /* Order must be shutdown then dec_ref so it gets destroyed */ pjsip_transport_shutdown(g[tid].loop); + pjsip_transport_dec_ref(g[tid].loop); g[tid].loop = 0; } } @@ -1213,10 +1216,11 @@ static int perform_tsx_test(unsigned tid, int dummy, char *target_uri, } /* Check tdata reference counter. */ - if (pj_atomic_get(tdata->ref_cnt) != 1) { + if (pj_atomic_get(tdata->ref_cnt) > 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %ld", pj_atomic_get(tdata->ref_cnt))); - pjsip_tx_data_dec_ref(tdata); + while (pj_atomic_get(tdata->ref_cnt) > 1) + pjsip_tx_data_dec_ref(tdata); return -150; } @@ -1308,17 +1312,21 @@ static int tsx_uac_retransmit_test(unsigned tid) */ static int tsx_resolve_error_test(unsigned tid) { + char target[80]; int status = 0; PJ_LOG(3,(THIS_FILE, " test2: resolve error test")); + pj_ansi_snprintf(target, sizeof(target), + "sip:%d@unresolved-host;transport=%s", + tid, g[tid].test_param->tp_type); + /* * Variant (a): immediate resolve error. */ PJ_LOG(3,(THIS_FILE, " variant a: immediate resolving error")); - status = perform_tsx_test(tid, -800, - "sip:bob@unresolved-host", + status = perform_tsx_test(tid, -800, target, g[tid].FROM_URI, TEST2_BRANCH_ID, 20, &pjsip_options_method); if (status != 0) diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c index 50c13fe874..d6f23cf1ab 100644 --- a/pjsip/src/test/tsx_uas_test.c +++ b/pjsip/src/test/tsx_uas_test.c @@ -1852,8 +1852,12 @@ int tsx_uas_test(unsigned tid) on_return: - if (g[tid].loop) + if (g[tid].loop) { + /* Order must be shutdown then dec_ref so it gets destroyed */ + pjsip_transport_shutdown(g[tid].loop); pjsip_transport_dec_ref(g[tid].loop); + g[tid].loop = NULL; + } unregister_modules(tid); return status; From bd96226cab88535b79620e55b2f649d959ec8577 Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 16 Jul 2024 11:39:19 +0700 Subject: [PATCH 34/79] Attempt to fix unresolved winmm.lib API (e.g. timeKillEvent, timeGetTime) called from BaseClasses --- third_party/BaseClasses/renbase.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/third_party/BaseClasses/renbase.cpp b/third_party/BaseClasses/renbase.cpp index b354b5fb6d..8ea70c953f 100644 --- a/third_party/BaseClasses/renbase.cpp +++ b/third_party/BaseClasses/renbase.cpp @@ -17,6 +17,10 @@ #pragma warning(disable:4355) +#if defined(_MSC_VER) +# pragma comment(lib, "winmm.lib") +#endif + // Helper function for clamping time differences int inline TimeDiff(REFERENCE_TIME rt) { From c930d4cf75ac11324b2035f6517dedabe26962ed Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 16 Jul 2024 16:05:53 +0700 Subject: [PATCH 35/79] Split exclusive part of transport_loop_test --- pjsip/src/test/test.c | 12 ++-- pjsip/src/test/test.h | 1 + pjsip/src/test/transport_loop_test.c | 83 +++++++++++++++++----------- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index f8d54cc21b..e1f3320656 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -356,8 +356,10 @@ int test_main(int argc, char *argv[]) } #endif - /* Note: put exclusive tests last */ - +#if INCLUDE_LOOP_TEST + UT_ADD_TEST(&test_app.ut_app, transport_loop_test, 0); +#endif + #if INCLUDE_UDP_TEST /* Transport tests share same testing codes which are not reentrant */ UT_ADD_TEST(&test_app.ut_app, transport_udp_test, 0); @@ -367,12 +369,14 @@ int test_main(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, transport_tcp_test, 0); #endif - /* Loop test needs to be exclusive, because there must NOT be any other + /* Note: put exclusive tests last */ + + /* This needs to be exclusive, because there must NOT be any other * loop transport otherwise some test will fail (e.g. sending will * fallback to that transport) */ #if INCLUDE_LOOP_TEST - UT_ADD_TEST(&test_app.ut_app, transport_loop_test, + UT_ADD_TEST(&test_app.ut_app, transport_loop_resolve_error_test, PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); #endif diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h index 51526a112b..7fac3e3c61 100644 --- a/pjsip/src/test/test.h +++ b/pjsip/src/test/test.h @@ -86,6 +86,7 @@ int tsx_destroy_test(void); int transport_udp_test(void); int transport_loop_test(void); int transport_loop_multi_test(void); +int transport_loop_resolve_error_test(void); int transport_tcp_test(void); int resolve_test(void); int regc_test(void); diff --git a/pjsip/src/test/transport_loop_test.c b/pjsip/src/test/transport_loop_test.c index 4434f0a6e9..0d13230db9 100644 --- a/pjsip/src/test/transport_loop_test.c +++ b/pjsip/src/test/transport_loop_test.c @@ -273,40 +273,14 @@ int transport_loop_test() enum { LOOP = 8 }; pjsip_transport *loop = NULL; char host_port_transport[64]; - pj_bool_t disable_no_selector = PJ_FALSE; int i, pkt_lost; int status; long ref_cnt; int rtt[LOOP], min_rtt; - PJ_LOG(3,(THIS_FILE, " Loop transport count: %d", - pjsip_tpmgr_get_transport_count_by_type( - pjsip_endpt_get_tpmgr(endpt), - PJSIP_TRANSPORT_LOOP_DGRAM))); - - /* if there is another transport, wait sometime until it's gone */ - loop = wait_loop_transport_clear(10); - - if (loop) { - /* We are not supposed to have another instance of loop transport, but - * we found one. If there are more than one, test will fail. Otherwise - * test should succeed - */ - PJ_LOG(2,(THIS_FILE, - "Warning: found %d loop transport instances", - pjsip_tpmgr_get_transport_count_by_type( - pjsip_endpt_get_tpmgr(endpt), - PJSIP_TRANSPORT_LOOP_DGRAM))); - disable_no_selector = PJ_TRUE; - pjsip_loop_set_discard(loop, 0, NULL); - pjsip_loop_set_failure(loop, 0, NULL); - pjsip_loop_set_delay(loop, 0); - } else { - PJ_TEST_EQ(loop, NULL, NULL, return -2); - PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -3); - pjsip_transport_add_ref(loop); - } + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -3); + pjsip_transport_add_ref(loop); pj_ansi_snprintf(host_port_transport, sizeof(host_port_transport), "%.*s:%d;transport=loop-dgram", @@ -317,11 +291,6 @@ int transport_loop_test() /* Get initial reference counter */ ref_cnt = pj_atomic_get(loop->ref_cnt); - /* Resolve error */ - status = loop_resolve_error(disable_no_selector); - if (status) - goto on_return; - /* Test basic transport attributes */ status = generic_transport_test(loop); if (status != PJ_SUCCESS) @@ -387,3 +356,51 @@ int transport_loop_test() pjsip_transport_dec_ref(loop); return status; } + + +/* tgus needs to be exclusive, because there must NOT be any other + * loop transport otherwise some test will fail (e.g. sending will + * fallback to that transport) + */ +int transport_loop_resolve_error_test() +{ + pjsip_transport *loop; + pj_bool_t disable_no_selector = PJ_FALSE; + int status; + + PJ_LOG(3,(THIS_FILE, " Loop transport count: %d", + pjsip_tpmgr_get_transport_count_by_type( + pjsip_endpt_get_tpmgr(endpt), + PJSIP_TRANSPORT_LOOP_DGRAM))); + + /* if there is another transport, wait sometime until it's gone */ + loop = wait_loop_transport_clear(10); + + if (loop) { + /* We are not supposed to have another instance of loop transport, but + * we found one. If there are more than one, test will fail. Otherwise + * test should succeed + */ + PJ_LOG(2,(THIS_FILE, + "Warning: found %d loop transport instances", + pjsip_tpmgr_get_transport_count_by_type( + pjsip_endpt_get_tpmgr(endpt), + PJSIP_TRANSPORT_LOOP_DGRAM))); + disable_no_selector = PJ_TRUE; + pjsip_loop_set_discard(loop, 0, NULL); + pjsip_loop_set_failure(loop, 0, NULL); + pjsip_loop_set_delay(loop, 0); + } else { + PJ_TEST_EQ(loop, NULL, NULL, return -2); + PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -3); + pjsip_transport_add_ref(loop); + } + + /* Resolve error */ + status = loop_resolve_error(disable_no_selector); + +on_return: + /* Decrement reference. */ + pjsip_transport_dec_ref(loop); + return status; +} From bc8f174c8c63d38256e8af752229a126040c3b05 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 17 Jul 2024 19:15:36 +0700 Subject: [PATCH 36/79] CI mods: split steps, envs, shorten job names --- .github/workflows/ci-linux.yml | 59 ++++++++----- .github/workflows/ci-mac.yml | 61 +++++++++----- .github/workflows/ci-win.yml | 148 ++++++++++++++++++++++++++++----- 3 files changed, 204 insertions(+), 64 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 6253e9293c..d6fe1952ab 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -5,8 +5,11 @@ on: - 'master' pull_request: types: [opened, synchronize, reopened] +env: + CI_ARGS: ${{ vars.CI_LIN_ARGS }} + CI_MODE: ${{ vars.CI_MODE }} jobs: - build-ubuntu-default: + build-default: # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest steps: @@ -16,7 +19,7 @@ jobs: - name: make run: make - ubuntu-default-full-bundle-1: + default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -36,10 +39,16 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.10' - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjlib-util-test pjmedia-test pjsua-test + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - ubuntu-default-full-bundle-2: + default-full-bundle-2: # full bundle 2: running pjnath test runs-on: ubuntu-latest steps: @@ -52,10 +61,10 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: make - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test + - name: pjnath-test + run: make pjnath-test - ubuntu-default-full-bundle-3: + default-full-bundle-3: # full bundle 3: running pjsip test runs-on: ubuntu-latest steps: @@ -68,10 +77,10 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: make - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test + - name: pjsip-test + run: make pjsip-test - build-ubuntu-no-tls: + build-no-tls: # no TLS runs-on: ubuntu-latest steps: @@ -88,7 +97,7 @@ jobs: # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) - build-ubuntu-gnu-tls: + build-gnu-tls: # TLS: with GnuTLS runs-on: ubuntu-latest steps: @@ -102,7 +111,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - ubuntu-video-openh264-1: + video-openh264-1: # video: video enabled with vpx and openh264 # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -126,10 +135,16 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjlib-util-test pjmedia-test pjsua-test + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - ubuntu-video-openh264-2: + video-openh264-2: # video 2: running pjnath test runs-on: ubuntu-latest steps: @@ -146,10 +161,10 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: make - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test + - name: pjnath-test + run: make pjnath-test - ubuntu-video-openh264-3: + video-openh264-3: # video: 3: running pjsip test runs-on: ubuntu-latest steps: @@ -166,10 +181,10 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: make - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test + - name: pjsip-test + run: make pjsip-test - build-ubuntu-video-ffmpeg: + build-video-ffmpeg: # video enabled with vpx and ffmpeg and x264 runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index d9babf731d..8870349a7d 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -5,8 +5,11 @@ on: - 'master' pull_request: types: [opened, synchronize, reopened] +env: + CI_ARGS: ${{ vars.CI_LIN_ARGS }} + CI_MODE: ${{ vars.CI_MODE }} jobs: - build-mac-default: + build-default: # checking pure lib source distribution with plain configure & make runs-on: macos-latest steps: @@ -16,7 +19,7 @@ jobs: - name: make run: make - mac-default-full-bundle-1: + default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: macos-latest @@ -38,10 +41,16 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjmedia-test pjlib-util-test pjsua-test + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - mac-default-full-bundle-2: + default-full-bundle-2: # full bundle 2: running pjnath test runs-on: macos-latest steps: @@ -56,10 +65,10 @@ jobs: run: make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test + - name: pjnath-test + run: make pjnath-test - mac-default-full-bundle-3: + default-full-bundle-3: # full bundle 3: running pjsip test runs-on: macos-latest steps: @@ -74,13 +83,13 @@ jobs: run: make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test + - name: pjsip-test + run: make pjsip-test # build-ubuntu-no-tls: # no TLS (same as build-mac-default) - build-mac-openssl: + build-openssl: # TLS: with OpenSSL runs-on: macos-latest steps: @@ -98,7 +107,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - build-mac-gnu-tls: + build-gnu-tls: # TLS: with GnuTLS runs-on: macos-latest steps: @@ -116,7 +125,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - mac-video-openh264-1: + video-openh264-1: # video: video enabled with vpx and openh264 # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: macos-latest @@ -138,10 +147,16 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjlib-test pjmedia-test pjlib-util-test pjsua-test + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjsua-test + run: make pjsua-test - mac-video-openh264-2: + video-openh264-2: # video 2: running pjnath test runs-on: macos-latest steps: @@ -156,10 +171,10 @@ jobs: run: make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjnath-test + - name: pjnath-test + run: make pjnath-test - mac-video-openh264-3: + video-openh264-3: # video 3: running pjsip test runs-on: macos-latest steps: @@ -174,10 +189,10 @@ jobs: run: make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: unit tests - run: make CI_ARGS="${{ vars.CI_ARGS }}" CI_MODE="${{ vars.CI_MODE }}" pjsip-test + - name: pjsip-test + run: make pjsip-test - build-mac-video-ffmpeg: + build-video-ffmpeg: # video enabled with vpx and ffmpeg and x264 runs-on: macos-latest steps: @@ -203,7 +218,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - build-mac-video-vid-toolbox: + build-video-vid-toolbox: # video enabled with vpx and video toolbox runs-on: macos-latest steps: diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index dc635e8651..0e76ae129f 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -6,7 +6,7 @@ on: pull_request: types: [opened, synchronize, reopened] jobs: - build-windows-default: + build-default: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -40,7 +40,7 @@ jobs: msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug /p:Platform=win32 /p:UseEnv=true shell: cmd - windows-with-openssl-unit-test-1: + openssl-1: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -75,16 +75,29 @@ jobs: set LIB=%LIB%;%OPENSSL_DIR%\lib msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - - name: unit tests + - name: pjlib-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} ${{ vars.CI_MODE }} - cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} - cd ../../pjmedia/bin/; ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} + cd pjlib/bin + ./pjlib-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + shell: powershell + - name: pjlib-util-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjlib-util/bin + ./pjlib-util-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + shell: powershell + - name: pjmedia-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjmedia/bin + ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - windows-with-openssl-unit-test-2: + openssl-2: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -132,15 +145,22 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: unit tests + - name: pjsip-test + run: | + $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:PATH+=";$env:OPENSSL_DIR\bin" + cd pjsip/bin + ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + shell: powershell + - name: python pjsua tests run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd tests/pjsua; python runall.py - cd ../../pjsip/bin; ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_ARGS }} + cd tests/pjsua + python runall.py shell: powershell - build-windows-gnu-tls: + build-gnu-tls: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -178,7 +198,100 @@ jobs: msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - windows-with-video-libvpx-schannel-unit-test-1: + vid-libvpx-schannel-1: + runs-on: windows-latest + steps: + - uses: actions/checkout@master + - name: get vpx + run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/vpx-1.12-win.zip" -Outfile "vpx.zip" + shell: powershell + - name: expand libvpx + run: | + Expand-Archive -LiteralPath .\vpx.zip -DestinationPath .; pwd + cd vpx_build + Add-Content ..\vpx_dir.txt $pwd.Path + shell: powershell + - name: check vpx folder + run: | + set /P VPX_DIR= Date: Thu, 18 Jul 2024 06:13:51 +0700 Subject: [PATCH 37/79] Fix failed tsx_basic_test --- pjsip/src/test/tsx_basic_test.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c index 97d2a52ab9..01bd2a86bb 100644 --- a/pjsip/src/test/tsx_basic_test.c +++ b/pjsip/src/test/tsx_basic_test.c @@ -27,6 +27,7 @@ static struct tsx_basic_test_global_t { char TARGET_URI[PJSIP_MAX_URL_SIZE]; char FROM_URI[PJSIP_MAX_URL_SIZE]; + pjsip_transport *tp; } g[MAX_TSX_TESTS]; @@ -57,6 +58,16 @@ static int tsx_layer_test(unsigned tid) return -120; } + if (g[tid].tp) { + pjsip_tpselector tp_sel; + + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].tp; + + pjsip_tsx_set_transport(tsx, &tp_sel); + } + pj_strdup(tdata->pool, &tsx_key, &tsx->transaction_key); found = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE); @@ -103,6 +114,16 @@ static int double_terminate(unsigned tid) return -20; } + if (g[tid].tp) { + pjsip_tpselector tp_sel; + + pj_bzero(&tp_sel, sizeof(tp_sel)); + tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT; + tp_sel.u.transport = g[tid].tp; + + pjsip_tsx_set_transport(tsx, &tp_sel); + } + /* Save transaction key for later. */ pj_strdup_with_null(tdata->pool, &tsx_key, &tsx->transaction_key); @@ -144,9 +165,12 @@ int tsx_basic_test(unsigned tid) pjsip_transport *loop = NULL; int status; + g[tid].tp = NULL; + if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) { PJ_TEST_SUCCESS(pjsip_loop_start(endpt, &loop), NULL, return -10); pjsip_transport_add_ref(loop); + g[tid].tp = loop; } pj_ansi_snprintf(g[tid].TARGET_URI, sizeof(g[tid].TARGET_URI), "sip:tsx_basic_test@127.0.0.1:%d;transport=%s", From 51484843dddb0861f00a38cdf74eaf6e185f84ff Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 18 Jul 2024 07:00:48 +0700 Subject: [PATCH 38/79] Restore previously shortened job names --- .github/workflows/ci-linux.yml | 20 ++++++++++---------- .github/workflows/ci-mac.yml | 22 +++++++++++----------- .github/workflows/ci-win.yml | 14 +++++++------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index d6fe1952ab..30e4b107e5 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -9,7 +9,7 @@ env: CI_ARGS: ${{ vars.CI_LIN_ARGS }} CI_MODE: ${{ vars.CI_MODE }} jobs: - build-default: + build-lin-default: # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest steps: @@ -19,7 +19,7 @@ jobs: - name: make run: make - default-full-bundle-1: + lin-default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -48,7 +48,7 @@ jobs: - name: pjsua-test run: make pjsua-test - default-full-bundle-2: + lin-default-full-bundle-2: # full bundle 2: running pjnath test runs-on: ubuntu-latest steps: @@ -64,7 +64,7 @@ jobs: - name: pjnath-test run: make pjnath-test - default-full-bundle-3: + lin-default-full-bundle-3: # full bundle 3: running pjsip test runs-on: ubuntu-latest steps: @@ -80,7 +80,7 @@ jobs: - name: pjsip-test run: make pjsip-test - build-no-tls: + build-lin-no-tls: # no TLS runs-on: ubuntu-latest steps: @@ -97,7 +97,7 @@ jobs: # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) - build-gnu-tls: + build-lin-gnu-tls: # TLS: with GnuTLS runs-on: ubuntu-latest steps: @@ -111,7 +111,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - video-openh264-1: + lin-vid-openh264-1: # video: video enabled with vpx and openh264 # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -144,7 +144,7 @@ jobs: - name: pjsua-test run: make pjsua-test - video-openh264-2: + lin-vid-openh264-2: # video 2: running pjnath test runs-on: ubuntu-latest steps: @@ -164,7 +164,7 @@ jobs: - name: pjnath-test run: make pjnath-test - video-openh264-3: + lin-vid-openh264-3: # video: 3: running pjsip test runs-on: ubuntu-latest steps: @@ -184,7 +184,7 @@ jobs: - name: pjsip-test run: make pjsip-test - build-video-ffmpeg: + build-lin-vid-ffmpeg: # video enabled with vpx and ffmpeg and x264 runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 8870349a7d..106121ccfb 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -9,7 +9,7 @@ env: CI_ARGS: ${{ vars.CI_LIN_ARGS }} CI_MODE: ${{ vars.CI_MODE }} jobs: - build-default: + build-mac-default: # checking pure lib source distribution with plain configure & make runs-on: macos-latest steps: @@ -19,7 +19,7 @@ jobs: - name: make run: make - default-full-bundle-1: + mac-default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: macos-latest @@ -50,7 +50,7 @@ jobs: - name: pjsua-test run: make pjsua-test - default-full-bundle-2: + mac-default-full-bundle-2: # full bundle 2: running pjnath test runs-on: macos-latest steps: @@ -68,7 +68,7 @@ jobs: - name: pjnath-test run: make pjnath-test - default-full-bundle-3: + mac-default-full-bundle-3: # full bundle 3: running pjsip test runs-on: macos-latest steps: @@ -89,7 +89,7 @@ jobs: # build-ubuntu-no-tls: # no TLS (same as build-mac-default) - build-openssl: + build-mac-openssl: # TLS: with OpenSSL runs-on: macos-latest steps: @@ -107,7 +107,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - build-gnu-tls: + build-mac-gnu-tls: # TLS: with GnuTLS runs-on: macos-latest steps: @@ -125,7 +125,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - video-openh264-1: + mac-video-openh264-1: # video: video enabled with vpx and openh264 # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: macos-latest @@ -156,7 +156,7 @@ jobs: - name: pjsua-test run: make pjsua-test - video-openh264-2: + mac-video-openh264-2: # video 2: running pjnath test runs-on: macos-latest steps: @@ -174,7 +174,7 @@ jobs: - name: pjnath-test run: make pjnath-test - video-openh264-3: + mac-video-openh264-3: # video 3: running pjsip test runs-on: macos-latest steps: @@ -192,7 +192,7 @@ jobs: - name: pjsip-test run: make pjsip-test - build-video-ffmpeg: + build-mac-video-ffmpeg: # video enabled with vpx and ffmpeg and x264 runs-on: macos-latest steps: @@ -218,7 +218,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - build-video-vid-toolbox: + build-mac-video-vid-toolbox: # video enabled with vpx and video toolbox runs-on: macos-latest steps: diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 0e76ae129f..be0d211ab1 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -6,7 +6,7 @@ on: pull_request: types: [opened, synchronize, reopened] jobs: - build-default: + build-win-default: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -40,7 +40,7 @@ jobs: msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug /p:Platform=win32 /p:UseEnv=true shell: cmd - openssl-1: + win-openssl-1: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -97,7 +97,7 @@ jobs: ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - openssl-2: + win-openssl-2: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -160,7 +160,7 @@ jobs: python runall.py shell: powershell - build-gnu-tls: + build-win-gnu-tls: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -198,7 +198,7 @@ jobs: msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - vid-libvpx-schannel-1: + win-vid-libvpx-schannel-1: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -291,7 +291,7 @@ jobs: ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - vid-libvpx-schannel-2: + win-vid-libvpx-schannel-2: runs-on: windows-latest steps: - uses: actions/checkout@master @@ -373,7 +373,7 @@ jobs: cd tests/pjsua python runall.py - build-vid-ffmpeg: + build-win-vid-ffmpeg: runs-on: windows-latest steps: - uses: actions/checkout@master From cf5d2ccb8c92635fa3e3e55d352ffe680828fdb4 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 18 Jul 2024 15:36:31 +0700 Subject: [PATCH 39/79] Replace assertion with PJ_TEST_XX in resolver_test --- .../src/pjlib-util-test/resolver_test.c | 370 ++++++++---------- 1 file changed, 166 insertions(+), 204 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index a0d133dd0f..6ba757d60e 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -170,7 +170,7 @@ static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos, if (size < 8) return -1; - pj_assert(rr->dnsclass == 1); + PJ_TEST_EQ(rr->dnsclass, 1, NULL, return -1); write16(p+0, (pj_uint16_t)rr->type); /* type */ write16(p+2, (pj_uint16_t)rr->dnsclass); /* class */ @@ -430,7 +430,6 @@ static void destroy(void); static int init(pj_bool_t use_ipv6) { - pj_status_t status; pj_str_t nameservers[2]; pj_uint16_t ports[2]; int i; @@ -450,52 +449,45 @@ static int init(pj_bool_t use_ipv6) pool = pj_pool_create(mem, NULL, 2000, 2000, NULL); - status = pj_sem_create(pool, NULL, 0, 2, &sem); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_sem_create(pool, NULL, 0, 2, &sem), NULL, return -5); thread_quit = PJ_FALSE; for (i=0; i<2; ++i) { pj_sockaddr addr; - status = pj_sock_socket((use_ipv6? pj_AF_INET6() : pj_AF_INET()), - pj_SOCK_DGRAM(), 0, &g_server[i].sock); - if (status != PJ_SUCCESS) - return -10; + PJ_TEST_SUCCESS(pj_sock_socket( + (use_ipv6? pj_AF_INET6() : pj_AF_INET()), + pj_SOCK_DGRAM(), 0, &g_server[i].sock), + NULL, return -10); pj_sockaddr_init((use_ipv6? pj_AF_INET6() : pj_AF_INET()), &addr, NULL, (pj_uint16_t)g_server[i].port); - status = pj_sock_bind(g_server[i].sock, &addr, pj_sockaddr_get_len(&addr)); - if (status != PJ_SUCCESS) - return -20; + PJ_TEST_SUCCESS(pj_sock_bind(g_server[i].sock, &addr, + pj_sockaddr_get_len(&addr)), + NULL, return -20); - status = pj_thread_create(pool, NULL, &server_thread, &g_server[i], - 0, 0, &g_server[i].thread); - if (status != PJ_SUCCESS) - return -30; + PJ_TEST_SUCCESS(pj_thread_create(pool, NULL, &server_thread, + &g_server[i], + 0, 0, &g_server[i].thread), + NULL, return -30); } - status = pj_timer_heap_create(pool, 16, &timer_heap); - pj_assert(status == PJ_SUCCESS); - - status = pj_ioqueue_create(pool, 16, &ioqueue); - pj_assert(status == PJ_SUCCESS); - - status = pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver); - if (status != PJ_SUCCESS) - return -40; + PJ_TEST_SUCCESS(pj_timer_heap_create(pool, 16, &timer_heap), NULL, return -31); + PJ_TEST_SUCCESS(pj_ioqueue_create(pool, 16, &ioqueue), NULL, return -32); + PJ_TEST_SUCCESS(pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver), + NULL, return -40); pj_dns_resolver_get_settings(resolver, &set); set.good_ns_ttl = 20; set.bad_ns_ttl = 20; pj_dns_resolver_set_settings(resolver, &set); - status = pj_dns_resolver_set_ns(resolver, 2, nameservers, ports); - pj_assert(status == PJ_SUCCESS); - - status = pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(resolver, 2, nameservers, ports), + NULL, return -41); + PJ_TEST_SUCCESS(pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread), + NULL, return -42); return 0; } @@ -529,7 +521,6 @@ static int a_parser_test(void) { pj_dns_parsed_packet pkt; pj_dns_a_record rec; - pj_status_t rc; PJ_LOG(3,(THIS_FILE, " DNS A record parser tests")); @@ -570,12 +561,11 @@ static int a_parser_test(void) pkt.ans[2].rdata.a.ip_addr.s_addr = 0x0203; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(rec.alias.slen == 0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].s_addr == 0x01020304); + PJ_TEST_SUCCESS(pj_dns_parse_a_response(&pkt, &rec), NULL, return -100) + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -110); + PJ_TEST_EQ(rec.alias.slen, 0, NULL, return -112); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -114); + PJ_TEST_EQ(rec.addr[0].s_addr, 0x01020304, NULL, return -116); /* Answer with the target corresponds to a CNAME entry, but not * as the first record, and with additions of some CNAME and A @@ -617,12 +607,11 @@ static int a_parser_test(void) pkt.ans[3].ttl = 1; pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].s_addr == 0x02020202); + PJ_TEST_SUCCESS(pj_dns_parse_a_response(&pkt, &rec), NULL, return -120); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -122); + PJ_TEST_EQ(pj_strcmp2(&rec.alias, "ahostalias"), 0, NULL, return -124); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -126); + PJ_TEST_EQ(rec.addr[0].s_addr, 0x02020202, NULL, return -128); /* * No query section. @@ -631,8 +620,8 @@ static int a_parser_test(void) pkt.hdr.qdcount = 0; pkt.hdr.anscount = 0; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSINANSWER); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSINANSWER, + NULL, return -130); /* * No answer section. @@ -645,8 +634,8 @@ static int a_parser_test(void) pkt.q[0].name = pj_str("ahost"); pkt.hdr.anscount = 0; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -140); /* * Answer doesn't match query. @@ -666,8 +655,8 @@ static int a_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -150); /* @@ -688,8 +677,8 @@ static int a_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -160); /* @@ -717,9 +706,8 @@ static int a_parser_test(void) pkt.ans[1].ttl = 1; pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304; - rc = pj_dns_parse_a_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); - PJ_UNUSED_ARG(rc); + PJ_TEST_EQ(pj_dns_parse_a_response(&pkt, &rec), PJLIB_UTIL_EDNSNOANSWERREC, + NULL, return -170); return 0; } @@ -731,7 +719,6 @@ static int addr_parser_test(void) { pj_dns_parsed_packet pkt; pj_dns_addr_record rec; - pj_status_t rc; PJ_LOG(3,(THIS_FILE, " DNS A/AAAA record parser tests")); @@ -779,13 +766,14 @@ static int addr_parser_test(void) s6_addr32(pkt.ans[3].rdata.aaaa.ip_addr, 0) = 0x01020304; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(rec.alias.slen == 0); - pj_assert(rec.addr_count == 2); - pj_assert(rec.addr[0].af==pj_AF_INET() && rec.addr[0].ip.v4.s_addr == 0x01020304); - pj_assert(rec.addr[1].af==pj_AF_INET6() && s6_addr32(rec.addr[1].ip.v6, 0) == 0x01020304); + PJ_TEST_SUCCESS(pj_dns_parse_addr_response(&pkt, &rec), NULL, return -200); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -210); + PJ_TEST_EQ(rec.alias.slen, 0, NULL, return -212); + PJ_TEST_EQ(rec.addr_count, 2, NULL, return -214); + PJ_TEST_EQ(rec.addr[0].af, pj_AF_INET(), NULL, return -220); + PJ_TEST_EQ(rec.addr[0].ip.v4.s_addr, 0x01020304, NULL, return -222); + PJ_TEST_EQ(rec.addr[1].af, pj_AF_INET6(), NULL, return -230); + PJ_TEST_EQ(s6_addr32(rec.addr[1].ip.v6, 0), 0x01020304, NULL, return -232); /* Answer with the target corresponds to a CNAME entry, but not * as the first record, and with additions of some CNAME and A @@ -827,12 +815,11 @@ static int addr_parser_test(void) pkt.ans[3].ttl = 1; pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJ_SUCCESS); - pj_assert(pj_strcmp2(&rec.name, "ahost")==0); - pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0); - pj_assert(rec.addr_count == 1); - pj_assert(rec.addr[0].ip.v4.s_addr == 0x02020202); + PJ_TEST_SUCCESS(pj_dns_parse_addr_response(&pkt, &rec), NULL, return -240); + PJ_TEST_EQ(pj_strcmp2(&rec.name, "ahost"), 0, NULL, return -242); + PJ_TEST_EQ(pj_strcmp2(&rec.alias, "ahostalias"), 0, NULL, return -244); + PJ_TEST_EQ(rec.addr_count, 1, NULL, return -246); + PJ_TEST_EQ(rec.addr[0].ip.v4.s_addr, 0x02020202, NULL, return -248); /* * No query section. @@ -841,8 +828,8 @@ static int addr_parser_test(void) pkt.hdr.qdcount = 0; pkt.hdr.anscount = 0; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSINANSWER); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSINANSWER, NULL, return -245); /* * No answer section. @@ -855,8 +842,8 @@ static int addr_parser_test(void) pkt.q[0].name = pj_str("ahost"); pkt.hdr.anscount = 0; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -250); /* * Answer doesn't match query. @@ -876,8 +863,8 @@ static int addr_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -260); /* @@ -898,8 +885,8 @@ static int addr_parser_test(void) pkt.ans[0].ttl = 1; pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -270); /* @@ -927,9 +914,8 @@ static int addr_parser_test(void) pkt.ans[1].ttl = 1; pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304; - rc = pj_dns_parse_addr_response(&pkt, &rec); - pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); - PJ_UNUSED_ARG(rc); + PJ_TEST_EQ(pj_dns_parse_addr_response(&pkt, &rec), + PJLIB_UTIL_EDNSNOANSWERREC, NULL, return -280); return 0; } @@ -960,7 +946,6 @@ static int simple_test(void) { pj_str_t name = pj_str("helloworld"); pj_dns_parsed_packet *r; - pj_status_t status; PJ_LOG(3,(THIS_FILE, " simple successful test")); @@ -995,18 +980,18 @@ static int simple_test(void) r->ans[0].name = name; r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0; - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback, NULL, NULL), + NULL, return -300); pj_sem_wait(sem); pj_thread_sleep(1000); /* Both servers must get packet */ - pj_assert(g_server[0].pkt_count == 1); - pj_assert(g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -310); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -320); return 0; } @@ -1024,8 +1009,8 @@ static void dns_callback_1b(void *user_data, pj_sem_post(sem); - PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), - return); + PJ_TEST_EQ(status, PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), + NULL, return); } @@ -1035,7 +1020,6 @@ static void dns_callback_1b(void *user_data, static int dns_test(void) { pj_str_t name = pj_str("name00"); - pj_status_t status; PJ_LOG(3,(THIS_FILE, " simple error response test")); @@ -1045,10 +1029,10 @@ static int dns_test(void) g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -400); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1056,7 +1040,8 @@ static int dns_test(void) /* Now only one of the servers should get packet, since both servers are * in STATE_ACTIVE state */ - pj_assert(g_server[0].pkt_count + g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count + g_server[1].pkt_count, 1, + NULL, return -410); /* Wait to allow active period to complete and get into probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", @@ -1074,15 +1059,16 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name01"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -420); pj_sem_wait(sem); /* Both servers must get packet as both are in probing state */ - pj_assert(g_server[0].pkt_count >= 1 && g_server[1].pkt_count == 1); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1096,16 +1082,17 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name02"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -440); pj_sem_wait(sem); pj_thread_sleep(1000); /* Both servers must get packet as both are in probing & active state */ - pj_assert(g_server[0].pkt_count >= 1 && g_server[1].pkt_count == 1); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", @@ -1125,17 +1112,17 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name03"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -460); pj_sem_wait(sem); pj_thread_sleep(1000); /* Only server 1 get the request */ - pj_assert(g_server[0].pkt_count == 0); - pj_assert(g_server[1].pkt_count == 1); + PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", @@ -1152,10 +1139,10 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name04"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -480); pj_sem_wait(sem); @@ -1175,18 +1162,17 @@ static int dns_test(void) g_server[1].pkt_count = 0; name = pj_str("name05"); - status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, - &dns_callback_1b, NULL, NULL); - if (status != PJ_SUCCESS) - return -1000; + PJ_TEST_SUCCESS(pj_dns_resolver_start_query( + resolver, &name, PJ_DNS_TYPE_A, 0, + &dns_callback_1b, NULL, NULL), + NULL, return -484); pj_sem_wait(sem); pj_thread_sleep(1000); /* Only good NS should get request */ - pj_assert(g_server[0].pkt_count == 1); - pj_assert(g_server[1].pkt_count == 0); - + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; } @@ -1371,7 +1357,6 @@ static void srv_cb_1d(void *user_data, static int srv_resolver_test(void) { - pj_status_t status; pj_str_t domain = pj_str("somedomain.com"); pj_str_t res_name = pj_str("_sip._udp."); @@ -1388,15 +1373,16 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -500); pj_sem_wait(sem); /* Because of previous tests, only NS 1 should get the request */ - pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ - pj_assert(g_server[1].pkt_count == 0); + PJ_TEST_EQ(g_server[0].pkt_count, 2, NULL, return -510); /* 2 because of SRV and A resolution */ + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -512); /* Wait until cache expires */ @@ -1415,10 +1401,11 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, - PJ_DNS_SRV_RESOLVE_AAAA, - NULL, &srv_cb_1c, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, + PJ_DNS_SRV_RESOLVE_AAAA, + NULL, &srv_cb_1c, NULL), + NULL, return -520); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1434,10 +1421,11 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, - PJ_DNS_SRV_RESOLVE_AAAA_ONLY, - NULL, &srv_cb_1d, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, + PJ_DNS_SRV_RESOLVE_AAAA_ONLY, + NULL, &srv_cb_1d, NULL), + NULL, return -530); pj_sem_wait(sem); pj_thread_sleep(1000); @@ -1448,21 +1436,23 @@ static int srv_resolver_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -540); - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1, NULL), + NULL, return -550); pj_sem_wait(sem); pj_sem_wait(sem); /* Only server one should get a query */ - pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ - pj_assert(g_server[1].pkt_count == 0); + PJ_TEST_EQ(g_server[0].pkt_count, 2, NULL, return -554); /* 2 because of SRV and A resolution */ + PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -556); /* Since TTL is one, subsequent queries should fail */ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache expires scenario")); @@ -1472,14 +1462,15 @@ static int srv_resolver_test(void) g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; - status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, - NULL, &srv_cb_1b, NULL); - pj_assert(status == PJ_SUCCESS); + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 5061, pool, resolver, PJ_TRUE, + NULL, &srv_cb_1b, NULL), + NULL, return -560); pj_sem_wait(sem); pj_thread_sleep(1000); - return status; + return 0; } @@ -1642,7 +1633,6 @@ static void srv_cb_2b(void *user_data, static int srv_resolver_fallback_test(void) { - pj_status_t status; pj_str_t domain = pj_str(TARGET); pj_str_t res_name = pj_str("_sip._udp."); int cb_err = 0; @@ -1655,43 +1645,31 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_2, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -10; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_2, NULL), + NULL, return -600); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -20; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -605); /* Subsequent query should just get the response from the cache */ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache test")); g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_2, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -30; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_2, NULL), + NULL, return -610); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -40; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -615); - if (g_server[0].pkt_count != 0 || g_server[1].pkt_count != 0) { - PJ_LOG(3,("test", " srv_resolve() not from cache")); - return -50; - } + PJ_TEST_EQ(g_server[0].pkt_count, 0, "must be from cache", return -620); + PJ_TEST_EQ(g_server[1].pkt_count, 0, "must be from cache", return -625); /* Clear cache */ pj_thread_sleep(1000); @@ -1704,20 +1682,15 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, - PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA, - &cb_err, &srv_cb_2a, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -60; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, + PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA, + &cb_err, &srv_cb_2a, NULL), + NULL, return -630); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -70; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -635); /* Clear cache */ pj_thread_sleep(1000); @@ -1730,20 +1703,15 @@ static int srv_resolver_fallback_test(void) g_server[1].action = ACTION_CB; g_server[1].action_cb = &action2_1; - status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, - PJ_DNS_SRV_FALLBACK_AAAA, - &cb_err, &srv_cb_2b, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -80; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, PORT2, pool, resolver, + PJ_DNS_SRV_FALLBACK_AAAA, + &cb_err, &srv_cb_2b, NULL), + NULL, return -640); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -90; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -645); /* Clear cache */ pj_thread_sleep(1000); @@ -1856,7 +1824,6 @@ static void srv_cb_3(void *user_data, static int srv_resolver_many_test(void) { - pj_status_t status; pj_str_t domain = pj_str(DOMAIN3); pj_str_t res_name = pj_str("_sip._udp."); int cb_err = 0; @@ -1872,19 +1839,14 @@ static int srv_resolver_many_test(void) g_server[0].pkt_count = 0; g_server[1].pkt_count = 0; - status = pj_dns_srv_resolve(&domain, &res_name, 1, pool, resolver, PJ_TRUE, - &cb_err, &srv_cb_3, NULL); - if (status != PJ_SUCCESS) { - app_perror(" srv_resolve error", status); - return -10; - } + PJ_TEST_SUCCESS(pj_dns_srv_resolve( + &domain, &res_name, 1, pool, resolver, PJ_TRUE, + &cb_err, &srv_cb_3, NULL), + NULL, return -700); pj_sem_wait(sem); - if (cb_err != 0) { - PJ_LOG(3,("test", " srv_resolve cb error, code=%d", cb_err)); - return -20; - } + PJ_TEST_EQ(cb_err, 0, "srv_resolve cb error", return -710); return 0; } From 4cfff2fef36c73224a4422498147031fb63f3159 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 18 Jul 2024 16:07:55 +0700 Subject: [PATCH 40/79] Add --stdout-buf and --stderr-buf options in test apps to control stdout/stderr buffering --- pjlib/src/pjlib-test/test_util.h | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 9c7a67620a..1076c4a7a9 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -221,6 +221,11 @@ PJ_INLINE(void) ut_usage() puts(" --stop-err Stop testing on error"); puts(" --shuffle Shuffle the test order"); puts(" --seed N Set shuffle random seed (must be >= 0)"); + puts(" --stdout-buf N Set stdout buffering mode:"); + puts(" --stderr-buf N Set stderr buffering mode:"); + puts(" 0: unbufferred (default for stderr)"); + puts(" 1: line"); + puts(" 2: fully bufferred (default for stdout)"); puts(" -v, --verbose Show info when starting/stopping tests"); } @@ -276,6 +281,34 @@ PJ_INLINE(pj_status_t) ut_parse_args(ut_app_t *ut_app, int *argc, char *argv[]) ut_app->verbosity = pj_argparse_get_bool(argc, argv, "-v") || pj_argparse_get_bool(argc, argv, "--verbose"); + itmp = -101; + if (pj_argparse_get_int(argc, argv, "--stdout-buf", &itmp)==PJ_SUCCESS && + itmp != -101) + { + switch (itmp) { + case 0: setvbuf(stdout, NULL, _IONBF, 0); break; + case 1: setvbuf(stdout, NULL, _IOLBF, 0); break; + case 2: setvbuf(stdout, NULL, _IOFBF, 0); break; + default: + printf("Error: invalid --stdout-buf value %d\n", itmp); + return PJ_EINVAL; + } + } + + itmp = -101; + if (pj_argparse_get_int(argc, argv, "--stderr-buf", &itmp)==PJ_SUCCESS && + itmp != -101) + { + switch (itmp) { + case 0: setvbuf(stderr, NULL, _IONBF, 0); break; + case 1: setvbuf(stderr, NULL, _IOLBF, 0); break; + case 2: setvbuf(stderr, NULL, _IOFBF, 0); break; + default: + printf("Error: invalid --stderr-buf value %d\n", itmp); + return PJ_EINVAL; + } + } + return PJ_SUCCESS; } From 72d425e1abffc4e90a82cdf3448e73a13d8ef601 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 18 Jul 2024 17:54:37 +0700 Subject: [PATCH 41/79] Fix missing sipp exe on Windows CI --- .github/workflows/ci-win.yml | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index be0d211ab1..dd1993efff 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -116,6 +116,14 @@ jobs: dir "%OPENSSL_DIR%\include" dir "%OPENSSL_DIR%\lib" shell: cmd + - name: install SIPp + run: | + Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/sipp-3.2-win.zip" -OutFile ".\sipp.zip" + Expand-Archive -LiteralPath .\sipp.zip -DestinationPath . + cd sipp + Add-Content ..\sipp_dir.txt $pwd.Path + cd .. + shell: powershell - name: config site run: cd pjlib/include/pj; cp config_site_test.h config_site.h; Add-Content config_site.h "#define PJ_HAS_SSL_SOCK 1" @@ -145,19 +153,21 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: pjsip-test + - name: python pjsua tests run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:SIPP_DIR = Get-Content .\sipp_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjsip/bin - ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $env:PATH+=";$env:SIPP_DIR" + cd tests/pjsua + python runall.py shell: powershell - - name: python pjsua tests + - name: pjsip-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd tests/pjsua - python runall.py + cd pjsip/bin + ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell build-win-gnu-tls: @@ -325,6 +335,14 @@ jobs: dir "%SDL_DIR%\include" dir "%SDL_DIR%\lib\x86" shell: cmd + - name: install SIPp + run: | + Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/sipp-3.2-win.zip" -OutFile ".\sipp.zip" + Expand-Archive -LiteralPath .\sipp.zip -DestinationPath . + cd sipp + Add-Content ..\sipp_dir.txt $pwd.Path + cd .. + shell: powershell - name: config site run: | cd pjlib/include/pj; cp config_site_test.h config_site.h @@ -369,9 +387,12 @@ jobs: - name: python pjsua tests run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt + $env:SIPP_DIR = Get-Content .\sipp_dir.txt $env:PATH+=";$env:SDL_DIR\lib\x86;" + $env:PATH+=";$env:SIPP_DIR" cd tests/pjsua python runall.py + shell: powershell build-win-vid-ffmpeg: runs-on: windows-latest From 311edb9a362b4eb636660d094ec49ffd0d2ae993 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 18 Jul 2024 18:19:54 +0700 Subject: [PATCH 42/79] Fix wrong CI args on Mac. Use -j for faster make --- .github/workflows/ci-linux.yml | 39 +++++++++++++++++----------------- .github/workflows/ci-mac.yml | 27 +++++++++++------------ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 30e4b107e5..5d2f36c027 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -8,6 +8,7 @@ on: env: CI_ARGS: ${{ vars.CI_LIN_ARGS }} CI_MODE: ${{ vars.CI_MODE }} + MAKE_FAST: make -j 3 jobs: build-lin-default: # checking pure lib source distribution with plain configure & make @@ -17,7 +18,7 @@ jobs: - name: configure run: ./configure - name: make - run: make + run: $MAKE_FAST lin-default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS @@ -32,9 +33,9 @@ jobs: - name: configure run: CFLAGS="-g -fPIC" CXXFLAGS="-g -fPIC" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: cd pjsip-apps/src/swig && $MAKE_FAST - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v2 with: @@ -60,7 +61,7 @@ jobs: - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: pjnath-test run: make pjnath-test @@ -76,7 +77,7 @@ jobs: - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: pjsip-test run: make pjsip-test @@ -90,9 +91,9 @@ jobs: - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --disable-ssl - name: make - run: make + run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: cd pjsip-apps/src/swig && $MAKE_FAST # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) @@ -107,9 +108,9 @@ jobs: - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/ - name: make - run: make + run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: cd pjsip-apps/src/swig && $MAKE_FAST lin-vid-openh264-1: # video: video enabled with vpx and openh264 @@ -122,15 +123,15 @@ jobs: - name: get openh264 run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g -fPIC -DHAS_VID_CODEC_TEST=0" CXXFLAGS="-g -fPIC" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: cd pjsip-apps/src/swig && $MAKE_FAST - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v4 with: @@ -154,13 +155,13 @@ jobs: - name: get openh264 run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: pjnath-test run: make pjnath-test @@ -174,13 +175,13 @@ jobs: - name: get openh264 run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 - run: cd openh264 && make && sudo make install && sudo ldconfig + run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h && echo "#define PJMEDIA_HAS_VIDEO 1" >> config_site.h - name: configure run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make - run: make + run: $MAKE_FAST - name: pjsip-test run: make pjsip-test @@ -196,12 +197,12 @@ jobs: - name: configure ffmpeg run: cd FFmpeg && ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg - run: cd FFmpeg && make -j10 && sudo make install + run: cd FFmpeg && $MAKE_FAST && sudo make install - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && make + run: cd pjsip-apps/src/swig && $MAKE_FAST diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 106121ccfb..7c8b4de3d2 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -6,8 +6,9 @@ on: pull_request: types: [opened, synchronize, reopened] env: - CI_ARGS: ${{ vars.CI_LIN_ARGS }} + CI_ARGS: ${{ vars.CI_MAC_ARGS }} CI_MODE: ${{ vars.CI_MODE }} + MAKE_FAST: make -j 2 jobs: build-mac-default: # checking pure lib source distribution with plain configure & make @@ -17,7 +18,7 @@ jobs: - name: configure run: ./configure - name: make - run: make + run: $MAKE_FAST mac-default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS @@ -32,7 +33,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -62,7 +63,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: pjnath-test @@ -80,7 +81,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: pjsip-test @@ -99,7 +100,7 @@ jobs: - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -117,7 +118,7 @@ jobs: - name: configure run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -138,7 +139,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -DHAS_VID_CODEC_TEST=0 -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -168,7 +169,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: pjnath-test @@ -186,7 +187,7 @@ jobs: - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure - name: make - run: make + run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: pjsip-test @@ -204,13 +205,13 @@ jobs: - name: configure ffmpeg run: cd FFmpeg && LDFLAGS="-Wl,-ld_classic" ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg - run: cd FFmpeg && make -j10 && sudo make install + run: cd FFmpeg && $MAKE_FAST && sudo make install - name: config site run: echo -e "#define PJMEDIA_HAS_VIDEO 1\n" > pjlib/include/pj/config_site.h - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: @@ -230,7 +231,7 @@ jobs: - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure - name: make - run: make + run: $MAKE_FAST - name: set up Python uses: actions/setup-python@v4 with: From 78c9adf50a16e81d702fb8483984fe896f7b564f Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 19 Jul 2024 07:10:43 +0700 Subject: [PATCH 43/79] Replace pj_rand() with own rand in unittest because rand() yields different sequence on different platform even with the same srand, making it hard to reproduce test sequence --- pjlib/src/pj/unittest.c | 28 +++++++++++++++++++++------- pjlib/src/pjlib-test/test_util.h | 12 ++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index d8ac75132d..fc5e9e09d9 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -114,14 +114,27 @@ PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) pj_list_push_back(&suite->tests, tc); } +/* Own PRNG with Linear congruential generator because rand() yields + * difference sequence on different machines even with the same seed. + */ +static unsigned rand_int(unsigned seed) +{ + enum { + M = 1<<31, + A = 1103515245, + C = 12345 + }; + + return ((A*seed) + C) % M; +} + /* Shuffle */ PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) { pj_test_case src, *tc; - unsigned total, movable; + unsigned rand, total, movable; - if (seed >= 0) - pj_srand(seed); + rand = (seed >= 0) ? seed : pj_rand(); /* Move tests to new list */ pj_list_init(&src); @@ -147,10 +160,11 @@ PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) /* Shuffle non KEEP_LAST tests */ while (movable > 0) { - int step = pj_rand() % total; - if (step < 0) - continue; - + unsigned step; + + rand = rand_int(rand); + step = rand % total; + for (tc=src.next; step>0; tc=tc->next, --step) ; diff --git a/pjlib/src/pjlib-test/test_util.h b/pjlib/src/pjlib-test/test_util.h index 1076c4a7a9..7eb4575c7e 100644 --- a/pjlib/src/pjlib-test/test_util.h +++ b/pjlib/src/pjlib-test/test_util.h @@ -154,6 +154,12 @@ PJ_INLINE(pj_status_t) ut_run_tests(ut_app_t *ut_app, const char *title, pj_test_stat stat; pj_status_t status; + if (ut_app->prm_shuffle) { + PJ_LOG(3,(THIS_FILE, "Shuffling tests, random seed=%d", + ut_app->prm_seed)); + pj_test_suite_shuffle(&ut_app->suite, ut_app->prm_seed); + } + if (ut_app->prm_list_test) { ut_list_tests(ut_app, title); return PJ_SUCCESS; @@ -192,12 +198,6 @@ PJ_INLINE(pj_status_t) ut_run_tests(ut_app_t *ut_app, const char *title, ut_app->ntests, title, runner_prm.nthreads, runner_prm.nthreads>1?"s":"")); - if (ut_app->prm_shuffle) { - PJ_LOG(3,(THIS_FILE, "Shuffling tests, random seed=%d", - ut_app->prm_seed)); - pj_test_suite_shuffle(&ut_app->suite, ut_app->prm_seed); - } - pj_test_run(runner, &ut_app->suite); pj_test_runner_destroy(runner); pj_test_display_log_messages(&ut_app->suite, ut_app->prm_logging_policy); From b08343b4fd283e3b641755d3c70e9b6bdbb20daa Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 19 Jul 2024 13:36:07 +0700 Subject: [PATCH 44/79] Fix swig make error on Linux and runall.py error reading log file on Windows --- .github/workflows/ci-linux.yml | 10 +++++----- tests/pjsua/runall.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 5d2f36c027..001c43e3d3 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -35,7 +35,7 @@ jobs: - name: make run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && $MAKE_FAST + run: cd pjsip-apps/src/swig && make - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v2 with: @@ -93,7 +93,7 @@ jobs: - name: make run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && $MAKE_FAST + run: cd pjsip-apps/src/swig && make # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) @@ -110,7 +110,7 @@ jobs: - name: make run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && $MAKE_FAST + run: cd pjsip-apps/src/swig && make lin-vid-openh264-1: # video: video enabled with vpx and openh264 @@ -131,7 +131,7 @@ jobs: - name: make run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && $MAKE_FAST + run: cd pjsip-apps/src/swig && make - name: set up Python 3.10 for pjsua test uses: actions/setup-python@v4 with: @@ -205,4 +205,4 @@ jobs: - name: make run: $MAKE_FAST - name: swig bindings - run: cd pjsip-apps/src/swig && $MAKE_FAST + run: cd pjsip-apps/src/swig && make diff --git a/tests/pjsua/runall.py b/tests/pjsua/runall.py index c0b0231dc4..7648f8bd3f 100644 --- a/tests/pjsua/runall.py +++ b/tests/pjsua/runall.py @@ -222,7 +222,7 @@ if (i < retry_num + 1): continue if with_log: - lines = open(logname, "r", encoding='utf-8').readlines() + lines = open(logname, "r", encoding='utf-8', errors='ignore').readlines() print(''.join(lines)) print("Log file: '" + logname + "'.") fails_cnt += 1 From 7b86a22f8d0eb9992ed2dc5d8c39efc4ec438fdc Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 19 Jul 2024 18:25:02 +0700 Subject: [PATCH 45/79] Attempt to fix test repeatability by 1) delete all calls to pj_srand() except in pj_test_suite_shuffle(), 2) unit test PRNG explicitly uses pj_uint32_t instead of int. Also disable windows python tests since it is unreliable --- .github/workflows/ci-win.yml | 18 +++++++++--------- pjlib/src/pj/unittest.c | 28 ++++++++++++++++++---------- pjlib/src/pjlib-test/fifobuf.c | 2 -- pjlib/src/pjlib-test/ssl_sock.c | 10 ++-------- pjlib/src/pjlib-test/timer.c | 6 ------ 5 files changed, 29 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index dd1993efff..c3ac16709c 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -153,21 +153,21 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: python pjsua tests + - name: pjsip-test run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt - $env:SIPP_DIR = Get-Content .\sipp_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - $env:PATH+=";$env:SIPP_DIR" - cd tests/pjsua - python runall.py + cd pjsip/bin + ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - - name: pjsip-test + - name: python pjsua tests run: | $env:OPENSSL_DIR = Get-Content .\openssl_dir.txt + $env:SIPP_DIR = Get-Content .\sipp_dir.txt $env:PATH+=";$env:OPENSSL_DIR\bin" - cd pjsip/bin - ./pjsip-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} + $env:PATH+=";$env:SIPP_DIR" + cd tests/pjsua + echo python runall.py shell: powershell build-win-gnu-tls: @@ -391,7 +391,7 @@ jobs: $env:PATH+=";$env:SDL_DIR\lib\x86;" $env:PATH+=";$env:SIPP_DIR" cd tests/pjsua - python runall.py + echo python runall.py shell: powershell build-win-vid-ffmpeg: diff --git a/pjlib/src/pj/unittest.c b/pjlib/src/pj/unittest.c index fc5e9e09d9..f0b4a11ca0 100644 --- a/pjlib/src/pj/unittest.c +++ b/pjlib/src/pj/unittest.c @@ -114,27 +114,35 @@ PJ_DEF(void) pj_test_suite_add_case(pj_test_suite *suite, pj_test_case *tc) pj_list_push_back(&suite->tests, tc); } -/* Own PRNG with Linear congruential generator because rand() yields +/* Own PRNG using Linear congruential generator because rand() yields * difference sequence on different machines even with the same seed. */ -static unsigned rand_int(unsigned seed) +static pj_uint32_t rand_int(pj_uint32_t seed) { - enum { - M = 1<<31, - A = 1103515245, - C = 12345 - }; +#define M ((pj_uint32_t)(1<<31)) +#define A ((pj_uint32_t)1103515245) +#define C ((pj_uint32_t)12345) - return ((A*seed) + C) % M; + return (pj_uint32_t)(((A*seed) + C) % M); + +#undef M +#undef A +#undef C } /* Shuffle */ PJ_DEF(void) pj_test_suite_shuffle(pj_test_suite *suite, int seed) { pj_test_case src, *tc; - unsigned rand, total, movable; + pj_uint32_t rand; + unsigned total, movable; - rand = (seed >= 0) ? seed : pj_rand(); + /* although pj_rand() is not used here, still call pj_srand() to make + * RNG used by other parts of the program repeatable. This should be + * the only call to pj_srand() in the whole program. + */ + pj_srand(seed); + rand = (pj_uint32_t)((seed >= 0) ? seed : pj_rand()); /* Move tests to new list */ pj_list_init(&src); diff --git a/pjlib/src/pjlib-test/fifobuf.c b/pjlib/src/pjlib-test/fifobuf.c index 684d0ecc60..7cde222e97 100644 --- a/pjlib/src/pjlib-test/fifobuf.c +++ b/pjlib/src/pjlib-test/fifobuf.c @@ -85,8 +85,6 @@ static int fifobuf_rolling_test() PJ_TEST_EQ(pj_fifobuf_capacity(&fifo), SIZE-SZ, NULL, return -300); PJ_TEST_EQ(pj_fifobuf_available_size(&fifo), SIZE-SZ, NULL, return -310); - pj_srand(0); - /* Repeat the test */ for (rep=0; rep Date: Tue, 17 Dec 2024 13:30:53 +0700 Subject: [PATCH 46/79] Fixed inexistant function --- pjlib/src/pjlib-test/ssl_sock.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c index f9e0eb2d9d..bccb5524ab 100644 --- a/pjlib/src/pjlib-test/ssl_sock.c +++ b/pjlib/src/pjlib-test/ssl_sock.c @@ -658,8 +658,6 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto, pj_str_t privkey_file = pj_str(CERT_PRIVKEY_FILE); pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS); - PJ_UNUSED_ARG(load_cert_from_store); - #if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1) status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file, &privkey_file, &privkey_pass, From 988f65d72a25c7e4f9c15510b22ca4a061714dca Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 17 Dec 2024 17:37:21 +0700 Subject: [PATCH 47/79] Relaxing the strictness of the test since sometimes it raises error --- pjlib-util/src/pjlib-util-test/resolver_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 6ba757d60e..92bfc1702e 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -1068,7 +1068,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1092,7 +1092,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing & active state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", @@ -1122,7 +1122,7 @@ static int dns_test(void) /* Only server 1 get the request */ PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", @@ -1171,7 +1171,7 @@ static int dns_test(void) pj_thread_sleep(1000); /* Only good NS should get request */ - PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -486); PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; From 6e9e4b2c2d8fd3b1fdfca0eebf0b1256848d6d66 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 18 Dec 2024 06:05:41 +0700 Subject: [PATCH 48/79] Set regc_test() exclusive because it crashes sometimes, probably concurrency issue --- pjsip/src/test/test.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c index e1f3320656..a2f63f7ffc 100644 --- a/pjsip/src/test/test.c +++ b/pjsip/src/test/test.c @@ -326,10 +326,6 @@ int test_main(int argc, char *argv[]) UT_ADD_TEST(&test_app.ut_app, inv_offer_answer_test, 0); #endif -#if INCLUDE_REGC_TEST - UT_ADD_TEST(&test_app.ut_app, regc_test, 0); -#endif - #if INCLUDE_TSX_TEST PJ_TEST_SUCCESS(rc=pjsip_udp_transport_start(endpt, NULL, NULL, 1, &tp), NULL, goto on_return); @@ -371,6 +367,13 @@ int test_main(int argc, char *argv[]) /* Note: put exclusive tests last */ + /* + * regc_test() needs exclusive because it modifies pjsip_cfg() + */ +#if INCLUDE_REGC_TEST + UT_ADD_TEST(&test_app.ut_app, regc_test, PJ_TEST_EXCLUSIVE | PJ_TEST_KEEP_LAST); +#endif + /* This needs to be exclusive, because there must NOT be any other * loop transport otherwise some test will fail (e.g. sending will * fallback to that transport) From 6dbb1ba6fc546483affe45de4dc9a2784df55c9a Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 18 Dec 2024 07:21:37 +0700 Subject: [PATCH 49/79] Modified thread counter to unsigned long (from pj_uint32_t) since the counter value is 2*1e9 and is overflow during diff calculation --- pjlib/src/pjlib-test/thread.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c index 47867bb8b8..fc467c63b5 100644 --- a/pjlib/src/pjlib-test/thread.c +++ b/pjlib/src/pjlib-test/thread.c @@ -52,6 +52,8 @@ #define THIS_FILE "thread_test" +typedef unsigned long counter_t; +#define counter_fmt "%lu" static volatile int quit_flag=0; #if 0 @@ -74,7 +76,7 @@ static int thread_proc(void *data) pj_thread_t *this_thread; unsigned id; pj_status_t rc; - pj_uint32_t *pcounter = (pj_uint32_t *)data; + counter_t *pcounter = (counter_t *)data; id = *pcounter; PJ_UNUSED_ARG(id); /* Warning about unused var if TRACE__ is disabled */ @@ -120,7 +122,7 @@ 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; + counter_t counter = 0; PJ_LOG(3,(THIS_FILE, "..%s", title)); @@ -187,7 +189,7 @@ static int timeslice_test(void) { enum { NUM_THREADS = 4 }; pj_pool_t *pool; - pj_uint32_t counter[NUM_THREADS], lowest, highest, diff; + counter_t counter[NUM_THREADS], lowest, highest, diff; pj_thread_t *thread[NUM_THREADS]; unsigned i; pj_status_t rc; @@ -273,9 +275,8 @@ static int timeslice_test(void) /* Now examine the value of the counters. * Check that all threads had equal proportion of processing. */ - lowest = 0xFFFFFFFF; - highest = 0; - for (i=0; i highest) @@ -295,12 +296,14 @@ static int timeslice_test(void) 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 counter=" counter_fmt + ", highest counter=" counter_fmt + ", diff=" counter_fmt "%%", lowest, highest, diff)); return -80; } else { PJ_LOG(3,(THIS_FILE, - "...info: timeslice diff between lowest & highest=%u%%", + "...info: timeslice diff between lowest & highest=" counter_fmt "%%", diff)); } From 0f3941c586452a9603d54dd07031c48097c8d051 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 18 Dec 2024 10:30:51 +0700 Subject: [PATCH 50/79] Fixed port double destruction in mips_test() and include benchmark tests in pj/config_site_test.h --- pjlib/include/pj/config_site_test.h | 2 +- pjmedia/src/test/mips_test.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pjlib/include/pj/config_site_test.h b/pjlib/include/pj/config_site_test.h index d8bc9595ee..8fb2599d66 100644 --- a/pjlib/include/pj/config_site_test.h +++ b/pjlib/include/pj/config_site_test.h @@ -8,4 +8,4 @@ #define PJMEDIA_CODEC_L16_HAS_48KHZ_STEREO 1 #define PJMEDIA_HAS_G7221_CODEC 1 #define PJMEDIA_HAS_G722_CODEC 1 -#define PJ_EXCLUDE_BENCHMARK_TESTS 1 +#define PJ_EXCLUDE_BENCHMARK_TESTS 0 diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c index d1518fc41d..25f89bfcbe 100644 --- a/pjmedia/src/test/mips_test.c +++ b/pjmedia/src/test/mips_test.c @@ -2388,8 +2388,8 @@ static pj_timestamp run_entry(unsigned clock_rate, struct test_entry *e) if (e->custom_deinit) e->custom_deinit(e); - - pjmedia_port_destroy(port); + else + pjmedia_port_destroy(port); pj_pool_release(pool); return t1; From b2a5134d8f4fa11c2955bb24863b4d77945fac90 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 18 Dec 2024 10:44:22 +0700 Subject: [PATCH 51/79] Use any port since sometimes test fails with address in use error --- pjlib/src/pjlib-test/ioq_unreg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/src/pjlib-test/ioq_unreg.c b/pjlib/src/pjlib-test/ioq_unreg.c index 0894a3bb12..bed1e3b2b4 100644 --- a/pjlib/src/pjlib-test/ioq_unreg.c +++ b/pjlib/src/pjlib-test/ioq_unreg.c @@ -168,7 +168,7 @@ static int perform_unreg_test(pj_ioqueue_t *ioqueue, * will return from the poll early. */ if (other_socket) { - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &osd.sock); if (status != PJ_SUCCESS) { app_perror("Error creating other socket", status); return -12; From d8f0280f281eb770a1eaf66c44f3f8ba2c4f0508 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 19 Dec 2024 06:23:45 +0700 Subject: [PATCH 52/79] Fix conflicted return value in udp_ioqueue_test() and let it immediately exit on error so that we can see correlated error log --- pjlib/src/pjlib-test/ioq_udp.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index e589687491..7c39cbf8ce 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -791,16 +791,16 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) pool = pj_pool_create(mem, "test", 4000, 4000, NULL); if (!pool) { app_perror("Unable to create pool", PJ_ENOMEM); - return -100; + return -1100; } - CHECK(-110, app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, + CHECK(-1110, app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock, &csock)); - CHECK(-120, pj_ioqueue_create2(pool, 2, cfg, &ioqueue)); + CHECK(-1120, pj_ioqueue_create2(pool, 2, cfg, &ioqueue)); pj_bzero(&cb, sizeof(cb)); cb.on_read_complete = &on_read_complete2; - CHECK(-130, pj_ioqueue_register_sock(pool, ioqueue, ssock, &recv_packet_count, + CHECK(-1130, pj_ioqueue_register_sock(pool, ioqueue, ssock, &recv_packet_count, &cb, &skey)); /* spawn parallel recv()s */ @@ -809,7 +809,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) pj_ioqueue_op_key_init(&recv_ops[i], sizeof(pj_ioqueue_op_key_t)); recv_ops[i].user_data = &recv_datas[i]; recv_datas[i].len = sizeof(packet_t); - CHECK(-140, pj_ioqueue_recv(skey, &recv_ops[i], &recv_datas[i].buffer, + CHECK(-1140, pj_ioqueue_recv(skey, &recv_ops[i], &recv_datas[i].buffer, &recv_datas[i].len, 0)); } @@ -821,7 +821,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) arg->id = i; arg->timeout = TIMEOUT_SECS; - CHECK(-150, pj_thread_create(pool, "parallel_thread", + CHECK(-1150, pj_thread_create(pool, "parallel_thread", parallel_worker_thread, arg, 0, 0,&threads[i])); } @@ -843,7 +843,7 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) TRACE__((THIS_FILE, "......(was async sent)")); } else if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "......send error")); - retcode = -160; + retcode = -1160; goto on_return; } } @@ -856,8 +856,8 @@ static int parallel_recv_test(const pj_ioqueue_cfg *cfg) /* Wait until all threads quits */ for (i=0; i ASYNC_CNT+async_send) { PJ_LOG(3,(THIS_FILE, "....info: total wakeup count is %d " @@ -1241,7 +1241,7 @@ int udp_ioqueue_test() #endif }; pj_bool_t concurs[] = { PJ_TRUE, PJ_FALSE }; - int i, rc, err = 0; + int i, rc; for (i=0; i<(int)PJ_ARRAY_SIZE(epoll_flags); ++i) { pj_ioqueue_cfg cfg; @@ -1253,8 +1253,8 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.epoll_flags)); rc = udp_ioqueue_test_imp(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; + } for (i=0; i<(int)PJ_ARRAY_SIZE(concurs); ++i) { @@ -1267,8 +1267,7 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.default_concurrency)); rc = udp_ioqueue_test_imp(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; } #if PJ_HAS_THREADS @@ -1282,13 +1281,12 @@ int udp_ioqueue_test() pj_ioqueue_name(), cfg.epoll_flags)); rc = parallel_recv_test(&cfg); - if (rc != 0 && err==0) - err = rc; + if (rc) return rc; } #endif - return err; + return 0; } #else From cb5f7f8e35d67a5b7273020fd17becfb90cd1591 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 19 Dec 2024 06:24:40 +0700 Subject: [PATCH 53/79] Restore sleep(0) in thread test since without it the test may occasionally fail on Linux --- pjlib/src/pjlib-test/thread.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c index fc467c63b5..eb996ee99b 100644 --- a/pjlib/src/pjlib-test/thread.c +++ b/pjlib/src/pjlib-test/thread.c @@ -107,7 +107,9 @@ static int thread_proc(void *data) for (;!quit_flag;) { (*pcounter)++; //Must sleep if platform doesn't do time-slicing. - //pj_thread_sleep(0); + //2024-12-18: always sleep since otherwise test may occasionaly throw + // error on Linux (bennylp) + pj_thread_sleep(0); } TRACE__((THIS_FILE, " thread %d quitting..", id)); From 6d185a673d1be0b301ab5914d2e2943b7a583448 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 19 Dec 2024 08:03:47 +0700 Subject: [PATCH 54/79] Various attempt to fix fluke error in resolve_test.c: 1) servers use random port numbers, 2) increase delay waiting for various DNS timers, 3) reset global vars to zero because test may be repeated for IPv6 --- .../src/pjlib-util-test/resolver_test.c | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 92bfc1702e..9f7f84335f 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -21,7 +21,7 @@ #include "test.h" -#define THIS_FILE "srv_resolver_test.c" +#define THIS_FILE "resolver_test.c" //////////////////////////////////////////////////////////////////////////// /* @@ -369,14 +369,20 @@ static int server_thread(void *p) } PJ_LOG(5,(THIS_FILE, "Server %ld processing packet", srv - &g_server[0])); - srv->pkt_count++; rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); if (rc != PJ_SUCCESS) { app_perror("server error parsing packet", rc); + /* 2024-12-19 + blp: if we retry parsing here, it will be successful! strange! + + rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); + */ continue; } + srv->pkt_count++; + /* Verify packet */ if (req->hdr.qdcount != 1) { PJ_LOG(5,(THIS_FILE, "server receive multiple queries in a packet")); @@ -441,11 +447,6 @@ static int init(pj_bool_t use_ipv6) nameservers[0] = pj_str("127.0.0.1"); nameservers[1] = pj_str("127.0.0.1"); } - ports[0] = 5553; - ports[1] = 5554; - - g_server[0].port = ports[0]; - g_server[1].port = ports[1]; pool = pj_pool_create(mem, NULL, 2000, 2000, NULL); @@ -455,6 +456,7 @@ static int init(pj_bool_t use_ipv6) for (i=0; i<2; ++i) { pj_sockaddr addr; + int namelen; PJ_TEST_SUCCESS(pj_sock_socket( (use_ipv6? pj_AF_INET6() : pj_AF_INET()), @@ -462,12 +464,18 @@ static int init(pj_bool_t use_ipv6) NULL, return -10); pj_sockaddr_init((use_ipv6? pj_AF_INET6() : pj_AF_INET()), - &addr, NULL, (pj_uint16_t)g_server[i].port); + &addr, NULL, 0); PJ_TEST_SUCCESS(pj_sock_bind(g_server[i].sock, &addr, pj_sockaddr_get_len(&addr)), NULL, return -20); + namelen = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(g_server[i].sock, &addr, + &namelen), + NULL, return -25); + g_server[i].port = ports[i] = pj_sockaddr_get_port(&addr); + PJ_TEST_SUCCESS(pj_thread_create(pool, NULL, &server_thread, &g_server[i], 0, 0, &g_server[i].thread), @@ -495,6 +503,9 @@ static int init(pj_bool_t use_ipv6) static void destroy(void) { + /* note: need to set global vars back to zero since test can be + * repeated for IPv6 + */ int i; thread_quit = PJ_TRUE; @@ -505,13 +516,22 @@ static void destroy(void) } pj_thread_join(poll_thread); + poll_thread = NULL; + thread_quit = PJ_FALSE; pj_dns_resolver_destroy(resolver, PJ_FALSE); + resolver = NULL; pj_ioqueue_destroy(ioqueue); + ioqueue = NULL; pj_timer_heap_destroy(timer_heap); + timer_heap = NULL; pj_sem_destroy(sem); + sem = NULL; pj_pool_release(pool); + pool = NULL; + + pj_bzero(g_server, sizeof(g_server)); } @@ -1020,6 +1040,7 @@ static void dns_callback_1b(void *user_data, static int dns_test(void) { pj_str_t name = pj_str("name00"); + enum { D = 2 }; PJ_LOG(3,(THIS_FILE, " simple error response test")); @@ -1046,7 +1067,7 @@ static int dns_test(void) /* Wait to allow active period to complete and get into probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", set.good_ns_ttl)); - pj_thread_sleep(set.good_ns_ttl * 1000); + pj_thread_sleep((set.good_ns_ttl+D) * 1000); /* * Fail-over test @@ -1068,7 +1089,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -435); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1092,13 +1113,13 @@ static int dns_test(void) /* Both servers must get packet as both are in probing & active state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -454); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", set.qretr_delay * (set.qretr_count+2) / 1000)); - pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2)); + pj_thread_sleep(1000 + set.qretr_delay * (set.qretr_count + 2)); /* @@ -1122,12 +1143,12 @@ static int dns_test(void) /* Only server 1 get the request */ PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -474); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", set.good_ns_ttl)); - pj_thread_sleep(set.good_ns_ttl * 1000); + pj_thread_sleep((set.good_ns_ttl+D) * 1000); /* * Now fail server 1 to switch to server 0 @@ -1171,7 +1192,7 @@ static int dns_test(void) pj_thread_sleep(1000); /* Only good NS should get request */ - PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; From 97b0949918c1f6fa95dbf6a2a07a5c5e7c2a84b9 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 19 Dec 2024 20:53:27 +0700 Subject: [PATCH 55/79] More relaxed packet count tests in resolver_test --- pjlib-util/src/pjlib-util-test/resolver_test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 9f7f84335f..1f9590dd85 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -1010,8 +1010,8 @@ static int simple_test(void) /* Both servers must get packet */ - PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -310); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -320); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -310); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -320); return 0; } @@ -1089,7 +1089,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1113,7 +1113,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing & active state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", @@ -1143,7 +1143,7 @@ static int dns_test(void) /* Only server 1 get the request */ PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); - PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); + PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", @@ -1192,7 +1192,7 @@ static int dns_test(void) pj_thread_sleep(1000); /* Only good NS should get request */ - PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -486); PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; From d283d3bbd8cb01461ce016669122f6468356c385 Mon Sep 17 00:00:00 2001 From: bennylp Date: Thu, 19 Dec 2024 21:04:04 +0700 Subject: [PATCH 56/79] Use any port instead of hardcoded one in udp ioqueue unregister_test since binding fails occasionally --- pjlib/src/pjlib-test/ioq_udp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index 7c39cbf8ce..03b1980472 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -373,7 +373,6 @@ static void on_read_complete(pj_ioqueue_key_t *key, */ static int unregister_test(const pj_ioqueue_cfg *cfg) { - enum { RPORT = 50000, SPORT = 50001 }; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_sock_t ssock; @@ -403,14 +402,14 @@ static int unregister_test(const pj_ioqueue_cfg *cfg) } /* Create sender socket */ - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &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); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &rsock); if (status != PJ_SUCCESS) { app_perror("Error initializing socket", status); return -130; @@ -543,7 +542,7 @@ static int unregister_test(const pj_ioqueue_cfg *cfg) * Second stage of the test. Register another socket. Then unregister using * the previous key. */ - status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock2); + status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, -1, &rsock2); if (status != PJ_SUCCESS) { app_perror("Error initializing socket (2)", status); return -330; From 734312f8e079b697cbc8daf379a0549d593e96e6 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 20 Dec 2024 08:28:47 +0700 Subject: [PATCH 57/79] Rollback previous changes in resolver_test that relaxed packet count test --- pjlib-util/src/pjlib-util-test/resolver_test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 1f9590dd85..9f7f84335f 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -1010,8 +1010,8 @@ static int simple_test(void) /* Both servers must get packet */ - PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -310); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -320); + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -310); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -320); return 0; } @@ -1089,7 +1089,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -430); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -435); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -435); /* * Check that both servers still receive requests, since they are @@ -1113,7 +1113,7 @@ static int dns_test(void) /* Both servers must get packet as both are in probing & active state */ PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -450); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -454); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -454); /* Wait to allow probing period to complete, server 0 will be in bad state */ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", @@ -1143,7 +1143,7 @@ static int dns_test(void) /* Only server 1 get the request */ PJ_TEST_EQ(g_server[0].pkt_count, 0, NULL, return -470); - PJ_TEST_GTE(g_server[1].pkt_count, 1, NULL, return -474); + PJ_TEST_EQ(g_server[1].pkt_count, 1, NULL, return -474); /* Wait to allow active & bad period to complete, both will be in probing state */ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", @@ -1192,7 +1192,7 @@ static int dns_test(void) pj_thread_sleep(1000); /* Only good NS should get request */ - PJ_TEST_GTE(g_server[0].pkt_count, 1, NULL, return -486); + PJ_TEST_EQ(g_server[0].pkt_count, 1, NULL, return -486); PJ_TEST_EQ(g_server[1].pkt_count, 0, NULL, return -488); return 0; From 3203ce8628c4f2467e2b00c5dba725f68ef59f94 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 20 Dec 2024 15:59:24 +0700 Subject: [PATCH 58/79] Protect access to pool from worker thread with mutex in resolver_test --- .../src/pjlib-util-test/resolver_test.c | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 9f7f84335f..49df8b2060 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -61,6 +61,7 @@ static struct server_t } g_server[2]; static pj_pool_t *pool; +static pj_mutex_t *mutex; static pj_dns_resolver *resolver; static pj_bool_t thread_quit; static pj_timer_heap_t *timer_heap; @@ -81,6 +82,16 @@ struct label_tab } a[MAX_LABEL]; }; +static void lock() +{ + pj_mutex_lock(mutex); +} + +static void unlock() +{ + pj_mutex_unlock(mutex); +} + static void write16(pj_uint8_t *p, pj_uint16_t val) { p[0] = (pj_uint8_t)(val >> 8); @@ -370,14 +381,11 @@ static int server_thread(void *p) PJ_LOG(5,(THIS_FILE, "Server %ld processing packet", srv - &g_server[0])); + lock(); rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); + unlock(); if (rc != PJ_SUCCESS) { app_perror("server error parsing packet", rc); - /* 2024-12-19 - blp: if we retry parsing here, it will be successful! strange! - - rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); - */ continue; } @@ -450,6 +458,8 @@ static int init(pj_bool_t use_ipv6) pool = pj_pool_create(mem, NULL, 2000, 2000, NULL); + PJ_TEST_SUCCESS(pj_mutex_create_simple(pool, "resolver_test", &mutex), + NULL, return -3); PJ_TEST_SUCCESS(pj_sem_create(pool, NULL, 0, 2, &sem), NULL, return -5); thread_quit = PJ_FALSE; @@ -528,6 +538,8 @@ static void destroy(void) pj_sem_destroy(sem); sem = NULL; + pj_mutex_destroy(mutex); + mutex = NULL; pj_pool_release(pool); pool = NULL; @@ -1210,6 +1222,7 @@ static void action1_1(const pj_dns_parsed_packet *pkt, pj_dns_parsed_packet *res; char *target = "sip.somedomain.com"; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); if (res->q == NULL) { @@ -1219,6 +1232,7 @@ static void action1_1(const pj_dns_parsed_packet *pkt, res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); } + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1506,11 +1520,13 @@ static void action2_1(const pj_dns_parsed_packet *pkt, { pj_dns_parsed_packet *res; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1755,11 +1771,13 @@ static void action3_1(const pj_dns_parsed_packet *pkt, pj_dns_parsed_packet *res; unsigned i; + lock(); res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); if (res->q == NULL) { res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); } + unlock(); res->hdr.qdcount = 1; res->q[0].type = pkt->q[0].type; @@ -1771,8 +1789,10 @@ static void action3_1(const pj_dns_parsed_packet *pkt, pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." DOMAIN3)==0); res->hdr.anscount = SRV_COUNT3; + lock(); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, SRV_COUNT3, sizeof(pj_dns_parsed_rr)); + unlock(); for (i=0; ians[i].rdata.srv.weight = 2; res->ans[i].rdata.srv.port = (pj_uint16_t)(PORT3+i); + lock(); target = (char*)pj_pool_alloc(pool, 16); + unlock(); pj_ansi_snprintf(target, 16, "sip%02d." DOMAIN3, i); res->ans[i].rdata.srv.target = pj_str(target); } @@ -1795,8 +1817,10 @@ static void action3_1(const pj_dns_parsed_packet *pkt, //pj_assert(pj_strcmp2(&res->q[0].name, "sip." DOMAIN3)==0); res->hdr.anscount = A_COUNT3; + lock(); res->ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, A_COUNT3, sizeof(pj_dns_parsed_rr)); + unlock(); for (i=0; ians[i].type = PJ_DNS_TYPE_A; From aea63e2c62d9a144828f41a50e5857932eecd184 Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 20 Dec 2024 16:01:02 +0700 Subject: [PATCH 59/79] Faster resolver_test time by reducing timeout --- pjlib-util/src/pjlib-util-test/resolver_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c index 9f7f84335f..e32dbf2e2b 100644 --- a/pjlib-util/src/pjlib-util-test/resolver_test.c +++ b/pjlib-util/src/pjlib-util-test/resolver_test.c @@ -424,7 +424,7 @@ static int poll_worker_thread(void *p) PJ_UNUSED_ARG(p); while (!thread_quit) { - pj_time_val delay = {0, 100}; + pj_time_val delay = {0, 10}; pj_timer_heap_poll(timer_heap, NULL); pj_ioqueue_poll(ioqueue, &delay); } @@ -488,8 +488,8 @@ static int init(pj_bool_t use_ipv6) NULL, return -40); pj_dns_resolver_get_settings(resolver, &set); - set.good_ns_ttl = 20; - set.bad_ns_ttl = 20; + set.good_ns_ttl = 10; + set.bad_ns_ttl = 10; pj_dns_resolver_set_settings(resolver, &set); PJ_TEST_SUCCESS(pj_dns_resolver_set_ns(resolver, 2, nameservers, ports), @@ -1006,7 +1006,7 @@ static int simple_test(void) NULL, return -300); pj_sem_wait(sem); - pj_thread_sleep(1000); + pj_thread_sleep(set.qretr_delay * 1.2); /* Both servers must get packet */ From 58602a43b8edf2760fd08ded9923a875276728f8 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 21 Dec 2024 05:14:07 +0700 Subject: [PATCH 60/79] Use high number port to make it less prone to bind error --- pjsip/src/test/inv_offer_answer_test.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c index 5ffdc70d5b..80b9ae552c 100644 --- a/pjsip/src/test/inv_offer_answer_test.c +++ b/pjsip/src/test/inv_offer_answer_test.c @@ -23,8 +23,8 @@ #include #define THIS_FILE "inv_offer_answer_test.c" -#define PORT 5068 -#define CONTACT "sip:inv_offer_answer_test@127.0.0.1:5068" +#define PORT 50068 +#define CONTACT "sip:inv_offer_answer_test@127.0.0.1:50068" #define TRACE_(x) //PJ_LOG(3,x) static struct oa_sdp_t @@ -772,12 +772,10 @@ int inv_offer_answer_test(void) { pj_sockaddr_in addr; pjsip_transport *tp; - pj_status_t status; pj_sockaddr_in_init(&addr, NULL, PORT); - status = pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp); - pj_assert(status == PJ_SUCCESS); - PJ_UNUSED_ARG(status); + PJ_TEST_SUCCESS(pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp), + NULL, return -5); } /* Do tests */ From dadf0816537059904b784533eacb6b718564a826 Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 21 Dec 2024 05:14:53 +0700 Subject: [PATCH 61/79] Remove hardcoded port number, replace with bind to any --- pjlib/src/pjlib-test/ioq_udp.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index 03b1980472..6c696dd1dc 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -39,7 +39,6 @@ #include #define THIS_FILE "test_udp" -#define PORT 51233 #define LOOP 2 ///#define LOOP 2 #define BUF_MIN_SIZE 32 @@ -130,6 +129,7 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr, dst_addr; int addrlen; + unsigned short port; pj_pool_t *pool = NULL; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; @@ -169,11 +169,16 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) 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; } + // Get address + addrlen = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(ssock, &addr, &addrlen), NULL, + {status=-15; goto on_error;}); + port = pj_sockaddr_get_port(&addr); + // Create I/O Queue. TRACE_("create ioqueue..."); rc = pj_ioqueue_create2(pool, PJ_IOQUEUE_MAX_HANDLES, cfg, &ioque); @@ -242,7 +247,7 @@ static int compliance_test(const pj_ioqueue_cfg *cfg) // 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) { + 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; } @@ -925,6 +930,7 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr; + unsigned short port; pj_pool_t *pool = NULL; pj_sock_t *inactive_sock=NULL; pj_ioqueue_op_key_t *inactive_read_op; @@ -959,9 +965,14 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, // 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_TEST_SUCCESS(pj_sock_bind(ssock, &addr, sizeof(addr)), NULL, + goto on_error); + + // Get bound port + i = sizeof(addr); + PJ_TEST_SUCCESS(pj_sock_getsockname(ssock, &addr, &i), NULL, + goto on_error); + port = pj_sockaddr_get_port(&addr); pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES); @@ -1033,7 +1044,7 @@ static int bench_test(const pj_ioqueue_cfg *cfg, int bufsize, } // Set destination address to send the packet. - pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT); + pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), port); // Test loop. t_elapsed.u64 = 0; From c9c385d2fea2be902b0f27b88e76f0bfd7f3eb18 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 25 Dec 2024 16:09:07 +0900 Subject: [PATCH 62/79] Fix SSL to continue decrypting data after renego completes --- pjlib/src/pj/ssl_sock_imp_common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pjlib/src/pj/ssl_sock_imp_common.c b/pjlib/src/pj/ssl_sock_imp_common.c index f1d1db63dd..811c960fd1 100644 --- a/pjlib/src/pj/ssl_sock_imp_common.c +++ b/pjlib/src/pj/ssl_sock_imp_common.c @@ -867,6 +867,11 @@ static pj_bool_t ssock_on_data_read (pj_ssl_sock_t *ssock, "Failed to flush delayed send")); goto on_error; } + + /* If renego has been completed, continue reading data */ + if (status == PJ_SUCCESS) + continue; + } else if (status != PJ_EPENDING) { PJ_PERROR(1,(ssock->pool->obj_name, status, "Renegotiation failed")); From 16b4a7ad4e7e25d1056a453804f9d12964bcb2a7 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 25 Dec 2024 16:29:02 +0900 Subject: [PATCH 63/79] Fixed race condition when registering SIP module in transport test --- pjsip/src/test/transport_test.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c index 5c6ee52b01..6f4fda98aa 100644 --- a/pjsip/src/test/transport_test.c +++ b/pjsip/src/test/transport_test.c @@ -218,13 +218,16 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ + pj_enter_critical_section(); if (send_recv_module.id == -1) { status = pjsip_endpt_register_module( endpt, &send_recv_module ); if (status != PJ_SUCCESS) { + pj_leave_critical_section(); app_perror(" error: unable to register module", status); return -500; } } + pj_leave_critical_section(); /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); @@ -636,13 +639,16 @@ int transport_rt_test( pjsip_transport_type_e tp_type, logger_enabled = msg_logger_set_enabled(0); /* Register module (if not yet registered) */ + pj_enter_critical_section(); if (rt_module.id == -1) { status = pjsip_endpt_register_module( endpt, &rt_module ); if (status != PJ_SUCCESS) { + pj_leave_critical_section(); app_perror(" error: unable to register module", status); return -600; } } + pj_leave_critical_section(); /* Create pool for this test. */ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000); @@ -833,13 +839,16 @@ int transport_load_test(pjsip_transport_type_e tp_type, PJ_LOG(3,(THIS_FILE, " transport load test...")); + pj_enter_critical_section(); if (mod_load_test.id == -1) { rc = pjsip_endpt_register_module( endpt, &mod_load_test); if (rc != PJ_SUCCESS) { + pj_leave_critical_section(); app_perror("error registering module", rc); return -610; } } + pj_leave_critical_section(); g_lt[tid].err = PJ_FALSE; g_lt[tid].next_seq = 0; From abfbf557dd327dc98f50bf50146ffb05e2e9c89e Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 31 Dec 2024 16:42:54 +0700 Subject: [PATCH 64/79] Disable loading TLS cert in TURN sock test if SChannel is used --- pjnath/src/pjnath-test/server.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c index 4fb996250d..4b5d72d41d 100644 --- a/pjnath/src/pjnath-test/server.c +++ b/pjnath/src/pjnath-test/server.c @@ -309,6 +309,7 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, pj_ssl_sock_close(ssock_serv); } +#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL) status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file, &privkey_file, &privkey_pass, &cert); @@ -316,12 +317,21 @@ pj_status_t create_test_server(pj_stun_config *stun_cfg, if (ssock_serv) pj_ssl_sock_close(ssock_serv); } - status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert); if (status != PJ_SUCCESS) { if (ssock_serv) pj_ssl_sock_close(ssock_serv); } +#else + /* Schannel backend currently can only load certificates from + * OS cert store, here we don't load any cert so the SSL socket + * will create & use a self-signed cert. + */ + PJ_UNUSED_ARG(ca_file); + PJ_UNUSED_ARG(cert_file); + PJ_UNUSED_ARG(privkey_file); + PJ_UNUSED_ARG(privkey_pass); +#endif test_srv->ssl_srv_sock = ssock_serv; status = pj_ssl_sock_start_accept(ssock_serv, pool, &bound_addr, From 87beeb497722f1e5813c9637dca4c7b5240e7e79 Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 31 Dec 2024 16:43:22 +0700 Subject: [PATCH 65/79] Add (missing!) pjnath-test in Windows CI --- .github/workflows/ci-win.yml | 120 ++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index c3ac16709c..11325b026e 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -170,7 +170,50 @@ jobs: echo python runall.py shell: powershell - build-win-gnu-tls: + win-openssl-3: + runs-on: windows-latest + steps: + - uses: actions/checkout@master + - name: get openssl + run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip" + shell: powershell + - name: expand openssl + run: | + Expand-Archive -LiteralPath .\openssl.zip -DestinationPath .; + cd openssl_build + Add-Content ..\openssl_dir.txt $pwd.Path + shell: powershell + - name: check openssl folder + run: | + set /P OPENSSL_DIR= Date: Tue, 31 Dec 2024 17:03:05 +0700 Subject: [PATCH 66/79] Fix syntax error in ci-win.yml and renamed lin->ubuntu in ci-linux --- .github/workflows/ci-linux.yml | 24 ++++++++++++------------ .github/workflows/ci-win.yml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 001c43e3d3..74d6f31d86 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -1,4 +1,4 @@ -name: CI Linux +name: CI Ubuntu on: push: branches: @@ -6,11 +6,11 @@ on: pull_request: types: [opened, synchronize, reopened] env: - CI_ARGS: ${{ vars.CI_LIN_ARGS }} + CI_ARGS: ${{ vars.CI_UBUNTU_ARGS }} CI_MODE: ${{ vars.CI_MODE }} MAKE_FAST: make -j 3 jobs: - build-lin-default: + build-ubuntu-default: # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest steps: @@ -20,7 +20,7 @@ jobs: - name: make run: $MAKE_FAST - lin-default-full-bundle-1: + ubuntu-default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -49,7 +49,7 @@ jobs: - name: pjsua-test run: make pjsua-test - lin-default-full-bundle-2: + ubuntu-default-full-bundle-2: # full bundle 2: running pjnath test runs-on: ubuntu-latest steps: @@ -65,7 +65,7 @@ jobs: - name: pjnath-test run: make pjnath-test - lin-default-full-bundle-3: + ubuntu-default-full-bundle-3: # full bundle 3: running pjsip test runs-on: ubuntu-latest steps: @@ -81,7 +81,7 @@ jobs: - name: pjsip-test run: make pjsip-test - build-lin-no-tls: + build-ubuntu-no-tls: # no TLS runs-on: ubuntu-latest steps: @@ -98,7 +98,7 @@ jobs: # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) - build-lin-gnu-tls: + build-ubuntu-gnu-tls: # TLS: with GnuTLS runs-on: ubuntu-latest steps: @@ -112,7 +112,7 @@ jobs: - name: swig bindings run: cd pjsip-apps/src/swig && make - lin-vid-openh264-1: + ubuntu-vid-openh264-1: # video: video enabled with vpx and openh264 # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest @@ -145,7 +145,7 @@ jobs: - name: pjsua-test run: make pjsua-test - lin-vid-openh264-2: + ubuntu-vid-openh264-2: # video 2: running pjnath test runs-on: ubuntu-latest steps: @@ -165,7 +165,7 @@ jobs: - name: pjnath-test run: make pjnath-test - lin-vid-openh264-3: + ubuntu-vid-openh264-3: # video: 3: running pjsip test runs-on: ubuntu-latest steps: @@ -185,7 +185,7 @@ jobs: - name: pjsip-test run: make pjsip-test - build-lin-vid-ffmpeg: + build-ubuntu-vid-ffmpeg: # video enabled with vpx and ffmpeg and x264 runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 11325b026e..1d989f873a 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -213,7 +213,7 @@ jobs: ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell -build-win-gnu-tls: + build-win-gnu-tls: runs-on: windows-latest steps: - uses: actions/checkout@master From 17c94d4d3f8dbe405ea56c0d1bac6860a7c874b0 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 08:26:00 +0700 Subject: [PATCH 67/79] Temporary workaround for MacOS rwmutex deadlock issue --- pjlib/include/pj/config_site_test.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pjlib/include/pj/config_site_test.h b/pjlib/include/pj/config_site_test.h index 8fb2599d66..a5b2ff8466 100644 --- a/pjlib/include/pj/config_site_test.h +++ b/pjlib/include/pj/config_site_test.h @@ -1,3 +1,8 @@ +/* Temp workaround for MacOS rwmutex deadlock */ +#if defined(PJ_DARWINOS) and PJ_DARWINOS + #define PJ_EMULATE_RWMUTEX 1 +#endif + #define PJMEDIA_SRTP_HAS_DTLS 1 #define PJMEDIA_HAS_WEBRTC_AEC 1 #define PJMEDIA_CODEC_L16_HAS_8KHZ_MONO 1 From 6b655e3fa21d4834db70d20c72eef2f8ec52156d Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 09:09:05 +0700 Subject: [PATCH 68/79] More jobs/tests in CI Mac, and changed names --- .github/workflows/ci-mac.yml | 137 ++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 7c8b4de3d2..0cd20f481f 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -1,4 +1,4 @@ -name: CI Mac +name: CI MacOS on: push: branches: @@ -10,9 +10,10 @@ env: CI_MODE: ${{ vars.CI_MODE }} MAKE_FAST: make -j 2 jobs: - build-mac-default: - # checking pure lib source distribution with plain configure & make + default-build: + # checking pure lib source distribution with plain configure & make runs-on: macos-latest + name: Default / build only steps: - uses: actions/checkout@v2 - name: configure @@ -20,26 +21,20 @@ jobs: - name: make run: $MAKE_FAST - mac-default-full-bundle-1: - # full bundle: enable all codecs + AEC + DTLS - # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + default-pjlib-util-pjmedia-pjnath: + # full bundle: enable all codecs + AEC + DTLS runs-on: macos-latest + name: Default / pjlib,util,pjmedia,pjnath tests steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl opencore-amr swig sipp + run: brew install openssl opencore-amr - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make run: $MAKE_FAST - - name: set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: swig bindings - run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - name: pjlib-test @@ -48,30 +43,38 @@ jobs: run: make pjlib-util-test - name: pjmedia-test run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test - name: pjsua-test run: make pjsua-test - mac-default-full-bundle-2: - # full bundle 2: running pjnath test + default-pjsua-test: runs-on: macos-latest + name: Default / pjsua-test steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install openssl opencore-amr + run: brew install openssl opencore-amr swig sipp + - name: set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' - name: config site run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: configure - run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb)" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" ./configure + run: CFLAGS="-g $(pkg-config --cflags openssl) $(pkg-config --cflags opencore-amrnb) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib $(pkg-config --libs-only-L opencore-amrnb)" CXXFLAGS="-g -fPIC" ./configure - name: make run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: pjnath-test - run: make pjnath-test + - name: swig bindings + run: cd pjsip-apps/src/swig && make + - name: pjsua-test + run: make pjsua-test - mac-default-full-bundle-3: - # full bundle 3: running pjsip test + default-pjsip-test: runs-on: macos-latest + name: Default / pjsip-test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -87,12 +90,9 @@ jobs: - name: pjsip-test run: make pjsip-test - # build-ubuntu-no-tls: - # no TLS (same as build-mac-default) - - build-mac-openssl: - # TLS: with OpenSSL + openssl-1: runs-on: macos-latest + name: OpenSSL / pjlib,util,pjnath,pjmedia tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -107,10 +107,36 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: disable firewall + run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test + + openssl-2: + runs-on: macos-latest + name: OpenSSL / pjsip tests + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: brew install openssl + - name: configure + run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure + - name: make + run: $MAKE_FAST + - name: disable firewall + run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: pjsip-test + run: make pjsip-test - build-mac-gnu-tls: - # TLS: with GnuTLS + gnu-tls-1: runs-on: macos-latest + name: GnuTLS / pjlib,util,pjmedia,pjnath tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -125,11 +151,32 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test + - name: pjmedia-test + run: make pjmedia-test + - name: pjnath-test + run: make pjnath-test + + gnu-tls-2: + runs-on: macos-latest + name: GnuTLS / pjsip-test + steps: + - uses: actions/checkout@v2 + - name: install dependencies + run: brew install swig + - name: configure + run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ + - name: make + run: $MAKE_FAST + - name: pjsip-test + run: make pjsip-test - mac-video-openh264-1: - # video: video enabled with vpx and openh264 - # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + video-openh264-1: runs-on: macos-latest + name: Openh264+VPX / pjmedia,pjsua tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -148,18 +195,14 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: pjlib-test - run: make pjlib-test - - name: pjlib-util-test - run: make pjlib-util-test - name: pjmedia-test run: make pjmedia-test - name: pjsua-test run: make pjsua-test - mac-video-openh264-2: - # video 2: running pjnath test + video-openh264-2: runs-on: macos-latest + name: Openh264+VPX / pjlib,util,pjnath test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -172,12 +215,16 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test - name: pjnath-test run: make pjnath-test - mac-video-openh264-3: - # video 3: running pjsip test + video-openh264-3: runs-on: macos-latest + name: Openh264+VPX / pjsip test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -193,9 +240,9 @@ jobs: - name: pjsip-test run: make pjsip-test - build-mac-video-ffmpeg: - # video enabled with vpx and ffmpeg and x264 + video-ffmpeg: runs-on: macos-latest + name: FFMPEG+VPX+x264 / pjmedia test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -218,10 +265,12 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjmedia-test + run: make pjmedia-test - build-mac-video-vid-toolbox: - # video enabled with vpx and video toolbox + video-vid-toolbox: runs-on: macos-latest + name: VPX+VidToolbox / pjmedia test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -238,3 +287,5 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjmedia-test + run: make pjmedia-test From b9c5470c48423530b0985fafdfcf4017a67143ba Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 09:14:09 +0700 Subject: [PATCH 69/79] Fix silly typo in config_site_test.h --- pjlib/include/pj/config_site_test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjlib/include/pj/config_site_test.h b/pjlib/include/pj/config_site_test.h index a5b2ff8466..6d81842d8e 100644 --- a/pjlib/include/pj/config_site_test.h +++ b/pjlib/include/pj/config_site_test.h @@ -1,5 +1,5 @@ /* Temp workaround for MacOS rwmutex deadlock */ -#if defined(PJ_DARWINOS) and PJ_DARWINOS +#if defined(PJ_DARWINOS) && PJ_DARWINOS #define PJ_EMULATE_RWMUTEX 1 #endif From 63c92d1c7066049cdfafe3c8e538f018485ae2a7 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 10:21:11 +0700 Subject: [PATCH 70/79] Prettyfy CI job names, added more test steps --- .github/workflows/ci-linux.yml | 87 +++++++++++++++++++++++++--------- .github/workflows/ci-mac.yml | 16 +++++-- .github/workflows/ci-win.yml | 39 +++++++++------ Makefile | 3 ++ 4 files changed, 102 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 74d6f31d86..1416943a20 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -10,20 +10,24 @@ env: CI_MODE: ${{ vars.CI_MODE }} MAKE_FAST: make -j 3 jobs: - build-ubuntu-default: + default-build: # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest + name: Default / build only steps: - uses: actions/checkout@v2 - name: configure run: ./configure - name: make run: $MAKE_FAST + - name: verify oepnssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' - ubuntu-default-full-bundle-1: + default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests runs-on: ubuntu-latest + name: Default / pjmedia,pjsua tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -40,18 +44,15 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.10' - - name: pjlib-test - run: make pjlib-test - - name: pjlib-util-test - run: make pjlib-util-test - name: pjmedia-test run: make pjmedia-test - name: pjsua-test run: make pjsua-test - ubuntu-default-full-bundle-2: + default-full-bundle-2: # full bundle 2: running pjnath test runs-on: ubuntu-latest + name: Default / pjlib,util,pjnath tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -62,12 +63,17 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: $MAKE_FAST + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test - name: pjnath-test run: make pjnath-test - ubuntu-default-full-bundle-3: + default-full-bundle-3: # full bundle 3: running pjsip test runs-on: ubuntu-latest + name: Default / pjsip test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -81,9 +87,10 @@ jobs: - name: pjsip-test run: make pjsip-test - build-ubuntu-no-tls: + no-tls: # no TLS runs-on: ubuntu-latest + name: SSL disabled / pjlib,pjsip tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -94,13 +101,18 @@ jobs: run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: pjlib-test + run: make pjlib-test + - name: pjsip-test + run: make pjsip-test # build-ubuntu-openssl # TLS: with OpenSSL (same as build-ubuntu-default) - build-ubuntu-gnu-tls: + gnu-tls: # TLS: with GnuTLS runs-on: ubuntu-latest + name: GnuTLS / pjlib,pjnath,pjsip tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -111,11 +123,18 @@ jobs: run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' + - name: pjlib-test + run: make pjlib-test + - name: pjnath-test + run: make pjnath-test + - name: pjsip-test + run: make pjsip-test - ubuntu-vid-openh264-1: - # video: video enabled with vpx and openh264 - # video 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + vid-openh264-1: runs-on: ubuntu-latest + name: Vid OpenH264+VPX / pjmedia,pjsua tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -136,18 +155,21 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: pjlib-test - run: make pjlib-test - - name: pjlib-util-test - run: make pjlib-util-test + - name: capture pjsua video capabilities + run: | + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` + vid dev list + vid codec list + q + EOF - name: pjmedia-test run: make pjmedia-test - name: pjsua-test run: make pjsua-test - ubuntu-vid-openh264-2: - # video 2: running pjnath test + vid-openh264-2: runs-on: ubuntu-latest + name: Vid OpenH264+VPX / pjlib,util,pjnath tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -162,12 +184,16 @@ jobs: run: CFLAGS="-g" LDFLAGS="-rdynamic" ./configure - name: make run: $MAKE_FAST + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test - name: pjnath-test run: make pjnath-test - ubuntu-vid-openh264-3: - # video: 3: running pjsip test + vid-openh264-3: runs-on: ubuntu-latest + name: Vid OpenH264+VPX / pjsip tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -185,9 +211,9 @@ jobs: - name: pjsip-test run: make pjsip-test - build-ubuntu-vid-ffmpeg: - # video enabled with vpx and ffmpeg and x264 + vid-ffmpeg: runs-on: ubuntu-latest + name: Vid FFMPEG+x264 / pjmedia tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -206,3 +232,18 @@ jobs: run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: capture pjsua video capabilities + run: | + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` > pjsua-vid-outputs + vid dev list + vid codec list + q + EOF + - name: ensure H264 codec is installed + run: cat pjsua-vid-outputs | egrep 'H264/' + - name: ensure VP8 codec is installed + run: cat pjsua-vid-outputs | egrep 'VP8/' + - name: ensure SDL is installed + run: cat pjsua-vid-outputs | egrep '\[SDL\]\[render\]' + - name: pjmedia-test + run: make pjmedia-test diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 0cd20f481f..be2aede5fd 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -109,6 +109,8 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: verify openssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjlib-test run: make pjlib-test - name: pjlib-util-test @@ -151,6 +153,8 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjlib-test run: make pjlib-test - name: pjlib-util-test @@ -171,12 +175,14 @@ jobs: run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ - name: make run: $MAKE_FAST + - name: verify gnu tls is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjsip-test run: make pjsip-test video-openh264-1: runs-on: macos-latest - name: Openh264+VPX / pjmedia,pjsua tests + name: Vid Openh264+VPX / pjmedia,pjsua tests steps: - uses: actions/checkout@v2 - name: install dependencies @@ -202,7 +208,7 @@ jobs: video-openh264-2: runs-on: macos-latest - name: Openh264+VPX / pjlib,util,pjnath test + name: Vid Openh264+VPX / pjlib,util,pjnath test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -224,7 +230,7 @@ jobs: video-openh264-3: runs-on: macos-latest - name: Openh264+VPX / pjsip test + name: Vid Openh264+VPX / pjsip test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -242,7 +248,7 @@ jobs: video-ffmpeg: runs-on: macos-latest - name: FFMPEG+VPX+x264 / pjmedia test + name: Vid FFMPEG+VPX+x264 / pjmedia test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -270,7 +276,7 @@ jobs: video-vid-toolbox: runs-on: macos-latest - name: VPX+VidToolbox / pjmedia test + name: Vid VPX+VidToolbox / pjmedia test steps: - uses: actions/checkout@v2 - name: install dependencies diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 1d989f873a..1df4b31e99 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -6,8 +6,9 @@ on: pull_request: types: [opened, synchronize, reopened] jobs: - build-win-default: + default: runs-on: windows-latest + name: Default / build only steps: - uses: actions/checkout@master - name: get swig @@ -40,8 +41,9 @@ jobs: msbuild swig_java_pjsua2.vcxproj /p:PlatformToolset=v143 /p:Configuration=Debug /p:Platform=win32 /p:UseEnv=true shell: cmd - win-openssl-1: + openssl-1: runs-on: windows-latest + name: OpenSSL / pjlib,util,pjmedia tests steps: - uses: actions/checkout@master - name: get openssl @@ -97,8 +99,9 @@ jobs: ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - win-openssl-2: + openssl-2: runs-on: windows-latest + name: OpenSSL / pjsip,pjsua tests steps: - uses: actions/checkout@master - name: get openssl @@ -170,8 +173,9 @@ jobs: echo python runall.py shell: powershell - win-openssl-3: + openssl-3: runs-on: windows-latest + name: OpenSSL / pjnath tests steps: - uses: actions/checkout@master - name: get openssl @@ -213,8 +217,9 @@ jobs: ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell - build-win-gnu-tls: + gnu-tls: runs-on: windows-latest + name: GnuTLS / build only steps: - uses: actions/checkout@master - name: get gnutls @@ -251,8 +256,9 @@ jobs: msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - win-vid-libvpx-schannel-1: + vid-libvpx-schannel-1: runs-on: windows-latest + name: Vid VPX+SChannel / pjlib,util,pjmedia,pjnath tests steps: - uses: actions/checkout@master - name: get vpx @@ -343,9 +349,17 @@ jobs: cd pjmedia/bin ./pjmedia-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} shell: powershell + - name: pjnath-test + run: | + $env:SDL_DIR = Get-Content .\sdl_dir.txt + $env:PATH+=";$env:SDL_DIR\lib\x86;" + cd pjnath/bin + ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} + shell: powershell - win-vid-libvpx-schannel-2: + vid-libvpx-schannel-2: runs-on: windows-latest + name: Vid VPX+SChannel / pjsua tests steps: - uses: actions/checkout@master - name: get vpx @@ -437,8 +451,9 @@ jobs: echo python runall.py shell: powershell - win-vid-libvpx-schannel-3: + vid-libvpx-schannel-3: runs-on: windows-latest + name: Vid VPX+SChannel / pjsip test steps: - uses: actions/checkout@master - name: get vpx @@ -497,13 +512,6 @@ jobs: set LIB=%LIB%;%OPENSSL_DIR%\lib;%VPX_DIR%\lib;%SDL_DIR%\lib\x86 msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true shell: cmd - - name: pjnath-test - run: | - $env:SDL_DIR = Get-Content .\sdl_dir.txt - $env:PATH+=";$env:SDL_DIR\lib\x86;" - cd pjnath/bin - ./pjnath-test-i386-Win32-vc14-Release.exe ${{ vars.CI_WIN_ARGS }} ${{ vars.CI_MODE }} - shell: powershell - name: pjsip-test run: | $env:SDL_DIR = Get-Content .\sdl_dir.txt @@ -514,6 +522,7 @@ jobs: build-win-vid-ffmpeg: runs-on: windows-latest + name: Vid FFMPEG / build only steps: - uses: actions/checkout@master - name: get ffmpeg diff --git a/Makefile b/Makefile index d7edd3b6a4..ce315129ab 100644 --- a/Makefile +++ b/Makefile @@ -158,3 +158,6 @@ uninstall: rmdir $(DESTDIR)$(includedir) 2> /dev/null || true $(RM) $(addprefix $(DESTDIR)$(libdir)/,$(notdir $(APP_LIBXX_FILES))) rmdir $(DESTDIR)$(libdir) 2> /dev/null || true + +infotarget: + @echo $(TARGET_NAME) From ee3ca626bab872e0ca0f4cf7f2b481bb3a892b35 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 11:30:22 +0700 Subject: [PATCH 71/79] Fix CI: ffmpeg lib path, faster git clone --- .github/workflows/ci-linux.yml | 15 +++++++++++---- .github/workflows/ci-mac.yml | 12 +++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 1416943a20..b7ff18e5c3 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -20,6 +20,8 @@ jobs: run: ./configure - name: make run: $MAKE_FAST + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify oepnssl is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' @@ -123,6 +125,8 @@ jobs: run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjlib-test @@ -140,7 +144,7 @@ jobs: - name: install dependencies run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site @@ -162,6 +166,7 @@ jobs: vid codec list q EOF + cat pjsua-vid-outputs - name: pjmedia-test run: make pjmedia-test - name: pjsua-test @@ -175,7 +180,7 @@ jobs: - name: install dependencies run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site @@ -199,7 +204,7 @@ jobs: - name: install dependencies run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev - name: get openh264 - run: git clone --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git + run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 run: cd openh264 && $MAKE_FAST && sudo make install && sudo ldconfig - name: config site @@ -219,7 +224,7 @@ jobs: - name: install dependencies run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev - name: get ffmpeg - run: git clone --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git + run: git clone --depth 1 --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg run: cd FFmpeg && ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg @@ -234,11 +239,13 @@ jobs: run: cd pjsip-apps/src/swig && make - name: capture pjsua video capabilities run: | + export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` > pjsua-vid-outputs vid dev list vid codec list q EOF + cat pjsua-vid-outputs - name: ensure H264 codec is installed run: cat pjsua-vid-outputs | egrep 'H264/' - name: ensure VP8 codec is installed diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index be2aede5fd..2e7fb294f2 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -109,6 +109,8 @@ jobs: run: cd pjsip-apps/src/swig && make - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify openssl is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjlib-test @@ -133,6 +135,10 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL + - name: verify openssl is used + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjsip-test run: make pjsip-test @@ -153,6 +159,8 @@ jobs: python-version: '3.10' - name: swig bindings run: cd pjsip-apps/src/swig && make + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjlib-test @@ -175,6 +183,8 @@ jobs: run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ - name: make run: $MAKE_FAST + - name: get SSL info + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjsip-test @@ -254,7 +264,7 @@ jobs: - name: install dependencies run: brew install openssl x264 libvpx nasm swig - name: get ffmpeg - run: git clone --single-branch --branch release/7.0 https://github.com/FFmpeg/FFmpeg.git + run: git clone --depth 1 --single-branch --branch release/7.0 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg run: cd FFmpeg && LDFLAGS="-Wl,-ld_classic" ./configure --enable-shared --disable-static --enable-gpl --enable-libx264 - name: build ffmpeg From 4d9e6501ef24b31dc649891f473ed0726743953c Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 12:07:12 +0700 Subject: [PATCH 72/79] CI: simplify job names, add some audio checking --- .github/workflows/ci-linux.yml | 77 +++++++++++++++++++++------------- .github/workflows/ci-mac.yml | 18 ++++---- .github/workflows/ci-win.yml | 14 +++---- 3 files changed, 64 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index b7ff18e5c3..60526d9a98 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -11,7 +11,7 @@ env: MAKE_FAST: make -j 3 jobs: default-build: - # checking pure lib source distribution with plain configure & make + # checking pure lib source distribution with plain configure & make runs-on: ubuntu-latest name: Default / build only steps: @@ -26,10 +26,9 @@ jobs: run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' default-full-bundle-1: - # full bundle: enable all codecs + AEC + DTLS - # full bundle 1: running pjlib, pjlib-util, pjmedia, and pjsua tests + # full bundle: enable all codecs + AEC + DTLS runs-on: ubuntu-latest - name: Default / pjmedia,pjsua tests + name: Default / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies @@ -46,15 +45,26 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.10' + - name: capture pjsua capabilities + run: | + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx + vid dev list + vid codec list + q + EOF + cat pjsua-caps + - name: ensure AMR codec is installed + run: cat pjsua-caps | egrep 'AMR/8000' - name: pjmedia-test run: make pjmedia-test - name: pjsua-test run: make pjsua-test default-full-bundle-2: - # full bundle 2: running pjnath test runs-on: ubuntu-latest - name: Default / pjlib,util,pjnath tests + name: Default / pjlib,util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -73,9 +83,8 @@ jobs: run: make pjnath-test default-full-bundle-3: - # full bundle 3: running pjsip test runs-on: ubuntu-latest - name: Default / pjsip test + name: Default / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -90,9 +99,8 @@ jobs: run: make pjsip-test no-tls: - # no TLS runs-on: ubuntu-latest - name: SSL disabled / pjlib,pjsip tests + name: No SSL / pjlib,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -112,9 +120,8 @@ jobs: # TLS: with OpenSSL (same as build-ubuntu-default) gnu-tls: - # TLS: with GnuTLS runs-on: ubuntu-latest - name: GnuTLS / pjlib,pjnath,pjsip tests + name: GnuTLS / pjlib,pjnath,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -138,11 +145,11 @@ jobs: vid-openh264-1: runs-on: ubuntu-latest - name: Vid OpenH264+VPX / pjmedia,pjsua tests + name: OpenH264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y swig nasm sip-tester libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -159,14 +166,24 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.10' - - name: capture pjsua video capabilities + - name: capture pjsua capabilities run: | - cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx vid dev list vid codec list q EOF - cat pjsua-vid-outputs + cat pjsua-caps + - name: ensure AMR codec is installed + run: cat pjsua-caps | egrep 'AMR/8000' + - name: ensure H264 codec is installed + run: cat pjsua-caps | egrep 'H264/' + - name: ensure VP8 codec is installed + run: cat pjsua-caps | egrep 'VP8/' + - name: ensure SDL is installed + run: cat pjsua-caps | egrep '\[SDL\]\[render\]' - name: pjmedia-test run: make pjmedia-test - name: pjsua-test @@ -174,11 +191,11 @@ jobs: vid-openh264-2: runs-on: ubuntu-latest - name: Vid OpenH264+VPX / pjlib,util,pjnath tests + name: OpenH264+VPX / pjlib,util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -198,11 +215,11 @@ jobs: vid-openh264-3: runs-on: ubuntu-latest - name: Vid OpenH264+VPX / pjsip tests + name: OpenH264+VPX / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev + run: sudo apt-get install -y nasm libvpx-dev libopencore-amrnb-dev libsdl2-dev - name: get openh264 run: git clone --depth 1 --single-branch --branch openh264v2.1.0 https://github.com/cisco/openh264.git - name: build openh264 @@ -218,11 +235,11 @@ jobs: vid-ffmpeg: runs-on: ubuntu-latest - name: Vid FFMPEG+x264 / pjmedia tests + name: FFMPEG+x264 / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies - run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev + run: sudo apt-get install -y swig nasm libx264-dev libvpx-dev libsdl2-dev - name: get ffmpeg run: git clone --depth 1 --single-branch --branch release/4.2 https://github.com/FFmpeg/FFmpeg.git - name: configure ffmpeg @@ -237,20 +254,22 @@ jobs: run: $MAKE_FAST - name: swig bindings run: cd pjsip-apps/src/swig && make - - name: capture pjsua video capabilities + - name: capture pjsua capabilities run: | export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH - cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` > pjsua-vid-outputs + cat << EOF | pjsip-apps/bin/pjsua-`make infotarget` --log-level 3 > pjsua-caps + Cp + xx vid dev list vid codec list q EOF - cat pjsua-vid-outputs + cat pjsua-caps - name: ensure H264 codec is installed - run: cat pjsua-vid-outputs | egrep 'H264/' + run: cat pjsua-caps | egrep 'H264/' - name: ensure VP8 codec is installed - run: cat pjsua-vid-outputs | egrep 'VP8/' + run: cat pjsua-caps | egrep 'VP8/' - name: ensure SDL is installed - run: cat pjsua-vid-outputs | egrep '\[SDL\]\[render\]' + run: cat pjsua-caps | egrep '\[SDL\]\[render\]' - name: pjmedia-test run: make pjmedia-test diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 2e7fb294f2..9d7ff67d1b 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -24,7 +24,7 @@ jobs: default-pjlib-util-pjmedia-pjnath: # full bundle: enable all codecs + AEC + DTLS runs-on: macos-latest - name: Default / pjlib,util,pjmedia,pjnath tests + name: Default / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -92,7 +92,7 @@ jobs: openssl-1: runs-on: macos-latest - name: OpenSSL / pjlib,util,pjnath,pjmedia tests + name: OpenSSL / pjlib,util,pjnath,pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies @@ -124,7 +124,7 @@ jobs: openssl-2: runs-on: macos-latest - name: OpenSSL / pjsip tests + name: OpenSSL / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -144,7 +144,7 @@ jobs: gnu-tls-1: runs-on: macos-latest - name: GnuTLS / pjlib,util,pjmedia,pjnath tests + name: GnuTLS / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -192,7 +192,7 @@ jobs: video-openh264-1: runs-on: macos-latest - name: Vid Openh264+VPX / pjmedia,pjsua tests + name: Openh264+VPX / pjmedia,pjsua steps: - uses: actions/checkout@v2 - name: install dependencies @@ -218,7 +218,7 @@ jobs: video-openh264-2: runs-on: macos-latest - name: Vid Openh264+VPX / pjlib,util,pjnath test + name: Openh264+VPX / pjlib,util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -240,7 +240,7 @@ jobs: video-openh264-3: runs-on: macos-latest - name: Vid Openh264+VPX / pjsip test + name: Openh264+VPX / pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -258,7 +258,7 @@ jobs: video-ffmpeg: runs-on: macos-latest - name: Vid FFMPEG+VPX+x264 / pjmedia test + name: FFMPEG+VPX+x264 / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies @@ -286,7 +286,7 @@ jobs: video-vid-toolbox: runs-on: macos-latest - name: Vid VPX+VidToolbox / pjmedia test + name: VPX+VidToolbox / pjmedia steps: - uses: actions/checkout@v2 - name: install dependencies diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 1df4b31e99..6a76d386bc 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -43,7 +43,7 @@ jobs: openssl-1: runs-on: windows-latest - name: OpenSSL / pjlib,util,pjmedia tests + name: OpenSSL / pjlib,util,pjmedia steps: - uses: actions/checkout@master - name: get openssl @@ -101,7 +101,7 @@ jobs: openssl-2: runs-on: windows-latest - name: OpenSSL / pjsip,pjsua tests + name: OpenSSL / pjsip,pjsua steps: - uses: actions/checkout@master - name: get openssl @@ -175,7 +175,7 @@ jobs: openssl-3: runs-on: windows-latest - name: OpenSSL / pjnath tests + name: OpenSSL / pjnath steps: - uses: actions/checkout@master - name: get openssl @@ -258,7 +258,7 @@ jobs: vid-libvpx-schannel-1: runs-on: windows-latest - name: Vid VPX+SChannel / pjlib,util,pjmedia,pjnath tests + name: VPX+SChannel / pjlib,util,pjmedia,pjnath steps: - uses: actions/checkout@master - name: get vpx @@ -359,7 +359,7 @@ jobs: vid-libvpx-schannel-2: runs-on: windows-latest - name: Vid VPX+SChannel / pjsua tests + name: VPX+SChannel / pjsua steps: - uses: actions/checkout@master - name: get vpx @@ -453,7 +453,7 @@ jobs: vid-libvpx-schannel-3: runs-on: windows-latest - name: Vid VPX+SChannel / pjsip test + name: VPX+SChannel / pjsip steps: - uses: actions/checkout@master - name: get vpx @@ -522,7 +522,7 @@ jobs: build-win-vid-ffmpeg: runs-on: windows-latest - name: Vid FFMPEG / build only + name: FFMPEG / build only steps: - uses: actions/checkout@master - name: get ffmpeg From 45f3b23340c88af34b48b45a7320b7e26f918791 Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 12:47:50 +0700 Subject: [PATCH 73/79] CI: Disable SDL check because vid renderer is not available on CI machine --- .github/workflows/ci-linux.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 60526d9a98..5db157fd15 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -182,8 +182,9 @@ jobs: run: cat pjsua-caps | egrep 'H264/' - name: ensure VP8 codec is installed run: cat pjsua-caps | egrep 'VP8/' - - name: ensure SDL is installed - run: cat pjsua-caps | egrep '\[SDL\]\[render\]' + # SDL error: no avilable vid dev + #- name: ensure SDL is installed + # run: cat pjsua-caps | egrep '\[SDL\]\[render\]' - name: pjmedia-test run: make pjmedia-test - name: pjsua-test @@ -269,7 +270,8 @@ jobs: run: cat pjsua-caps | egrep 'H264/' - name: ensure VP8 codec is installed run: cat pjsua-caps | egrep 'VP8/' - - name: ensure SDL is installed - run: cat pjsua-caps | egrep '\[SDL\]\[render\]' + # SDL error: no avilable vid dev + #- name: ensure SDL is installed + # run: cat pjsua-caps | egrep '\[SDL\]\[render\]' - name: pjmedia-test run: make pjmedia-test From 86be063ee78f4067019773c316ebedfa4e6415ae Mon Sep 17 00:00:00 2001 From: bennylp Date: Wed, 1 Jan 2025 14:32:14 +0700 Subject: [PATCH 74/79] Fixed missing ffmpeg shared lib when running pjmedia test --- .github/workflows/ci-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 5db157fd15..cc88a3e4b3 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -274,4 +274,4 @@ jobs: #- name: ensure SDL is installed # run: cat pjsua-caps | egrep '\[SDL\]\[render\]' - name: pjmedia-test - run: make pjmedia-test + run: export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH && make pjmedia-test From 8abba1cd3ac3e19234c1942da4cd5ffb8cb36b11 Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 3 Jan 2025 14:12:02 +0800 Subject: [PATCH 75/79] CI Mac: install gnutls --- .github/workflows/ci-mac.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 9d7ff67d1b..0884156431 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -148,9 +148,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install swig + run: brew install gnutls swig - name: configure - run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ + run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` - name: make run: $MAKE_FAST - name: set up Python @@ -178,9 +178,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: install dependencies - run: brew install swig + run: brew install gnutls swig - name: configure - run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=/usr/local/ + run: CFLAGS="-fPIC" CXXFLAGS="-fPIC" ./configure --with-gnutls=`brew --prefix gnutls` - name: make run: $MAKE_FAST - name: get SSL info From 24852bbcf79c1edc3dd27e71a0107338e0a3e24e Mon Sep 17 00:00:00 2001 From: sauwming Date: Fri, 3 Jan 2025 14:57:46 +0800 Subject: [PATCH 76/79] Lookup renderer in video codec test --- pjmedia/src/test/vid_codec_test.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c index 286f2f96b0..4d2c2402df 100644 --- a/pjmedia/src/test/vid_codec_test.c +++ b/pjmedia/src/test/vid_codec_test.c @@ -200,6 +200,7 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, char codec_name[5]; pj_status_t status; int rc = 0; + unsigned i, count; switch (packing) { case PJMEDIA_VID_PACKING_PACKETS: @@ -268,10 +269,28 @@ static int encode_decode_test(pj_pool_t *pool, const char *codec_id, cap_idx = CAPTURE_DEV; #endif - /* Lookup SDL renderer */ - status = pjmedia_vid_dev_lookup("SDL", "SDL renderer", &rdr_idx); - if (status != PJ_SUCCESS) { - rc = 207; goto on_return; + /* Lookup renderer */ + rdr_idx = PJMEDIA_VID_INVALID_DEV; + count = pjmedia_vid_dev_count(); + for (i = 0; i < count; ++i) { + pjmedia_vid_dev_info cdi; + + status = pjmedia_vid_dev_get_info(i, &cdi); + if (status != PJ_SUCCESS) + rc = 211; goto on_return; + + /* Only interested with render device */ + if ((cdi.dir & PJMEDIA_DIR_RENDER) != 0) { + rdr_idx = i; + break; + } + } + if (rdr_idx == PJMEDIA_VID_INVALID_DEV) { + PJ_LOG(3, (THIS_FILE, "Unable to find renderer device")); + /* We may be on a machine that doesn't have access to a renderer + * device, don't fail the test. + */ + rc = 0; goto on_return; } /* Prepare codec */ From 090a8422c52b17363cb10aaf91c35b0e2496062c Mon Sep 17 00:00:00 2001 From: bennylp Date: Fri, 3 Jan 2025 16:55:14 +0900 Subject: [PATCH 77/79] CI MacOS: attempt to fix failed OpenSSL test --- .github/workflows/ci-mac.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 9d7ff67d1b..9b79431ac5 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -45,8 +45,6 @@ jobs: run: make pjmedia-test - name: pjnath-test run: make pjnath-test - - name: pjsua-test - run: make pjsua-test default-pjsua-test: runs-on: macos-latest @@ -99,6 +97,8 @@ jobs: run: brew install openssl swig - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: make run: $MAKE_FAST - name: set up Python @@ -131,6 +131,8 @@ jobs: run: brew install openssl - name: configure run: CFLAGS="$(pkg-config --cflags openssl) -fPIC" LDFLAGS="$(pkg-config --libs-only-L openssl) $(pkg-config --libs-only-L openssl)/lib" CXXFLAGS="-fPIC" ./configure + - name: config site + run: cd pjlib/include/pj && cp config_site_test.h config_site.h - name: make run: $MAKE_FAST - name: disable firewall From 25b806a6bdf4bd26b59b4dfea23a72380fa5f17a Mon Sep 17 00:00:00 2001 From: bennylp Date: Sat, 4 Jan 2025 12:33:25 +0900 Subject: [PATCH 78/79] CI Mac: better test split to make duration more uniform across jobs --- .github/workflows/ci-mac.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 0f91711aee..961292d108 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -24,7 +24,7 @@ jobs: default-pjlib-util-pjmedia-pjnath: # full bundle: enable all codecs + AEC + DTLS runs-on: macos-latest - name: Default / pjlib,util,pjmedia,pjnath + name: Default / pjmedia,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -37,10 +37,6 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: pjlib-test - run: make pjlib-test - - name: pjlib-util-test - run: make pjlib-util-test - name: pjmedia-test run: make pjmedia-test - name: pjnath-test @@ -72,7 +68,7 @@ jobs: default-pjsip-test: runs-on: macos-latest - name: Default / pjsip-test + name: Default / pjlib,util,pjsip-test steps: - uses: actions/checkout@v2 - name: install dependencies @@ -85,6 +81,10 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: pjlib-test + run: make pjlib-test + - name: pjlib-util-test + run: make pjlib-util-test - name: pjsip-test run: make pjsip-test @@ -220,7 +220,7 @@ jobs: video-openh264-2: runs-on: macos-latest - name: Openh264+VPX / pjlib,util,pjnath + name: Openh264+VPX / util,pjnath steps: - uses: actions/checkout@v2 - name: install dependencies @@ -233,8 +233,6 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - - name: pjlib-test - run: make pjlib-test - name: pjlib-util-test run: make pjlib-util-test - name: pjnath-test @@ -242,7 +240,7 @@ jobs: video-openh264-3: runs-on: macos-latest - name: Openh264+VPX / pjsip + name: Openh264+VPX / pjlib,pjsip steps: - uses: actions/checkout@v2 - name: install dependencies @@ -255,6 +253,8 @@ jobs: run: $MAKE_FAST - name: disable firewall run: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + - name: pjlib-test + run: make pjlib-test - name: pjsip-test run: make pjsip-test From 22b703878776809c999c36ab8d49c0138ee33efe Mon Sep 17 00:00:00 2001 From: bennylp Date: Tue, 7 Jan 2025 11:02:22 +0700 Subject: [PATCH 79/79] Replace deprecated egrep with grep -E --- .github/workflows/ci-linux.yml | 24 ++++++++++++------------ .github/workflows/ci-mac.yml | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index cc88a3e4b3..7a5293a7fa 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -23,7 +23,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify oepnssl is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' default-full-bundle-1: # full bundle: enable all codecs + AEC + DTLS @@ -56,7 +56,7 @@ jobs: EOF cat pjsua-caps - name: ensure AMR codec is installed - run: cat pjsua-caps | egrep 'AMR/8000' + run: cat pjsua-caps | grep -E 'AMR/8000' - name: pjmedia-test run: make pjmedia-test - name: pjsua-test @@ -135,7 +135,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjlib-test run: make pjlib-test - name: pjnath-test @@ -177,14 +177,14 @@ jobs: EOF cat pjsua-caps - name: ensure AMR codec is installed - run: cat pjsua-caps | egrep 'AMR/8000' + run: cat pjsua-caps | grep -E 'AMR/8000' - name: ensure H264 codec is installed - run: cat pjsua-caps | egrep 'H264/' + run: cat pjsua-caps | grep -E 'H264/' - name: ensure VP8 codec is installed - run: cat pjsua-caps | egrep 'VP8/' - # SDL error: no avilable vid dev + run: cat pjsua-caps | grep -E 'VP8/' + # SDL error: no available vid dev #- name: ensure SDL is installed - # run: cat pjsua-caps | egrep '\[SDL\]\[render\]' + # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' - name: pjmedia-test run: make pjmedia-test - name: pjsua-test @@ -267,11 +267,11 @@ jobs: EOF cat pjsua-caps - name: ensure H264 codec is installed - run: cat pjsua-caps | egrep 'H264/' + run: cat pjsua-caps | grep -E 'H264/' - name: ensure VP8 codec is installed - run: cat pjsua-caps | egrep 'VP8/' - # SDL error: no avilable vid dev + run: cat pjsua-caps | grep -E 'VP8/' + # SDL error: no available vid dev #- name: ensure SDL is installed - # run: cat pjsua-caps | egrep '\[SDL\]\[render\]' + # run: cat pjsua-caps | grep -E '\[SDL\]\[render\]' - name: pjmedia-test run: export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH && make pjmedia-test diff --git a/.github/workflows/ci-mac.yml b/.github/workflows/ci-mac.yml index 961292d108..a550773842 100644 --- a/.github/workflows/ci-mac.yml +++ b/.github/workflows/ci-mac.yml @@ -112,7 +112,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify openssl is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjlib-test run: make pjlib-test - name: pjlib-util-test @@ -140,7 +140,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify openssl is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+1' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+1' - name: pjsip-test run: make pjsip-test @@ -164,7 +164,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjlib-test run: make pjlib-test - name: pjlib-util-test @@ -188,7 +188,7 @@ jobs: - name: get SSL info run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep SSL - name: verify gnu tls is used - run: pjlib/bin/pjlib-test-`make infotarget` --config --list | egrep 'PJ_SSL_SOCK_IMP\s+:\s+2' + run: pjlib/bin/pjlib-test-`make infotarget` --config --list | grep -E 'PJ_SSL_SOCK_IMP\s+:\s+2' - name: pjsip-test run: make pjsip-test