1 /* libcoap unit tests
2  *
3  * Copyright (C) 2013,2015,2022-2023 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * This file is part of the CoAP library libcoap. Please see
8  * README for terms of use.
9  */
10 
11 #include "test_common.h"
12 #include "test_sendqueue.h"
13 
14 #include <stdio.h>
15 
16 #if COAP_CLIENT_SUPPORT
17 static coap_context_t *ctx; /* Holds the coap context for most tests */
18 static coap_session_t *session; /* Holds a reference-counted session object */
19 
20 /* timestamps for tests. The first element in this array denotes the
21  * base time in ticks, the following elements are timestamps relative
22  * to this basetime.
23  */
24 static coap_tick_t timestamp[] = {
25   0, 100, 200, 30, 160
26 };
27 
28 /* nodes for testing. node[0] is left empty */
29 coap_queue_t *node[5];
30 
31 static coap_tick_t
add_timestamps(coap_queue_t *queue, size_t num)32 add_timestamps(coap_queue_t *queue, size_t num) {
33   coap_tick_t t = 0;
34   while (queue && num--) {
35     t += queue->t;
36     queue = queue->next;
37   }
38 
39   return t;
40 }
41 
42 static void
t_sendqueue1(void)43 t_sendqueue1(void) {
44   int result = coap_insert_node(&ctx->sendqueue, node[1]);
45 
46   CU_ASSERT(result > 0);
47   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
48   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[1]);
49   CU_ASSERT(node[1]->t == timestamp[1]);
50 }
51 
52 static void
t_sendqueue2(void)53 t_sendqueue2(void) {
54   int result;
55 
56   result = coap_insert_node(&ctx->sendqueue, node[2]);
57 
58   CU_ASSERT(result > 0);
59   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[1]);
60   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next, node[2]);
61 
62   CU_ASSERT(ctx->sendqueue->t == timestamp[1]);
63   CU_ASSERT(node[2]->t == timestamp[2] - timestamp[1]);
64 }
65 
66 /* insert new node as first element in queue */
67 static void
t_sendqueue3(void)68 t_sendqueue3(void) {
69   int result;
70   result = coap_insert_node(&ctx->sendqueue, node[3]);
71 
72   CU_ASSERT(result > 0);
73 
74   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[3]);
75   CU_ASSERT(node[3]->t == timestamp[3]);
76 
77   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next);
78   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next->next);
79 
80   CU_ASSERT(ctx->sendqueue->next->t == timestamp[1] - timestamp[3]);
81   CU_ASSERT(ctx->sendqueue->next->next->t == timestamp[2] - timestamp[1]);
82 }
83 
84 /* insert new node as fourth element in queue */
85 static void
t_sendqueue4(void)86 t_sendqueue4(void) {
87   int result;
88 
89   result = coap_insert_node(&ctx->sendqueue, node[4]);
90 
91   CU_ASSERT(result > 0);
92 
93   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[3]);
94 
95   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next);
96   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next, node[1]);
97 
98   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next->next);
99   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next->next, node[4]);
100 
101   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next->next->next);
102   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next->next->next, node[2]);
103 
104   CU_ASSERT(ctx->sendqueue->next->t == timestamp[1] - timestamp[3]);
105   CU_ASSERT(add_timestamps(ctx->sendqueue, 1) == timestamp[3]);
106   CU_ASSERT(add_timestamps(ctx->sendqueue, 2) == timestamp[1]);
107   CU_ASSERT(add_timestamps(ctx->sendqueue, 3) == timestamp[4]);
108   CU_ASSERT(add_timestamps(ctx->sendqueue, 4) == timestamp[2]);
109 }
110 
111 static void
t_sendqueue5(void)112 t_sendqueue5(void) {
113   const coap_tick_diff_t delta1 = 20, delta2 = 130;
114   unsigned int result;
115   coap_tick_t now;
116 
117   /* space for saving the current node timestamps */
118   static coap_tick_t times[sizeof(timestamp)/sizeof(coap_tick_t)];
119   coap_queue_t *p;
120   int i;
121 
122   /* save timestamps of nodes in the sendqueue in their actual order */
123   memset(times, 0, sizeof(times));
124   for (p = ctx->sendqueue, i = 0; p; p = p->next, i++) {
125     times[i] = p->t;
126   }
127 
128   coap_ticks(&now);
129   ctx->sendqueue_basetime = now;
130 
131   now -= delta1;
132   result = coap_adjust_basetime(ctx, now);
133 
134   CU_ASSERT(result == 0);
135   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
136   CU_ASSERT(ctx->sendqueue_basetime == now);
137   CU_ASSERT(ctx->sendqueue->t == timestamp[3] + delta1);
138 
139   now += delta2;
140   result = coap_adjust_basetime(ctx, now);
141   CU_ASSERT(result == 2);
142   CU_ASSERT(ctx->sendqueue_basetime == now);
143   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
144   CU_ASSERT(ctx->sendqueue->t == 0);
145 
146   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next);
147   CU_ASSERT(ctx->sendqueue->next->t == 0);
148 
149   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next->next);
150   CU_ASSERT(ctx->sendqueue->next->next->t == delta2 - delta1 - timestamp[1]);
151 
152   /* restore timestamps of nodes in the sendqueue */
153   for (p = ctx->sendqueue, i = 0; p; p = p->next, i++) {
154     p->t = times[i];
155   }
156 }
157 
158 static void
t_sendqueue6(void)159 t_sendqueue6(void) {
160   unsigned int result;
161   coap_tick_t now;
162   const coap_tick_diff_t delta = 20;
163   coap_queue_t *tmpqueue = ctx->sendqueue;
164 
165   /* space for saving the current node timestamps */
166   static coap_tick_t times[sizeof(timestamp)/sizeof(coap_tick_t)];
167   coap_queue_t *p;
168   int i;
169 
170   /* save timestamps of nodes in the sendqueue in their actual order */
171   memset(times, 0, sizeof(times));
172   for (p = ctx->sendqueue, i = 0; p; p = p->next, i++) {
173     times[i] = p->t;
174   }
175 
176   coap_ticks(&now);
177   ctx->sendqueue = NULL;
178   ctx->sendqueue_basetime = now;
179 
180   result = coap_adjust_basetime(ctx, now + delta);
181 
182   CU_ASSERT(result == 0);
183   CU_ASSERT(ctx->sendqueue_basetime == now + delta);
184 
185   /* restore sendqueue */
186   ctx->sendqueue = tmpqueue;
187 }
188 
189 static void
t_sendqueue7(void)190 t_sendqueue7(void) {
191   int result;
192   coap_queue_t *tmp_node;
193 
194   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
195   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[3]);
196 
197   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next);
198   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next, node[1]);
199 
200   result = coap_remove_from_queue(&ctx->sendqueue, session, 3, &tmp_node);
201 
202   CU_ASSERT(result == 1);
203   CU_ASSERT_PTR_NOT_NULL(tmp_node);
204   CU_ASSERT_PTR_EQUAL(tmp_node, node[3]);
205 
206   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
207   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[1]);
208 
209   CU_ASSERT(ctx->sendqueue->t == timestamp[1]);
210 }
211 
212 static void
t_sendqueue8(void)213 t_sendqueue8(void) {
214   int result;
215   coap_queue_t *tmp_node;
216 
217   result = coap_remove_from_queue(&ctx->sendqueue, session, 4, &tmp_node);
218 
219   CU_ASSERT(result == 1);
220   CU_ASSERT_PTR_NOT_NULL(tmp_node);
221   CU_ASSERT_PTR_EQUAL(tmp_node, node[4]);
222 
223   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
224   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[1]);
225   CU_ASSERT(ctx->sendqueue->t == timestamp[1]);
226 
227   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue->next);
228   CU_ASSERT_PTR_EQUAL(ctx->sendqueue->next, node[2]);
229   CU_ASSERT(ctx->sendqueue->next->t == timestamp[2] - timestamp[1]);
230 
231   CU_ASSERT_PTR_NULL(ctx->sendqueue->next->next);
232 }
233 
234 static void
t_sendqueue9(void)235 t_sendqueue9(void) {
236   coap_queue_t *tmp_node;
237   tmp_node = coap_peek_next(ctx);
238 
239   CU_ASSERT_PTR_NOT_NULL(tmp_node);
240   CU_ASSERT_PTR_EQUAL(tmp_node, node[1]);
241   CU_ASSERT_PTR_EQUAL(tmp_node, ctx->sendqueue);
242 
243   tmp_node = coap_pop_next(ctx);
244 
245   CU_ASSERT_PTR_NOT_NULL(tmp_node);
246   CU_ASSERT_PTR_EQUAL(tmp_node, node[1]);
247 
248   CU_ASSERT_PTR_NOT_NULL(ctx->sendqueue);
249   CU_ASSERT_PTR_EQUAL(ctx->sendqueue, node[2]);
250 
251   CU_ASSERT(tmp_node->t == timestamp[1]);
252   CU_ASSERT(ctx->sendqueue->t == timestamp[2]);
253 
254   CU_ASSERT_PTR_NULL(ctx->sendqueue->next);
255 }
256 
257 static void
t_sendqueue10(void)258 t_sendqueue10(void) {
259   coap_queue_t *tmp_node;
260 
261   tmp_node = coap_pop_next(ctx);
262 
263   CU_ASSERT_PTR_NOT_NULL(tmp_node);
264   CU_ASSERT_PTR_EQUAL(tmp_node, node[2]);
265 
266   CU_ASSERT_PTR_NULL(ctx->sendqueue);
267 
268   CU_ASSERT(tmp_node->t == timestamp[2]);
269 }
270 
271 /* This function creates a set of nodes for testing. These nodes
272  * will exist for all tests and are modified by coap_insert_node()
273  * and coap_remove_from_queue().
274  */
275 static int
t_sendqueue_tests_create(void)276 t_sendqueue_tests_create(void) {
277   size_t n, error = 0;
278   coap_address_t addr;
279   coap_address_init(&addr);
280 
281   addr.size = sizeof(struct sockaddr_in6);
282   addr.addr.sin6.sin6_family = AF_INET6;
283   addr.addr.sin6.sin6_addr = in6addr_any;
284   addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
285 
286   ctx = coap_new_context(&addr);
287 
288   addr.addr.sin6.sin6_addr = in6addr_loopback;
289   session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP);
290 
291   coap_ticks(&timestamp[0]);
292 
293   memset(node, 0, sizeof(node));
294   for (n = 1; n < sizeof(node)/sizeof(coap_queue_t *); n++) {
295     node[n] = coap_new_node();
296     if (!node[n]) {
297       error = 1;
298       break;
299     }
300 
301     node[n]->id = n;
302     node[n]->t = timestamp[n];
303     node[n]->session = coap_session_reference(session);
304   }
305 
306   if (error) {
307     /* destroy all test nodes and set entry to zero */
308     for (n = 0; n < sizeof(node)/sizeof(coap_queue_t *); n++) {
309       if (node[n]) {
310         coap_delete_node(node[n]);
311         node[n] = NULL;
312       }
313     }
314     coap_free_context(ctx);
315     ctx = NULL;
316   }
317 
318   return error;
319 }
320 
321 static int
t_sendqueue_tests_remove(void)322 t_sendqueue_tests_remove(void) {
323   size_t n;
324   for (n = 0; n < sizeof(node)/sizeof(coap_queue_t *); n++) {
325     if (node[n]) {
326       coap_delete_node(node[n]);
327       node[n] = NULL;
328     }
329   }
330   coap_free_context(ctx);
331   return 0;
332 }
333 
334 CU_pSuite
t_init_sendqueue_tests(void)335 t_init_sendqueue_tests(void) {
336   CU_pSuite suite;
337 
338   suite = CU_add_suite("sendqueue",
339                        t_sendqueue_tests_create, t_sendqueue_tests_remove);
340   if (!suite) {                        /* signal error */
341     fprintf(stderr, "W: cannot add sendqueue test suite (%s)\n",
342             CU_get_error_msg());
343 
344     return NULL;
345   }
346 
347 #define SENDQUEUE_TEST(s,t)                                              \
348   if (!CU_ADD_TEST(s,t)) {                                              \
349     fprintf(stderr, "W: cannot add sendqueue test (%s)\n",              \
350             CU_get_error_msg());                                      \
351   }
352 
353   SENDQUEUE_TEST(suite, t_sendqueue1);
354   SENDQUEUE_TEST(suite, t_sendqueue2);
355   SENDQUEUE_TEST(suite, t_sendqueue3);
356   SENDQUEUE_TEST(suite, t_sendqueue4);
357   SENDQUEUE_TEST(suite, t_sendqueue5);
358   SENDQUEUE_TEST(suite, t_sendqueue6);
359   SENDQUEUE_TEST(suite, t_sendqueue7);
360   SENDQUEUE_TEST(suite, t_sendqueue8);
361   SENDQUEUE_TEST(suite, t_sendqueue9);
362   SENDQUEUE_TEST(suite, t_sendqueue10);
363 
364   return suite;
365 }
366 #endif /* COAP_CLIENT_SUPPORT */
367