1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
4 #define DISPATCH_NEW_PRIVATE
5 #define DISPATCH_PRIVATE
6 
7 #include "test/test.h"
8 
9 #include "lib/dispatch/dispatch.h"
10 #include "lib/dispatch/dispatch_cfg.h"
11 #include "lib/dispatch/dispatch_st.h"
12 #include "lib/dispatch/msgtypes.h"
13 
14 #include "lib/log/escape.h"
15 #include "lib/malloc/malloc.h"
16 #include "lib/string/printf.h"
17 
18 #include <stdio.h>
19 #include <string.h>
20 
21 static dispatch_t *dispatcher_in_use=NULL;
22 
23 static void
test_dispatch_max_in_u16_sl(void * arg)24 test_dispatch_max_in_u16_sl(void *arg)
25 {
26   (void)arg;
27   smartlist_t *sl = smartlist_new();
28   uint16_t nums[] = { 10, 20, 30 };
29   tt_int_op(-1, OP_EQ, max_in_u16_sl(sl, -1));
30 
31   smartlist_add(sl, NULL);
32   tt_int_op(-1, OP_EQ, max_in_u16_sl(sl, -1));
33 
34   smartlist_add(sl, &nums[1]);
35   tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
36 
37   smartlist_add(sl, &nums[0]);
38   tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
39 
40   smartlist_add(sl, NULL);
41   tt_int_op(20, OP_EQ, max_in_u16_sl(sl, -1));
42 
43   smartlist_add(sl, &nums[2]);
44   tt_int_op(30, OP_EQ, max_in_u16_sl(sl, -1));
45 
46  done:
47   smartlist_free(sl);
48 }
49 
50 /* Construct an empty dispatch_t. */
51 static void
test_dispatch_empty(void * arg)52 test_dispatch_empty(void *arg)
53 {
54   (void)arg;
55 
56   dispatch_t *d=NULL;
57   dispatch_cfg_t *cfg=NULL;
58 
59   cfg = dcfg_new();
60   d = dispatch_new(cfg);
61   tt_assert(d);
62 
63  done:
64   dispatch_free(d);
65   dcfg_free(cfg);
66 }
67 
68 static int total_recv1_simple = 0;
69 static int total_recv2_simple = 0;
70 
71 static void
simple_recv1(const msg_t * m)72 simple_recv1(const msg_t *m)
73 {
74   total_recv1_simple += m->aux_data__.u64;
75 }
76 
77 static char *recv2_received = NULL;
78 
79 static void
simple_recv2(const msg_t * m)80 simple_recv2(const msg_t *m)
81 {
82   tor_free(recv2_received);
83   recv2_received = dispatch_fmt_msg_data(dispatcher_in_use, m);
84 
85   total_recv2_simple += m->aux_data__.u64*10;
86 }
87 
88 /* Construct a dispatch_t with two messages, make sure that they both get
89  * delivered. */
90 static void
test_dispatch_simple(void * arg)91 test_dispatch_simple(void *arg)
92 {
93   (void)arg;
94 
95   dispatch_t *d=NULL;
96   dispatch_cfg_t *cfg=NULL;
97   int r;
98 
99   cfg = dcfg_new();
100   r = dcfg_msg_set_type(cfg,0,0);
101   r += dcfg_msg_set_chan(cfg,0,0);
102   r += dcfg_add_recv(cfg,0,1,simple_recv1);
103   r += dcfg_msg_set_type(cfg,1,0);
104   r += dcfg_msg_set_chan(cfg,1,0);
105   r += dcfg_add_recv(cfg,1,1,simple_recv2);
106   r += dcfg_add_recv(cfg,1,1,simple_recv2); /* second copy */
107   tt_int_op(r, OP_EQ, 0);
108 
109   d = dispatch_new(cfg);
110   tt_assert(d);
111   dispatcher_in_use = d;
112 
113   msg_aux_data_t data = {.u64 = 7};
114   r = dispatch_send(d, 99, 0, 0, 0, data);
115   tt_int_op(r, OP_EQ, 0);
116   tt_int_op(total_recv1_simple, OP_EQ, 0);
117 
118   r = dispatch_flush(d, 0, INT_MAX);
119   tt_int_op(r, OP_EQ, 0);
120   tt_int_op(total_recv1_simple, OP_EQ, 7);
121   tt_int_op(total_recv2_simple, OP_EQ, 0);
122 
123   total_recv1_simple = 0;
124   r = dispatch_send(d, 99, 0, 1, 0, data);
125   tt_int_op(r, OP_EQ, 0);
126   r = dispatch_flush(d, 0, INT_MAX);
127   tt_int_op(total_recv1_simple, OP_EQ, 0);
128   tt_int_op(total_recv2_simple, OP_EQ, 140);
129 
130   tt_str_op(recv2_received, OP_EQ, "<>"); // no format function was set.
131 
132  done:
133   dispatch_free(d);
134   dcfg_free(cfg);
135   tor_free(recv2_received);
136 }
137 
138 /* Construct a dispatch_t with a message and no receiver; make sure that it
139  * gets dropped properly. */
140 static void
test_dispatch_no_recipient(void * arg)141 test_dispatch_no_recipient(void *arg)
142 {
143   (void)arg;
144 
145   dispatch_t *d=NULL;
146   dispatch_cfg_t *cfg=NULL;
147   int r;
148 
149   cfg = dcfg_new();
150   r = dcfg_msg_set_type(cfg,0,0);
151   r += dcfg_msg_set_chan(cfg,0,0);
152   tt_int_op(r, OP_EQ, 0);
153 
154   d = dispatch_new(cfg);
155   tt_assert(d);
156   dispatcher_in_use = d;
157 
158   msg_aux_data_t data = { .u64 = 7};
159   r = dispatch_send(d, 99, 0, 0, 0, data);
160   tt_int_op(r, OP_EQ, 0);
161 
162   r = dispatch_flush(d, 0, INT_MAX);
163   tt_int_op(r, OP_EQ, 0);
164 
165  done:
166   dispatch_free(d);
167   dcfg_free(cfg);
168 }
169 
170 struct coord_t { int x; int y; };
171 static void
free_coord(msg_aux_data_t d)172 free_coord(msg_aux_data_t d)
173 {
174   tor_free(d.ptr);
175 }
176 static char *
fmt_coord(msg_aux_data_t d)177 fmt_coord(msg_aux_data_t d)
178 {
179   char *v;
180   struct coord_t *c = d.ptr;
181   tor_asprintf(&v, "[%d, %d]", c->x, c->y);
182   return v;
183 }
184 static dispatch_typefns_t coord_fns = {
185   .fmt_fn = fmt_coord,
186   .free_fn = free_coord,
187 };
188 static void
alert_run_immediate(dispatch_t * d,channel_id_t ch,void * arg)189 alert_run_immediate(dispatch_t *d, channel_id_t ch, void *arg)
190 {
191   (void)arg;
192   dispatch_flush(d, ch, INT_MAX);
193 }
194 
195 static char *received_data=NULL;
196 
197 static void
recv_typed_data(const msg_t * m)198 recv_typed_data(const msg_t *m)
199 {
200   tor_free(received_data);
201   received_data = dispatch_fmt_msg_data(dispatcher_in_use, m);
202 }
203 
204 static void
test_dispatch_with_types(void * arg)205 test_dispatch_with_types(void *arg)
206 {
207   (void)arg;
208 
209   dispatch_t *d=NULL;
210   dispatch_cfg_t *cfg=NULL;
211   int r;
212 
213   cfg = dcfg_new();
214   r = dcfg_msg_set_type(cfg,5,3);
215   r += dcfg_msg_set_chan(cfg,5,2);
216   r += dcfg_add_recv(cfg,5,0,recv_typed_data);
217   r += dcfg_type_set_fns(cfg,3,&coord_fns);
218   tt_int_op(r, OP_EQ, 0);
219 
220   d = dispatch_new(cfg);
221   tt_assert(d);
222   dispatcher_in_use = d;
223 
224   /* Make this message get run immediately. */
225   r = dispatch_set_alert_fn(d, 2, alert_run_immediate, NULL);
226   tt_int_op(r, OP_EQ, 0);
227 
228   struct coord_t *xy = tor_malloc(sizeof(*xy));
229   xy->x = 13;
230   xy->y = 37;
231   msg_aux_data_t data = {.ptr = xy};
232   r = dispatch_send(d, 99/*sender*/, 2/*channel*/, 5/*msg*/, 3/*type*/, data);
233   tt_int_op(r, OP_EQ, 0);
234   tt_str_op(received_data, OP_EQ, "[13, 37]");
235 
236  done:
237   dispatch_free(d);
238   dcfg_free(cfg);
239   tor_free(received_data);
240   dispatcher_in_use = NULL;
241 }
242 
243 static void
test_dispatch_bad_type_setup(void * arg)244 test_dispatch_bad_type_setup(void *arg)
245 {
246   (void)arg;
247   static dispatch_typefns_t fns;
248   dispatch_cfg_t *cfg = dcfg_new();
249 
250   tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &coord_fns));
251 
252   fns = coord_fns;
253   fns.fmt_fn = NULL;
254   tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
255 
256   fns = coord_fns;
257   fns.free_fn = NULL;
258   tt_int_op(-1, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
259 
260   fns = coord_fns;
261   tt_int_op(0, OP_EQ, dcfg_type_set_fns(cfg, 7, &fns));
262 
263  done:
264   dcfg_free(cfg);
265 }
266 
267 #define T(name)                                                 \
268   { #name, test_dispatch_ ## name, TT_FORK, NULL, NULL }
269 
270 struct testcase_t dispatch_tests[] = {
271   T(max_in_u16_sl),
272   T(empty),
273   T(simple),
274   T(no_recipient),
275   T(with_types),
276   T(bad_type_setup),
277   END_OF_TESTCASES
278 };
279