1 /*
2  * Copyright (c) 2022 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*************************************************************************************************/
8 /*                                        Dependencies                                           */
9 /*************************************************************************************************/
10 #include <zephyr/ztest.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/atomic.h>
13 #include <string.h>
14 
15 #include <zephyr/modem/chat.h>
16 #include <modem_backend_mock.h>
17 
18 /*************************************************************************************************/
19 /*                                         Instances                                             */
20 /*************************************************************************************************/
21 static struct modem_chat cmd;
22 static uint8_t cmd_delimiter[] = {'\r', '\n'};
23 static uint8_t cmd_receive_buf[128];
24 static uint8_t *cmd_argv[32];
25 static uint32_t cmd_user_data = 0x145212;
26 
27 static struct modem_backend_mock mock;
28 static uint8_t mock_rx_buf[128];
29 static uint8_t mock_tx_buf[128];
30 static struct modem_pipe *mock_pipe;
31 
32 /*************************************************************************************************/
33 /*                                        Track callbacks                                        */
34 /*************************************************************************************************/
35 #define MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT		 (0)
36 #define MODEM_CHAT_UTEST_ON_CREG_CALLED_BIT		 (1)
37 #define MODEM_CHAT_UTEST_ON_CGREG_CALLED_BIT		 (2)
38 #define MODEM_CHAT_UTEST_ON_QENG_SERVINGCELL_CALLED_BIT	 (3)
39 #define MODEM_CHAT_UTEST_ON_NO_CARRIER_CALLED_BIT	 (4)
40 #define MODEM_CHAT_UTEST_ON_ERROR_CALLED_BIT		 (5)
41 #define MODEM_CHAT_UTEST_ON_RDY_CALLED_BIT		 (6)
42 #define MODEM_CHAT_UTEST_ON_APP_RDY_CALLED_BIT		 (7)
43 #define MODEM_CHAT_UTEST_ON_NORMAL_POWER_DOWN_CALLED_BIT (8)
44 #define MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT		 (9)
45 #define MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_CALLED_BIT	 (10)
46 #define MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_ANY_CALLED_BIT	 (11)
47 
48 static atomic_t callback_called;
49 
50 /*************************************************************************************************/
51 /*                                  Script callbacks args copy                                   */
52 /*************************************************************************************************/
53 static uint8_t argv_buffers[32][128];
54 static uint16_t argc_buffers;
55 
clone_args(char ** argv,uint16_t argc)56 static void clone_args(char **argv, uint16_t argc)
57 {
58 	argc_buffers = argc;
59 
60 	for (uint16_t i = 0; i < argc; i++) {
61 		memcpy(argv_buffers[i], argv[i], strlen(argv[i]) + 1);
62 	}
63 }
64 
65 /*************************************************************************************************/
66 /*                                   Script match callbacks                                      */
67 /*************************************************************************************************/
on_imei(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)68 static void on_imei(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
69 {
70 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT);
71 	clone_args(argv, argc);
72 }
73 
on_creg(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)74 static void on_creg(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
75 {
76 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CREG_CALLED_BIT);
77 	clone_args(argv, argc);
78 }
79 
on_cgreg(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)80 static void on_cgreg(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
81 {
82 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CGREG_CALLED_BIT);
83 	clone_args(argv, argc);
84 }
85 
on_qeng_serving_cell(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)86 static void on_qeng_serving_cell(struct modem_chat *cmd, char **argv, uint16_t argc,
87 				 void *user_data)
88 {
89 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_QENG_SERVINGCELL_CALLED_BIT);
90 	clone_args(argv, argc);
91 }
92 
on_no_carrier(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)93 static void on_no_carrier(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
94 {
95 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_NO_CARRIER_CALLED_BIT);
96 	clone_args(argv, argc);
97 }
98 
on_error(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)99 static void on_error(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
100 {
101 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_ERROR_CALLED_BIT);
102 	clone_args(argv, argc);
103 }
104 
on_rdy(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)105 static void on_rdy(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
106 {
107 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_RDY_CALLED_BIT);
108 	clone_args(argv, argc);
109 }
110 
on_app_rdy(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)111 static void on_app_rdy(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
112 {
113 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_APP_RDY_CALLED_BIT);
114 	clone_args(argv, argc);
115 }
116 
on_normal_power_down(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)117 static void on_normal_power_down(struct modem_chat *cmd, char **argv, uint16_t argc,
118 				 void *user_data)
119 {
120 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_NORMAL_POWER_DOWN_CALLED_BIT);
121 	clone_args(argv, argc);
122 }
123 
on_cmgl_partial(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)124 static void on_cmgl_partial(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
125 {
126 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_CALLED_BIT);
127 	clone_args(argv, argc);
128 }
129 
on_cmgl_any_partial(struct modem_chat * cmd,char ** argv,uint16_t argc,void * user_data)130 static void on_cmgl_any_partial(struct modem_chat *cmd, char **argv, uint16_t argc,
131 				void *user_data)
132 {
133 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_ANY_CALLED_BIT);
134 	clone_args(argv, argc);
135 }
136 
137 /*************************************************************************************************/
138 /*                                       Script callback                                         */
139 /*************************************************************************************************/
140 static enum modem_chat_script_result script_result;
141 static void *script_result_user_data;
142 
on_script_result(struct modem_chat * cmd,enum modem_chat_script_result result,void * user_data)143 static void on_script_result(struct modem_chat *cmd, enum modem_chat_script_result result,
144 			     void *user_data)
145 {
146 	atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
147 	script_result = result;
148 	script_result_user_data = user_data;
149 }
150 
151 /*************************************************************************************************/
152 /*                                            Script                                             */
153 /*************************************************************************************************/
154 MODEM_CHAT_MATCH_DEFINE(ok_match, "OK", "", NULL);
155 MODEM_CHAT_MATCH_DEFINE(imei_match, "", "", on_imei);
156 MODEM_CHAT_MATCH_DEFINE(creg_match, "CREG: ", ",", on_creg);
157 MODEM_CHAT_MATCH_DEFINE(cgreg_match, "CGREG: ", ",", on_cgreg);
158 MODEM_CHAT_MATCH_DEFINE(qeng_servinc_cell_match, "+QENG: \"servingcell\",", ",",
159 			on_qeng_serving_cell);
160 
161 MODEM_CHAT_MATCHES_DEFINE(unsol_matches, MODEM_CHAT_MATCH("RDY", "", on_rdy),
162 			  MODEM_CHAT_MATCH("APP RDY", "", on_app_rdy),
163 			  MODEM_CHAT_MATCH("NORMAL POWER DOWN", "", on_normal_power_down));
164 
165 MODEM_CHAT_SCRIPT_CMDS_DEFINE(
166 	script_cmds, MODEM_CHAT_SCRIPT_CMD_RESP("AT", ok_match),
167 	MODEM_CHAT_SCRIPT_CMD_RESP("ATE0", ok_match),
168 	MODEM_CHAT_SCRIPT_CMD_RESP("IMEI?", imei_match), MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
169 	MODEM_CHAT_SCRIPT_CMD_RESP("AT+CREG?;+CGREG?", creg_match),
170 	MODEM_CHAT_SCRIPT_CMD_RESP("", cgreg_match), MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
171 	MODEM_CHAT_SCRIPT_CMD_RESP("AT+QENG=\"servingcell\"", qeng_servinc_cell_match),
172 	MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match));
173 
174 MODEM_CHAT_MATCHES_DEFINE(abort_matches, MODEM_CHAT_MATCH("NO CARRIER", "", on_no_carrier),
175 			  MODEM_CHAT_MATCH("ERROR ", ",:", on_error));
176 
177 MODEM_CHAT_SCRIPT_DEFINE(script, script_cmds, abort_matches, on_script_result, 4);
178 
179 /*************************************************************************************************/
180 /*                             Script implementing partial matches                               */
181 /*************************************************************************************************/
182 MODEM_CHAT_MATCHES_DEFINE(
183 	cmgl_matches,
184 	MODEM_CHAT_MATCH_INITIALIZER("+CMGL: ", ",", on_cmgl_partial, false, true),
185 	MODEM_CHAT_MATCH_INITIALIZER("", "", on_cmgl_any_partial, false, true),
186 	MODEM_CHAT_MATCH_INITIALIZER("OK", "", NULL, false, false)
187 );
188 
189 MODEM_CHAT_SCRIPT_CMDS_DEFINE(
190 	script_partial_cmds,
191 	MODEM_CHAT_SCRIPT_CMD_RESP_MULT("AT+CMGL=4", cmgl_matches),
192 );
193 
194 MODEM_CHAT_SCRIPT_DEFINE(script_partial, script_partial_cmds, abort_matches, on_script_result, 4);
195 
196 /*************************************************************************************************/
197 /*                         Script containing timeout script chat command                         */
198 /*************************************************************************************************/
199 MODEM_CHAT_SCRIPT_CMDS_DEFINE(
200 	script_timeout_cmd_cmds,
201 	MODEM_CHAT_SCRIPT_CMD_RESP("AT", ok_match),
202 	MODEM_CHAT_SCRIPT_CMD_RESP_NONE("", 4000),
203 	MODEM_CHAT_SCRIPT_CMD_RESP("AT", ok_match),
204 );
205 
206 MODEM_CHAT_SCRIPT_DEFINE(script_timeout_cmd, script_timeout_cmd_cmds, abort_matches,
207 			 on_script_result, 10);
208 
209 /*************************************************************************************************/
210 /*                           Small echo script and mock transactions                             */
211 /*************************************************************************************************/
212 static const uint8_t at_echo_data[] = {'A', 'T', '\r', '\n'};
213 static const struct modem_backend_mock_transaction at_echo_transaction = {
214 	.get = at_echo_data,
215 	.get_size = sizeof(at_echo_data),
216 	.put = at_echo_data,
217 	.put_size = sizeof(at_echo_data),
218 };
219 
220 static const uint8_t at_echo_error_data[] = {'E', 'R', 'R', 'O', 'R', ' ', '1', '\r', '\n'};
221 static const struct modem_backend_mock_transaction at_echo_error_transaction = {
222 	.get = at_echo_data,
223 	.get_size = sizeof(at_echo_data),
224 	.put = at_echo_error_data,
225 	.put_size = sizeof(at_echo_error_data),
226 };
227 
228 MODEM_CHAT_MATCH_DEFINE(at_match, "AT", "", NULL);
229 
230 MODEM_CHAT_SCRIPT_CMDS_DEFINE(
231 	script_echo_cmds,
232 	MODEM_CHAT_SCRIPT_CMD_RESP("AT", at_match),
233 );
234 
235 MODEM_CHAT_SCRIPT_DEFINE(script_echo, script_echo_cmds, abort_matches, on_script_result, 4);
236 
237 /*************************************************************************************************/
238 /*                                      Script responses                                         */
239 /*************************************************************************************************/
240 static const char at_response[] = "AT\r\n";
241 static const char ok_response[] = "OK\r\n";
242 static const char imei_response[] = "23412354123123\r\n";
243 static const char creg_response[] = "CREG: 1,2\r\n";
244 static const char cgreg_response[] = "CGREG: 10,43\r\n";
245 
246 static const char qeng_servinc_cell_response[] = "+QENG: \"servingcell\",\"NOCONN\",\"GSM\",260"
247 						 ",03,E182,AEAD,52,32,2,-68,255,255,0,38,38,1,,"
248 						 ",,,,,,,,\r\n";
249 
250 static const char cmgl_response_0[] = "+CMGL: 1,1,,50\r\n";
251 static const char cmgl_response_1[] = "07911326060032F064A9542954\r\n";
252 
253 /*************************************************************************************************/
254 /*                                         Test setup                                            */
255 /*************************************************************************************************/
test_modem_chat_setup(void)256 static void *test_modem_chat_setup(void)
257 {
258 	const struct modem_chat_config cmd_config = {
259 		.user_data = &cmd_user_data,
260 		.receive_buf = cmd_receive_buf,
261 		.receive_buf_size = ARRAY_SIZE(cmd_receive_buf),
262 		.delimiter = cmd_delimiter,
263 		.delimiter_size = ARRAY_SIZE(cmd_delimiter),
264 		.filter = NULL,
265 		.filter_size = 0,
266 		.argv = cmd_argv,
267 		.argv_size = ARRAY_SIZE(cmd_argv),
268 		.unsol_matches = unsol_matches,
269 		.unsol_matches_size = ARRAY_SIZE(unsol_matches),
270 	};
271 
272 	zassert(modem_chat_init(&cmd, &cmd_config) == 0, "Failed to init modem CMD");
273 
274 	const struct modem_backend_mock_config mock_config = {
275 		.rx_buf = mock_rx_buf,
276 		.rx_buf_size = ARRAY_SIZE(mock_rx_buf),
277 		.tx_buf = mock_tx_buf,
278 		.tx_buf_size = ARRAY_SIZE(mock_tx_buf),
279 		.limit = 8,
280 	};
281 
282 	mock_pipe = modem_backend_mock_init(&mock, &mock_config);
283 	zassert(modem_pipe_open(mock_pipe, K_SECONDS(10)) == 0, "Failed to open mock pipe");
284 	zassert(modem_chat_attach(&cmd, mock_pipe) == 0, "Failed to attach pipe mock to modem CMD");
285 	return NULL;
286 }
287 
test_modem_chat_before(void * f)288 static void test_modem_chat_before(void *f)
289 {
290 	/* Reset callback called */
291 	atomic_set(&callback_called, 0);
292 
293 	/* Reset mock pipe */
294 	modem_backend_mock_reset(&mock);
295 }
296 
test_modem_chat_after(void * f)297 static void test_modem_chat_after(void *f)
298 {
299 	/* Abort script */
300 	modem_chat_script_abort(&cmd);
301 
302 	k_msleep(100);
303 }
304 
305 /*************************************************************************************************/
306 /*                                          Buffers                                              */
307 /*************************************************************************************************/
308 static uint8_t buffer[4096];
309 
310 /*************************************************************************************************/
311 /*                                           Tests                                               */
312 /*************************************************************************************************/
ZTEST(modem_chat,test_script_no_error)313 ZTEST(modem_chat, test_script_no_error)
314 {
315 	bool called;
316 
317 	zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
318 	k_msleep(100);
319 
320 	/*
321 	 * Script sends "AT\r\n"
322 	 * Modem responds "AT\r\n"
323 	 * Modem responds "OK\r\n"
324 	 */
325 
326 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
327 	zassert_true(memcmp(buffer, "AT\r", sizeof("AT\r") - 1) == 0,
328 		     "Request not sent as expected");
329 
330 	modem_backend_mock_put(&mock, at_response, sizeof(at_response) - 1);
331 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
332 
333 	k_msleep(100);
334 
335 	/*
336 	 * Script sends "ATE0\r\n"
337 	 * Modem responds "OK\r\n"
338 	 */
339 
340 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
341 	zassert_true(memcmp(buffer, "ATE0\r\n", sizeof("ATE0\r\n") - 1) == 0,
342 		     "Request not sent as expected");
343 
344 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
345 
346 	k_msleep(100);
347 
348 	/*
349 	 * Script sends "IMEI?\r\n"
350 	 * Modem responds "23412354123123\r\n"
351 	 * Modem responds "OK\r\n"
352 	 */
353 
354 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
355 	zassert_true(memcmp(buffer, "IMEI?\r\n", sizeof("IMEI?\r\n") - 1) == 0,
356 		     "Request not sent as expected");
357 
358 	modem_backend_mock_put(&mock, imei_response, sizeof(imei_response) - 1);
359 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
360 
361 	k_msleep(100);
362 
363 	zassert_true(atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT) == true,
364 		     "Expected IMEI callback called");
365 
366 	zassert_true(argv_buffers[0][0] == '\0', "Unexpected argv");
367 	zassert_true(memcmp(argv_buffers[1], "23412354123123", sizeof("23412354123123")) == 0,
368 		     "Unexpected argv");
369 
370 	zassert_true(argc_buffers == 2, "Unexpected argc");
371 
372 	/*
373 	 * Script sends "AT+CREG?;+CGREG?\r\n"
374 	 * Modem responds "CREG: 1,2\r\n"
375 	 * Modem responds "CGREG: 1,2\r\n"
376 	 * Modem responds "OK\r\n"
377 	 */
378 
379 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
380 	zassert_true(memcmp(buffer, "AT+CREG?;+CGREG?\r\n", sizeof("AT+CREG?;+CGREG?\r\n") - 1) ==
381 			     0,
382 		     "Request not sent as expected");
383 
384 	modem_backend_mock_put(&mock, creg_response, sizeof(creg_response) - 1);
385 
386 	k_msleep(100);
387 
388 	zassert_true(memcmp(argv_buffers[0], "CREG: ", sizeof("CREG: ")) == 0, "Unexpected argv");
389 	zassert_true(memcmp(argv_buffers[1], "1", sizeof("1")) == 0, "Unexpected argv");
390 	zassert_true(memcmp(argv_buffers[2], "2", sizeof("2")) == 0, "Unexpected argv");
391 	zassert_true(argc_buffers == 3, "Unexpected argc");
392 	modem_backend_mock_put(&mock, cgreg_response, sizeof(cgreg_response) - 1);
393 
394 	k_msleep(100);
395 
396 	zassert_true(memcmp(argv_buffers[0], "CGREG: ", sizeof("CGREG: ")) == 0, "Unexpected argv");
397 	zassert_true(memcmp(argv_buffers[1], "10", sizeof("10")) == 0, "Unexpected argv");
398 	zassert_true(memcmp(argv_buffers[2], "43", sizeof("43")) == 0, "Unexpected argv");
399 	zassert_true(argc_buffers == 3, "Unexpected argc");
400 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
401 
402 	k_msleep(100);
403 
404 	/*
405 	 * Script sends "AT+QENG=\"servingcell\"\r\n"
406 	 * Modem responds qeng_servinc_cell_response (long string)
407 	 * Modem responds "OK\r\n"
408 	 */
409 
410 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
411 	zassert_true(memcmp(buffer, "AT+QENG=\"servingcell\"\r\n",
412 			    sizeof("AT+QENG=\"servingcell\"\r\n") - 1) == 0,
413 		     "Request not sent as expected");
414 
415 	modem_backend_mock_put(&mock, qeng_servinc_cell_response,
416 			       sizeof(qeng_servinc_cell_response) - 1);
417 
418 	k_msleep(100);
419 
420 	zassert_true(memcmp(argv_buffers[0], "+QENG: \"servingcell\",",
421 			    sizeof("+QENG: \"servingcell\",")) == 0,
422 		     "Unexpected argv");
423 
424 	zassert_true(memcmp(argv_buffers[1], "\"NOCONN\"", sizeof("\"NOCONN\"")) == 0,
425 		     "Unexpected argv");
426 
427 	zassert_true(memcmp(argv_buffers[10], "-68", sizeof("-68")) == 0, "Unexpected argv");
428 	zassert_true(argv_buffers[25][0] == '\0', "Unexpected argv");
429 	zassert_true(argc_buffers == 26, "Unexpected argc");
430 
431 	/*
432 	 * Script ends after modem responds OK
433 	 */
434 
435 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
436 	zassert_true(called == false, "Script callback should not have been called yet");
437 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
438 
439 	k_msleep(100);
440 
441 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
442 	zassert_true(called == true, "Script callback should have been called");
443 	zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
444 		     "Script result should be SUCCESS");
445 	zassert_true(script_result_user_data == &cmd_user_data,
446 		     "Script result callback user data is incorrect");
447 }
448 
ZTEST(modem_chat,test_start_script_twice_then_abort)449 ZTEST(modem_chat, test_start_script_twice_then_abort)
450 {
451 	bool called;
452 
453 	zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
454 
455 	k_msleep(100);
456 
457 	zassert_true(modem_chat_script_run(&cmd, &script) == -EBUSY,
458 		     "Started new script while script is running");
459 
460 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
461 	zassert_true(called == false, "Script callback should not have been called yet");
462 	modem_chat_script_abort(&cmd);
463 
464 	k_msleep(100);
465 
466 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
467 	zassert_true(called == true, "Script callback should have been called");
468 	zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_ABORT,
469 		     "Script result should be ABORT");
470 	zassert_true(script_result_user_data == &cmd_user_data,
471 		     "Script result callback user data is incorrect");
472 }
473 
ZTEST(modem_chat,test_start_script_then_time_out)474 ZTEST(modem_chat, test_start_script_then_time_out)
475 {
476 	bool called;
477 
478 	zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
479 	k_msleep(100);
480 
481 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
482 	zassert_true(called == false, "Script callback should not have been called yet");
483 
484 	k_msleep(5900);
485 
486 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
487 	zassert_true(called == true, "Script callback should have been called");
488 	zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_TIMEOUT,
489 		     "Script result should be TIMEOUT");
490 	zassert_true(script_result_user_data == &cmd_user_data,
491 		     "Script result callback user data is incorrect");
492 }
493 
ZTEST(modem_chat,test_script_with_partial_matches)494 ZTEST(modem_chat, test_script_with_partial_matches)
495 {
496 	bool called;
497 
498 	zassert_true(modem_chat_script_run(&cmd, &script_partial) == 0, "Failed to start script");
499 	k_msleep(100);
500 
501 	/*
502 	 * Script sends "AT+CMGL=4\r";
503 	 */
504 
505 	modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
506 	zassert_true(memcmp(buffer, "AT+CMGL=4\r", sizeof("AT+CMGL=4\r") - 1) == 0,
507 		     "Request not sent as expected");
508 
509 	/*
510 	 * Modem will return the following sequence 3 times
511 	 * "+CMGL: 1,1,,50\r";
512 	 * "07911326060032F064A9542954\r"
513 	 */
514 
515 	for (uint8_t i = 0; i < 3; i++) {
516 		atomic_set(&callback_called, 0);
517 		modem_backend_mock_put(&mock, cmgl_response_0, sizeof(cmgl_response_0) - 1);
518 		k_msleep(100);
519 
520 		called = atomic_test_bit(&callback_called,
521 					 MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_CALLED_BIT);
522 		zassert_equal(called, true, "Match callback not called");
523 		zassert_equal(argc_buffers, 5, "Incorrect number of args");
524 		zassert_str_equal(argv_buffers[0], "+CMGL: ",
525 				  "Incorrect argv received");
526 		zassert_str_equal(argv_buffers[1], "1",
527 				  "Incorrect argv received");
528 		zassert_str_equal(argv_buffers[2], "1",
529 				  "Incorrect argv received");
530 		zassert_str_equal(argv_buffers[3], "",
531 				  "Incorrect argv received");
532 		zassert_str_equal(argv_buffers[4], "50",
533 				  "Incorrect argv received");
534 
535 		atomic_set(&callback_called, 0);
536 		modem_backend_mock_put(&mock, cmgl_response_1, sizeof(cmgl_response_1) - 1);
537 		k_msleep(100);
538 
539 		called = atomic_test_bit(&callback_called,
540 					 MODEM_CHAT_UTEST_ON_CMGL_PARTIAL_ANY_CALLED_BIT);
541 		zassert_equal(called, true, "Match callback not called");
542 		zassert_equal(argc_buffers, 2, "Incorrect number of args");
543 		zassert_str_equal(argv_buffers[0], "",
544 				  "Incorrect argv received");
545 		zassert_str_equal(argv_buffers[1],
546 				  "07911326060032F064A9542954",
547 				  "Incorrect argv received");
548 	}
549 
550 	atomic_set(&callback_called, 0);
551 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
552 	k_msleep(100);
553 
554 	/*
555 	 * Modem returns "OK\r"
556 	 * Script terminates
557 	 */
558 
559 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
560 	zassert_true(called == true, "Script callback should have been called");
561 	zassert_equal(script_result, MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
562 		      "Script should have stopped with success");
563 
564 	/* Assert no data was sent except the request */
565 	zassert_equal(modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer)), 0,
566 		      "Script sent too many requests");
567 }
568 
ZTEST(modem_chat,test_script_run_sync_complete)569 ZTEST(modem_chat, test_script_run_sync_complete)
570 {
571 	modem_backend_mock_prime(&mock, &at_echo_transaction);
572 	zassert_ok(modem_chat_run_script(&cmd, &script_echo), "Failed to run echo script");
573 }
574 
ZTEST(modem_chat,test_script_run_sync_timeout)575 ZTEST(modem_chat, test_script_run_sync_timeout)
576 {
577 	zassert_equal(modem_chat_run_script(&cmd, &script_echo), -EAGAIN,
578 		      "Failed to run echo script");
579 }
580 
ZTEST(modem_chat,test_script_run_sync_abort)581 ZTEST(modem_chat, test_script_run_sync_abort)
582 {
583 	modem_backend_mock_prime(&mock, &at_echo_error_transaction);
584 	zassert_equal(modem_chat_run_script(&cmd, &script_echo), -EAGAIN,
585 		      "Echo script should time out and return -EAGAIN");
586 }
587 
ZTEST(modem_chat,test_script_run_dynamic_script_sync)588 ZTEST(modem_chat, test_script_run_dynamic_script_sync)
589 {
590 	char match[] = "AT";
591 	char separators[] = ",";
592 	char request[] = "AT";
593 	char name[] = "Dynamic";
594 
595 	struct modem_chat_match stack_response_match = {
596 		.match = NULL,
597 		.match_size = 0,
598 		.separators = NULL,
599 		.separators_size = 0,
600 		.wildcards = false,
601 		.partial = false,
602 		.callback = NULL,
603 	};
604 
605 	struct modem_chat_script_chat stack_script_chat = {
606 		.request = NULL,
607 		.response_matches = &stack_response_match,
608 		.response_matches_size = 1,
609 		.timeout = 0,
610 	};
611 
612 	struct modem_chat_script stack_script = {
613 		.name = name,
614 		.script_chats = &stack_script_chat,
615 		.script_chats_size = 1,
616 		.abort_matches = NULL,
617 		.abort_matches_size = 0,
618 		.callback = NULL,
619 		.timeout = 1,
620 	};
621 
622 	stack_response_match.match = match;
623 	stack_response_match.match_size = strlen(match);
624 	stack_response_match.separators = separators;
625 	stack_response_match.separators_size = strlen(match);
626 	stack_script_chat.request = request;
627 	stack_script_chat.request_size = strlen(request);
628 
629 	modem_backend_mock_prime(&mock, &at_echo_transaction);
630 	zassert_ok(modem_chat_run_script(&cmd, &stack_script), "Failed to run script");
631 }
632 
ZTEST(modem_chat,test_script_chat_timeout_cmd)633 ZTEST(modem_chat, test_script_chat_timeout_cmd)
634 {
635 	int ret;
636 	bool called;
637 
638 	zassert_ok(modem_chat_run_script_async(&cmd, &script_timeout_cmd),
639 		   "Failed to start script");
640 	k_msleep(100);
641 
642 	/*
643 	 * Script sends "AT\r\n";
644 	 */
645 	ret = modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
646 	zassert_equal(ret, sizeof("AT\r\n") - 1);
647 	zassert_true(memcmp(buffer, "AT\r\n", sizeof("AT\r\n") - 1) == 0,
648 		     "Request not sent as expected");
649 
650 	/*
651 	 * Modem responds OK
652 	 */
653 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
654 
655 	/*
656 	 * Script waits 4 seconds
657 	 */
658 	k_msleep(3000);
659 	zassert_equal(modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer)), 0);
660 	k_msleep(2000);
661 
662 	/*
663 	 * Script sends "AT\r\n";
664 	 */
665 	ret = modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
666 	zassert_equal(ret, sizeof("AT\r\n") - 1);
667 	zassert_true(memcmp(buffer, "AT\r\n", sizeof("AT\r\n") - 1) == 0,
668 		     "Request not sent as expected");
669 
670 	/*
671 	 * Modem responds OK
672 	 */
673 	modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
674 	k_msleep(100);
675 
676 	called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
677 	zassert_true(called == true, "Script callback should have been called");
678 	zassert_equal(script_result, MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
679 		      "Script should have stopped with success");
680 
681 	/* Assert no data was sent except the request */
682 	zassert_equal(modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer)), 0,
683 		      "Script sent too many requests");
684 }
685 
ZTEST(modem_chat,test_runtime_match)686 ZTEST(modem_chat, test_runtime_match)
687 {
688 	int ret;
689 	struct modem_chat_match test_match;
690 
691 	modem_chat_match_init(&test_match);
692 
693 	ret = modem_chat_match_set_match(&test_match, "AT345");
694 	zassert_ok(ret, "Failed to set match");
695 	zassert_ok(strcmp(test_match.match, "AT345"), "Failed to set match");
696 	zassert_equal(test_match.match_size, 5, "Failed to set size of match");
697 
698 	ret = modem_chat_match_set_separators(&test_match, ",*");
699 	zassert_ok(ret, "Failed to set match");
700 	zassert_ok(strcmp(test_match.separators, ",*"), "Failed to set separators");
701 	zassert_equal(test_match.separators_size, 2, "Failed to set size of separators");
702 
703 	modem_chat_match_set_partial(&test_match, true);
704 	zassert_equal(test_match.partial, true);
705 	modem_chat_match_set_partial(&test_match, false);
706 	zassert_equal(test_match.partial, false);
707 
708 	modem_chat_match_enable_wildcards(&test_match, true);
709 	zassert_equal(test_match.wildcards, true);
710 	modem_chat_match_enable_wildcards(&test_match, false);
711 	zassert_equal(test_match.wildcards, false);
712 }
713 
ZTEST(modem_chat,test_runtime_script_chat)714 ZTEST(modem_chat, test_runtime_script_chat)
715 {
716 	int ret;
717 	struct modem_chat_script_chat test_script_chat;
718 	struct modem_chat_match test_response_matches[2];
719 
720 	modem_chat_script_chat_init(&test_script_chat);
721 
722 	ret = modem_chat_script_chat_set_request(&test_script_chat, "AT345");
723 	zassert_ok(ret, "Failed to set request");
724 	zassert_ok(strcmp(test_script_chat.request, "AT345"), "Failed to set script_chat");
725 	zassert_equal(test_script_chat.request_size, 5, "Failed to set size of script_chat");
726 
727 	ret = modem_chat_script_chat_set_response_matches(&test_script_chat,
728 							  test_response_matches,
729 							  ARRAY_SIZE(test_response_matches));
730 	zassert_ok(ret, "Failed to set response matches");
731 	zassert_equal(test_script_chat.response_matches, test_response_matches,
732 		      "Failed to set response_matches");
733 	zassert_equal(test_script_chat.response_matches_size, ARRAY_SIZE(test_response_matches),
734 		      "Failed to set response_matches");
735 
736 	ret = modem_chat_script_chat_set_response_matches(&test_script_chat,
737 							  test_response_matches, 0);
738 	zassert_equal(ret, -EINVAL, "Should have failed to set response matches");
739 
740 	ret = modem_chat_script_chat_set_response_matches(&test_script_chat, NULL, 1);
741 	zassert_equal(ret, -EINVAL, "Should have failed to set response matches");
742 }
743 
ZTEST(modem_chat,test_runtime_script)744 ZTEST(modem_chat, test_runtime_script)
745 {
746 	int ret;
747 	struct modem_chat_script test_script;
748 	struct modem_chat_script_chat test_script_chats[2];
749 	struct modem_chat_match test_abort_matches[2];
750 
751 	modem_chat_script_init(&test_script);
752 	zassert_equal(strlen(test_script.name), 0, "Failed to set default name");
753 
754 	ret = modem_chat_script_set_script_chats(&test_script, test_script_chats,
755 						 ARRAY_SIZE(test_script_chats));
756 	zassert_ok(ret, "Failed to set script chats");
757 	zassert_equal(test_script.script_chats, test_script_chats,
758 		      "Failed to set script_chats");
759 	zassert_equal(test_script.script_chats_size, ARRAY_SIZE(test_script_chats),
760 		      "Failed to set script_chats_size");
761 
762 	ret = modem_chat_script_set_script_chats(&test_script, test_script_chats, 0);
763 	zassert_equal(ret, -EINVAL, "Should have failed to set script chats");
764 
765 	ret = modem_chat_script_set_script_chats(&test_script, NULL, 1);
766 	zassert_equal(ret, -EINVAL, "Should have failed to set script chats");
767 
768 	ret = modem_chat_script_set_abort_matches(&test_script, test_abort_matches,
769 						  ARRAY_SIZE(test_abort_matches));
770 	zassert_ok(ret, "Failed to set abort matches");
771 	zassert_equal(test_script.abort_matches, test_abort_matches,
772 		      "Failed to set script_chats");
773 	zassert_equal(test_script.abort_matches_size, ARRAY_SIZE(test_abort_matches),
774 		      "Failed to set script_chats_size");
775 
776 	ret = modem_chat_script_set_abort_matches(&test_script, test_abort_matches, 0);
777 	zassert_equal(ret, -EINVAL, "Should have failed to set abort matches");
778 
779 	ret = modem_chat_script_set_abort_matches(&test_script, NULL, 1);
780 	zassert_equal(ret, -EINVAL, "Should have failed to set abort matches");
781 }
782 
783 /*************************************************************************************************/
784 /*                                         Test suite                                            */
785 /*************************************************************************************************/
786 ZTEST_SUITE(modem_chat, NULL, test_modem_chat_setup, test_modem_chat_before, test_modem_chat_after,
787 	    NULL);
788