| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /*
- * libwebsockets - small server side websockets and web server implementation
- *
- * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- *
- * An abstract transport that is useful for unit testing an abstract protocol.
- * It doesn't actually connect to anything, but checks the protocol's response
- * to provided canned packets from an array of test vectors.
- */
- #include "private-lib-core.h"
- #include "private-lib-abstract.h"
- /* this is the transport priv instantiated at abs->ati */
- typedef struct lws_abstxp_unit_test_priv {
- char note[128];
- struct lws_abs *abs;
- struct lws_sequencer *seq;
- lws_unit_test_t *current_test;
- lws_unit_test_packet_t *expect;
- lws_unit_test_packet_test_cb result_cb;
- const void *result_cb_arg;
- lws_unit_test_packet_disposition disposition;
- /* synthesized protocol timeout */
- time_t timeout;
- uint8_t established:1;
- uint8_t connecting:1;
- } abs_unit_test_priv_t;
- typedef struct seq_priv {
- lws_abs_t *ai;
- } seq_priv_t;
- enum {
- UTSEQ_MSG_WRITEABLE = LWSSEQ_USER_BASE,
- UTSEQ_MSG_CLOSING,
- UTSEQ_MSG_TIMEOUT,
- UTSEQ_MSG_CONNECTING,
- UTSEQ_MSG_POST_TX_KICK,
- UTSEQ_MSG_DISPOSITION_KNOWN
- };
- /*
- * A definitive result has appeared for the current test
- */
- 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)
- {
- assert(priv->disposition == LPE_CONTINUE);
- lwsl_info("%s: %d\n", __func__, disp);
- if (note)
- lws_strncpy(priv->note, note, sizeof(priv->note));
- priv->disposition = disp;
- lws_seq_queue_event(priv->seq, UTSEQ_MSG_DISPOSITION_KNOWN,
- NULL, NULL);
- return disp;
- }
- /*
- * start on the next step of the test
- */
- lws_unit_test_packet_disposition
- process_expect(abs_unit_test_priv_t *priv)
- {
- assert(priv->disposition == LPE_CONTINUE);
- while (priv->expect->flags & LWS_AUT_EXPECT_RX &&
- priv->disposition == LPE_CONTINUE) {
- int f = priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE, s;
- if (priv->expect->pre)
- priv->expect->pre(priv->abs);
- lwsl_info("%s: rx()\n", __func__);
- lwsl_hexdump_debug(priv->expect->buffer, priv->expect->len);
- s = priv->abs->ap->rx(priv->abs->api, priv->expect->buffer,
- priv->expect->len);
- if (!!f != !!s) {
- lwsl_notice("%s: expected rx return %d, got %d\n",
- __func__, !!f, s);
- return lws_unit_test_packet_dispose(priv, LPE_FAILED,
- "rx unexpected return");
- }
- if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
- lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
- break;
- }
- priv->expect++;
- }
- return LPE_CONTINUE;
- }
- static lws_seq_cb_return_t
- unit_test_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
- void *data, void *aux)
- {
- seq_priv_t *s = (seq_priv_t *)user;
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)s->ai->ati;
- time_t now;
- switch ((int)event) {
- case LWSSEQ_CREATED: /* our sequencer just got started */
- lwsl_notice("%s: %s: created\n", __func__,
- lws_seq_name(seq));
- if (s->ai->at->client_conn(s->ai)) {
- lwsl_notice("%s: %s: abstract client conn failed\n",
- __func__, lws_seq_name(seq));
- return LWSSEQ_RET_DESTROY;
- }
- break;
- case LWSSEQ_DESTROYED:
- /*
- * This sequencer is about to be destroyed. If we have any
- * other assets in play, detach them from us.
- */
- if (priv->abs)
- lws_abs_destroy_instance(&priv->abs);
- break;
- case LWSSEQ_HEARTBEAT:
- /* synthesize a wsi-style timeout */
- if (!priv->timeout)
- goto ph;
- time(&now);
- if (now <= priv->timeout)
- goto ph;
- if (priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) {
- lwsl_user("%s: test got expected timeout\n",
- __func__);
- lws_unit_test_packet_dispose(priv,
- LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
- return LWSSEQ_RET_DESTROY;
- }
- lwsl_user("%s: seq timed out\n", __func__);
- ph:
- if (priv->abs->ap->heartbeat)
- priv->abs->ap->heartbeat(priv->abs->api);
- break;
- case UTSEQ_MSG_DISPOSITION_KNOWN:
- lwsl_info("%s: %s: DISPOSITION_KNOWN %s: %s\n", __func__,
- priv->abs->ap->name,
- priv->current_test->name,
- priv->disposition == LPE_SUCCEEDED ? "OK" : "FAIL");
- /*
- * if the test has a callback, call it back to let it
- * know the result
- */
- if (priv->result_cb)
- priv->result_cb(priv->result_cb_arg, priv->disposition);
- return LWSSEQ_RET_DESTROY;
- case UTSEQ_MSG_CONNECTING:
- lwsl_debug("UTSEQ_MSG_CONNECTING\n");
- if (priv->abs->ap->accept)
- priv->abs->ap->accept(priv->abs->api);
- priv->established = 1;
- /* fallthru */
- case UTSEQ_MSG_POST_TX_KICK:
- if (priv->disposition)
- break;
- if (process_expect(priv) != LPE_CONTINUE) {
- lwsl_notice("%s: UTSEQ_MSG_POST_TX_KICK failed\n",
- __func__);
- return LWSSEQ_RET_DESTROY;
- }
- break;
- case UTSEQ_MSG_WRITEABLE:
- /*
- * inform the protocol our transport is writeable now
- */
- priv->abs->ap->writeable(priv->abs->api, 1024);
- break;
- case UTSEQ_MSG_CLOSING:
- if (!(priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE)) {
- lwsl_user("%s: got unexpected close\n", __func__);
- lws_unit_test_packet_dispose(priv,
- LPE_FAILED_UNEXPECTED_CLOSE, NULL);
- goto done;
- }
- /* tell the abstract protocol we are closing on them */
- if (priv->abs && priv->abs->ap->closed)
- priv->abs->ap->closed(priv->abs->api);
- goto done;
- case UTSEQ_MSG_TIMEOUT: /* current step timed out */
- s->ai->at->close(s->ai->ati);
- if (!(priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT)) {
- lwsl_user("%s: got unexpected timeout\n", __func__);
- lws_unit_test_packet_dispose(priv,
- LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
- return LWSSEQ_RET_DESTROY;
- }
- goto done;
- done:
- lws_seq_timeout_us(lws_seq_from_user(s),
- LWSSEQTO_NONE);
- priv->expect++;
- if (!priv->expect->buffer) {
- /* the sequence has completed */
- lwsl_user("%s: sequence completed OK\n", __func__);
- return LWSSEQ_RET_DESTROY;
- }
- break;
- default:
- break;
- }
- return LWSSEQ_RET_CONTINUE;
- }
- static int
- lws_atcut_close(lws_abs_transport_inst_t *ati)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
- lwsl_notice("%s\n", __func__);
- lws_seq_queue_event(priv->seq, UTSEQ_MSG_CLOSING, NULL, NULL);
- return 0;
- }
- static int
- lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
- assert(priv->disposition == LPE_CONTINUE);
- lwsl_info("%s: received tx\n", __func__);
- if (priv->expect->pre)
- priv->expect->pre(priv->abs);
- if (!(priv->expect->flags & LWS_AUT_EXPECT_TX)) {
- lwsl_notice("%s: unexpected tx\n", __func__);
- lwsl_hexdump_notice(buf, len);
- lws_unit_test_packet_dispose(priv, LPE_FAILED, "unexpected tx");
- return 1;
- }
- if (len != priv->expect->len) {
- lwsl_notice("%s: unexpected tx len %zu, expected %zu\n",
- __func__, len, priv->expect->len);
- lws_unit_test_packet_dispose(priv, LPE_FAILED,
- "tx len mismatch");
- return 1;
- }
- if (memcmp(buf, priv->expect->buffer, len)) {
- lwsl_notice("%s: tx mismatch (exp / actual)\n", __func__);
- lwsl_hexdump_debug(priv->expect->buffer, len);
- lwsl_hexdump_debug(buf, len);
- lws_unit_test_packet_dispose(priv, LPE_FAILED,
- "tx data mismatch");
- return 1;
- }
- if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
- lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
- return 1;
- }
- priv->expect++;
- lws_seq_queue_event(priv->seq, UTSEQ_MSG_POST_TX_KICK, NULL, NULL);
- return 0;
- }
- #if defined(LWS_WITH_CLIENT)
- static int
- lws_atcut_client_conn(const lws_abs_t *abs)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)abs->ati;
- const lws_token_map_t *tm;
- if (priv->established) {
- lwsl_err("%s: already established\n", __func__);
- return 1;
- }
- /* set up the test start pieces... the array of test expects... */
- tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_TEST);
- if (!tm) {
- lwsl_notice("%s: unit_test needs LTMI_PEER_V_EXPECT_TEST\n",
- __func__);
- return 1;
- }
- priv->current_test = (lws_unit_test_t *)tm->u.value;
- /* ... and the callback to deliver the result to */
- tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_RESULT_CB);
- if (tm)
- priv->result_cb = (lws_unit_test_packet_test_cb)tm->u.value;
- else
- priv->result_cb = NULL;
- /* ... and the arg to deliver it with */
- tm = lws_abs_get_token(abs->at_tokens,
- LTMI_PEER_V_EXPECT_RESULT_CB_ARG);
- if (tm)
- priv->result_cb_arg = tm->u.value;
- priv->expect = priv->current_test->expect_array;
- priv->disposition = LPE_CONTINUE;
- priv->note[0] = '\0';
- lws_seq_timeout_us(priv->seq, priv->current_test->max_secs *
- LWS_US_PER_SEC);
- lwsl_notice("%s: %s: test '%s': start\n", __func__, abs->ap->name,
- priv->current_test->name);
- lws_seq_queue_event(priv->seq, UTSEQ_MSG_CONNECTING, NULL, NULL);
- return 0;
- }
- #endif
- static int
- lws_atcut_ask_for_writeable(lws_abs_transport_inst_t *ati)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
- if (!priv->established)
- return 1;
- /*
- * Queue a writeable event... this won't be handled by teh sequencer
- * until we have returned to the event loop, just like a real
- * callback_on_writable()
- */
- lws_seq_queue_event(priv->seq, UTSEQ_MSG_WRITEABLE, NULL, NULL);
- return 0;
- }
- /*
- * An abstract protocol + transport has been instantiated
- */
- static int
- lws_atcut_create(lws_abs_t *ai)
- {
- abs_unit_test_priv_t *priv;
- struct lws_sequencer *seq;
- lws_seq_info_t i;
- seq_priv_t *s;
- memset(&i, 0, sizeof(i));
- i.context = ai->vh->context;
- i.user_size = sizeof(*s);
- i.puser = (void **)&s;
- i.cb = unit_test_sequencer_cb;
- i.name = "unit-test-seq";
- /*
- * Create the sequencer for the steps in a single unit test
- */
- seq = lws_seq_create(&i);
- if (!seq) {
- lwsl_err("%s: unable to create sequencer\n", __func__);
- return 1;
- }
- priv = ai->ati;
- memset(s, 0, sizeof(*s));
- memset(priv, 0, sizeof(*priv));
- /* the sequencer priv just points to the lws_abs_t */
- s->ai = ai;
- priv->abs = ai;
- priv->seq = seq;
- return 0;
- }
- static void
- lws_atcut_destroy(lws_abs_transport_inst_t **pati)
- {
- /*
- * We don't free anything because the abstract layer combined our
- * allocation with that of the instance, and it will free the whole
- * thing after this.
- */
- *pati = NULL;
- }
- static int
- lws_atcut_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
- time_t now;
- time(&now);
- if (secs)
- priv->timeout = now + secs;
- else
- priv->timeout = 0;
- return 0;
- }
- static int
- lws_atcut_state(lws_abs_transport_inst_t *ati)
- {
- abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
- if (!priv || (!priv->established && !priv->connecting))
- return 0;
- return 1;
- }
- static const char *dnames[] = {
- "INCOMPLETE",
- "PASS",
- "FAIL",
- "FAIL(TIMEOUT)",
- "FAIL(UNEXPECTED PASS)",
- "FAIL(UNEXPECTED CLOSE)",
- "SKIPPED"
- "?",
- "?"
- };
- const char *
- lws_unit_test_result_name(int in)
- {
- if (in < 0 || in > (int)LWS_ARRAY_SIZE(dnames))
- return "unknown";
- return dnames[in];
- }
- static int
- lws_atcut_compare(lws_abs_t *abs1, lws_abs_t *abs2)
- {
- return 0;
- }
- const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
- .name = "unit_test",
- .alloc = sizeof(abs_unit_test_priv_t),
- .create = lws_atcut_create,
- .destroy = lws_atcut_destroy,
- .compare = lws_atcut_compare,
- .tx = lws_atcut_tx,
- #if !defined(LWS_WITH_CLIENT)
- .client_conn = NULL,
- #else
- .client_conn = lws_atcut_client_conn,
- #endif
- .close = lws_atcut_close,
- .ask_for_writeable = lws_atcut_ask_for_writeable,
- .set_timeout = lws_atcut_set_timeout,
- .state = lws_atcut_state,
- };
|