1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * An abstract transport that is useful for unit testing an abstract protocol.
25 * It doesn't actually connect to anything, but checks the protocol's response
26 * to provided canned packets from an array of test vectors.
27 */
28
29 #include "private-lib-core.h"
30 #include "private-lib-abstract.h"
31
32 /* this is the transport priv instantiated at abs->ati */
33
34 typedef struct lws_abstxp_unit_test_priv {
35 char note[128];
36 struct lws_abs *abs;
37
38 struct lws_sequencer *seq;
39 lws_unit_test_t *current_test;
40 lws_unit_test_packet_t *expect;
41 lws_unit_test_packet_test_cb result_cb;
42 const void *result_cb_arg;
43
44 lws_unit_test_packet_disposition disposition;
45 /* synthesized protocol timeout */
46 time_t timeout;
47
48 uint8_t established:1;
49 uint8_t connecting:1;
50 } abs_unit_test_priv_t;
51
52 typedef struct seq_priv {
53 lws_abs_t *ai;
54 } seq_priv_t;
55
56 enum {
57 UTSEQ_MSG_WRITEABLE = LWSSEQ_USER_BASE,
58 UTSEQ_MSG_CLOSING,
59 UTSEQ_MSG_TIMEOUT,
60 UTSEQ_MSG_CONNECTING,
61 UTSEQ_MSG_POST_TX_KICK,
62 UTSEQ_MSG_DISPOSITION_KNOWN
63 };
64
65 /*
66 * A definitive result has appeared for the current test
67 */
68
69 static lws_unit_test_packet_disposition
lws_unit_test_packet_dispose(abs_unit_test_priv_t * priv,lws_unit_test_packet_disposition disp,const char * note)70 lws_unit_test_packet_dispose(abs_unit_test_priv_t *priv,
71 lws_unit_test_packet_disposition disp,
72 const char *note)
73 {
74 assert(priv->disposition == LPE_CONTINUE);
75
76 lwsl_info("%s: %d\n", __func__, disp);
77
78 if (note)
79 lws_strncpy(priv->note, note, sizeof(priv->note));
80
81 priv->disposition = disp;
82
83 lws_seq_queue_event(priv->seq, UTSEQ_MSG_DISPOSITION_KNOWN,
84 NULL, NULL);
85
86 return disp;
87 }
88
89 /*
90 * start on the next step of the test
91 */
92
93 lws_unit_test_packet_disposition
process_expect(abs_unit_test_priv_t * priv)94 process_expect(abs_unit_test_priv_t *priv)
95 {
96 assert(priv->disposition == LPE_CONTINUE);
97
98 while (priv->expect->flags & LWS_AUT_EXPECT_RX &&
99 priv->disposition == LPE_CONTINUE) {
100 int f = priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE, s;
101
102 if (priv->expect->pre)
103 priv->expect->pre(priv->abs);
104
105 lwsl_info("%s: rx()\n", __func__);
106 lwsl_hexdump_debug(priv->expect->buffer, priv->expect->len);
107 s = priv->abs->ap->rx(priv->abs->api, priv->expect->buffer,
108 priv->expect->len);
109
110 if (!!f != !!s) {
111 lwsl_notice("%s: expected rx return %d, got %d\n",
112 __func__, !!f, s);
113
114 return lws_unit_test_packet_dispose(priv, LPE_FAILED,
115 "rx unexpected return");
116 }
117
118 if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
119 lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
120 break;
121 }
122
123 priv->expect++;
124 }
125
126 return LPE_CONTINUE;
127 }
128
129 static lws_seq_cb_return_t
unit_test_sequencer_cb(struct lws_sequencer * seq,void * user,int event,void * data,void * aux)130 unit_test_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
131 void *data, void *aux)
132 {
133 seq_priv_t *s = (seq_priv_t *)user;
134 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)s->ai->ati;
135 time_t now;
136
137 switch ((int)event) {
138 case LWSSEQ_CREATED: /* our sequencer just got started */
139 lwsl_notice("%s: %s: created\n", __func__,
140 lws_seq_name(seq));
141 if (s->ai->at->client_conn(s->ai)) {
142 lwsl_notice("%s: %s: abstract client conn failed\n",
143 __func__, lws_seq_name(seq));
144
145 return LWSSEQ_RET_DESTROY;
146 }
147 break;
148
149 case LWSSEQ_DESTROYED:
150 /*
151 * This sequencer is about to be destroyed. If we have any
152 * other assets in play, detach them from us.
153 */
154
155 if (priv->abs)
156 lws_abs_destroy_instance(&priv->abs);
157
158 break;
159
160 case LWSSEQ_HEARTBEAT:
161
162 /* synthesize a wsi-style timeout */
163
164 if (!priv->timeout)
165 goto ph;
166
167 time(&now);
168
169 if (now <= priv->timeout)
170 goto ph;
171
172 if (priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) {
173 lwsl_user("%s: test got expected timeout\n",
174 __func__);
175 lws_unit_test_packet_dispose(priv,
176 LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
177
178 return LWSSEQ_RET_DESTROY;
179 }
180 lwsl_user("%s: seq timed out\n", __func__);
181
182 ph:
183 if (priv->abs->ap->heartbeat)
184 priv->abs->ap->heartbeat(priv->abs->api);
185 break;
186
187 case UTSEQ_MSG_DISPOSITION_KNOWN:
188
189 lwsl_info("%s: %s: DISPOSITION_KNOWN %s: %s\n", __func__,
190 priv->abs->ap->name,
191 priv->current_test->name,
192 priv->disposition == LPE_SUCCEEDED ? "OK" : "FAIL");
193
194 /*
195 * if the test has a callback, call it back to let it
196 * know the result
197 */
198 if (priv->result_cb)
199 priv->result_cb(priv->result_cb_arg, priv->disposition);
200
201 return LWSSEQ_RET_DESTROY;
202
203 case UTSEQ_MSG_CONNECTING:
204 lwsl_debug("UTSEQ_MSG_CONNECTING\n");
205
206 if (priv->abs->ap->accept)
207 priv->abs->ap->accept(priv->abs->api);
208
209 priv->established = 1;
210
211 /* fallthru */
212
213 case UTSEQ_MSG_POST_TX_KICK:
214 if (priv->disposition)
215 break;
216
217 if (process_expect(priv) != LPE_CONTINUE) {
218 lwsl_notice("%s: UTSEQ_MSG_POST_TX_KICK failed\n",
219 __func__);
220 return LWSSEQ_RET_DESTROY;
221 }
222 break;
223
224 case UTSEQ_MSG_WRITEABLE:
225 /*
226 * inform the protocol our transport is writeable now
227 */
228 priv->abs->ap->writeable(priv->abs->api, 1024);
229 break;
230
231 case UTSEQ_MSG_CLOSING:
232
233 if (!(priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE)) {
234 lwsl_user("%s: got unexpected close\n", __func__);
235
236 lws_unit_test_packet_dispose(priv,
237 LPE_FAILED_UNEXPECTED_CLOSE, NULL);
238 goto done;
239 }
240
241 /* tell the abstract protocol we are closing on them */
242
243 if (priv->abs && priv->abs->ap->closed)
244 priv->abs->ap->closed(priv->abs->api);
245
246 goto done;
247
248 case UTSEQ_MSG_TIMEOUT: /* current step timed out */
249
250 s->ai->at->close(s->ai->ati);
251
252 if (!(priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT)) {
253 lwsl_user("%s: got unexpected timeout\n", __func__);
254
255 lws_unit_test_packet_dispose(priv,
256 LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
257 return LWSSEQ_RET_DESTROY;
258 }
259 goto done;
260
261 done:
262 lws_seq_timeout_us(lws_seq_from_user(s),
263 LWSSEQTO_NONE);
264 priv->expect++;
265 if (!priv->expect->buffer) {
266 /* the sequence has completed */
267 lwsl_user("%s: sequence completed OK\n", __func__);
268
269 return LWSSEQ_RET_DESTROY;
270 }
271 break;
272
273 default:
274 break;
275 }
276
277 return LWSSEQ_RET_CONTINUE;
278 }
279
280 static int
lws_atcut_close(lws_abs_transport_inst_t * ati)281 lws_atcut_close(lws_abs_transport_inst_t *ati)
282 {
283 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
284
285 lwsl_notice("%s\n", __func__);
286
287 lws_seq_queue_event(priv->seq, UTSEQ_MSG_CLOSING, NULL, NULL);
288
289 return 0;
290 }
291
292 static int
lws_atcut_tx(lws_abs_transport_inst_t * ati,uint8_t * buf,size_t len)293 lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
294 {
295 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
296
297 assert(priv->disposition == LPE_CONTINUE);
298
299 lwsl_info("%s: received tx\n", __func__);
300
301 if (priv->expect->pre)
302 priv->expect->pre(priv->abs);
303
304 if (!(priv->expect->flags & LWS_AUT_EXPECT_TX)) {
305 lwsl_notice("%s: unexpected tx\n", __func__);
306 lwsl_hexdump_notice(buf, len);
307 lws_unit_test_packet_dispose(priv, LPE_FAILED, "unexpected tx");
308
309 return 1;
310 }
311
312 if (len != priv->expect->len) {
313 lwsl_notice("%s: unexpected tx len %zu, expected %zu\n",
314 __func__, len, priv->expect->len);
315 lws_unit_test_packet_dispose(priv, LPE_FAILED,
316 "tx len mismatch");
317
318 return 1;
319 }
320
321 if (memcmp(buf, priv->expect->buffer, len)) {
322 lwsl_notice("%s: tx mismatch (exp / actual)\n", __func__);
323 lwsl_hexdump_debug(priv->expect->buffer, len);
324 lwsl_hexdump_debug(buf, len);
325 lws_unit_test_packet_dispose(priv, LPE_FAILED,
326 "tx data mismatch");
327
328 return 1;
329 }
330
331 if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
332 lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
333
334 return 1;
335 }
336
337 priv->expect++;
338
339 lws_seq_queue_event(priv->seq, UTSEQ_MSG_POST_TX_KICK, NULL, NULL);
340
341 return 0;
342 }
343
344 #if defined(LWS_WITH_CLIENT)
345 static int
lws_atcut_client_conn(const lws_abs_t * abs)346 lws_atcut_client_conn(const lws_abs_t *abs)
347 {
348 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)abs->ati;
349 const lws_token_map_t *tm;
350
351 if (priv->established) {
352 lwsl_err("%s: already established\n", __func__);
353 return 1;
354 }
355
356 /* set up the test start pieces... the array of test expects... */
357
358 tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_TEST);
359 if (!tm) {
360 lwsl_notice("%s: unit_test needs LTMI_PEER_V_EXPECT_TEST\n",
361 __func__);
362
363 return 1;
364 }
365 priv->current_test = (lws_unit_test_t *)tm->u.value;
366
367 /* ... and the callback to deliver the result to */
368 tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_RESULT_CB);
369 if (tm)
370 priv->result_cb = (lws_unit_test_packet_test_cb)tm->u.value;
371 else
372 priv->result_cb = NULL;
373
374 /* ... and the arg to deliver it with */
375 tm = lws_abs_get_token(abs->at_tokens,
376 LTMI_PEER_V_EXPECT_RESULT_CB_ARG);
377 if (tm)
378 priv->result_cb_arg = tm->u.value;
379
380 priv->expect = priv->current_test->expect_array;
381 priv->disposition = LPE_CONTINUE;
382 priv->note[0] = '\0';
383
384 lws_seq_timeout_us(priv->seq, priv->current_test->max_secs *
385 LWS_US_PER_SEC);
386
387 lwsl_notice("%s: %s: test '%s': start\n", __func__, abs->ap->name,
388 priv->current_test->name);
389
390 lws_seq_queue_event(priv->seq, UTSEQ_MSG_CONNECTING, NULL, NULL);
391
392 return 0;
393 }
394 #endif
395
396 static int
lws_atcut_ask_for_writeable(lws_abs_transport_inst_t * ati)397 lws_atcut_ask_for_writeable(lws_abs_transport_inst_t *ati)
398 {
399 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
400
401 if (!priv->established)
402 return 1;
403
404 /*
405 * Queue a writeable event... this won't be handled by teh sequencer
406 * until we have returned to the event loop, just like a real
407 * callback_on_writable()
408 */
409 lws_seq_queue_event(priv->seq, UTSEQ_MSG_WRITEABLE, NULL, NULL);
410
411 return 0;
412 }
413
414 /*
415 * An abstract protocol + transport has been instantiated
416 */
417
418 static int
lws_atcut_create(lws_abs_t * ai)419 lws_atcut_create(lws_abs_t *ai)
420 {
421 abs_unit_test_priv_t *priv;
422 struct lws_sequencer *seq;
423 lws_seq_info_t i;
424 seq_priv_t *s;
425
426 memset(&i, 0, sizeof(i));
427 i.context = ai->vh->context;
428 i.user_size = sizeof(*s);
429 i.puser = (void **)&s;
430 i.cb = unit_test_sequencer_cb;
431 i.name = "unit-test-seq";
432
433 /*
434 * Create the sequencer for the steps in a single unit test
435 */
436
437 seq = lws_seq_create(&i);
438 if (!seq) {
439 lwsl_err("%s: unable to create sequencer\n", __func__);
440
441 return 1;
442 }
443
444 priv = ai->ati;
445 memset(s, 0, sizeof(*s));
446 memset(priv, 0, sizeof(*priv));
447
448 /* the sequencer priv just points to the lws_abs_t */
449 s->ai = ai;
450 priv->abs = ai;
451 priv->seq = seq;
452
453 return 0;
454 }
455
456 static void
lws_atcut_destroy(lws_abs_transport_inst_t ** pati)457 lws_atcut_destroy(lws_abs_transport_inst_t **pati)
458 {
459 /*
460 * We don't free anything because the abstract layer combined our
461 * allocation with that of the instance, and it will free the whole
462 * thing after this.
463 */
464 *pati = NULL;
465 }
466
467 static int
lws_atcut_set_timeout(lws_abs_transport_inst_t * ati,int reason,int secs)468 lws_atcut_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs)
469 {
470 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
471 time_t now;
472
473 time(&now);
474
475 if (secs)
476 priv->timeout = now + secs;
477 else
478 priv->timeout = 0;
479
480 return 0;
481 }
482
483 static int
lws_atcut_state(lws_abs_transport_inst_t * ati)484 lws_atcut_state(lws_abs_transport_inst_t *ati)
485 {
486 abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
487
488 if (!priv || (!priv->established && !priv->connecting))
489 return 0;
490
491 return 1;
492 }
493
494 static const char *dnames[] = {
495 "INCOMPLETE",
496 "PASS",
497 "FAIL",
498 "FAIL(TIMEOUT)",
499 "FAIL(UNEXPECTED PASS)",
500 "FAIL(UNEXPECTED CLOSE)",
501 "SKIPPED"
502 "?",
503 "?"
504 };
505
506
507 const char *
lws_unit_test_result_name(int in)508 lws_unit_test_result_name(int in)
509 {
510 if (in < 0 || in > (int)LWS_ARRAY_SIZE(dnames))
511 return "unknown";
512
513 return dnames[in];
514 }
515
516 static int
lws_atcut_compare(lws_abs_t * abs1,lws_abs_t * abs2)517 lws_atcut_compare(lws_abs_t *abs1, lws_abs_t *abs2)
518 {
519 return 0;
520 }
521
522 const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
523 .name = "unit_test",
524 .alloc = sizeof(abs_unit_test_priv_t),
525
526 .create = lws_atcut_create,
527 .destroy = lws_atcut_destroy,
528 .compare = lws_atcut_compare,
529
530 .tx = lws_atcut_tx,
531 #if !defined(LWS_WITH_CLIENT)
532 .client_conn = NULL,
533 #else
534 .client_conn = lws_atcut_client_conn,
535 #endif
536 .close = lws_atcut_close,
537 .ask_for_writeable = lws_atcut_ask_for_writeable,
538 .set_timeout = lws_atcut_set_timeout,
539 .state = lws_atcut_state,
540 };
541