offeranswer_tester.c 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/*
	liblinphone_tester - liblinphone test suite
	Copyright (C) 2013  Belledonne Communications SARL

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <sys/types.h>
#include <sys/stat.h>
#include "linphonecore.h"
#include "lpconfig.h"
#include "private.h"
#include "liblinphone_tester.h"

static int get_codec_position(const MSList *l, const char *mime_type, int rate){
	const MSList *elem;
	int i;
	for (elem=l, i=0; elem!=NULL; elem=elem->next,i++){
		PayloadType *pt=(PayloadType*)elem->data;
		if (strcasecmp(pt->mime_type, mime_type)==0 && pt->clock_rate==rate) return i;
	}
	return -1;
}

/*check basic things about codecs at startup: order and enablement*/
static void start_with_no_config(void){
	LinphoneCoreVTable vtable={0};
	LinphoneCore *lc=linphone_core_new(&vtable, NULL, NULL, NULL);
	const MSList *codecs=linphone_core_get_audio_codecs(lc);
	int opus_codec_pos;
	int speex_codec_pos=get_codec_position(codecs, "speex", 8000);
	int speex16_codec_pos=get_codec_position(codecs, "speex", 16000);
	PayloadType *pt;
	opus_codec_pos=get_codec_position(codecs, "opus", 48000);
46 47
	if (opus_codec_pos!=-1) BC_ASSERT_EQUAL(opus_codec_pos,0,int, "%d");
	BC_ASSERT_LOWER(speex16_codec_pos,speex_codec_pos,int,"%d");
48

49
	pt=linphone_core_find_payload_type(lc, "speex", 16000, 1);
50
	BC_ASSERT_PTR_NOT_NULL(pt);
51
	if (pt) {
52
		BC_ASSERT_TRUE(linphone_core_payload_type_enabled(lc, pt));
53 54 55 56 57 58 59
	}
	linphone_core_destroy(lc);
}

static void check_payload_type_numbers(LinphoneCall *call1, LinphoneCall *call2, int expected_number){
	const LinphoneCallParams *params=linphone_call_get_current_params(call1);
	const PayloadType *pt=linphone_call_params_get_used_audio_codec(params);
60
	BC_ASSERT_PTR_NOT_NULL(pt);
61
	if (pt){
62
		BC_ASSERT_EQUAL(linphone_core_get_payload_type_number(linphone_call_get_core(call1),pt),expected_number, int, "%d");
63 64 65
	}
	params=linphone_call_get_current_params(call2);
	pt=linphone_call_params_get_used_audio_codec(params);
66
	BC_ASSERT_PTR_NOT_NULL(pt);
67
	if (pt){
68
		BC_ASSERT_EQUAL(linphone_core_get_payload_type_number(linphone_call_get_core(call1),pt),expected_number, int, "%d");
69 70 71 72 73 74 75 76 77 78 79 80 81 82
	}
}

static void simple_call_with_different_codec_mappings(void) {
	int begin;
	int leaked_objects;
	LinphoneCoreManager* marie;
	LinphoneCoreManager* pauline;
	LinphoneCall *pauline_call;

	belle_sip_object_enable_leak_detector(TRUE);
	begin=belle_sip_object_get_object_count();

	marie = linphone_core_manager_new( "marie_rc");
83
	pauline = linphone_core_manager_new( "pauline_tcp_rc");
84

85 86
	disable_all_audio_codecs_except_one(marie->lc,"pcmu",-1);
	disable_all_audio_codecs_except_one(pauline->lc,"pcmu",-1);
87

88 89 90 91
	/*marie set a fantasy number to PCMU*/
	linphone_core_set_payload_type_number(marie->lc,
		linphone_core_find_payload_type(marie->lc, "PCMU", 8000, -1),
		104);
92 93

	BC_ASSERT_TRUE(call(marie,pauline));
94
	pauline_call=linphone_core_get_current_call(pauline->lc);
95
	BC_ASSERT_PTR_NOT_NULL(pauline_call);
96 97 98 99
	if (pauline_call){
		LinphoneCallParams *params;
		check_payload_type_numbers(linphone_core_get_current_call(marie->lc), pauline_call, 104);
		/*make a reinvite in the other direction*/
100
		linphone_core_update_call(pauline->lc, pauline_call,
101 102
			params=linphone_core_create_call_params(pauline->lc, pauline_call));
		linphone_call_params_unref(params);
103 104 105 106
		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallUpdating,1));
		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallUpdatedByRemote,1));
		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2));
		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2));
107 108 109 110 111 112 113 114 115
		/*payload type numbers shall remain the same*/
		check_payload_type_numbers(linphone_core_get_current_call(marie->lc), pauline_call, 104);
	}

	end_call(marie,pauline);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);

	leaked_objects=belle_sip_object_get_object_count()-begin;
116
	BC_ASSERT_EQUAL(leaked_objects, 0, int, "%d");
117 118 119 120 121 122 123 124 125 126 127 128 129
	if (leaked_objects>0){
		belle_sip_object_dump_active_objects();
	}
}

static void call_failed_because_of_codecs(void) {
	int begin,leaked_objects;

	belle_sip_object_enable_leak_detector(TRUE);
	begin=belle_sip_object_get_object_count();

	{
		LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
130
		LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
131 132 133 134 135 136
		LinphoneCall* out_call;

		disable_all_audio_codecs_except_one(marie->lc,"pcmu",-1);
		disable_all_audio_codecs_except_one(pauline->lc,"pcma",-1);
		out_call = linphone_core_invite_address(pauline->lc,marie->identity);
		linphone_call_ref(out_call);
137
		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1));
138 139

		/*flexisip will retain the 488 until the "urgent reply" timeout (I.E 5s) arrives.*/
140 141 142 143
		BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,7000));
		BC_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonNotAcceptable, int, "%d");
		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0, int, "%d");
		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallReleased,0, int, "%d");
144 145 146 147 148 149

		linphone_call_unref(out_call);
		linphone_core_manager_destroy(marie);
		linphone_core_manager_destroy(pauline);
	}
	leaked_objects=belle_sip_object_get_object_count()-begin;
150
	BC_ASSERT_EQUAL(leaked_objects, 0, int, "%d");
151 152 153 154 155 156
	if (leaked_objects>0){
		belle_sip_object_dump_active_objects();
	}
}


157
static void profile_call_base(bool_t avpf1, LinphoneMediaEncryption srtp1,bool_t avpf2, LinphoneMediaEncryption srtp2, bool_t encryption_mandatory, const char *expected_profile) {
158
	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
159
	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
160 161 162 163 164 165 166 167 168 169 170 171 172
	LinphoneProxyConfig *lpc;
	const LinphoneCallParams *params;

	if (avpf1) {
		linphone_core_get_default_proxy(marie->lc, &lpc);
		linphone_proxy_config_enable_avpf(lpc, TRUE);
		linphone_proxy_config_set_avpf_rr_interval(lpc, 3);
	}
	if (avpf2) {
		linphone_core_get_default_proxy(pauline->lc, &lpc);
		linphone_proxy_config_enable_avpf(lpc, TRUE);
		linphone_proxy_config_set_avpf_rr_interval(lpc, 3);
	}
173 174 175 176 177 178

	if (encryption_mandatory) {
		linphone_core_set_media_encryption_mandatory(marie->lc,TRUE);
		linphone_core_set_media_encryption_mandatory(pauline->lc,TRUE);
	}

jehan's avatar
jehan committed
179 180 181 182 183 184
	if (linphone_core_media_encryption_supported(marie->lc, srtp1)) {
		linphone_core_set_media_encryption(marie->lc, srtp1);
	} else {
		ms_message("Unsupported [%s] encryption type, cannot test",linphone_media_encryption_to_string(srtp1));
		goto end;

185
	}
jehan's avatar
jehan committed
186 187 188 189 190 191
	if (linphone_core_media_encryption_supported(pauline->lc, srtp2)) {
		linphone_core_set_media_encryption(pauline->lc, srtp2);
	}else {
		ms_message("Unsupported [%s] encryption type, cannot test",linphone_media_encryption_to_string(srtp2));
		goto end;

192 193
	}

194 195 196
	BC_ASSERT_TRUE(call(marie, pauline));
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1));
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1));
197 198
	if (linphone_core_get_current_call(marie->lc)) {
		params = linphone_call_get_current_params(linphone_core_get_current_call(marie->lc));
199
		BC_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile);
200 201 202
	}
	if (linphone_core_get_current_call(pauline->lc)) {
		params = linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc));
203
		BC_ASSERT_STRING_EQUAL(linphone_call_params_get_rtp_profile(params), expected_profile);
204
	}
205 206

	linphone_core_terminate_all_calls(marie->lc);
207 208 209 210
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1));
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1));
	BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallConnected, 1, int, "%d");
	BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallConnected, 1, int, "%d");
jehan's avatar
jehan committed
211
end:
212 213 214 215
	linphone_core_manager_destroy(pauline);
	linphone_core_manager_destroy(marie);
}

216 217 218
static void profile_call(bool_t avpf1, LinphoneMediaEncryption srtp1, bool_t avpf2, LinphoneMediaEncryption srtp2, const char *expected_profile) {
	return profile_call_base(avpf1, srtp1, avpf2,srtp2,FALSE,expected_profile);
}
219
static void avp_to_avp_call(void) {
jehan's avatar
jehan committed
220
	profile_call(FALSE, LinphoneMediaEncryptionNone, FALSE, LinphoneMediaEncryptionNone, "RTP/AVP");
221 222 223
}

static void avp_to_avpf_call(void) {
jehan's avatar
jehan committed
224
	profile_call(FALSE, LinphoneMediaEncryptionNone, TRUE, LinphoneMediaEncryptionNone, "RTP/AVP");
225 226 227
}

static void avp_to_savp_call(void) {
jehan's avatar
jehan committed
228
	profile_call(FALSE, LinphoneMediaEncryptionNone, FALSE, LinphoneMediaEncryptionSRTP, "RTP/AVP");
229 230 231
}

static void avp_to_savpf_call(void) {
jehan's avatar
jehan committed
232
	profile_call(FALSE, LinphoneMediaEncryptionNone, TRUE, LinphoneMediaEncryptionSRTP, "RTP/AVP");
233 234 235
}

static void avpf_to_avp_call(void) {
jehan's avatar
jehan committed
236
	profile_call(TRUE, LinphoneMediaEncryptionNone, FALSE, LinphoneMediaEncryptionNone, "RTP/AVPF");
237 238 239
}

static void avpf_to_avpf_call(void) {
jehan's avatar
jehan committed
240
	profile_call(TRUE, LinphoneMediaEncryptionNone, TRUE, LinphoneMediaEncryptionNone, "RTP/AVPF");
241 242 243
}

static void avpf_to_savp_call(void) {
jehan's avatar
jehan committed
244
	profile_call(TRUE, LinphoneMediaEncryptionNone, FALSE, LinphoneMediaEncryptionSRTP, "RTP/AVPF");
245 246 247
}

static void avpf_to_savpf_call(void) {
jehan's avatar
jehan committed
248
	profile_call(TRUE, LinphoneMediaEncryptionNone, TRUE, LinphoneMediaEncryptionSRTP, "RTP/AVPF");
249 250 251
}

static void savp_to_avp_call(void) {
jehan's avatar
jehan committed
252
	profile_call(FALSE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneMediaEncryptionNone, "RTP/SAVP");
253 254 255
}

static void savp_to_avpf_call(void) {
jehan's avatar
jehan committed
256
	profile_call(FALSE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneMediaEncryptionNone, "RTP/SAVP");
257 258 259
}

static void savp_to_savp_call(void) {
jehan's avatar
jehan committed
260
	profile_call(FALSE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneMediaEncryptionSRTP, "RTP/SAVP");
261 262 263
}

static void savp_to_savpf_call(void) {
jehan's avatar
jehan committed
264
	profile_call(FALSE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneMediaEncryptionSRTP, "RTP/SAVP");
265 266 267
}

static void savpf_to_avp_call(void) {
jehan's avatar
jehan committed
268
	profile_call(TRUE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneMediaEncryptionNone, "RTP/SAVPF");
269 270 271
}

static void savpf_to_avpf_call(void) {
jehan's avatar
jehan committed
272
	profile_call(TRUE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneMediaEncryptionNone, "RTP/SAVPF");
273 274 275
}

static void savpf_to_savp_call(void) {
jehan's avatar
jehan committed
276
	profile_call(TRUE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneMediaEncryptionSRTP, "RTP/SAVPF");
277 278 279
}

static void savpf_to_savpf_call(void) {
jehan's avatar
jehan committed
280 281 282 283 284 285
	profile_call(TRUE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneMediaEncryptionSRTP, "RTP/SAVPF");
}

static void savpf_dtls_to_savpf_dtls_call(void) {
	profile_call(TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneMediaEncryptionDTLS, "UDP/TLS/RTP/SAVPF");
}
286 287 288 289 290 291
static void savpf_dtls_to_savpf_dtls_encryption_mandatory_call(void) {
	profile_call_base(TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneMediaEncryptionDTLS, TRUE, "UDP/TLS/RTP/SAVPF");
}
static void savpf_dtls_to_savpf_encryption_mandatory_call(void) {
	/*profile_call_base(TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneMediaEncryptionSRTP, TRUE, "UDP/TLS/RTP/SAVPF"); not sure of result*/
}
jehan's avatar
jehan committed
292 293 294 295 296 297 298

static void savpf_dtls_to_savpf_call(void) {
	profile_call(TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneMediaEncryptionSRTP, "UDP/TLS/RTP/SAVPF");
}

static void savpf_dtls_to_avpf_call(void) {
	profile_call(TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneMediaEncryptionNone, "UDP/TLS/RTP/SAVPF");
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
#ifdef VIDEO_ENABLED
static LinphonePayloadType * configure_core_for_avpf_and_video(LinphoneCore *lc) {
	LinphoneProxyConfig *lpc;
	LinphonePayloadType *lpt;
	LinphoneVideoPolicy policy = { 0 };

	policy.automatically_initiate = TRUE;
	policy.automatically_accept = TRUE;
	linphone_core_get_default_proxy(lc, &lpc);
	linphone_proxy_config_enable_avpf(lpc, TRUE);
	linphone_proxy_config_set_avpf_rr_interval(lpc, 3);
	linphone_core_set_video_device(lc, "StaticImage: Static picture");
	linphone_core_enable_video_capture(lc, TRUE);
	linphone_core_enable_video_display(lc, TRUE);
	linphone_core_set_video_policy(lc, &policy);
	lpt = linphone_core_find_payload_type(lc, "VP8", 90000, -1);
	if (lpt == NULL) {
		ms_warning("VP8 codec not available.");
	} else {
		disable_all_video_codecs_except_one(lc, "VP8");
	}
	return lpt;
}

static void check_avpf_features(LinphoneCore *lc, unsigned char expected_features) {
	LinphoneCall *lcall = linphone_core_get_current_call(lc);
	BC_ASSERT_PTR_NOT_NULL(lcall);
	if (lcall != NULL) {
		SalStreamDescription *desc = sal_media_description_find_stream(lcall->resultdesc, SalProtoRtpAvpf, SalVideo);
		BC_ASSERT_PTR_NOT_NULL(desc);
		if (desc != NULL) {
			BC_ASSERT_PTR_NOT_NULL(desc->payloads);
			if (desc->payloads) {
				PayloadType *pt = (PayloadType *)desc->payloads->data;
				BC_ASSERT_STRING_EQUAL(pt->mime_type, "VP8");
				BC_ASSERT_EQUAL(pt->avpf.features, expected_features, int, "%d");
			}
		}
	}
}

static void compatible_avpf_features(void) {
	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
	LinphonePayloadType *lpt;
Simon Morlat's avatar
Simon Morlat committed
346
	bool_t call_ok;
347 348 349 350

	if (configure_core_for_avpf_and_video(marie->lc) == NULL) goto end;
	lpt = configure_core_for_avpf_and_video(pauline->lc);

Simon Morlat's avatar
Simon Morlat committed
351 352
	BC_ASSERT_TRUE((call_ok=call(marie, pauline)));
	if (!call_ok) goto end;
353

354 355 356 357 358
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1));
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1));
	check_avpf_features(marie->lc, lpt->avpf.features);
	check_avpf_features(pauline->lc, lpt->avpf.features);

Simon Morlat's avatar
Simon Morlat committed
359
	end_call(marie,pauline);
360 361 362 363 364 365 366 367 368
end:
	linphone_core_manager_destroy(pauline);
	linphone_core_manager_destroy(marie);
}

static void incompatible_avpf_features(void) {
	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
	LinphonePayloadType *lpt;
Simon Morlat's avatar
Simon Morlat committed
369
	bool_t call_ok;
370 371 372 373 374

	if (configure_core_for_avpf_and_video(marie->lc) == NULL) goto end;
	lpt = configure_core_for_avpf_and_video(pauline->lc);
	lpt->avpf.features = PAYLOAD_TYPE_AVPF_NONE;

Simon Morlat's avatar
Simon Morlat committed
375 376
	BC_ASSERT_TRUE(call_ok=call(marie, pauline));
	if (!call_ok) goto end;
377 378 379 380 381
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1));
	BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1));
	check_avpf_features(marie->lc, PAYLOAD_TYPE_AVPF_NONE);
	check_avpf_features(pauline->lc, PAYLOAD_TYPE_AVPF_NONE);

Simon Morlat's avatar
Simon Morlat committed
382
	end_call(marie,pauline);
383 384 385 386 387 388
end:
	linphone_core_manager_destroy(pauline);
	linphone_core_manager_destroy(marie);
}
#endif

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
static test_t offeranswer_tests[] = {
	{ "Start with no config", start_with_no_config },
	{ "Call failed because of codecs", call_failed_because_of_codecs },
	{ "Simple call with different codec mappings", simple_call_with_different_codec_mappings},
	{ "AVP to AVP call", avp_to_avp_call },
	{ "AVP to AVPF call", avp_to_avpf_call },
	{ "AVP to SAVP call", avp_to_savp_call },
	{ "AVP to SAVPF call", avp_to_savpf_call },
	{ "AVPF to AVP call", avpf_to_avp_call },
	{ "AVPF to AVPF call", avpf_to_avpf_call },
	{ "AVPF to SAVP call", avpf_to_savp_call },
	{ "AVPF to SAVPF call", avpf_to_savpf_call },
	{ "SAVP to AVP call", savp_to_avp_call },
	{ "SAVP to AVPF call", savp_to_avpf_call },
	{ "SAVP to SAVP call", savp_to_savp_call },
	{ "SAVP to SAVPF call", savp_to_savpf_call },
	{ "SAVPF to AVP call", savpf_to_avp_call },
	{ "SAVPF to AVPF call", savpf_to_avpf_call },
	{ "SAVPF to SAVP call", savpf_to_savp_call },
	{ "SAVPF to SAVPF call", savpf_to_savpf_call },
jehan's avatar
jehan committed
409
	{ "SAVPF/DTLS to SAVPF/DTLS call", savpf_dtls_to_savpf_dtls_call},
410
	{ "SAVPF/DTLS to SAVPF/DTLS encryption mandatory call", savpf_dtls_to_savpf_dtls_encryption_mandatory_call},
jehan's avatar
jehan committed
411
	{ "SAVPF/DTLS to SAVPF call", savpf_dtls_to_savpf_call},
412
	{ "SAVPF/DTLS to SAVPF encryption mandatory call", savpf_dtls_to_savpf_encryption_mandatory_call},
jehan's avatar
jehan committed
413
	{ "SAVPF/DTLS to AVPF call", savpf_dtls_to_avpf_call},
414 415 416 417
#ifdef VIDEO_ENABLED
	{ "Compatible AVPF features", compatible_avpf_features },
	{ "Incompatible AVPF features", incompatible_avpf_features },
#endif
418 419
};

420 421
test_suite_t offeranswer_test_suite = {"Offer-answer", NULL, NULL, liblinphone_tester_before_each, NULL,
									   sizeof(offeranswer_tests) / sizeof(offeranswer_tests[0]), offeranswer_tests};