Modbus Library Test Coverage Report


lib/observer/
File: observer.c
Date: 2025-11-01 17:11:23
Lines:
108 of 108, 0 excluded
100.0%
Functions:
9 of 9, 0 excluded
100.0%
Branches:
114 of 114, 0 excluded
100.0%

Line Branch Exec Source
1 /**
2 * @file observer.c
3 * @brief Deterministic Observer pattern implementation for safety-critical systems.
4 *
5 * @details
6 * Implements a static, deterministic variant of the Observer pattern suitable
7 * for safety-critical and resource-constrained embedded environments.
8 *
9 * All API functions:
10 * - Validate input parameters.
11 * - Return deterministic status codes.
12 * - Avoid dynamic memory allocation.
13 * - Use single-point return for MISRA C Rule 15.5 compliance.
14 *
15 * @thread_safety
16 * None of the API functions are reentrant or thread-safe.
17 * Caller must ensure synchronization if used concurrently.
18 *
19 * @version 1.0.0
20 * @date 2025-10-25
21 * @ingroup observer
22 * @autho
23 * niwciu (niwciu@gmail.com)
24 *
25 * @copyright
26 * Copyright (c) 2025
27 *
28 * @safety
29 * Designed to meet MISRA C 2012 and ISO 26262 Part 6 software safety goals.
30 */
31
32 #include "observer.h"
33 #include "observer_public_types.h"
34 #include <stddef.h>
35 #include <stdint.h>
36
37 /* =========================================================================
38 * CALLBACKS WITHOUT ARGUMENT (void)
39 * ========================================================================= */
40
41 110 subscr_status_e subscribe(observer_cb_t *subscription_table, observer_cb_t cb_2_register, uint8_t subscription_table_size)
42 {
43 /* MISRA C: Rule 15.5 compliant — single exit point used. */
44 110 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
45
46
6/6
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 108 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 107 times.
✓ Branch 5 taken 1 time.
110 if ((subscription_table != NULL) && (cb_2_register != NULL) && (subscription_table_size != 0u))
47 {
48 uint8_t i;
49 107 uint8_t free_index = subscription_table_size; /* invalid by default */
50 107 status = OBSERVER_TABLE_FULL_ERROR;
51
52
2/2
✓ Branch 0 taken 980 times.
✓ Branch 1 taken 97 times.
1077 for (i = 0u; i < subscription_table_size; ++i)
53 {
54
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 970 times.
980 if (subscription_table[i] == cb_2_register)
55 {
56 10 status = OBSERVER_OK; /* already registered */
57 10 break;
58 }
59
60
4/4
✓ Branch 0 taken 678 times.
✓ Branch 1 taken 292 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 582 times.
970 if ((subscription_table[i] == NULL) && (free_index == subscription_table_size))
61 {
62 96 free_index = i; /* first free slot */
63 }
64 }
65
66
4/4
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 1 time.
107 if ((status != OBSERVER_OK) && (free_index < subscription_table_size))
67 {
68 96 subscription_table[free_index] = cb_2_register;
69 96 status = OBSERVER_OK;
70 }
71 }
72
73 110 return status;
74 }
75
76 16 subscr_status_e unsubscribe(observer_cb_t *subscription_table, observer_cb_t cb_2_remove, uint8_t subscription_table_size)
77 {
78 16 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
79
80
6/6
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1 time.
16 if ((subscription_table != NULL) && (cb_2_remove != NULL) && (subscription_table_size != 0u))
81 {
82 13 status = OBSERVER_TABLE_EMPTY_ERROR;
83
84
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 1 time.
70 for (uint8_t i = 0u; i < subscription_table_size; ++i)
85 {
86
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 57 times.
69 if (subscription_table[i] == cb_2_remove)
87 {
88
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 12 times.
73 for (uint8_t j = i; j < (uint8_t)(subscription_table_size - 1u); ++j)
89 {
90 61 subscription_table[j] = subscription_table[j + 1u];
91 }
92 12 subscription_table[subscription_table_size - 1u] = NULL;
93 12 status = OBSERVER_OK;
94 12 break;
95 }
96 }
97 }
98
99 16 return status;
100 }
101
102 7 subscr_status_e notify(observer_cb_t *subscription_table, uint8_t subscription_table_size)
103 {
104 7 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
105
106
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
7 if ((subscription_table != NULL) && (subscription_table_size != 0u))
107 {
108 5 status = OBSERVER_TABLE_EMPTY_ERROR;
109
110
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5 times.
55 for (uint8_t i = 0u; i < subscription_table_size; ++i)
111 {
112
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 39 times.
50 if (subscription_table[i] != NULL)
113 {
114 11 subscription_table[i]();
115 11 status = OBSERVER_OK;
116 }
117 }
118 }
119
120 7 return status;
121 }
122
123 /* =========================================================================
124 * CALLBACKS WITH event_state_e ARGUMENT
125 * ========================================================================= */
126
127 53 subscr_status_e subscribe_state_change(observer_cb_state_t *subscription_table, observer_cb_state_t cb_2_register, uint8_t subscription_table_size)
128 {
129 53 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
130
131
6/6
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 50 times.
✓ Branch 5 taken 1 time.
53 if ((subscription_table != NULL) && (cb_2_register != NULL) && (subscription_table_size != 0u))
132 {
133 50 uint8_t free_index = subscription_table_size;
134 uint8_t i;
135 50 status = OBSERVER_TABLE_FULL_ERROR;
136
137
2/2
✓ Branch 0 taken 410 times.
✓ Branch 1 taken 40 times.
450 for (i = 0u; i < subscription_table_size; ++i)
138 {
139
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 400 times.
410 if (subscription_table[i] == cb_2_register)
140 {
141 10 status = OBSERVER_OK;
142 10 break;
143 }
144
145
4/4
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 312 times.
400 if ((subscription_table[i] == NULL) && (free_index == subscription_table_size))
146 {
147 39 free_index = i;
148 }
149 }
150
151
4/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 1 time.
50 if ((status != OBSERVER_OK) && (free_index < subscription_table_size))
152 {
153 39 subscription_table[free_index] = cb_2_register;
154 39 status = OBSERVER_OK;
155 }
156 }
157
158 53 return status;
159 }
160
161 16 subscr_status_e unsubscribe_state_change(observer_cb_state_t *subscription_table, observer_cb_state_t cb_2_remove, uint8_t subscription_table_size)
162 {
163 16 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
164
165
6/6
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1 time.
16 if ((subscription_table != NULL) && (cb_2_remove != NULL) && (subscription_table_size != 0u))
166 {
167 13 status = OBSERVER_TABLE_EMPTY_ERROR;
168
169
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 1 time.
70 for (uint8_t i = 0u; i < subscription_table_size; ++i)
170 {
171
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 57 times.
69 if (subscription_table[i] == cb_2_remove)
172 {
173
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 12 times.
73 for (uint8_t j = i; j < (uint8_t)(subscription_table_size - 1u); ++j)
174 {
175 61 subscription_table[j] = subscription_table[j + 1u];
176 }
177 12 subscription_table[subscription_table_size - 1u] = NULL;
178 12 status = OBSERVER_OK;
179 12 break;
180 }
181 }
182 }
183
184 16 return status;
185 }
186
187 7 subscr_status_e notify_state_change(observer_cb_state_t *subscription_table, uint8_t subscription_table_size, event_state_e state)
188 {
189 7 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
190
191
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
7 if ((subscription_table != NULL) && (subscription_table_size != 0u))
192 {
193 5 status = OBSERVER_TABLE_EMPTY_ERROR;
194
195
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5 times.
55 for (uint8_t i = 0u; i < subscription_table_size; ++i)
196 {
197
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 39 times.
50 if (subscription_table[i] != NULL)
198 {
199 11 subscription_table[i](state);
200 11 status = OBSERVER_OK;
201 }
202 }
203 }
204
205 7 return status;
206 }
207
208 /* =========================================================================
209 * CALLBACKS WITH UINT8 ARGUMENT
210 * ========================================================================= */
211
212 53 subscr_status_e subscribe_u8(observer_cb_u8_arg_t *subscription_table, observer_cb_u8_arg_t cb_2_register, uint8_t subscription_table_size)
213 {
214 53 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
215
216
6/6
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 50 times.
✓ Branch 5 taken 1 time.
53 if ((subscription_table != NULL) && (cb_2_register != NULL) && (subscription_table_size != 0u))
217 {
218 uint8_t i;
219 50 uint8_t free_index = subscription_table_size;
220 50 status = OBSERVER_TABLE_FULL_ERROR;
221
222
2/2
✓ Branch 0 taken 410 times.
✓ Branch 1 taken 40 times.
450 for (i = 0u; i < subscription_table_size; ++i)
223 {
224
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 400 times.
410 if (subscription_table[i] == cb_2_register)
225 {
226 10 status = OBSERVER_OK;
227 10 break;
228 }
229
230
4/4
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 312 times.
400 if ((subscription_table[i] == NULL) && (free_index == subscription_table_size))
231 {
232 39 free_index = i;
233 }
234 }
235
236
4/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 1 time.
50 if ((status != OBSERVER_OK) && (free_index < subscription_table_size))
237 {
238 39 subscription_table[free_index] = cb_2_register;
239 39 status = OBSERVER_OK;
240 }
241 }
242
243 53 return status;
244 }
245
246 16 subscr_status_e unsubscribe_u8(observer_cb_u8_arg_t *subscription_table, observer_cb_u8_arg_t cb_2_remove, uint8_t subscription_table_size)
247 {
248 16 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
249
250
6/6
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1 time.
16 if ((subscription_table != NULL) && (cb_2_remove != NULL) && (subscription_table_size != 0u))
251 {
252 13 status = OBSERVER_TABLE_EMPTY_ERROR;
253
254
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 1 time.
70 for (uint8_t i = 0u; i < subscription_table_size; ++i)
255 {
256
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 57 times.
69 if (subscription_table[i] == cb_2_remove)
257 {
258
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 12 times.
73 for (uint8_t j = i; j < (uint8_t)(subscription_table_size - 1u); ++j)
259 {
260 61 subscription_table[j] = subscription_table[j + 1u];
261 }
262 12 subscription_table[subscription_table_size - 1u] = NULL;
263 12 status = OBSERVER_OK;
264 12 break;
265 }
266 }
267 }
268
269 16 return status;
270 }
271
272 7 subscr_status_e notify_u8(observer_cb_u8_arg_t *subscription_table, uint8_t subscription_table_size, uint8_t data)
273 {
274 7 subscr_status_e status = OBSERVER_INVALID_ARGUMENT_ERROR;
275
276
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
7 if ((subscription_table != NULL) && (subscription_table_size != 0u))
277 {
278 5 status = OBSERVER_TABLE_EMPTY_ERROR;
279
280
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 5 times.
55 for (uint8_t i = 0u; i < subscription_table_size; ++i)
281 {
282
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 39 times.
50 if (subscription_table[i] != NULL)
283 {
284 11 subscription_table[i](data);
285 11 status = OBSERVER_OK;
286 }
287 }
288 }
289
290 7 return status;
291 }
292