diff --git a/.gitignore b/.gitignore index 018c1c0d2d..c004439946 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,8 @@ Release/ /src/mod/applications/mod_osp/Makefile.in /src/mod/applications/mod_rss/Makefile /src/mod/applications/mod_snipe_hunt/Makefile +/src/mod/applications/mod_test/test/Makefile +/src/mod/applications/mod_test/test/Makefile.in /src/mod/codecs/mod_com_g729/Makefile /src/mod/codecs/mod_com_g729/Makefile.in /src/mod/codecs/mod_dahdi_codec/Makefile diff --git a/configure.ac b/configure.ac index fa802d491f..4106fa0b19 100644 --- a/configure.ac +++ b/configure.ac @@ -1861,6 +1861,8 @@ AC_CONFIG_FILES([Makefile src/mod/applications/mod_spandsp/Makefile src/mod/applications/mod_spy/Makefile src/mod/applications/mod_stress/Makefile + src/mod/applications/mod_test/Makefile + src/mod/applications/mod_test/test/Makefile src/mod/applications/mod_translate/Makefile src/mod/applications/mod_valet_parking/Makefile src/mod/applications/mod_vmd/Makefile diff --git a/src/include/test/switch_test.h b/src/include/test/switch_test.h index ec44e97d0c..cb6ba306de 100644 --- a/src/include/test/switch_test.h +++ b/src/include/test/switch_test.h @@ -35,26 +35,34 @@ #include +/** + * Get environment variable and save to var + ( ) + */ +static char *fst_getenv_default(const char *env, char *default_value, switch_bool_t required) +{ + char *val = getenv(env); + if (!val) { + if (required) { + fprintf(stderr, "Failed to start test: environment variable \"%s\" is not set!\n", env); + exit(1); + } + return default_value; + } + return val; +} /** * Get environment variable and save to var */ #define fst_getenv(env, default_value) \ - char *env = getenv(#env); \ - if (!env) { \ - env = (char *)default_value; \ - } + char *env = fst_getenv_default(#env, (char *)default_value, SWITCH_FALSE); /** * Get mandatory environment variable and save to var. Exit with error if missing. */ #define fst_getenv_required(env) \ - char *env = getenv(#env); \ - if (!env) { \ - fprintf(stderr, "Failed to start test: environment variable \"%s\" is not set!\n", #env); \ - exit(1); \ - } - + char *env = fst_getenv_default(#env, NULL, SWITCH_TRUE); /** * initialize FS core from optional configuration dir @@ -96,19 +104,16 @@ static void fst_init_core_and_modload(const char *confdir, const char *basedir) switch_core_init_and_modload(0, SWITCH_TRUE, &err); switch_sleep(1 * 1000000); - switch_core_set_variable("sound_prefix", ""); + switch_core_set_variable("sound_prefix", "." SWITCH_PATH_SEPARATOR); } /** * Park FreeSWITCH session. This is handy when wanting to use switch_core_session_execute_async() on the test session. * @param session to park */ -static void fst_session_park(switch_core_session_t *session) -{ - switch_ivr_park_session(session); +#define fst_session_park(session) \ + switch_ivr_park_session(session); \ switch_channel_wait_for_state(switch_core_session_get_channel(session), NULL, CS_PARK); -} - /** * check for test requirement - execute teardown on failure @@ -192,16 +197,19 @@ static void fst_session_park(switch_core_session_t *session) #define FST_CORE_BEGIN(confdir) \ FCT_BGN() \ { \ + switch_time_t fst_time_start = 0; \ switch_timer_t fst_timer = { 0 }; \ switch_memory_pool_t *fst_pool = NULL; \ + fst_getenv_default("FST_SUPPRESS_UNUSED_STATIC_WARNING", NULL, SWITCH_FALSE); \ fst_init_core_and_modload(confdir, NULL); \ - switch_time_t fst_time_start = 0; + { \ /** * Define the end of a freeswitch core test driver. */ #define FST_CORE_END() \ /*switch_core_destroy();*/ \ + } \ } \ FCT_END() @@ -348,6 +356,9 @@ static void fst_session_park(switch_core_session_t *session) if (fst_originate_vars) { \ switch_event_destroy(&fst_originate_vars); \ } \ + if (fst_session_pool) { \ + fst_session_pool = NULL; \ + } \ switch_core_session_rwunlock(fst_session); \ switch_sleep(1000000); \ } \ @@ -399,28 +410,29 @@ static void fst_session_park(switch_core_session_t *session) */ #define fst_test_core_asr(grammar, input_filename) \ { \ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Test recognizer: input = %s\n", input_filename); \ - fst_asr_result = NULL; \ - fst_requires(switch_core_asr_load_grammar(&ah, grammar, "") == SWITCH_STATUS_SUCCESS); \ /* feed file into ASR */ \ switch_status_t result; \ switch_file_handle_t file_handle = { 0 }; \ - file_handle.channels = 1; \ - file_handle.native_rate = 8000; \ - fst_requires(switch_core_file_open(&file_handle, input_filename, file_handle.channels, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) == SWITCH_STATUS_SUCCESS); \ - uint8_t *buf = (uint8_t *)switch_core_alloc(fst_pool, sizeof(uint8_t) * 160 * sizeof(uint16_t) * file_handle.channels); \ + uint8_t *buf; \ size_t len = 160; \ int got_result = 0; \ + fst_asr_result = NULL; \ + file_handle.channels = 1; \ + file_handle.native_rate = 8000; \ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Test recognizer: input = %s\n", input_filename); \ + fst_requires(switch_core_asr_load_grammar(&ah, grammar, "") == SWITCH_STATUS_SUCCESS); \ + fst_requires(switch_core_file_open(&file_handle, input_filename, file_handle.channels, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) == SWITCH_STATUS_SUCCESS); \ + buf = (uint8_t *)switch_core_alloc(fst_pool, sizeof(uint8_t) * 160 * sizeof(uint16_t) * file_handle.channels); \ switch_core_timer_sync(&fst_timer); \ while ((result = switch_core_file_read(&file_handle, buf, &len)) == SWITCH_STATUS_SUCCESS) { \ fst_requires(switch_core_asr_feed(&ah, buf, len * sizeof(int16_t), &flags) == SWITCH_STATUS_SUCCESS); \ switch_core_timer_next(&fst_timer); \ if (switch_core_asr_check_results(&ah, &flags) == SWITCH_STATUS_SUCCESS) { \ + char *xmlstr = NULL; \ + switch_event_t *headers = NULL; \ flags = SWITCH_ASR_FLAG_NONE; \ /* switch_ivr_detect_speech.. checks one in media bug then again in speech_thread */ \ fst_requires(switch_core_asr_check_results(&ah, &flags) == SWITCH_STATUS_SUCCESS); \ - char *xmlstr = NULL; \ - switch_event_t *headers = NULL; \ result = switch_core_asr_get_results(&ah, &xmlstr, &flags); \ if (result == SWITCH_STATUS_SUCCESS) { \ got_result++; \ @@ -514,7 +526,7 @@ static void fst_session_park(switch_core_session_t *session) char *args = NULL; \ fst_requires_module("mod_dptools"); \ switch_channel_set_variable(fst_channel, "detect_speech_result", ""); \ - fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "r") == SWITCH_STATUS_SUCCESS); \ + fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "mr") == SWITCH_STATUS_SUCCESS); \ args = switch_core_session_sprintf(fst_session, "%s detect:%s %s", prompt_filename, recognizer, grammar); \ fst_requires(switch_core_session_execute_application(fst_session, "play_and_detect_speech", args) == SWITCH_STATUS_SUCCESS); \ fst_asr_result = switch_channel_get_variable(fst_channel, "detect_speech_result"); \ @@ -543,7 +555,7 @@ static void fst_session_park(switch_core_session_t *session) { \ char *args = NULL; \ fst_asr_result = NULL; \ - fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "r") == SWITCH_STATUS_SUCCESS); \ + fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "mr") == SWITCH_STATUS_SUCCESS); \ switch_status_t status = switch_ivr_play_and_detect_speech(fst_session, prompt_filename, recognizer, grammar, (char **)&fst_asr_result, 0, input_args); \ fst_check(fst_asr_result != NULL); \ } diff --git a/src/mod/applications/mod_test/Makefile.am b/src/mod/applications/mod_test/Makefile.am new file mode 100644 index 0000000000..35cf0ed45e --- /dev/null +++ b/src/mod/applications/mod_test/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/build/modmake.rulesam +MODNAME=mod_test + +mod_LTLIBRARIES = mod_test.la +mod_test_la_SOURCES = mod_test.c +mod_test_la_CFLAGS = $(AM_CFLAGS) +mod_test_la_LIBADD = $(switch_builddir)/libfreeswitch.la +mod_test_la_LDFLAGS = -avoid-version -module -no-undefined -shared + +SUBDIRS=. test diff --git a/src/mod/applications/mod_test/mod_test.c b/src/mod/applications/mod_test/mod_test.c new file mode 100644 index 0000000000..d5460ac364 --- /dev/null +++ b/src/mod/applications/mod_test/mod_test.c @@ -0,0 +1,407 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2018, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * + * mod_test.c -- mock module interfaces for testing + * + */ + +#include + + +SWITCH_MODULE_LOAD_FUNCTION(mod_test_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_test_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_test_runtime); +SWITCH_MODULE_DEFINITION(mod_test, mod_test_load, mod_test_shutdown, mod_test_runtime); + + +typedef enum { + ASRFLAG_READY = (1 << 0), + ASRFLAG_INPUT_TIMERS = (1 << 1), + ASRFLAG_START_OF_SPEECH = (1 << 2), + ASRFLAG_RETURNED_START_OF_SPEECH = (1 << 3), + ASRFLAG_NOINPUT_TIMEOUT = (1 << 4), + ASRFLAG_RESULT = (1 << 5), + ASRFLAG_RETURNED_RESULT = (1 << 6) +} test_asr_flag_t; + +typedef struct { + uint32_t flags; + const char *result_text; + double result_confidence; + uint32_t thresh; + uint32_t silence_ms; + uint32_t voice_ms; + int no_input_timeout; + int speech_timeout; + switch_bool_t start_input_timers; + switch_time_t no_input_time; + switch_time_t speech_time; + char *grammar; + char *channel_uuid; + switch_vad_t *vad; +} test_asr_t; + + +static void test_asr_reset(test_asr_t *context) +{ + if (context->vad) { + switch_vad_reset(context->vad); + } + context->flags = 0; + context->result_text = "agent"; + context->result_confidence = 87.3; + switch_set_flag(context, ASRFLAG_READY); + context->no_input_time = switch_micro_time_now(); + if (context->start_input_timers) { + switch_set_flag(context, ASRFLAG_INPUT_TIMERS); + } +} + +static switch_status_t test_asr_open(switch_asr_handle_t *ah, const char *codec, int rate, const char *dest, switch_asr_flag_t *flags) +{ + test_asr_t *context; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_open attempt on CLOSED asr handle\n"); + return SWITCH_STATUS_FALSE; + } + + if (!(context = (test_asr_t *) switch_core_alloc(ah->memory_pool, sizeof(*context)))) { + return SWITCH_STATUS_MEMERR; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "codec = %s, rate = %d, dest = %s\n", codec, rate, dest); + + ah->private_info = context; + ah->codec = switch_core_strdup(ah->memory_pool, codec); + + if (rate > 16000) { + ah->native_rate = 16000; + } + + context->thresh = 400; + context->silence_ms = 700; + context->voice_ms = 60; + context->start_input_timers = 1; + context->no_input_timeout = 5000; + context->speech_timeout = 10000; + + context->vad = switch_vad_init(ah->native_rate, 1); + switch_vad_set_mode(context->vad, -1); + switch_vad_set_param(context->vad, "thresh", context->thresh); + switch_vad_set_param(context->vad, "silence_ms", context->silence_ms); + switch_vad_set_param(context->vad, "voice_ms", context->voice_ms); + switch_vad_set_param(context->vad, "debug", 0); + + test_asr_reset(context); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t test_asr_load_grammar(switch_asr_handle_t *ah, const char *grammar, const char *name) +{ + test_asr_t *context = (test_asr_t *)ah->private_info; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_open attempt on CLOSED asr handle\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "load grammar %s\n", grammar); + context->grammar = switch_core_strdup(ah->memory_pool, grammar); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t test_asr_unload_grammar(switch_asr_handle_t *ah, const char *name) +{ + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t test_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + test_asr_t *context = (test_asr_t *)ah->private_info; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Double ASR close!\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_NOTICE, "ASR closing ...\n"); + + if (context->vad) { + switch_vad_destroy(&context->vad); + } + + switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED); + return status; +} + +static switch_status_t test_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_vad_state_t vad_state; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + return SWITCH_STATUS_BREAK; + } + + if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) && switch_test_flag(ah, SWITCH_ASR_FLAG_AUTO_RESUME)) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Auto Resuming\n"); + test_asr_reset(context); + } + + if (switch_test_flag(context, ASRFLAG_READY)) { + vad_state = switch_vad_process(context->vad, (int16_t *)data, len / sizeof(uint16_t)); + if (vad_state == SWITCH_VAD_STATE_STOP_TALKING) { + switch_set_flag(context, ASRFLAG_RESULT); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "Talking stopped, have result.\n"); + switch_vad_reset(context->vad); + switch_clear_flag(context, ASRFLAG_READY); + } else if (vad_state == SWITCH_VAD_STATE_START_TALKING) { + switch_set_flag(context, ASRFLAG_START_OF_SPEECH); + context->speech_time = switch_micro_time_now(); + } + } + + return status; +} + +static switch_status_t test_asr_pause(switch_asr_handle_t *ah) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_pause attempt on CLOSED asr handle\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Pausing\n"); + context->flags = 0; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t test_asr_resume(switch_asr_handle_t *ah) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_resume attempt on CLOSED asr handle\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Resuming\n"); + test_asr_reset(context); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t test_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + + if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) || switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + return SWITCH_STATUS_BREAK; + } + + if (!switch_test_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH) && switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) { + return SWITCH_STATUS_SUCCESS; + } + + if ((!switch_test_flag(context, ASRFLAG_RESULT)) && (!switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT))) { + if (switch_test_flag(context, ASRFLAG_INPUT_TIMERS) && !(switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) && + context->no_input_timeout >= 0 && + (switch_micro_time_now() - context->no_input_time) / 1000 >= context->no_input_timeout) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "NO INPUT TIMEOUT %" SWITCH_TIME_T_FMT "ms\n", (switch_micro_time_now() - context->no_input_time) / 1000); + switch_set_flag(context, ASRFLAG_NOINPUT_TIMEOUT); + } else if (switch_test_flag(context, ASRFLAG_START_OF_SPEECH) && context->speech_timeout > 0 && (switch_micro_time_now() - context->speech_time) / 1000 >= context->speech_timeout) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "SPEECH TIMEOUT %" SWITCH_TIME_T_FMT "ms\n", (switch_micro_time_now() - context->speech_time) / 1000); + if (switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) { + switch_set_flag(context, ASRFLAG_RESULT); + } else { + switch_set_flag(context, ASRFLAG_NOINPUT_TIMEOUT); + } + } + } + + return switch_test_flag(context, ASRFLAG_RESULT) || switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_BREAK; +} + +static switch_status_t test_asr_get_results(switch_asr_handle_t *ah, char **resultstr, switch_asr_flag_t *flags) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_stream_handle_t result = { 0 }; + SWITCH_STANDARD_STREAM(result); + + if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) || switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + return SWITCH_STATUS_FALSE; + } + + if (switch_test_flag(context, ASRFLAG_RESULT)) { + + *resultstr = switch_mprintf("{\"grammar\": \"%s\", \"text\": \"%s\", \"confidence\": %f}", context->grammar, context->result_text, context->result_confidence); + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_ERROR, "Result: %s\n", *resultstr); + + status = SWITCH_STATUS_SUCCESS; + } else if (switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT)) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Result: NO INPUT\n"); + + *resultstr = switch_mprintf("{\"grammar\": \"%s\", \"text\": \"\", \"confidence\": 0, \"error\": \"no_input\"}", context->grammar); + + status = SWITCH_STATUS_SUCCESS; + } else if (!switch_test_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH) && switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) { + switch_set_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Result: START OF SPEECH\n"); + status = SWITCH_STATUS_BREAK; + } else { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_ERROR, "Unexpected call to asr_get_results - no results to return!\n"); + status = SWITCH_STATUS_FALSE; + } + + if (status == SWITCH_STATUS_SUCCESS) { + switch_set_flag(context, ASRFLAG_RETURNED_RESULT); + switch_clear_flag(context, ASRFLAG_READY); + } + + return status; +} + +static switch_status_t test_asr_start_input_timers(switch_asr_handle_t *ah) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + + if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_start_input_timers attempt on CLOSED asr handle\n"); + return SWITCH_STATUS_FALSE; + } + + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "start_input_timers\n"); + + if (!switch_test_flag(context, ASRFLAG_INPUT_TIMERS)) { + switch_set_flag(context, ASRFLAG_INPUT_TIMERS); + context->no_input_time = switch_micro_time_now(); + } else { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "Input timers already started\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + +static void test_asr_text_param(switch_asr_handle_t *ah, char *param, const char *val) +{ + test_asr_t *context = (test_asr_t *) ah->private_info; + + if (!zstr(param) && !zstr(val)) { + int nval = atoi(val); + double fval = atof(val); + + if (!strcasecmp("no-input-timeout", param) && switch_is_number(val)) { + context->no_input_timeout = nval; + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "no-input-timeout = %d\n", context->no_input_timeout); + } else if (!strcasecmp("speech-timeout", param) && switch_is_number(val)) { + context->speech_timeout = nval; + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "speech-timeout = %d\n", context->speech_timeout); + } else if (!strcasecmp("start-input-timers", param)) { + context->start_input_timers = switch_true(val); + if (context->start_input_timers) { + switch_set_flag(context, ASRFLAG_INPUT_TIMERS); + } else { + switch_clear_flag(context, ASRFLAG_INPUT_TIMERS); + } + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "start-input-timers = %d\n", context->start_input_timers); + } else if (!strcasecmp("vad-mode", param)) { + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "vad-mode = %s\n", val); + if (context->vad) switch_vad_set_mode(context->vad, nval); + } else if (!strcasecmp("vad-voice-ms", param) && nval > 0) { + context->voice_ms = nval; + switch_vad_set_param(context->vad, "voice_ms", nval); + } else if (!strcasecmp("vad-silence-ms", param) && nval > 0) { + context->silence_ms = nval; + switch_vad_set_param(context->vad, "silence_ms", nval); + } else if (!strcasecmp("vad-thresh", param) && nval > 0) { + context->thresh = nval; + switch_vad_set_param(context->vad, "thresh", nval); + } else if (!strcasecmp("channel-uuid", param)) { + context->channel_uuid = switch_core_strdup(ah->memory_pool, val); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "channel-uuid = %s\n", val); + } else if (!strcasecmp("result", param)) { + context->result_text = switch_core_strdup(ah->memory_pool, val); + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "result = %s\n", val); + } else if (!strcasecmp("confidence", param) && fval >= 0.0) { + context->result_confidence = fval; + switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "confidence = %f\n", fval); + } + } +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_test_load) +{ + switch_asr_interface_t *asr_interface; + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + asr_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ASR_INTERFACE); + asr_interface->interface_name = "test"; + asr_interface->asr_open = test_asr_open; + asr_interface->asr_load_grammar = test_asr_load_grammar; + asr_interface->asr_unload_grammar = test_asr_unload_grammar; + asr_interface->asr_close = test_asr_close; + asr_interface->asr_feed = test_asr_feed; + asr_interface->asr_resume = test_asr_resume; + asr_interface->asr_pause = test_asr_pause; + asr_interface->asr_check_results = test_asr_check_results; + asr_interface->asr_get_results = test_asr_get_results; + asr_interface->asr_start_input_timers = test_asr_start_input_timers; + asr_interface->asr_text_param = test_asr_text_param; + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_test_shutdown) +{ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_RUNTIME_FUNCTION(mod_test_runtime) +{ + return SWITCH_STATUS_TERM; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/mod/applications/mod_test/test/Makefile.am b/src/mod/applications/mod_test/test/Makefile.am new file mode 100644 index 0000000000..ed630acae0 --- /dev/null +++ b/src/mod/applications/mod_test/test/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/build/modmake.rulesam +bin_PROGRAMS = test_asr +test_asr_CFLAGS = $(AM_CFLAGS) -I../ +test_asr_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) ../mod_test.la $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) +TESTS = $(bin_PROGRAMS) diff --git a/src/mod/applications/mod_test/test/freeswitch.xml b/src/mod/applications/mod_test/test/freeswitch.xml new file mode 100644 index 0000000000..032569cd24 --- /dev/null +++ b/src/mod/applications/mod_test/test/freeswitch.xml @@ -0,0 +1,23 @@ + + +
+ + + + + + + + +
+ +
+ + + + + + + +
+
diff --git a/src/mod/applications/mod_test/test/sounds/agent.wav b/src/mod/applications/mod_test/test/sounds/agent.wav new file mode 100644 index 0000000000..dc9c44ebbf Binary files /dev/null and b/src/mod/applications/mod_test/test/sounds/agent.wav differ diff --git a/src/mod/applications/mod_test/test/sounds/hi.wav b/src/mod/applications/mod_test/test/sounds/hi.wav new file mode 100644 index 0000000000..fec0d6b073 Binary files /dev/null and b/src/mod/applications/mod_test/test/sounds/hi.wav differ diff --git a/src/mod/applications/mod_test/test/sounds/ivr-please_state_your_name_and_reason_for_calling.wav b/src/mod/applications/mod_test/test/sounds/ivr-please_state_your_name_and_reason_for_calling.wav new file mode 100644 index 0000000000..f6549736fd Binary files /dev/null and b/src/mod/applications/mod_test/test/sounds/ivr-please_state_your_name_and_reason_for_calling.wav differ diff --git a/src/mod/applications/mod_test/test/sounds/test.wav b/src/mod/applications/mod_test/test/sounds/test.wav new file mode 100644 index 0000000000..3c72ab4cee Binary files /dev/null and b/src/mod/applications/mod_test/test/sounds/test.wav differ diff --git a/src/mod/applications/mod_test/test/sounds/test_agent.wav b/src/mod/applications/mod_test/test/sounds/test_agent.wav new file mode 100644 index 0000000000..7202f6774a Binary files /dev/null and b/src/mod/applications/mod_test/test/sounds/test_agent.wav differ diff --git a/src/mod/applications/mod_test/test/test_asr.c b/src/mod/applications/mod_test/test/test_asr.c new file mode 100644 index 0000000000..3ab3452d27 --- /dev/null +++ b/src/mod/applications/mod_test/test/test_asr.c @@ -0,0 +1,292 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2018, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * + * + * test_asr.c -- tests for mock test asr interface + * + */ +#include +#include +#include + + +static const char *get_query_result_text(switch_memory_pool_t *pool, const char *result) +{ + const char *result_text = NULL; + cJSON *result_json = cJSON_Parse(result); + if (result_json) { + const char *text = cJSON_GetObjectCstr(result_json, "text"); + if (!zstr(text)) { + result_text = switch_core_strdup(pool, text); + } else { + text = cJSON_GetObjectCstr(result_json, "error"); + if (!zstr(text)) { + result_text = switch_core_strdup(pool, text); + } + } + cJSON_Delete(result_json); + } + return result_text; +} + + +FST_CORE_BEGIN(".") + +FST_MODULE_BEGIN(mod_test, test_asr) + +FST_SETUP_BEGIN() +{ + fst_requires_module("mod_tone_stream"); + fst_requires_module("mod_sndfile"); + fst_requires_module("mod_dptools"); +} +FST_SETUP_END() + +FST_TEARDOWN_BEGIN() +{ +} +FST_TEARDOWN_END() + + +FST_TEST_BEGIN(core_asr) +{ + const char* session_id = "123435"; + char *grammar = switch_core_sprintf(fst_pool, "{start-input-timers=true,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", session_id); + fst_test_core_asr_open("test"); + fst_test_core_asr( + grammar, + "file_string://silence_stream://3000,0!sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "silence_stream://30000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "no_input"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "silence_stream://30000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "no_input"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_pause(); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_close(); + + fst_test_core_asr_open("test"); + fst_test_core_asr( + grammar, + "file_string://silence_stream://1000,0!sounds/ivr-please_state_your_name_and_reason_for_calling.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_close(); +} +FST_TEST_END() + +FST_TEST_BEGIN(core_asr_auto_resume) +{ + const char* session_id = "123435"; + char *grammar = switch_core_sprintf(fst_pool, "{start-input-timers=true,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", session_id); + fst_test_core_asr_open("test"); + switch_set_flag(&ah, SWITCH_ASR_FLAG_AUTO_RESUME); + fst_test_core_asr( + grammar, + "file_string://silence_stream://3000,0!sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr( + grammar, + "silence_stream://30000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "no_input"); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr( + grammar, + "silence_stream://30000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "no_input"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://silence_stream://1000,0!sounds/ivr-please_state_your_name_and_reason_for_calling.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_close(); +} +FST_TEST_END() + +FST_TEST_BEGIN(core_asr_abuse) +{ + const char* session_id = "5351514"; + char *grammar = switch_core_sprintf(fst_pool, "{start-input-timers=true,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", session_id); + fst_test_core_asr_open("test"); + fst_test_core_asr( + grammar, + "file_string://silence_stream://3000,0!sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_resume(); + fst_test_core_asr_resume(); + fst_test_core_asr_resume(); + fst_test_core_asr_pause(); + fst_test_core_asr_resume(); + fst_test_core_asr( + grammar, + "file_string://sounds/agent.wav!silence_stream://3000,0"); + fst_check_string_equals(get_query_result_text(fst_pool, fst_asr_result), "agent"); + fst_test_core_asr_resume(); + + // Tested double-close, but FS core will crash... + fst_test_core_asr_close(); +} +FST_TEST_END() + + +FST_SESSION_BEGIN(play_and_detect_1) +{ + const char *result_text = NULL; + char *grammar = switch_core_session_sprintf(fst_session, "{start-input-timers=false,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", switch_core_session_get_uuid(fst_session)); + fst_play_and_detect_speech_test_begin(); + + /* initial welcome and request */ + fst_play_and_detect_speech_app_test("test", + grammar, + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "sounds/agent.wav"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "agent"); + + /* follow up request */ + fst_play_and_detect_speech_app_test("test", + grammar, + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "file_string://1000,0!sounds/agent.wav"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "agent"); + + fst_play_and_detect_speech_test_end(); +} +FST_SESSION_END() + +FST_SESSION_BEGIN(play_and_detect_no_input_follow_up) +{ + const char *result_text = NULL; + char *grammar = switch_core_session_sprintf(fst_session, "{start-input-timers=false,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}", switch_core_session_get_uuid(fst_session)); + + switch_ivr_schedule_hangup(switch_epoch_time_now(NULL) + 60, switch_core_session_get_uuid(fst_session), SWITCH_CAUSE_NORMAL_CLEARING, SWITCH_FALSE); + fst_play_and_detect_speech_test_begin(); + fst_play_and_detect_speech_app_test("test", + grammar, + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "file_string://silence_stream://4000,0!sounds/agent.wav"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "agent"); + + /* follow up request - no input */ + fst_play_and_detect_speech_app_test("test", + grammar, + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "silence_stream://10000,0"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "no_input"); + + fst_play_and_detect_speech_test_end(); +} +FST_SESSION_END() + +FST_SESSION_BEGIN(play_and_detect_no_input) +{ + const char *result_text = NULL; + + switch_ivr_schedule_hangup(switch_epoch_time_now(NULL) + 60, switch_core_session_get_uuid(fst_session), SWITCH_CAUSE_NORMAL_CLEARING, SWITCH_FALSE); + fst_play_and_detect_speech_test_begin(); + fst_play_and_detect_speech_app_test("test", + switch_core_session_sprintf(fst_session, + "{start-input-timers=false,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", + switch_core_session_get_uuid(fst_session)), + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "silence_stream://10000,0"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "no_input"); + + fst_play_and_detect_speech_test_end(); +} +FST_SESSION_END() + +FST_SESSION_BEGIN(play_and_detect_start_input_timers) +{ + const char *result_text = NULL; + + switch_ivr_schedule_hangup(switch_epoch_time_now(NULL) + 60, switch_core_session_get_uuid(fst_session), SWITCH_CAUSE_NORMAL_CLEARING, SWITCH_FALSE); + fst_play_and_detect_speech_test_begin(); + + fst_play_and_detect_speech_app_test("test", + switch_core_session_sprintf(fst_session, + "{start-input-timers=true,no-input-timeout=5000,speech-timeout=10000,channel-uuid=%s}default", + switch_core_session_get_uuid(fst_session)), + "sounds/ivr-please_state_your_name_and_reason_for_calling.wav", + "silence_stream://10000,0"); + result_text = get_query_result_text(fst_pool, fst_asr_result); + fst_requires(result_text != NULL); + fst_check_string_equals(result_text, "no_input"); + + fst_play_and_detect_speech_test_end(); + + fst_check_duration(5000, 500); +} +FST_SESSION_END() + +FST_TEST_BEGIN(unload_test) +{ + const char *err = NULL; + switch_sleep(1000000); + fst_check(switch_loadable_module_unload_module((char *)"../.libs", (char *)"mod_test", SWITCH_FALSE, &err) == SWITCH_STATUS_SUCCESS); +} +FST_TEST_END() + + +FST_MODULE_END() + +FST_CORE_END()