]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-11442 [mod_test] new module, mod_test - mock ASR interface.
authorChris Rienzo <chris@signalwire.com>
Wed, 10 Oct 2018 18:40:54 +0000 (18:40 +0000)
committerChris Rienzo <chris@signalwire.com>
Thu, 20 Dec 2018 15:17:50 +0000 (10:17 -0500)
13 files changed:
.gitignore
configure.ac
src/include/test/switch_test.h
src/mod/applications/mod_test/Makefile.am [new file with mode: 0644]
src/mod/applications/mod_test/mod_test.c [new file with mode: 0644]
src/mod/applications/mod_test/test/Makefile.am [new file with mode: 0644]
src/mod/applications/mod_test/test/freeswitch.xml [new file with mode: 0644]
src/mod/applications/mod_test/test/sounds/agent.wav [new file with mode: 0644]
src/mod/applications/mod_test/test/sounds/hi.wav [new file with mode: 0644]
src/mod/applications/mod_test/test/sounds/ivr-please_state_your_name_and_reason_for_calling.wav [new file with mode: 0644]
src/mod/applications/mod_test/test/sounds/test.wav [new file with mode: 0644]
src/mod/applications/mod_test/test/sounds/test_agent.wav [new file with mode: 0644]
src/mod/applications/mod_test/test/test_asr.c [new file with mode: 0644]

index 018c1c0d2d18585bc314a9f7e414cc1f018223d6..c0044399469a9aafcccd96b5cea57cc4b210853f 100644 (file)
@@ -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
index fa802d491f5c416de6e724f01aea78d5a970fcc3..4106fa0b1950a1ad65530aaa9dd8374eee14e838 100644 (file)
@@ -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
index ec44e97d0c394c8707abd2d2a8b58eece1a7e70e..cb6ba306debdf4eb39c00344e31e9a6809ddbc41 100644 (file)
 
 #include <test/switch_fct.h>
 
+/**
+ * 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 }; \
+       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); \
-       uint8_t *buf = (uint8_t *)switch_core_alloc(fst_pool, sizeof(uint8_t) * 160 * sizeof(uint16_t) * file_handle.channels); \
-       size_t len = 160; \
-       int got_result = 0; \
+       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 (file)
index 0000000..35cf0ed
--- /dev/null
@@ -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 (file)
index 0000000..d5460ac
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris@signalwire.com>
+ *
+ *
+ * mod_test.c -- mock module interfaces for testing
+ *
+ */
+
+#include <switch.h>
+
+
+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 (file)
index 0000000..ed630ac
--- /dev/null
@@ -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 (file)
index 0000000..032569c
--- /dev/null
@@ -0,0 +1,23 @@
+<document type="freeswitch/xml">
+
+  <section name="configuration" description="Various Configuration">
+    <configuration name="modules.conf" description="Modules">
+      <modules>
+        <load module="mod_loopback"/>
+        <load module="mod_tone_stream"/>
+        <load module="mod_dptools"/>
+        <load module="mod_sndfile"/>
+      </modules>
+    </configuration>
+  </section>
+
+  <section name="dialplan" description="Regex/XML Dialplan">
+    <context name="default">
+      <extension name="sample">
+        <condition>
+          <action application="info"/>
+        </condition>
+      </extension>
+    </context>
+  </section>
+</document>
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 (file)
index 0000000..dc9c44e
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 (file)
index 0000000..fec0d6b
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 (file)
index 0000000..f654973
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 (file)
index 0000000..3c72ab4
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 (file)
index 0000000..7202f67
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 (file)
index 0000000..3ab3452
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Rienzo <chris@signalwire.com>
+ *
+ *
+ * test_asr.c -- tests for mock test asr interface
+ *
+ */
+#include <switch.h>
+#include <test/switch_test.h>
+#include <stdlib.h>
+
+
+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()