1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2019, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Chris Rienzo <chris@signalwire.com>
27  * Seven Du <seven@signalwire.com>
28  *
29  * switch_test.h -- FreeSWITCH test macros
30  */
31 #ifndef SWITCH_FST_H
32 #define SWITCH_FST_H
33 
34 #include <switch.h>
35 
36 #include <test/switch_fct.h>
37 
38 /**
39  * Get environment variable and save to var
40  */
fst_getenv_default(const char * env,char * default_value,switch_bool_t required)41 static char *fst_getenv_default(const char *env, char *default_value, switch_bool_t required)
42 {
43 	char *val = getenv(env);
44 	if (!val) {
45 		if (required) {
46 			fprintf(stderr, "Failed to start test: environment variable \"%s\" is not set!\n", env);
47 			exit(1);
48 		}
49 		return default_value;
50 	}
51 	return val;
52 }
53 
54 /**
55  * Get environment variable and save to var
56  */
57 #define fst_getenv(env, default_value) \
58 	char *env = fst_getenv_default(#env, (char *)default_value, SWITCH_FALSE);
59 
60 /**
61  * Get mandatory environment variable and save to var.  Exit with error if missing.
62  */
63 #define fst_getenv_required(env) \
64 	char *env = fst_getenv_default(#env, NULL, SWITCH_TRUE);
65 
66 /**
67  * initialize FS core from optional configuration dir
68  */
fst_init_core_and_modload(const char * confdir,const char * basedir,int minimal,switch_core_flag_t flags)69 static switch_status_t fst_init_core_and_modload(const char *confdir, const char *basedir, int minimal, switch_core_flag_t flags)
70 {
71 	switch_status_t status;
72 	const char *err;
73 	unsigned long pid = switch_getpid();
74 	// Let FreeSWITCH core pick these
75 	//SWITCH_GLOBAL_dirs.base_dir = strdup("/usr/local/freeswitch");
76 	//SWITCH_GLOBAL_dirs.mod_dir = strdup("/usr/local/freeswitch/mod");
77 	//SWITCH_GLOBAL_dirs.lib_dir = strdup("/usr/local/freeswitch/lib");
78 	//SWITCH_GLOBAL_dirs.temp_dir = strdup("/tmp");
79 
80 #ifdef SWITCH_TEST_BASE_DIR_OVERRIDE
81 	basedir = SWITCH_TEST_BASE_DIR_OVERRIDE;
82 #else
83 #define SWITCH_TEST_BASE_DIR_OVERRIDE "."
84 #endif
85 
86 	if (zstr(basedir)) {
87 		basedir = ".";
88 	}
89 
90 	// Allow test to define the runtime dir
91 	if (!zstr(confdir)) {
92 #ifdef SWITCH_TEST_BASE_DIR_FOR_CONF
93 		SWITCH_GLOBAL_dirs.conf_dir = switch_mprintf("%s%s%s", SWITCH_TEST_BASE_DIR_FOR_CONF, SWITCH_PATH_SEPARATOR, confdir);
94 #else
95 		if (confdir[0] != '/') {
96 			SWITCH_GLOBAL_dirs.conf_dir = switch_mprintf(".%s%s", SWITCH_PATH_SEPARATOR, confdir);
97 		} else {
98 			SWITCH_GLOBAL_dirs.conf_dir = strdup(confdir);
99 		}
100 #endif
101 	} else {
102 		SWITCH_GLOBAL_dirs.conf_dir = switch_mprintf("%s%sconf", basedir, SWITCH_PATH_SEPARATOR);
103 	}
104 
105 	SWITCH_GLOBAL_dirs.log_dir = switch_mprintf("%s%s%lu%s", basedir, SWITCH_PATH_SEPARATOR, pid, SWITCH_PATH_SEPARATOR);
106 	SWITCH_GLOBAL_dirs.run_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
107 	SWITCH_GLOBAL_dirs.recordings_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
108 	SWITCH_GLOBAL_dirs.sounds_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
109 	SWITCH_GLOBAL_dirs.cache_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
110 	SWITCH_GLOBAL_dirs.db_dir = switch_mprintf("%s%s%lu%s", basedir, SWITCH_PATH_SEPARATOR, pid, SWITCH_PATH_SEPARATOR);
111 	SWITCH_GLOBAL_dirs.script_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
112 	SWITCH_GLOBAL_dirs.htdocs_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
113 	SWITCH_GLOBAL_dirs.grammar_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
114 	SWITCH_GLOBAL_dirs.fonts_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
115 	SWITCH_GLOBAL_dirs.images_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
116 	SWITCH_GLOBAL_dirs.storage_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
117 	SWITCH_GLOBAL_dirs.data_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
118 	SWITCH_GLOBAL_dirs.localstate_dir = switch_mprintf("%s%s", basedir, SWITCH_PATH_SEPARATOR);
119 
120 	switch_core_set_globals();
121 
122 	if (!minimal) {
123 		status = switch_core_init_and_modload(flags, SWITCH_TRUE, &err);
124 		switch_sleep(1 * 1000000);
125 		switch_core_set_variable("sound_prefix", "." SWITCH_PATH_SEPARATOR);
126 		if (status != SWITCH_STATUS_SUCCESS && err) {
127 			fprintf(stderr, "%s", err);
128 		}
129 		return status;
130 	}
131 	status = switch_core_init(SCF_MINIMAL, SWITCH_TRUE, &err);
132 	if (status != SWITCH_STATUS_SUCCESS && err) {
133 		fprintf(stderr, "%s", err);
134 	}
135 	return status;
136 }
137 
138 /**
139  * Park FreeSWITCH session.  This is handy when wanting to use switch_core_session_execute_async() on the test session.
140  * @param session to park
141  */
142 #define fst_session_park(session) \
143 	switch_ivr_park_session(session); \
144 	switch_channel_wait_for_state(switch_core_session_get_channel(session), NULL, CS_PARK);
145 
146 /**
147  * check for test requirement - execute teardown on failure
148  */
149 #define fst_requires fct_req
150 
151 /**
152  * check for required module - execute teardown on failure
153  */
154 #define fst_requires_module(modname) fct_req(switch_loadable_module_exists(modname) == SWITCH_STATUS_SUCCESS)
155 
156 /**
157  * test boolean expression - continue test execution on failure
158  */
159 #define fst_check fct_chk
160 
161 /**
162  * test integers for equality - continue test execution on failure
163  */
164 #define fst_check_int_equals fct_chk_eq_int
165 
166 /**
167  * test string for equality - continue test execution on failure
168  */
169 #define fst_check_string_equals fct_chk_eq_str
170 
171 /**
172  * test string for inequality - continue test execution on failure
173  */
174 #define fst_check_string_not_equals fct_chk_neq_str
175 
176 /**
177  * Test string for matching prefix
178 */
179 #define fst_check_string_starts_with fct_chk_startswith_str
180 
181 /**
182  * Test string for matching suffix
183 */
184 #define fst_check_string_ends_with fct_chk_endswith_str
185 
186 /**
187  * Test string for substring
188  */
189 #define fst_check_string_has fct_chk_incl_str
190 
191 /**
192  * Test string for exclusion of substring
193  */
194 #define fst_check_string_does_not_have fct_chk_excl_str
195 
196 /**
197  * Mark reference for time measure
198  */
199 #define fst_time_mark() \
200 	fst_time_start = switch_time_now();
201 
202 /**
203  * Check a test /w error message
204  */
205 #define fst_xcheck(expr, error_msg) \
206 	fct_xchk(expr, "%s", error_msg);
207 
208 /**
209  * Fail a test
210  */
211 #define fst_fail(error_msg) \
212 	fct_xchk(0, "%s", error_msg);
213 
214 /**
215  * Check duration relative to test start, last marked time, or last check.
216  */
217 #define fst_check_duration(duration_ms, precision_ms) \
218 	{ \
219 		int actual_duration_ms = (int)((switch_time_now() - fst_time_start) / 1000); \
220 		fct_xchk( \
221 			abs((actual_duration_ms - duration_ms)) <= precision_ms, \
222 			"fst_check_duration: %d != %d +/- %d", \
223 			(actual_duration_ms), \
224 			(duration_ms), \
225 			(precision_ms) \
226 		); \
227 	}
228 
229 /**
230  * Check if integer is in range
231  */
232 #define fst_check_int_range(actual, expected, precision) \
233 	fct_xchk( \
234 		abs((actual - expected)) <= precision, \
235 		"fst_check_int_range: %d != %d +/- %d", \
236 		(actual), \
237 		(expected), \
238 		(precision) \
239 	);
240 
241 /**
242  * Check if double-precision number is in range
243  */
244 #define fst_check_double_range(actual, expected, precision) \
245 	fct_xchk( \
246 		fabs((actual - expected)) <= precision, \
247 		"fst_check_double_range: %f != %f +/- %f", \
248 		(actual), \
249 		(expected), \
250 		(precision) \
251 	);
252 
253 /**
254  * Run test without loading FS core
255  */
256 #define FST_BEGIN() \
257 	FCT_BGN() \
258 	{ \
259 		int fst_core = 0; \
260 		switch_time_t fst_time_start = 0; \
261 		switch_timer_t fst_timer = { 0 }; \
262 		switch_memory_pool_t *fst_pool = NULL; \
263 		int fst_timer_started = 0; \
264 		fst_getenv_default("FST_SUPPRESS_UNUSED_STATIC_WARNING", NULL, SWITCH_FALSE); \
265 		if (fst_core) { \
266 			fst_init_core_and_modload(NULL, NULL, 0, 0); /* shuts up compiler */ \
267 		} \
268 		{ \
269 
270 
271 #define FST_END() \
272 		} \
273 		if (fst_time_start) { \
274 			/* shut up compiler */ \
275 			fst_time_start = 0; \
276 		} \
277 	} \
278 	FCT_END()
279 
280 /**
281  * Define the beginning of a freeswitch core test driver.  Only one per test application allowed.
282  * @param confdir directory containing freeswitch.xml configuration
283  */
284 #define FST_CORE_EX_BEGIN(confdir, flags) \
285 	FCT_BGN() \
286 	{ \
287 		int fst_core = 0; \
288 		switch_time_t fst_time_start = 0; \
289 		switch_timer_t fst_timer = { 0 }; \
290 		switch_memory_pool_t *fst_pool = NULL; \
291 		int fst_timer_started = 0; \
292 		fst_getenv_default("FST_SUPPRESS_UNUSED_STATIC_WARNING", NULL, SWITCH_FALSE); \
293 		if (fst_init_core_and_modload(confdir, confdir, 0, flags) == SWITCH_STATUS_SUCCESS) { \
294 			fst_core = 2; \
295 		} else { \
296 			fprintf(stderr, "Failed to load FS core\n"); \
297 			exit(1); \
298 		} \
299 		{
300 
301 
302 /**
303  * Define the end of a freeswitch core test driver.
304  */
305 #define FST_CORE_END() \
306 		switch_core_destroy(); \
307 		} \
308 		if (fst_time_start) { \
309 			/* shut up compiler */ \
310 			fst_time_start = 0; \
311 		} \
312 	} \
313 	FCT_END()
314 
315 #define FST_CORE_BEGIN(confdir) FST_CORE_EX_BEGIN(confdir, 0)
316 #define FST_CORE_DB_BEGIN(confdir) FST_CORE_EX_BEGIN(confdir, SCF_USE_SQL)
317 
318 /**
319  * Minimal FS core load
320  */
321 #define FST_MINCORE_BEGIN(confdir) \
322 	FCT_BGN() \
323 	{ \
324 		int fst_core = 0; \
325 		switch_time_t fst_time_start = 0; \
326 		switch_timer_t fst_timer = { 0 }; \
327 		switch_memory_pool_t *fst_pool = NULL; \
328 		int fst_timer_started = 0; \
329 		fst_getenv_default("FST_SUPPRESS_UNUSED_STATIC_WARNING", NULL, SWITCH_FALSE); \
330 		if (fst_init_core_and_modload(confdir, NULL, 1, 0) == SWITCH_STATUS_SUCCESS) { /* minimal load */ \
331 			fst_core = 1; \
332 		} else { \
333 			fprintf(stderr, "Failed to load FS core\n"); \
334 			exit(1); \
335 		} \
336 		{
337 
338 #define FST_MINCORE_END FST_CORE_END
339 
340 /**
341  * Define the beginning of a FreeSWITCH module test suite.  Loads the module for test.
342  * @param modname name of module to load.
343  * @param suite the name of this test suite
344  */
345 #ifdef WIN32
346 #define FST_MODULE_BEGIN(modname,suite) \
347 	{ \
348 		const char *fst_test_module = #modname; \
349 		if (fst_core && !zstr(fst_test_module)) { \
350 			const char *err; \
351 			switch_loadable_module_load_module((char *)"./mod", (char *)fst_test_module, SWITCH_TRUE, &err); \
352 		} \
353 		FCT_FIXTURE_SUITE_BGN(suite);
354 #else
355 #define FST_MODULE_BEGIN(modname,suite) \
356 	{ \
357 		const char *fst_test_module = #modname; \
358 		if (fst_core && !zstr(fst_test_module)) { \
359 			const char *err; \
360 			char path[1024]; \
361 			sprintf(path, "%s%s%s", SWITCH_TEST_BASE_DIR_OVERRIDE, SWITCH_PATH_SEPARATOR, "../.libs/"); \
362 			switch_loadable_module_load_module((char *)path, (char *)fst_test_module, SWITCH_TRUE, &err); \
363 		} \
364 		FCT_FIXTURE_SUITE_BGN(suite);
365 #endif
366 
367 /**
368  * Define the end of a FreeSWITCH module test suite.
369  */
370 #ifdef WIN32
371 #define FST_MODULE_END() \
372 		FCT_FIXTURE_SUITE_END(); \
373 		if (!zstr(fst_test_module) && switch_loadable_module_exists(fst_test_module) == SWITCH_STATUS_SUCCESS) { \
374 			const char *err; \
375 			switch_loadable_module_unload_module((char *)"./mod", (char *)fst_test_module, SWITCH_FALSE, &err); \
376 		} \
377 	}
378 #else
379 #define FST_MODULE_END() \
380 		FCT_FIXTURE_SUITE_END(); \
381 		if (!zstr(fst_test_module) && switch_loadable_module_exists(fst_test_module) == SWITCH_STATUS_SUCCESS) { \
382 			const char *err; \
383 			char path[1024]; \
384 			sprintf(path, "%s%s%s", SWITCH_TEST_BASE_DIR_OVERRIDE, SWITCH_PATH_SEPARATOR, "../.libs/"); \
385 			switch_loadable_module_unload_module((char*)path, (char *)fst_test_module, SWITCH_FALSE, &err); \
386 		} \
387 	}
388 #endif
389 
390 /**
391  * Define the beginning of a test suite not associated with a module.
392  * @param suite the name of this test suite
393  */
394 #define FST_SUITE_BEGIN(suite) \
395 	const char *fst_test_module = NULL; \
396 	FCT_FIXTURE_SUITE_BGN(suite)
397 
398 /**
399  * Define the end of a test suite.
400  */
401 #define FST_SUITE_END FCT_FIXTURE_SUITE_END
402 
403 
404 /**
405  * Define the test suite setup.  This is run before each test or session test.
406  */
407 #define FST_SETUP_BEGIN() \
408 	FCT_SETUP_BGN() \
409 		if (fst_core) { \
410 			switch_core_new_memory_pool(&fst_pool); \
411 			if (fst_core > 1) { \
412 				fst_timer_started = (switch_core_timer_init(&fst_timer, "soft", 20, 160, fst_pool) == SWITCH_STATUS_SUCCESS); \
413 			} \
414 		}
415 
416 /**
417  * Define the end of test suite setup.
418  */
419 #define FST_SETUP_END FCT_SETUP_END
420 
421 
422 /**
423  * Define the test suite teardown.  This is run after each test or session test.
424  */
425 #define FST_TEARDOWN_BEGIN() \
426 	FCT_TEARDOWN_BGN() \
427 		if (fst_core) { \
428 			if (fst_pool) switch_core_destroy_memory_pool(&fst_pool); \
429 			if (fst_core > 1) { \
430 				if (fst_timer_started) switch_core_timer_destroy(&fst_timer); \
431 			} \
432 		}
433 
434 /**
435  * Define the test suite teardown end.
436  */
437 #define FST_TEARDOWN_END FCT_TEARDOWN_END
438 
439 /**
440  * Define a test in a test suite.
441  * Defined vars:
442  *   switch_memory_pool_t *fst_pool;   A memory pool that is torn down on test completion
443  *   switch_core_timer_t *fst_timer;   A 8kHz, 20ms soft timer (160 samples per frame)
444  * @param name the name of this test
445  */
446 #define FST_TEST_BEGIN(name) \
447 	FCT_TEST_BGN(name) \
448 		if (fst_core) { \
449 			fst_requires(fst_pool != NULL); \
450 			if (fst_core > 1) { \
451 				fst_requires(fst_timer_started); \
452 			} \
453 			fst_time_mark(); \
454 		} \
455 		if (fst_test_module) { \
456 			fst_requires_module(fst_test_module); \
457 		}
458 
459 #define FST_TEST_END FCT_TEST_END
460 
461 
462 /**
463  * Define a session test in a test suite.  This can be used to test IVR functions.
464  *
465  * Records session audio to /tmp/name.wav where name is the name of the test.
466  *
467  * Required modules:
468  *   mod_loopback - for null endpoint
469  *   mod_sndfile  - for wav file support
470  *
471  * Defined vars:
472  *   switch_memory_pool_t *fst_pool;          A memory pool that is torn down on test completion
473  *   switch_core_timer_t *fst_timer;          A 8kHz, 20ms soft timer (160 samples per frame)
474  *   switch_core_session_t *fst_session;      The outbound null session.  L16, 1 channel, 8kHz.
475  *   switch_core_session_t *fst_session_pool; The outbound null session's pool.
476  *   switch_channel_t *fst_channel;           The outbound null session's channel.
477  *
478  * @param name the name of this test
479  * @param rate the rate of the channel
480  */
481 
482 #define FST_SESSION_BEGIN_RATE(name, rate) \
483 	FCT_TEST_BGN(name) \
484 	{ \
485 		if (fst_core) { \
486 			fst_requires(fst_pool != NULL); \
487 			if (fst_core > 1) { \
488 				fst_requires(fst_timer_started); \
489 			} \
490 			fst_time_mark(); \
491 		} \
492 	} \
493 	{ \
494 		switch_core_session_t *fst_session = NULL; \
495 		switch_event_t *fst_originate_vars = NULL; \
496 		switch_call_cause_t fst_cause = SWITCH_CAUSE_NORMAL_CLEARING; \
497 		fst_requires(fst_core); \
498 		if (fst_test_module) { \
499 			fst_requires_module(fst_test_module); \
500 		} \
501 		fst_requires_module("mod_loopback"); \
502 		fst_requires_module("mod_sndfile"); \
503 		fst_requires(switch_core_running()); \
504 		fst_requires(switch_event_create_plain(&fst_originate_vars, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS); \
505 		switch_event_add_header_string(fst_originate_vars, SWITCH_STACK_BOTTOM, "origination_caller_id_number", "+15551112222"); \
506 		switch_event_add_header(fst_originate_vars, SWITCH_STACK_BOTTOM, "rate", "%d", rate); \
507 		if (switch_ivr_originate(NULL, &fst_session, &fst_cause, "null/+15553334444", 2, NULL, NULL, NULL, NULL, fst_originate_vars, SOF_NONE, NULL, NULL) == SWITCH_STATUS_SUCCESS && fst_session) { \
508 			switch_memory_pool_t *fst_session_pool = switch_core_session_get_pool(fst_session); \
509 			switch_channel_t *fst_channel = switch_core_session_get_channel(fst_session); \
510 			switch_channel_set_state(fst_channel, CS_SOFT_EXECUTE); \
511 			switch_channel_wait_for_state(fst_channel, NULL, CS_SOFT_EXECUTE); \
512 			switch_channel_set_variable(fst_channel, "send_silence_when_idle", "-1"); \
513 			switch_channel_set_variable(fst_channel, "RECORD_STEREO", "true"); \
514 			switch_ivr_record_session(fst_session, (char *)"/tmp/"#name".wav", 0, NULL); \
515 			for(;;) {
516 
517 /**
518  * Define a session test in a test suite.  This can be used to test IVR functions.
519  * See FST_SESSION_BEGIN_RATE
520  */
521 
522 #define FST_SESSION_BEGIN(name) FST_SESSION_BEGIN_RATE(name, 8000)
523 
524 /* BODY OF TEST CASE HERE */
525 
526 /**
527  * Define the end of a session test in a test suite.
528  * Hangs up session under test.
529  */
530 #define FST_SESSION_END() \
531 				break; \
532 			} \
533 			if (switch_channel_ready(fst_channel)) { \
534 				switch_channel_hangup(fst_channel, SWITCH_CAUSE_NORMAL_CLEARING); \
535 			} \
536 			if (fst_originate_vars) { \
537 				switch_event_destroy(&fst_originate_vars); \
538 			} \
539 			if (fst_session_pool) { \
540 				fst_session_pool = NULL; \
541 			} \
542 			switch_core_session_rwunlock(fst_session); \
543 			switch_sleep(1000000); \
544 		} \
545 	} \
546 	FCT_TEST_END()
547 
548 
549 /* CORE ASR TEST MACROS */
550 
551 /**
552  * Open core ASR for a recognizer module.  Opens for L16, 1 channel, 8KHz.
553  *
554  * Test Requires:
555  *    switch_core_asr_open() == SWITCH_STATUS_SUCCESS
556  *
557  * Defined vars:
558  *   switch_asr_handle_t ah;              Core ASR handle
559  *   switch_asr_flag_t flags;             Core ASR flags used to open recognizer.
560  *   char *fst_asr_result;                Result of last recognition.  Allocated from test memory pool.
561  *
562  * @param recognizer name of recognizer to open (like gcloud_dialogflow)
563  *
564  */
565 #define fst_test_core_asr_open(recognizer) \
566 {\
567 	char *fst_asr_result = NULL; \
568 	switch_asr_handle_t ah = { 0 }; \
569 	switch_asr_flag_t flags = SWITCH_ASR_FLAG_NONE; \
570 	fst_requires(fst_core > 1); \
571 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Open recognizer: %s\n", recognizer); \
572 	/* open ASR interface and feed recorded audio into it and collect result */ \
573 	fst_requires(switch_core_asr_open(&ah, recognizer, "L16", 8000, "", &flags, fst_pool) == SWITCH_STATUS_SUCCESS); \
574 
575 /**
576  * Execute test on opened recognizer.  Reads audio from input file and passes it to the recognizer.
577  *
578  * Test Requires:
579  *   switch_core_asr_load_grammar(grammar) == SWITCH_STATUS_SUCCESS
580  *   switch_core_file_open(input_filename) == SWITCH_STATUS_SUCCESS
581  *   switch_core_file_close() == SWITCH_STATUS_SUCCESS
582  *
583  * Test Checks:
584  *   Got result from recognizer.
585  *
586  * Test Output:
587  *   fst_asr_result has the xmlstr from switch_core_file_get_results()
588  *
589  * @param grammar recognizer grammar
590  * @param input_filename name of file containing audio to send to recognizer.
591  */
592 #define fst_test_core_asr(grammar, input_filename) \
593 { \
594 	/* feed file into ASR */ \
595 	switch_status_t result; \
596 	switch_file_handle_t file_handle = { 0 }; \
597 	uint8_t *buf; \
598 	size_t len = 160; \
599 	int got_result = 0; \
600 	fst_asr_result = NULL; \
601 	file_handle.channels = 1; \
602 	file_handle.native_rate = 8000; \
603 	fst_requires(fst_core > 1); \
604 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Test recognizer: input = %s\n", input_filename); \
605 	fst_requires(switch_core_asr_load_grammar(&ah, grammar, "") == SWITCH_STATUS_SUCCESS); \
606 	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); \
607 	buf = (uint8_t *)switch_core_alloc(fst_pool, sizeof(uint8_t) * 160 * sizeof(uint16_t) * file_handle.channels); \
608 	switch_core_timer_sync(&fst_timer); \
609 	while ((result = switch_core_file_read(&file_handle, buf, &len)) == SWITCH_STATUS_SUCCESS) { \
610 		fst_requires(switch_core_asr_feed(&ah, buf, len * sizeof(int16_t), &flags) == SWITCH_STATUS_SUCCESS); \
611 		switch_core_timer_next(&fst_timer); \
612 		if (switch_core_asr_check_results(&ah, &flags) == SWITCH_STATUS_SUCCESS) { \
613 			char *xmlstr = NULL; \
614 			switch_event_t *headers = NULL; \
615 			flags = SWITCH_ASR_FLAG_NONE; \
616 			/* switch_ivr_detect_speech.. checks one in media bug then again in speech_thread  */ \
617 			fst_requires(switch_core_asr_check_results(&ah, &flags) == SWITCH_STATUS_SUCCESS); \
618 			result = switch_core_asr_get_results(&ah, &xmlstr, &flags); \
619 			if (result == SWITCH_STATUS_SUCCESS) { \
620 				got_result++; \
621 				switch_core_asr_get_result_headers(&ah, &headers, &flags); \
622 				if (headers) { \
623 					switch_event_destroy(&headers); \
624 				} \
625 				fst_check(xmlstr != NULL); \
626 				if (xmlstr != NULL) { \
627 					fst_asr_result = switch_core_strdup(fst_pool, xmlstr);\
628 				} \
629 				switch_safe_free(xmlstr); \
630 				break; \
631 			} \
632 		} \
633 		len = 160; \
634 	} \
635 	fst_check(got_result == 1); \
636 	fst_requires(switch_core_file_close(&file_handle) == SWITCH_STATUS_SUCCESS); \
637 }
638 
639 /**
640  * Pause an open recognizer.
641  *
642  * Test Requires:
643  *    switch_core_asr_pause(&ah) == SWITCH_STATUS_SUCCESS
644  */
645 #define fst_test_core_asr_pause() \
646 	fst_requires(fst_core > 1); \
647 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Pause recognizer\n"); \
648 	flags = SWITCH_ASR_FLAG_NONE; \
649 	fst_requires(switch_core_asr_pause(&ah) == SWITCH_STATUS_SUCCESS);
650 
651 /**
652  * Resumes an open recognizer
653  *
654  * Test Requires:
655  *    switch_core_asr_resume(&ah) == SWITCH_STATUS_SUCCESS
656  */
657 #define fst_test_core_asr_resume() \
658 	fst_requires(fst_core > 1); \
659 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Resume recognizer\n"); \
660 	flags = SWITCH_ASR_FLAG_NONE; \
661 	fst_requires(switch_core_asr_resume(&ah) == SWITCH_STATUS_SUCCESS);
662 
663 /**
664  * Close an open recognizer
665  *
666  * Test Requires:
667  *   switch_core_asr_close(&ah, flags) == SWITCH_STATUS_SUCCESS
668  */
669 #define fst_test_core_asr_close() \
670 	fst_requires(fst_core > 1); \
671 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Close recognizer\n"); \
672 	flags = SWITCH_ASR_FLAG_NONE; \
673 	fst_requires(switch_core_asr_close(&ah, &flags) == SWITCH_STATUS_SUCCESS); \
674 }
675 
676 
677 
678 /* PLAY AND DETECT SPEECH TEST MACROS - requires FST_SESSION */
679 
680 /**
681  * Define beginning of play_and_detect_speech recognizer test
682  *
683  * Defined vars:
684  *   const char *fst_asr_result;   Result of last recognition.  Allocated from test memory pool.
685  */
686 #define fst_play_and_detect_speech_test_begin() \
687 { \
688 	const char *fst_asr_result = NULL; \
689 	fst_requires(fst_core > 1);
690 
691 /**
692  * Use play_and_detect_speech APP to test recognizer
693  *
694  * Test Requires:
695  *   switch_ivr_displace_session(input_filename) == SWITCH_STATUS_SUCCESS
696  *   switch_core_session_execute_application(play_and_detect_speech) == SWITCH_STATUS_SUCCESS
697  *   mod_dptools is loaded
698  *
699  * Test Checks:
700  *   fst_asr_result != NULL after recognition completes
701  *
702  * Test Output:
703  *   fst_asr_result has the result from detect_speech_result channel variable.
704  *
705  * @param recognizer name of recognizer
706  * @param grammar recognizer grammar
707  * @param prompt_filename name of prompt to play
708  * @param input_filename name of file containing input audio for the recognizer
709  */
710 #define fst_play_and_detect_speech_app_test(recognizer, grammar, prompt_filename, input_filename) \
711 { \
712 	char *args = NULL; \
713 	fst_requires(fst_core > 1); \
714 	fst_requires_module("mod_dptools"); \
715 	switch_channel_set_variable(fst_channel, "detect_speech_result", ""); \
716 	fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "mrf") == SWITCH_STATUS_SUCCESS); \
717 	args = switch_core_session_sprintf(fst_session, "%s detect:%s %s", prompt_filename, recognizer, grammar); \
718 	fst_requires(switch_core_session_execute_application(fst_session, "play_and_detect_speech", args) == SWITCH_STATUS_SUCCESS); \
719 	fst_asr_result = switch_channel_get_variable(fst_channel, "detect_speech_result"); \
720 	fst_check(fst_asr_result != NULL); \
721 }
722 
723 /**
724  * Use play_and_detect_speech core function to test recognizer
725  *
726  * Test Requires:
727  *   switch_ivr_displace_session(input_filename) == SWITCH_STATUS_SUCCESS
728  *
729  * Test Checks:
730  *   fst_asr_result != NULL after recognition completes
731  *
732  * Test Output:
733  *   fst_asr_result has the result from detect_speech_result channel variable.
734  *
735  * @param recognizer name of recognizer
736  * @param grammar recognizer grammar
737  * @param prompt_filename name of prompt to play
738  * @param input_filename name of file containing input audio for the recognizer
739  * @param input_args input callback args
740  */
741 #define fst_play_and_detect_speech_test(recognizer, grammar, prompt_filename, input_filename, input_args) \
742 { \
743 	char *args = NULL; \
744 	fst_asr_result = NULL; \
745 	fst_requires(fst_core > 1); \
746 	fst_requires(switch_ivr_displace_session(fst_session, input_filename, 0, "mrf") == SWITCH_STATUS_SUCCESS); \
747 	switch_status_t status = switch_ivr_play_and_detect_speech(fst_session, prompt_filename, recognizer, grammar, (char **)&fst_asr_result, 0, input_args); \
748 	fst_check(fst_asr_result != NULL); \
749 }
750 
751 /**
752  * Define end of play_and_detect_speech recognizer test
753  */
754 #define fst_play_and_detect_speech_test_end() \
755 }
756 
757 
758 /**
759  * Compare extension dialplan apps and args with expected apps and args
760  * @param expected NULL terminated string array of app arg and names.
761  *     const char *expected[] = { "playback", "https://example.com/foo.wav", "park", "", NULL };
762  * @param extension the switch_caller_extension_t to check
763  */
764 #define fst_check_extension_apps(expected, extension) \
765 	{ \
766 		fst_xcheck(extension != NULL, "Missing extension\n"); \
767 		if (extension) { \
768 			int i; \
769 			switch_caller_application_t *cur_app = extension->applications; \
770 			for (i = 0; ; i += 2, cur_app = cur_app->next) { \
771 				int cur_app_num = i / 2 + 1; \
772 				if (!expected[i]) { \
773 					if (cur_app != NULL) { \
774 						fst_fail(switch_core_sprintf(fst_pool, "Unexpected application #%d \"%s\"\n", cur_app_num, cur_app->application_name)); \
775 					} \
776 					break; \
777 				} \
778 				fst_xcheck(cur_app != NULL, switch_core_sprintf(fst_pool, "Extension application #%d \"%s\" is missing", cur_app_num, expected[i])); \
779 				if (!cur_app) { \
780 					break; \
781 				} \
782 				fst_xcheck(cur_app->application_name && !strcmp(expected[i], cur_app->application_name), switch_core_sprintf(fst_pool, "Expected application #%d name is \"%s\", but is \"%s\"\n", cur_app_num, expected[i], cur_app->application_name)); \
783 				fst_xcheck(cur_app->application_data && !strcmp(expected[i + 1], cur_app->application_data), switch_core_sprintf(fst_pool, "Expected application #%d %s data is \"%s\", but is \"%s\"\n", cur_app_num, expected[i], expected[i + 1], cur_app->application_data)); \
784 			} \
785 		} \
786 	}
787 
788 
789 /**
790  * Inject DTMF into the session to be detected.
791  *
792  * Test Requires:
793  *   switch_api_execute(sched_api) == SWITCH_STATUS_SUCCESS
794  *   mod_commands is loaded
795  *
796  * @param when string describing when to send dtmf
797  * @param digits to send
798  */
799 #define fst_sched_recv_dtmf(when, digits) \
800 { \
801 	switch_stream_handle_t stream = { 0 }; \
802 	SWITCH_STANDARD_STREAM(stream); \
803 	fst_requires(fst_core > 1); \
804 	fst_requires_module("mod_commands"); \
805 	switch_status_t api_result = switch_api_execute("sched_api", switch_core_session_sprintf(fst_session, "%s none uuid_recv_dtmf %s %s", when, switch_core_session_get_uuid(fst_session), digits), NULL, &stream); \
806 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fst_session), SWITCH_LOG_INFO, "Injecting DTMF %s at %s\n", digits, when); \
807 	fst_requires(api_result == SWITCH_STATUS_SUCCESS); \
808 	switch_safe_free(stream.data); \
809 }
810 
811 #define fst_xml_start() \
812 	switch_stream_handle_t fst_xml_stream = { 0 }; \
813 	SWITCH_STANDARD_STREAM(fst_xml_stream);
814 	int fst_tag_children = 0;
815 	int fst_tag_body = 0;
816 
817 #define fst_xml_open_tag(tag_name) \
818 	fst_xml_stream.write_function(&fst_xml_stream, "<%s", #tag_name); \
819 	fst_tag_children++;
820 
821 #define fst_xml_attr(attr) \
822 	if (!zstr(attr)) fst_xml_stream.write_function(&fst_xml_stream, " %s=\"%s\"", #attr, attr);
823 
824 #define fst_xml_close_tag(tag_name) \
825 	--fst_tag_children; \
826 	if (fst_tag_children > 0 || fst_tag_body) { \
827 		fst_xml_stream.write_function(&fst_xml_stream, "</%s>", #tag_name); \
828 	} else { \
829 		fst_xml_stream.write_function(&fst_xml_stream, "/>"); \
830 	} \
831 	fst_tag_body = 0;
832 
833 #define fst_xml_body(body) \
834 	if (fst_tag_body) { \
835 		fst_xml_stream.write_function(&fst_xml_stream, "%s", body); \
836 	} else { \
837 		fst_tag_body = 1; \
838 		fst_xml_stream.write_function(&fst_xml_stream, ">%s", body); \
839 	}
840 
841 #define fst_xml_end() \
842 	switch_xml_parse_str_dynamic((char *)fst_xml_stream.data, SWITCH_FALSE);
843 
844 
845 /**
846  * Parse JSON file and save to varname
847  *
848  * Test Requires:
849  *   JSON file can be opened and parsed
850  *
851  * Test Output:
852  *   varname points at cJSON object
853  *
854  * @param varname name of var to store the resulting cJSON object
855  * @param file name of file to parse
856  */
857 #define fst_parse_json_file(varname, file) \
858 cJSON *varname = NULL; \
859 { \
860 	char *buf; \
861 	struct stat s; \
862 	int size; \
863 	int fd = open(file, O_RDONLY); \
864 	fst_requires(fd >= 0); \
865 	fstat(fd, &s); \
866 	switch_zmalloc(buf, s.st_size + 1); \
867 	fst_requires(buf); \
868 	size = read(fd, buf, s.st_size); \
869 	fst_requires(size == s.st_size); \
870 	close(fd); \
871 	varname = cJSON_Parse(buf); \
872 	free(buf); \
873 	fst_requires(varname); \
874 }
875 
876 #endif
877