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