• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libcoap unit tests
2  *
3  * Copyright (C) 2013,2015-2018,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_session.h"
13 
14 #if COAP_CLIENT_SUPPORT
15 #include <stdio.h>
16 
17 /* The error threshold for timeout calculations. The precision of
18  * coap_calc_timeout() is assumed to be sufficient if the resulting
19  * value deviates at most FP_ERR_THRESHOLD percent from the value that
20  * was calculated with double precision floating point arithmetic
21  * instead of fixed point integers.
22  */
23 #define FP_ERR_THRESHOLD (1.0)
24 
25 /* The maximum number of bits for fixed point integer representation.
26  * This number must be identical to the definition of MAX_BITS in
27  * coap_net.c
28  */
29 #define MAX_BITS 8
30 
31 static coap_context_t *ctx; /* Holds the coap context for most tests */
32 static coap_session_t *session; /* Holds a reference-counted session object */
33 
34 COAP_STATIC_INLINE int
fpeq(const coap_fixed_point_t a,const coap_fixed_point_t b)35 fpeq(const coap_fixed_point_t a, const coap_fixed_point_t b) {
36   return (a.integer_part == b.integer_part) &&
37          (a.fractional_part == b.fractional_part);
38 }
39 
40 static void
t_session1(void)41 t_session1(void) {
42   CU_ASSERT(session->ref == 1);
43 
44   coap_session_reference(session);
45   CU_ASSERT(session->ref == 2);
46 
47   coap_session_release(session);
48   CU_ASSERT(session->ref == 1);
49 }
50 
51 static void
t_session2(void)52 t_session2(void) {
53   CU_ASSERT(session->max_retransmit == COAP_DEFAULT_MAX_RETRANSMIT);
54   CU_ASSERT(fpeq(session->ack_timeout, COAP_DEFAULT_ACK_TIMEOUT));
55   CU_ASSERT(fpeq(session->ack_random_factor, COAP_DEFAULT_ACK_RANDOM_FACTOR));
56 
57   CU_ASSERT(coap_session_get_max_retransmit(session) == COAP_DEFAULT_MAX_RETRANSMIT);
58   CU_ASSERT(fpeq(coap_session_get_ack_timeout(session), COAP_DEFAULT_ACK_TIMEOUT));
59   CU_ASSERT(fpeq(coap_session_get_ack_random_factor(session), COAP_DEFAULT_ACK_RANDOM_FACTOR));
60 }
61 
62 COAP_STATIC_INLINE double
fp_to_double(const coap_fixed_point_t fp)63 fp_to_double(const coap_fixed_point_t fp) {
64   return fp.integer_part + fp.fractional_part/1000.0;
65 }
66 
67 COAP_STATIC_INLINE double
q_to_double(uint8_t q)68 q_to_double(uint8_t q) {
69   return (double)(q) / (1 << MAX_BITS);
70 }
71 
72 /* Calculates the timeout using the following formula:
73  *
74  * COAP_TICKS_PER_SECOND * ack_timeout * (1 + (ack_random_factor - 1) * r)
75  *
76  * where @p r is the randomization value represented as Q0.MAX_BITS.
77  * This function returns the result as unsigned int to be
78  * checked against the result of coap_calc_timeout().
79  */
80 COAP_STATIC_INLINE unsigned int
timeout(const coap_fixed_point_t ack_timeout,const coap_fixed_point_t ack_random_factor,uint8_t r)81 timeout(const coap_fixed_point_t ack_timeout,
82         const coap_fixed_point_t ack_random_factor,
83         uint8_t r) {
84   const unsigned int ctps = COAP_TICKS_PER_SECOND;
85   double ato = fp_to_double(ack_timeout);
86   double rnd = 1 + (fp_to_double(ack_random_factor) - 1.0) * q_to_double(r);
87 
88   return (unsigned int)(ctps * ato * rnd);
89 }
90 
91 /* Checks if @p v is within FP_ERR_THRESHOLD percent of @p ref.  This
92  * function returns 1 if it is within range, 0 otherwise.
93  */
94 COAP_STATIC_INLINE int
good_enough(unsigned int v,unsigned int ref)95 good_enough(unsigned int v, unsigned int ref) {
96 #define delta(a,b) (((a) < (b)) ? ((b) - (a)) : ((a) - (b)))
97   return (delta(v,ref) / 100.0) <= FP_ERR_THRESHOLD;
98 }
99 
100 static void
t_session3(void)101 t_session3(void) {
102   const coap_fixed_point_t ato = COAP_DEFAULT_ACK_TIMEOUT;
103   const coap_fixed_point_t arf = COAP_DEFAULT_ACK_RANDOM_FACTOR;
104 
105   CU_ASSERT(good_enough(coap_calc_timeout(session, 0), timeout(ato, arf, 0)));
106   CU_ASSERT(good_enough(coap_calc_timeout(session, 12), timeout(ato, arf, 12)));
107   CU_ASSERT(good_enough(coap_calc_timeout(session, 55), timeout(ato, arf, 55)));
108   CU_ASSERT(good_enough(coap_calc_timeout(session, 83), timeout(ato, arf, 83)));
109   CU_ASSERT(good_enough(coap_calc_timeout(session, 117), timeout(ato, arf, 117)));
110   CU_ASSERT(good_enough(coap_calc_timeout(session, 210), timeout(ato, arf, 210)));
111   CU_ASSERT(good_enough(coap_calc_timeout(session, 255), timeout(ato, arf, 255)));
112 }
113 
114 static void
t_session4(void)115 t_session4(void) {
116   const coap_fixed_point_t ato = {8,300};
117   const coap_fixed_point_t arf = {2,011};
118 
119   coap_session_set_ack_timeout(session, ato);
120   coap_session_set_ack_random_factor(session, arf);
121 
122   CU_ASSERT(fpeq(coap_session_get_ack_timeout(session), ato));
123   CU_ASSERT(fpeq(coap_session_get_ack_random_factor(session), arf));
124 
125   CU_ASSERT(good_enough(coap_calc_timeout(session, 0), timeout(ato, arf, 0)));
126   CU_ASSERT(good_enough(coap_calc_timeout(session, 12), timeout(ato, arf, 12)));
127   CU_ASSERT(good_enough(coap_calc_timeout(session, 55), timeout(ato, arf, 55)));
128   CU_ASSERT(good_enough(coap_calc_timeout(session, 83), timeout(ato, arf, 83)));
129   CU_ASSERT(good_enough(coap_calc_timeout(session, 117), timeout(ato, arf, 117)));
130   CU_ASSERT(good_enough(coap_calc_timeout(session, 210), timeout(ato, arf, 210)));
131   CU_ASSERT(good_enough(coap_calc_timeout(session, 255), timeout(ato, arf, 255)));
132 }
133 
134 static void
t_session5(void)135 t_session5(void) {
136   coap_session_release(session);
137 
138   CU_ASSERT_PTR_NULL(ctx->sessions);
139   session = NULL;
140 }
141 
142 /* Test 6 creates a new session after the original session has been
143  * released by test 5 */
144 static void
t_session6(void)145 t_session6(void) {
146   coap_address_t laddr, saddr;
147   coap_address_init(&laddr);
148   coap_address_init(&saddr);
149 
150   CU_ASSERT_PTR_NULL(session);
151 
152   laddr.size = sizeof(struct sockaddr_in6);
153   laddr.addr.sin6.sin6_family = AF_INET6;
154   laddr.addr.sin6.sin6_addr = in6addr_any;
155   laddr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
156 
157   coap_address_copy(&saddr, &laddr);
158   saddr.addr.sin6.sin6_addr = in6addr_loopback;
159   saddr.addr.sin6.sin6_port = htons(20000);
160 
161   session = coap_new_client_session(ctx, &saddr, &laddr, COAP_PROTO_UDP);
162   CU_ASSERT_PTR_NOT_NULL(session);
163   CU_ASSERT_PTR_NOT_NULL(ctx->sessions);
164   CU_ASSERT(session->state == COAP_SESSION_STATE_ESTABLISHED);
165   coap_session_release(session);
166 }
167 
168 /* This function creates a set of nodes for testing. These nodes
169  * will exist for all tests and are modified by coap_insert_node()
170  * and coap_remove_from_queue().
171  */
172 static int
t_session_tests_create(void)173 t_session_tests_create(void) {
174   coap_address_t addr;
175   coap_address_init(&addr);
176 
177   addr.size = sizeof(struct sockaddr_in6);
178   addr.addr.sin6.sin6_family = AF_INET6;
179   addr.addr.sin6.sin6_addr = in6addr_any;
180   addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT);
181 
182   ctx = coap_new_context(&addr);
183 
184   if (ctx != NULL) {
185     addr.addr.sin6.sin6_addr = in6addr_loopback;
186     session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP);
187   }
188 
189   return (ctx == NULL) || (session == NULL);
190 }
191 
192 static int
t_session_tests_remove(void)193 t_session_tests_remove(void) {
194   coap_free_context(ctx);
195   return 0;
196 }
197 
198 CU_pSuite
t_init_session_tests(void)199 t_init_session_tests(void) {
200   CU_pSuite suite;
201 
202   suite = CU_add_suite("session",
203                        t_session_tests_create, t_session_tests_remove);
204   if (!suite) {                        /* signal error */
205     fprintf(stderr, "W: cannot add session test suite (%s)\n",
206             CU_get_error_msg());
207 
208     return NULL;
209   }
210 
211 #define SESSION_TEST(s,t)                                              \
212   if (!CU_ADD_TEST(s,t)) {                                              \
213     fprintf(stderr, "W: cannot add session test (%s)\n",              \
214             CU_get_error_msg());                                      \
215   }
216 
217   SESSION_TEST(suite, t_session1);
218   SESSION_TEST(suite, t_session2);
219   SESSION_TEST(suite, t_session3);
220   SESSION_TEST(suite, t_session4);
221   SESSION_TEST(suite, t_session5);
222   SESSION_TEST(suite, t_session6);
223 
224   return suite;
225 }
226 #endif /* COAP_CLIENT_SUPPORT */
227