• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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