1 /*
2  * Copyright (c) 2023 Trackunit Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*************************************************************************************************/
8 /*                                        Dependencies                                           */
9 /*************************************************************************************************/
10 #include <zephyr/kernel.h>
11 #include <zephyr/modem/pipe.h>
12 #include <zephyr/ztest.h>
13 #include <zephyr/sys/atomic.h>
14 #include <string.h>
15 
16 #define TEST_MODEM_PIPE_EVENT_OPENED_BIT        0
17 #define TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT 1
18 #define TEST_MODEM_PIPE_EVENT_RECEIVE_READY_BIT 2
19 #define TEST_MODEM_PIPE_EVENT_CLOSED_BIT        3
20 #define TEST_MODEM_PIPE_NOTIFY_TIMEOUT          K_MSEC(10)
21 #define TEST_MODEM_PIPE_WAIT_TIMEOUT            K_MSEC(20)
22 
23 /*************************************************************************************************/
24 /*                                   Fake modem_pipe backend                                     */
25 /*************************************************************************************************/
26 struct modem_backend_fake {
27 	struct modem_pipe pipe;
28 
29 	struct k_work_delayable opened_dwork;
30 	struct k_work_delayable transmit_idle_dwork;
31 	struct k_work_delayable closed_dwork;
32 
33 	const uint8_t *transmit_buffer;
34 	size_t transmit_buffer_size;
35 
36 	uint8_t *receive_buffer;
37 	size_t receive_buffer_size;
38 
39 	uint8_t synchronous : 1;
40 	uint8_t open_called : 1;
41 	uint8_t transmit_called : 1;
42 	uint8_t receive_called : 1;
43 	uint8_t close_called : 1;
44 };
45 
modem_backend_fake_opened_handler(struct k_work * item)46 static void modem_backend_fake_opened_handler(struct k_work *item)
47 {
48 	struct k_work_delayable *dwork = k_work_delayable_from_work(item);
49 	struct modem_backend_fake *backend =
50 		CONTAINER_OF(dwork, struct modem_backend_fake, opened_dwork);
51 
52 	modem_pipe_notify_opened(&backend->pipe);
53 }
54 
modem_backend_fake_open(void * data)55 static int modem_backend_fake_open(void *data)
56 {
57 	struct modem_backend_fake *backend = data;
58 
59 	backend->open_called = true;
60 
61 	if (backend->synchronous) {
62 		modem_pipe_notify_opened(&backend->pipe);
63 	} else {
64 		k_work_schedule(&backend->opened_dwork, TEST_MODEM_PIPE_NOTIFY_TIMEOUT);
65 	}
66 
67 	return 0;
68 }
69 
modem_backend_fake_transmit_idle_handler(struct k_work * item)70 static void modem_backend_fake_transmit_idle_handler(struct k_work *item)
71 {
72 	struct k_work_delayable *dwork = k_work_delayable_from_work(item);
73 	struct modem_backend_fake *backend =
74 		CONTAINER_OF(dwork, struct modem_backend_fake, transmit_idle_dwork);
75 
76 	modem_pipe_notify_transmit_idle(&backend->pipe);
77 }
78 
modem_backend_fake_transmit(void * data,const uint8_t * buf,size_t size)79 static int modem_backend_fake_transmit(void *data, const uint8_t *buf, size_t size)
80 {
81 	struct modem_backend_fake *backend = data;
82 
83 	backend->transmit_called = true;
84 	backend->transmit_buffer = buf;
85 	backend->transmit_buffer_size = size;
86 
87 	if (backend->synchronous) {
88 		modem_pipe_notify_transmit_idle(&backend->pipe);
89 	} else {
90 		k_work_schedule(&backend->transmit_idle_dwork, TEST_MODEM_PIPE_NOTIFY_TIMEOUT);
91 	}
92 
93 	return size;
94 }
95 
modem_backend_fake_receive(void * data,uint8_t * buf,size_t size)96 static int modem_backend_fake_receive(void *data, uint8_t *buf, size_t size)
97 {
98 	struct modem_backend_fake *backend = data;
99 
100 	backend->receive_called = true;
101 	backend->receive_buffer = buf;
102 	backend->receive_buffer_size = size;
103 	return size;
104 }
105 
modem_backend_fake_closed_handler(struct k_work * item)106 static void modem_backend_fake_closed_handler(struct k_work *item)
107 {
108 	struct k_work_delayable *dwork = k_work_delayable_from_work(item);
109 	struct modem_backend_fake *backend =
110 		CONTAINER_OF(dwork, struct modem_backend_fake, closed_dwork);
111 
112 	modem_pipe_notify_closed(&backend->pipe);
113 }
114 
modem_backend_fake_close(void * data)115 static int modem_backend_fake_close(void *data)
116 {
117 	struct modem_backend_fake *backend = data;
118 
119 	backend->close_called = true;
120 
121 	if (backend->synchronous) {
122 		modem_pipe_notify_closed(&backend->pipe);
123 	} else {
124 		k_work_schedule(&backend->closed_dwork, TEST_MODEM_PIPE_NOTIFY_TIMEOUT);
125 	}
126 
127 	return 0;
128 }
129 
130 static struct modem_pipe_api modem_backend_fake_api = {
131 	.open = modem_backend_fake_open,
132 	.transmit = modem_backend_fake_transmit,
133 	.receive = modem_backend_fake_receive,
134 	.close = modem_backend_fake_close,
135 };
136 
modem_backend_fake_init(struct modem_backend_fake * backend)137 static struct modem_pipe *modem_backend_fake_init(struct modem_backend_fake *backend)
138 {
139 	k_work_init_delayable(&backend->opened_dwork,
140 			      modem_backend_fake_opened_handler);
141 	k_work_init_delayable(&backend->transmit_idle_dwork,
142 			      modem_backend_fake_transmit_idle_handler);
143 	k_work_init_delayable(&backend->closed_dwork,
144 			      modem_backend_fake_closed_handler);
145 
146 	modem_pipe_init(&backend->pipe, backend, &modem_backend_fake_api);
147 	return &backend->pipe;
148 }
149 
modem_backend_fake_reset(struct modem_backend_fake * backend)150 static void modem_backend_fake_reset(struct modem_backend_fake *backend)
151 {
152 	backend->transmit_buffer = NULL;
153 	backend->transmit_buffer_size = 0;
154 	backend->receive_buffer = NULL;
155 	backend->transmit_buffer_size = 0;
156 	backend->open_called = false;
157 	backend->transmit_called = false;
158 	backend->receive_called = false;
159 	backend->close_called = false;
160 }
161 
modem_backend_fake_set_sync(struct modem_backend_fake * backend,bool sync)162 static void modem_backend_fake_set_sync(struct modem_backend_fake *backend, bool sync)
163 {
164 	backend->synchronous = sync;
165 }
166 
167 /*************************************************************************************************/
168 /*                                          Instances                                            */
169 /*************************************************************************************************/
170 static struct modem_backend_fake test_backend;
171 static struct modem_pipe *test_pipe;
172 static uint32_t test_user_data;
173 static atomic_t test_state;
174 static uint8_t test_buffer[4];
175 static size_t test_buffer_size = sizeof(test_buffer);
176 
177 /*************************************************************************************************/
178 /*                                          Callbacks                                            */
179 /*************************************************************************************************/
modem_pipe_fake_handler(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)180 static void modem_pipe_fake_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
181 				    void *user_data)
182 {
183 	__ASSERT(pipe == test_pipe, "Incorrect pipe provided with callback");
184 	__ASSERT(user_data == (void *)&test_user_data, "Incorrect user data ptr");
185 
186 	switch (event) {
187 	case MODEM_PIPE_EVENT_OPENED:
188 		atomic_set_bit(&test_state, TEST_MODEM_PIPE_EVENT_OPENED_BIT);
189 		break;
190 
191 	case MODEM_PIPE_EVENT_RECEIVE_READY:
192 		atomic_set_bit(&test_state, TEST_MODEM_PIPE_EVENT_RECEIVE_READY_BIT);
193 		break;
194 
195 	case MODEM_PIPE_EVENT_TRANSMIT_IDLE:
196 		atomic_set_bit(&test_state, TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT);
197 		break;
198 
199 	case MODEM_PIPE_EVENT_CLOSED:
200 		atomic_set_bit(&test_state, TEST_MODEM_PIPE_EVENT_CLOSED_BIT);
201 		break;
202 	}
203 }
204 
test_reset(void)205 static void test_reset(void)
206 {
207 	modem_backend_fake_reset(&test_backend);
208 	atomic_set(&test_state, 0);
209 }
210 
modem_backend_fake_setup(void)211 static void *modem_backend_fake_setup(void)
212 {
213 	test_pipe = modem_backend_fake_init(&test_backend);
214 	return NULL;
215 }
216 
modem_backend_fake_before(void * f)217 static void modem_backend_fake_before(void *f)
218 {
219 	modem_backend_fake_set_sync(&test_backend, false);
220 	modem_pipe_attach(test_pipe, modem_pipe_fake_handler, &test_user_data);
221 	test_reset();
222 }
223 
modem_backend_fake_after(void * f)224 static void modem_backend_fake_after(void *f)
225 {
226 	__ASSERT(modem_pipe_close(test_pipe, K_SECONDS(10)) == 0, "Failed to close pipe");
227 	modem_pipe_release(test_pipe);
228 }
229 
230 /* Opening pipe shall raise events OPENED and TRANSMIT_IDLE */
test_pipe_open(void)231 static void test_pipe_open(void)
232 {
233 	zassert_ok(modem_pipe_open(test_pipe, K_SECONDS(10)), "Failed to open pipe");
234 	zassert_true(test_backend.open_called, "open was not called");
235 	zassert_equal(atomic_get(&test_state),
236 		      BIT(TEST_MODEM_PIPE_EVENT_OPENED_BIT) |
237 		      BIT(TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT),
238 		      "Unexpected state %u", atomic_get(&test_state));
239 }
240 
241 /* Re-opening pipe shall have no effect */
test_pipe_reopen(void)242 static void test_pipe_reopen(void)
243 {
244 	zassert_ok(modem_pipe_open(test_pipe, K_SECONDS(10)), "Failed to re-open pipe");
245 	zassert_false(test_backend.open_called, "open was called");
246 	zassert_equal(atomic_get(&test_state), 0,
247 		      "Unexpected state %u", atomic_get(&test_state));
248 }
249 
250 /* Closing pipe shall raise event CLOSED */
test_pipe_close(void)251 static void test_pipe_close(void)
252 {
253 	zassert_ok(modem_pipe_close(test_pipe, K_SECONDS(10)), "Failed to close pipe");
254 	zassert_true(test_backend.close_called, "close was not called");
255 	zassert_equal(atomic_get(&test_state), BIT(TEST_MODEM_PIPE_EVENT_CLOSED_BIT),
256 		      "Unexpected state %u", atomic_get(&test_state));
257 }
258 
259 /* Re-closing pipe shall have no effect */
test_pipe_reclose(void)260 static void test_pipe_reclose(void)
261 {
262 	zassert_ok(modem_pipe_close(test_pipe, K_SECONDS(10)), "Failed to re-close pipe");
263 	zassert_false(test_backend.close_called, "close was called");
264 	zassert_equal(atomic_get(&test_state), 0,
265 		      "Unexpected state %u", atomic_get(&test_state));
266 }
267 
test_pipe_async_transmit(void)268 static void test_pipe_async_transmit(void)
269 {
270 	zassert_equal(modem_pipe_transmit(test_pipe, test_buffer, test_buffer_size),
271 		      test_buffer_size, "Failed to transmit");
272 	zassert_true(test_backend.transmit_called, "transmit was not called");
273 	zassert_equal(test_backend.transmit_buffer, test_buffer, "Incorrect buffer");
274 	zassert_equal(test_backend.transmit_buffer_size, test_buffer_size,
275 		      "Incorrect buffer size");
276 	zassert_equal(atomic_get(&test_state), 0, "Unexpected state %u",
277 		      atomic_get(&test_state));
278 	k_sleep(TEST_MODEM_PIPE_WAIT_TIMEOUT);
279 	zassert_equal(atomic_get(&test_state), BIT(TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT),
280 		      "Unexpected state %u", atomic_get(&test_state));
281 }
282 
test_pipe_sync_transmit(void)283 static void test_pipe_sync_transmit(void)
284 {
285 	zassert_equal(modem_pipe_transmit(test_pipe, test_buffer, test_buffer_size),
286 		      test_buffer_size, "Failed to transmit");
287 	zassert_true(test_backend.transmit_called, "transmit was not called");
288 	zassert_equal(test_backend.transmit_buffer, test_buffer, "Incorrect buffer");
289 	zassert_equal(test_backend.transmit_buffer_size, test_buffer_size,
290 		      "Incorrect buffer size");
291 	zassert_equal(atomic_get(&test_state), BIT(TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT),
292 		      "Unexpected state %u", atomic_get(&test_state));
293 }
294 
test_pipe_attach_receive_not_ready_transmit_idle(void)295 static void test_pipe_attach_receive_not_ready_transmit_idle(void)
296 {
297 	modem_pipe_attach(test_pipe, modem_pipe_fake_handler, &test_user_data);
298 	zassert_equal(atomic_get(&test_state), BIT(TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT),
299 		      "Unexpected state %u", atomic_get(&test_state));
300 }
301 
test_pipe_attach_receive_ready_transmit_idle(void)302 static void test_pipe_attach_receive_ready_transmit_idle(void)
303 {
304 	modem_pipe_attach(test_pipe, modem_pipe_fake_handler, &test_user_data);
305 	zassert_equal(atomic_get(&test_state),
306 		      BIT(TEST_MODEM_PIPE_EVENT_TRANSMIT_IDLE_BIT) |
307 		      BIT(TEST_MODEM_PIPE_EVENT_RECEIVE_READY_BIT),
308 		      "Unexpected state %u", atomic_get(&test_state));
309 }
310 
test_pipe_receive(void)311 static void test_pipe_receive(void)
312 {
313 	zassert_equal(modem_pipe_receive(test_pipe, test_buffer, test_buffer_size),
314 		      test_buffer_size, "Failed to receive");
315 	zassert_true(test_backend.receive_called, "receive was not called");
316 	zassert_equal(test_backend.receive_buffer, test_buffer, "Incorrect buffer");
317 	zassert_equal(test_backend.receive_buffer_size, test_buffer_size,
318 		      "Incorrect buffer size");
319 	zassert_equal(atomic_get(&test_state), 0, "Unexpected state %u",
320 		      atomic_get(&test_state));
321 }
322 
test_pipe_notify_receive_ready(void)323 static void test_pipe_notify_receive_ready(void)
324 {
325 	modem_pipe_notify_receive_ready(test_pipe);
326 	zassert_equal(atomic_get(&test_state), BIT(TEST_MODEM_PIPE_EVENT_RECEIVE_READY_BIT),
327 		      "Unexpected state %u", atomic_get(&test_state));
328 }
329 
ZTEST(modem_pipe,test_async_open_close)330 ZTEST(modem_pipe, test_async_open_close)
331 {
332 	test_pipe_open();
333 	test_reset();
334 	test_pipe_reopen();
335 	test_reset();
336 	test_pipe_close();
337 	test_reset();
338 	test_pipe_reclose();
339 }
340 
ZTEST(modem_pipe,test_sync_open_close)341 ZTEST(modem_pipe, test_sync_open_close)
342 {
343 	modem_backend_fake_set_sync(&test_backend, true);
344 	test_pipe_open();
345 	test_reset();
346 	test_pipe_reopen();
347 	test_reset();
348 	test_pipe_close();
349 	test_reset();
350 	test_pipe_reclose();
351 }
352 
ZTEST(modem_pipe,test_async_transmit)353 ZTEST(modem_pipe, test_async_transmit)
354 {
355 	test_pipe_open();
356 	test_reset();
357 	test_pipe_async_transmit();
358 }
359 
ZTEST(modem_pipe,test_sync_transmit)360 ZTEST(modem_pipe, test_sync_transmit)
361 {
362 	modem_backend_fake_set_sync(&test_backend, true);
363 	test_pipe_open();
364 	test_reset();
365 	test_pipe_sync_transmit();
366 }
367 
ZTEST(modem_pipe,test_attach)368 ZTEST(modem_pipe, test_attach)
369 {
370 	test_pipe_open();
371 
372 	/*
373 	 * Attaching pipe shall reinvoke TRANSMIT IDLE, but not RECEIVE READY as
374 	 * receive is not ready.
375 	 */
376 	test_reset();
377 	test_pipe_attach_receive_not_ready_transmit_idle();
378 
379 	/*
380 	 * Notify receive ready and expect receive ready to be re-invoked every
381 	 * time the pipe is attached to.
382 	 */
383 	test_reset();
384 	test_pipe_notify_receive_ready();
385 	test_reset();
386 	test_pipe_attach_receive_ready_transmit_idle();
387 	test_reset();
388 	test_pipe_attach_receive_ready_transmit_idle();
389 
390 	/*
391 	 * Receiving data from the pipe shall clear the receive ready state, stopping
392 	 * the invocation of receive ready on attach.
393 	 */
394 	test_reset();
395 	test_pipe_receive();
396 	test_reset();
397 	test_pipe_attach_receive_not_ready_transmit_idle();
398 }
399 
400 ZTEST_SUITE(modem_pipe, NULL, modem_backend_fake_setup, modem_backend_fake_before,
401 	    modem_backend_fake_after, NULL);
402