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