1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/comparator.h>
8 #include <zephyr/drivers/comparator/fake_comp.h>
9 #include <zephyr/fff.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/shell/shell.h>
12 #include <zephyr/shell/shell_dummy.h>
13 #include <zephyr/ztest.h>
14 
15 DEFINE_FFF_GLOBALS;
16 
17 #define FAKE_COMP_NODE DT_NODELABEL(fake_comp)
18 #define FAKE_COMP_NAME DEVICE_DT_NAME(FAKE_COMP_NODE)
19 
20 #define TEST_TRIGGER_DELAY K_SECONDS(1)
21 
22 #define TEST_AWAIT_TRIGGER_TIMEOUT_BELOW_MIN_CMD \
23 	("comp await_trigger " FAKE_COMP_NAME " 0")
24 
25 #define TEST_AWAIT_TRIGGER_TIMEOUT_ABOVE_MAX_CMD						\
26 	("comp await_trigger " FAKE_COMP_NAME " "						\
27 	 STRINGIFY(CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT + 1))
28 
29 #define TEST_AWAIT_TRIGGER_TIMEOUT_BROKEN_CMD \
30 	("comp await_trigger " FAKE_COMP_NAME " d")
31 
32 static const struct shell *test_sh;
33 static const struct device *test_dev = DEVICE_DT_GET(FAKE_COMP_NODE);
34 static comparator_callback_t test_callback;
35 static void *test_callback_user_data;
36 static struct k_spinlock test_callback_spinlock;
37 static struct k_work_delayable test_trigger_dwork;
38 
test_get_output_stub_1(const struct device * dev)39 static int test_get_output_stub_1(const struct device *dev)
40 {
41 	ARG_UNUSED(dev);
42 
43 	return 1;
44 }
45 
test_get_output_stub_0(const struct device * dev)46 static int test_get_output_stub_0(const struct device *dev)
47 {
48 	ARG_UNUSED(dev);
49 
50 	return 0;
51 }
52 
test_get_output_stub_eio(const struct device * dev)53 static int test_get_output_stub_eio(const struct device *dev)
54 {
55 	ARG_UNUSED(dev);
56 
57 	return -EIO;
58 }
59 
test_set_trigger_stub_ok(const struct device * dev,enum comparator_trigger trigger)60 static int test_set_trigger_stub_ok(const struct device *dev, enum comparator_trigger trigger)
61 {
62 	ARG_UNUSED(dev);
63 	ARG_UNUSED(trigger);
64 
65 	return 0;
66 }
67 
test_set_trigger_stub_eio(const struct device * dev,enum comparator_trigger trigger)68 static int test_set_trigger_stub_eio(const struct device *dev, enum comparator_trigger trigger)
69 {
70 	ARG_UNUSED(dev);
71 	ARG_UNUSED(trigger);
72 
73 	return -EIO;
74 }
75 
test_set_trigger_callback_mock_0(const struct device * dev,comparator_callback_t callback,void * user_data)76 static int test_set_trigger_callback_mock_0(const struct device *dev,
77 					    comparator_callback_t callback,
78 					    void *user_data)
79 {
80 	ARG_UNUSED(dev);
81 
82 	K_SPINLOCK(&test_callback_spinlock) {
83 		test_callback = callback;
84 		test_callback_user_data = user_data;
85 	}
86 
87 	return 0;
88 }
89 
test_set_trigger_callback_stub_0(const struct device * dev,comparator_callback_t callback,void * user_data)90 static int test_set_trigger_callback_stub_0(const struct device *dev,
91 					    comparator_callback_t callback,
92 					    void *user_data)
93 {
94 	ARG_UNUSED(dev);
95 	ARG_UNUSED(callback);
96 	ARG_UNUSED(user_data);
97 
98 	return 0;
99 }
100 
test_set_trigger_callback_stub_eio(const struct device * dev,comparator_callback_t callback,void * user_data)101 static int test_set_trigger_callback_stub_eio(const struct device *dev,
102 					      comparator_callback_t callback,
103 					      void *user_data)
104 {
105 	ARG_UNUSED(dev);
106 	ARG_UNUSED(callback);
107 	ARG_UNUSED(user_data);
108 
109 	return -EIO;
110 }
111 
test_trigger_is_pending_stub_1(const struct device * dev)112 static int test_trigger_is_pending_stub_1(const struct device *dev)
113 {
114 	ARG_UNUSED(dev);
115 
116 	return 1;
117 }
118 
test_trigger_is_pending_stub_0(const struct device * dev)119 static int test_trigger_is_pending_stub_0(const struct device *dev)
120 {
121 	ARG_UNUSED(dev);
122 
123 	return 0;
124 }
125 
test_trigger_is_pending_stub_eio(const struct device * dev)126 static int test_trigger_is_pending_stub_eio(const struct device *dev)
127 {
128 	ARG_UNUSED(dev);
129 
130 	return -EIO;
131 }
132 
133 
test_trigger_handler(struct k_work * work)134 static void test_trigger_handler(struct k_work *work)
135 {
136 	ARG_UNUSED(work);
137 
138 	test_callback(test_dev, test_callback_user_data);
139 }
140 
test_schedule_trigger(void)141 static void test_schedule_trigger(void)
142 {
143 	k_work_schedule(&test_trigger_dwork, TEST_TRIGGER_DELAY);
144 }
145 
test_cancel_trigger(void)146 static void test_cancel_trigger(void)
147 {
148 	struct k_work_sync sync;
149 
150 	k_work_cancel_delayable_sync(&test_trigger_dwork, &sync);
151 }
152 
test_setup(void)153 static void *test_setup(void)
154 {
155 	k_work_init_delayable(&test_trigger_dwork, test_trigger_handler);
156 	test_sh = shell_backend_dummy_get_ptr();
157 	WAIT_FOR(shell_ready(test_sh), 20000, k_msleep(1));
158 	zassert_true(shell_ready(test_sh), "timed out waiting for dummy shell backend");
159 	return NULL;
160 }
161 
test_after(void * f)162 static void test_after(void *f)
163 {
164 	ARG_UNUSED(f);
165 
166 	test_cancel_trigger();
167 }
168 
ZTEST(comparator_shell,test_get_output)169 ZTEST(comparator_shell, test_get_output)
170 {
171 	int ret;
172 	const char *out;
173 	size_t out_size;
174 
175 	shell_backend_dummy_clear_output(test_sh);
176 	comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_1;
177 	ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME);
178 	zassert_ok(ret);
179 	zassert_equal(comp_fake_comp_get_output_fake.call_count, 1);
180 	zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev);
181 	out = shell_backend_dummy_get_output(test_sh, &out_size);
182 	zassert_str_equal(out, "\r\n1\r\n");
183 
184 	comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_0;
185 	ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME);
186 	zassert_ok(ret);
187 	zassert_equal(comp_fake_comp_get_output_fake.call_count, 2);
188 	zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev);
189 	out = shell_backend_dummy_get_output(test_sh, &out_size);
190 	zassert_str_equal(out, "\r\n0\r\n");
191 
192 	comp_fake_comp_get_output_fake.custom_fake = test_get_output_stub_eio;
193 	ret = shell_execute_cmd(test_sh, "comp get_output " FAKE_COMP_NAME);
194 	zassert_equal(ret, -EIO);
195 	zassert_equal(comp_fake_comp_get_output_fake.call_count, 3);
196 	zassert_equal(comp_fake_comp_get_output_fake.arg0_val, test_dev);
197 	out = shell_backend_dummy_get_output(test_sh, &out_size);
198 	zassert_str_equal(out, "\r\nfailed to get output\r\n");
199 }
200 
ZTEST(comparator_shell,test_set_trigger)201 ZTEST(comparator_shell, test_set_trigger)
202 {
203 	int ret;
204 	const char *out;
205 	size_t out_size;
206 
207 	comp_fake_comp_set_trigger_fake.custom_fake = test_set_trigger_stub_ok;
208 
209 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " NONE");
210 	zassert_ok(ret);
211 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 1);
212 	zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev);
213 	zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_NONE);
214 
215 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " RISING_EDGE");
216 	zassert_ok(ret);
217 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 2);
218 	zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev);
219 	zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_RISING_EDGE);
220 
221 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " FALLING_EDGE");
222 	zassert_ok(ret);
223 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 3);
224 	zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev);
225 	zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_FALLING_EDGE);
226 
227 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " BOTH_EDGES");
228 	zassert_ok(ret);
229 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 4);
230 	zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev);
231 	zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_BOTH_EDGES);
232 
233 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " INVALID");
234 	zassert_equal(ret, -EINVAL);
235 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 4);
236 
237 	comp_fake_comp_set_trigger_fake.custom_fake = test_set_trigger_stub_eio;
238 
239 	shell_backend_dummy_clear_output(test_sh);
240 	ret = shell_execute_cmd(test_sh, "comp set_trigger " FAKE_COMP_NAME " BOTH_EDGES");
241 	zassert_equal(ret, -EIO);
242 	zassert_equal(comp_fake_comp_set_trigger_fake.call_count, 5);
243 	zassert_equal(comp_fake_comp_set_trigger_fake.arg0_val, test_dev);
244 	zassert_equal(comp_fake_comp_set_trigger_fake.arg1_val, COMPARATOR_TRIGGER_BOTH_EDGES);
245 	out = shell_backend_dummy_get_output(test_sh, &out_size);
246 	zassert_str_equal(out, "\r\nfailed to set trigger\r\n");
247 }
248 
ZTEST(comparator_shell,test_await_trigger_set_callback_fail)249 ZTEST(comparator_shell, test_await_trigger_set_callback_fail)
250 {
251 	int ret;
252 	const char *out;
253 	size_t out_size;
254 
255 	shell_backend_dummy_clear_output(test_sh);
256 	comp_fake_comp_set_trigger_callback_fake.custom_fake = test_set_trigger_callback_stub_eio;
257 	ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME);
258 	zassert_ok(0);
259 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 1);
260 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val, 0);
261 	out = shell_backend_dummy_get_output(test_sh, &out_size);
262 	zassert_str_equal(out, "\r\nfailed to set trigger callback\r\n");
263 }
264 
ZTEST(comparator_shell,test_await_trigger_timeout)265 ZTEST(comparator_shell, test_await_trigger_timeout)
266 {
267 	int ret;
268 	const char *out;
269 	size_t out_size;
270 
271 	shell_backend_dummy_clear_output(test_sh);
272 	comp_fake_comp_set_trigger_callback_fake.custom_fake = test_set_trigger_callback_stub_0;
273 	ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME);
274 	zassert_ok(0);
275 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 2);
276 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[0], 0);
277 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[1], 0);
278 	out = shell_backend_dummy_get_output(test_sh, &out_size);
279 	zassert_str_equal(out, "\r\ntimed out\r\n");
280 }
281 
ZTEST(comparator_shell,test_await_trigger_invalid_timeout_arg)282 ZTEST(comparator_shell, test_await_trigger_invalid_timeout_arg)
283 {
284 	int ret;
285 
286 	ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_BELOW_MIN_CMD);
287 	zassert_not_ok(ret);
288 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0);
289 
290 	ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_ABOVE_MAX_CMD);
291 	zassert_not_ok(ret);
292 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0);
293 
294 	ret = shell_execute_cmd(test_sh, TEST_AWAIT_TRIGGER_TIMEOUT_BROKEN_CMD);
295 	zassert_not_ok(ret);
296 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 0);
297 }
298 
ZTEST(comparator_shell,test_await_trigger)299 ZTEST(comparator_shell, test_await_trigger)
300 {
301 	int ret;
302 	const char *out;
303 	size_t out_size;
304 	comparator_api_set_trigger_callback seq[2];
305 
306 	shell_backend_dummy_clear_output(test_sh);
307 	seq[0] = test_set_trigger_callback_mock_0;
308 	seq[1] = test_set_trigger_callback_stub_0;
309 	comp_fake_comp_set_trigger_callback_fake.custom_fake_seq = seq;
310 	comp_fake_comp_set_trigger_callback_fake.custom_fake_seq_len = ARRAY_SIZE(seq);
311 	test_schedule_trigger();
312 	ret = shell_execute_cmd(test_sh, "comp await_trigger " FAKE_COMP_NAME);
313 	zassert_ok(0);
314 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.call_count, 2);
315 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg0_history[0], test_dev);
316 	zassert_not_equal(comp_fake_comp_set_trigger_callback_fake.arg1_history[0], NULL);
317 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[0], 0);
318 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg0_history[1], test_dev);
319 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.arg1_history[1], NULL);
320 	zassert_equal(comp_fake_comp_set_trigger_callback_fake.return_val_history[1], 0);
321 	out = shell_backend_dummy_get_output(test_sh, &out_size);
322 	zassert_str_equal(out, "\r\ntriggered\r\n");
323 }
324 
ZTEST(comparator_shell,test_trigger_is_pending)325 ZTEST(comparator_shell, test_trigger_is_pending)
326 {
327 	int ret;
328 	const char *out;
329 	size_t out_size;
330 
331 	shell_backend_dummy_clear_output(test_sh);
332 	comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_1;
333 	ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME);
334 	zassert_ok(ret);
335 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 1);
336 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev);
337 	out = shell_backend_dummy_get_output(test_sh, &out_size);
338 	zassert_str_equal(out, "\r\n1\r\n");
339 
340 	comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_0;
341 	ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME);
342 	zassert_ok(ret);
343 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 2);
344 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev);
345 	out = shell_backend_dummy_get_output(test_sh, &out_size);
346 	zassert_str_equal(out, "\r\n0\r\n");
347 
348 	comp_fake_comp_trigger_is_pending_fake.custom_fake = test_trigger_is_pending_stub_eio;
349 	ret = shell_execute_cmd(test_sh, "comp trigger_is_pending " FAKE_COMP_NAME);
350 	zassert_equal(ret, -EIO);
351 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.call_count, 3);
352 	zassert_equal(comp_fake_comp_trigger_is_pending_fake.arg0_val, test_dev);
353 	out = shell_backend_dummy_get_output(test_sh, &out_size);
354 	zassert_str_equal(out, "\r\nfailed to get trigger status\r\n");
355 }
356 
357 ZTEST_SUITE(comparator_shell, NULL, test_setup, test_after, NULL, NULL);
358