mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
unit test sequencer
This commit is contained in:
parent
604a718e92
commit
a7e1bac4ac
18 changed files with 954 additions and 404 deletions
|
@ -927,7 +927,9 @@ if (LWS_WITH_NETWORK)
|
|||
)
|
||||
if (LWS_WITH_ABSTRACT)
|
||||
list(APPEND SOURCES
|
||||
lib/abstract/abstract.c)
|
||||
lib/abstract/abstract.c
|
||||
lib/abstract/test-sequencer.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_STATS)
|
||||
|
|
|
@ -534,6 +534,8 @@ struct lws;
|
|||
|
||||
#include <libwebsockets/abstract/abstract.h>
|
||||
|
||||
#include <libwebsockets/lws-test-sequencer.h>
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
|
|
|
@ -17,6 +17,12 @@
|
|||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is an abstract transport useful for unit testing abstract protocols.
|
||||
*
|
||||
* Instead of passing data anywhere, you give the transport a list of packets
|
||||
* to deliver and packets you expect back from the abstract protocol it's
|
||||
* bound to.
|
||||
*/
|
||||
|
||||
enum {
|
||||
|
@ -25,30 +31,48 @@ enum {
|
|||
LWS_AUT_EXPECT_DO_REMOTE_CLOSE = (1 << 2),
|
||||
LWS_AUT_EXPECT_TX /* expect this as tx from protocol */ = (1 << 3),
|
||||
LWS_AUT_EXPECT_RX /* present this as rx to protocol */ = (1 << 4),
|
||||
LWS_AUT_EXPECT_SHOULD_FAIL = (1 << 5),
|
||||
LWS_AUT_EXPECT_SHOULD_TIMEOUT = (1 << 6),
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LPE_CONTINUE,
|
||||
LPE_SUCCEEDED,
|
||||
LPE_FAILED,
|
||||
} lws_expect_disposition;
|
||||
LPE_FAILED_UNEXPECTED_TIMEOUT,
|
||||
LPE_FAILED_UNEXPECTED_PASS,
|
||||
LPE_FAILED_UNEXPECTED_CLOSE,
|
||||
LPE_SKIPPED,
|
||||
LPE_CLOSING
|
||||
} lws_unit_test_packet_disposition;
|
||||
|
||||
typedef struct lws_expect {
|
||||
void *buffer;
|
||||
size_t len;
|
||||
typedef int (*lws_unit_test_packet_test_cb)(const void *cb_user, int disposition);
|
||||
typedef int (*lws_unit_test_packet_cb)(lws_abs_t *instance);
|
||||
|
||||
uint32_t flags;
|
||||
} lws_expect_t;
|
||||
/* each step in the unit test */
|
||||
|
||||
typedef int (*lws_expect_test_instance_init)(lws_abs_t *instance);
|
||||
typedef struct lws_unit_test_packet {
|
||||
void *buffer;
|
||||
lws_unit_test_packet_cb pre;
|
||||
size_t len;
|
||||
|
||||
typedef struct lws_expect_test {
|
||||
const char *name; /* NULL indicates end of test array */
|
||||
lws_expect_t *expect;
|
||||
lws_expect_test_instance_init *init;
|
||||
} lws_expect_test_t;
|
||||
uint32_t flags;
|
||||
} lws_unit_test_packet_t;
|
||||
|
||||
/* each unit test */
|
||||
|
||||
typedef struct lws_unit_test {
|
||||
const char * name; /* NULL indicates end of test array */
|
||||
lws_unit_test_packet_t * expect_array;
|
||||
int max_secs;
|
||||
} lws_unit_test_t;
|
||||
|
||||
enum {
|
||||
LTMI_PEER_V_EXPECT_TEST = LTMI_TRANSPORT_BASE, /* u.value */
|
||||
LTMI_PEER_V_EXPECT_TEST_ARRAY, /* u.value */
|
||||
LTMI_PEER_V_EXPECT_RESULT_CB, /* u.value */
|
||||
LTMI_PEER_V_EXPECT_RESULT_CB_ARG, /* u.value */
|
||||
};
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_unit_test_result_name(int in);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ typedef enum {
|
|||
LWSSEQ_CREATED, /* sequencer created */
|
||||
LWSSEQ_DESTROYED, /* sequencer destroyed */
|
||||
LWSSEQ_TIMED_OUT, /* sequencer timeout */
|
||||
LWSSEQ_HEARTBEAT, /* 1Hz callback */
|
||||
|
||||
LWSSEQ_USER_BASE = 100 /* define your events from here */
|
||||
} lws_seq_events_t;
|
||||
|
@ -75,6 +76,7 @@ typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq,
|
|||
* user_size. The user area pointed to here is all zeroed
|
||||
* after successful sequencer creation.
|
||||
* \param cb: callback for events on this sequencer
|
||||
* \param name: Used in sequencer logging
|
||||
*
|
||||
* This binds an abstract sequencer to a per-thread (by default, the single
|
||||
* event loop of an lws_context). After the event loop starts, the sequencer
|
||||
|
@ -88,7 +90,7 @@ typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq,
|
|||
*/
|
||||
LWS_VISIBLE LWS_EXTERN lws_sequencer_t *
|
||||
lws_sequencer_create(struct lws_context *context, int tsi, size_t user_size,
|
||||
void **puser, lws_seq_event_cb cb);
|
||||
void **puser, lws_seq_event_cb cb, const char *name);
|
||||
|
||||
/**
|
||||
* lws_sequencer_destroy() - destroy the sequencer
|
||||
|
@ -171,3 +173,14 @@ lws_sequencer_from_user(void *u);
|
|||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sequencer_secs_since_creation(lws_sequencer_t *seq);
|
||||
|
||||
/**
|
||||
* lws_sequencer_name(): get the name of this sequencer
|
||||
*
|
||||
* \param seq: pointer to the lws_sequencer_t
|
||||
*
|
||||
* Returns the name given when the sequencer was created. This is useful to
|
||||
* annotate logging when then are multiple sequencers in play.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_sequencer_name(lws_sequencer_t *seq);
|
||||
|
|
60
include/libwebsockets/lws-test-sequencer.h
Normal file
60
include/libwebsockets/lws-test-sequencer.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* included from libwebsockets.h
|
||||
*
|
||||
* lws_test_sequencer manages running an array of unit tests.
|
||||
*/
|
||||
|
||||
typedef void (*lws_test_sequence_cb)(const void *cb_user);
|
||||
|
||||
typedef struct lws_test_sequencer_args {
|
||||
lws_abs_t *abs; /* abstract protocol + unit test txport */
|
||||
lws_unit_test_t *tests; /* array of lws_unit_test_t */
|
||||
int *results; /* takes result dispositions */
|
||||
int results_max; /* max space usable in results */
|
||||
int *count_tests; /* count of done tests */
|
||||
int *count_passes; /* count of passed tests */
|
||||
lws_test_sequence_cb cb; /* completion callback */
|
||||
void *cb_user; /* opaque user ptr given to cb */
|
||||
} lws_test_sequencer_args_t;
|
||||
|
||||
/**
|
||||
* lws_abs_unit_test_sequencer() - helper to sequence multiple unit tests
|
||||
*
|
||||
* \param args: lws_test_sequencer_args_t prepared with arguments for the tests
|
||||
*
|
||||
* This helper sequences one or more unit tests to run and collects the results.
|
||||
*
|
||||
* The incoming abs should be set up for the abstract protocol you want to test
|
||||
* and the lws unit-test transport.
|
||||
*
|
||||
* Results are one of
|
||||
*
|
||||
* LPE_SUCCEEDED
|
||||
* LPE_FAILED
|
||||
* LPE_FAILED_UNEXPECTED_TIMEOUT
|
||||
* LPE_FAILED_UNEXPECTED_PASS
|
||||
* LPE_FAILED_UNEXPECTED_CLOSE
|
||||
*
|
||||
* The callback args->cb is called when the tests have been done.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args);
|
|
@ -153,3 +153,18 @@ static const lws_token_map_t smtp_abs_tokens[] = {
|
|||
- add your `lws_abs_transport` to the list `available_abs_transports` in
|
||||
`./lib/abstract/abstract.c`
|
||||
|
||||
# Protocol testing
|
||||
|
||||
## unit tests
|
||||
|
||||
lws features an abstract transport designed to facilitate unit testing. This
|
||||
contains an lws_sequencer that performs the steps of tests involving sending the
|
||||
protocol test vector buffers and confirming the response of the protocol matches
|
||||
the test vectors.
|
||||
|
||||
## test-sequencer
|
||||
|
||||
test-sequencer is a helper that sequences running an array of unit tests and
|
||||
collects the statistics and gives a PASS / FAIL result.
|
||||
|
||||
See the SMTP client api test for an example of how to use.
|
||||
|
|
|
@ -89,15 +89,17 @@ lws_abs_get_token(const lws_token_map_t *token_map, short name_index)
|
|||
void
|
||||
lws_abs_destroy_instance(lws_abs_t **ai)
|
||||
{
|
||||
if ((*ai)->api)
|
||||
(*ai)->ap->destroy(&(*ai)->api);
|
||||
if ((*ai)->ati)
|
||||
(*ai)->at->destroy(&(*ai)->ati);
|
||||
lws_abs_t *a = *ai;
|
||||
|
||||
lws_dll2_remove(&(*ai)->abstract_instances);
|
||||
if (a->api)
|
||||
a->ap->destroy(&a->api);
|
||||
if (a->ati)
|
||||
a->at->destroy(&a->ati);
|
||||
|
||||
lws_dll2_remove(&a->abstract_instances);
|
||||
|
||||
free(*ai);
|
||||
*ai = NULL;
|
||||
free(a);
|
||||
}
|
||||
|
||||
lws_abs_t *
|
||||
|
|
|
@ -174,8 +174,6 @@ lws_smtp_client_abs_rx(lws_abs_protocol_inst_t *api, uint8_t *buf, size_t len)
|
|||
if (!e)
|
||||
return 0;
|
||||
|
||||
lwsl_debug("%s: rx: '%.*s'\n", __func__, (int)len, (const char *)buf);
|
||||
|
||||
n = atoi((char *)buf);
|
||||
if (n != retcodes[c->estate]) {
|
||||
lwsl_notice("%s: bad response from server: %d (state %d) %.*s\n",
|
||||
|
|
265
lib/abstract/test-sequencer.c
Normal file
265
lib/abstract/test-sequencer.c
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* libwebsockets lib/abstract/test-sequencer.c
|
||||
*
|
||||
* Copyright (C) 2019 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
*
|
||||
* A helper for running multiple unit tests against abstract protocols.
|
||||
*
|
||||
* An lws_sequencer_t is used to base its actions in the event loop and manage
|
||||
* the sequencing of multiple tests. A new abstract connection is instantiated
|
||||
* for each test using te
|
||||
*/
|
||||
|
||||
#include <core/private.h>
|
||||
|
||||
struct lws_seq_test_sequencer {
|
||||
lws_abs_t original_abs;
|
||||
|
||||
lws_test_sequencer_args_t args;
|
||||
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost;
|
||||
lws_sequencer_t *unit_test_seq;
|
||||
|
||||
/* holds the per-test token for the unit-test transport to consume */
|
||||
lws_token_map_t uttt[4];
|
||||
|
||||
lws_abs_t *instance;
|
||||
|
||||
int state;
|
||||
};
|
||||
|
||||
/* sequencer messages specific to this sequencer */
|
||||
|
||||
enum {
|
||||
SEQ_MSG_PASS = LWSSEQ_USER_BASE,
|
||||
SEQ_MSG_FAIL,
|
||||
SEQ_MSG_FAIL_TIMEOUT,
|
||||
};
|
||||
|
||||
/*
|
||||
* We get called back when the unit test transport has decided if the test
|
||||
* passed or failed. We get the priv, and report to the sequencer message queue
|
||||
* what the result was.
|
||||
*/
|
||||
|
||||
static int
|
||||
unit_test_result_cb(const void *cb_user, int disposition)
|
||||
{
|
||||
const struct lws_seq_test_sequencer *s =
|
||||
(const struct lws_seq_test_sequencer *)cb_user;
|
||||
int r;
|
||||
|
||||
lwsl_debug("%s: disp %d\n", __func__, disposition);
|
||||
|
||||
switch (disposition) {
|
||||
case LPE_FAILED_UNEXPECTED_PASS:
|
||||
case LPE_FAILED_UNEXPECTED_CLOSE:
|
||||
case LPE_FAILED:
|
||||
r = SEQ_MSG_FAIL;
|
||||
break;
|
||||
|
||||
case LPE_FAILED_UNEXPECTED_TIMEOUT:
|
||||
r = SEQ_MSG_FAIL_TIMEOUT;
|
||||
break;
|
||||
|
||||
case LPE_SUCCEEDED:
|
||||
r = SEQ_MSG_PASS;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_sequencer_event(s->unit_test_seq, r, NULL);
|
||||
|
||||
((struct lws_seq_test_sequencer *)s)->instance = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We receive the unit test result callback's messages via the message queue.
|
||||
*
|
||||
* We log the results and always move on to the next test until there are no
|
||||
* more tests.
|
||||
*/
|
||||
|
||||
static lws_seq_cb_return_t
|
||||
test_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
|
||||
{
|
||||
struct lws_seq_test_sequencer *s =
|
||||
(struct lws_seq_test_sequencer *)user;
|
||||
lws_unit_test_packet_t *exp = (lws_unit_test_packet_t *)
|
||||
s->args.tests[s->state].expect_array;
|
||||
lws_abs_t test_abs;
|
||||
|
||||
switch ((int)event) {
|
||||
case LWSSEQ_CREATED: /* our sequencer just got started */
|
||||
lwsl_notice("%s: %s: created\n", __func__,
|
||||
lws_sequencer_name(seq));
|
||||
s->state = 0; /* first thing we'll do is the first url */
|
||||
goto step;
|
||||
|
||||
case LWSSEQ_DESTROYED:
|
||||
/*
|
||||
* We are going down... if we have a child unit test sequencer
|
||||
* still around inform and destroy it
|
||||
*/
|
||||
if (s->instance) {
|
||||
s->instance->at->close(s->instance);
|
||||
s->instance = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case SEQ_MSG_FAIL_TIMEOUT: /* current step timed out */
|
||||
if (exp->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) {
|
||||
lwsl_user("%s: test %d got expected timeout\n",
|
||||
__func__, s->state);
|
||||
|
||||
goto pass;
|
||||
}
|
||||
lwsl_user("%s: seq timed out at step %d\n", __func__, s->state);
|
||||
|
||||
s->args.results[s->state] = LPE_FAILED_UNEXPECTED_TIMEOUT;
|
||||
goto done; /* always move on to the next test */
|
||||
|
||||
case SEQ_MSG_FAIL:
|
||||
if (exp->flags & LWS_AUT_EXPECT_SHOULD_FAIL) {
|
||||
/*
|
||||
* in this case, we expected to fail like this, it's OK
|
||||
*/
|
||||
lwsl_user("%s: test %d failed as expected\n",
|
||||
__func__, s->state);
|
||||
|
||||
goto pass; /* always move on to the next test */
|
||||
}
|
||||
|
||||
lwsl_user("%s: seq failed at step %d\n", __func__, s->state);
|
||||
|
||||
s->args.results[s->state] = LPE_FAILED;
|
||||
goto done; /* always move on to the next test */
|
||||
|
||||
case SEQ_MSG_PASS:
|
||||
if (exp->flags & (LWS_AUT_EXPECT_SHOULD_FAIL |
|
||||
LWS_AUT_EXPECT_SHOULD_TIMEOUT)) {
|
||||
/*
|
||||
* In these specific cases, done would be a failure,
|
||||
* we expected to timeout or fail
|
||||
*/
|
||||
lwsl_user("%s: seq failed at step %d\n", __func__,
|
||||
s->state);
|
||||
|
||||
s->args.results[s->state] = LPE_FAILED_UNEXPECTED_PASS;
|
||||
|
||||
goto done; /* always move on to the next test */
|
||||
}
|
||||
lwsl_info("%s: seq done test %d\n", __func__, s->state);
|
||||
pass:
|
||||
(*s->args.count_passes)++;
|
||||
s->args.results[s->state] = LPE_SUCCEEDED;
|
||||
|
||||
done:
|
||||
lws_sequencer_timeout(lws_sequencer_from_user(s), 0);
|
||||
s->state++;
|
||||
step:
|
||||
if (!s->args.tests[s->state].name) {
|
||||
/* the sequence has completed */
|
||||
lwsl_user("%s: sequence completed OK\n", __func__);
|
||||
|
||||
if (s->args.cb)
|
||||
s->args.cb(s->args.cb_user);
|
||||
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
}
|
||||
lwsl_info("%s: starting test %d\n", __func__, s->state);
|
||||
|
||||
if (s->state >= s->args.results_max) {
|
||||
lwsl_err("%s: results array is too small\n", __func__);
|
||||
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
}
|
||||
test_abs = s->original_abs;
|
||||
s->uttt[0].name_index = LTMI_PEER_V_EXPECT_TEST;
|
||||
s->uttt[0].u.value = (void *)&s->args.tests[s->state];
|
||||
s->uttt[1].name_index = LTMI_PEER_V_EXPECT_RESULT_CB;
|
||||
s->uttt[1].u.value = (void *)unit_test_result_cb;
|
||||
s->uttt[2].name_index = LTMI_PEER_V_EXPECT_RESULT_CB_ARG;
|
||||
s->uttt[2].u.value = (void *)s;
|
||||
/* give the unit test transport the test tokens */
|
||||
test_abs.at_tokens = s->uttt;
|
||||
|
||||
s->instance = lws_abs_bind_and_create_instance(&test_abs);
|
||||
if (!s->instance) {
|
||||
lwsl_notice("%s: failed to create step %d unit test\n",
|
||||
__func__, s->state);
|
||||
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
}
|
||||
(*s->args.count_tests)++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LWSSEQ_RET_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creates an lws_sequencer to manage the test sequence
|
||||
*/
|
||||
|
||||
int
|
||||
lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args)
|
||||
{
|
||||
struct lws_seq_test_sequencer *s;
|
||||
lws_sequencer_t *seq;
|
||||
|
||||
/*
|
||||
* Create a sequencer in the event loop to manage the tests
|
||||
*/
|
||||
|
||||
seq = lws_sequencer_create(args->abs->vh->context, 0,
|
||||
sizeof(struct lws_seq_test_sequencer),
|
||||
(void **)&s, test_sequencer_cb, "test-seq");
|
||||
if (!seq) {
|
||||
lwsl_err("%s: unable to create sequencer\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a copy of the original lws_abs_t we were passed so we can use
|
||||
* it as the basis of the lws_abs_t we create the individual tests with
|
||||
*/
|
||||
s->original_abs = *args->abs;
|
||||
|
||||
s->args = *args;
|
||||
|
||||
s->context = args->abs->vh->context;
|
||||
s->vhost = args->abs->vh;
|
||||
s->unit_test_seq = seq;
|
||||
|
||||
*s->args.count_tests = 0;
|
||||
*s->args.count_passes = 0;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -18,57 +18,67 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
*
|
||||
* 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 various canned packets.
|
||||
*
|
||||
* Although it doesn't use any socket itself, it still needs to respect the
|
||||
* event loop so it can reflect the associated behaviours correctly. So it
|
||||
* creates a wsi for these purposes, which is a RAW FILE open on /dev/null.
|
||||
* to provided canned packets from an array of test vectors.
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
#include "abstract/private.h"
|
||||
|
||||
/* this is the transport priv instantiated at abs->ati */
|
||||
|
||||
typedef struct lws_abstxp_unit_test_priv {
|
||||
char note[128];
|
||||
struct lws_abs *abs;
|
||||
char note[128];
|
||||
struct lws_abs *abs;
|
||||
|
||||
struct lws *wsi;
|
||||
lws_expect_test_t *current_test;
|
||||
lws_expect_t *expect;
|
||||
lws_expect_disposition disposition;
|
||||
int filefd;
|
||||
lws_sequencer_t *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_dll2_t same_abs_transport_list;
|
||||
lws_unit_test_packet_disposition disposition;
|
||||
/* synthesized protocol timeout */
|
||||
time_t timeout;
|
||||
|
||||
uint8_t established:1;
|
||||
uint8_t connecting:1;
|
||||
uint8_t established:1;
|
||||
uint8_t connecting:1;
|
||||
} abs_unit_test_priv_t;
|
||||
|
||||
struct vhd {
|
||||
lws_dll2_owner_t owner;
|
||||
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_expect_disposition
|
||||
lws_expect_dispose(abs_unit_test_priv_t *priv, lws_expect_disposition disp,
|
||||
const char *note)
|
||||
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;
|
||||
|
||||
lwsl_user("%s: %s: test %d: %s\n", priv->abs->ap->name,
|
||||
priv->current_test->name,
|
||||
(int)(priv->expect - priv->current_test->expect),
|
||||
disp == LPE_SUCCEEDED ? "OK" : "FAIL");
|
||||
lws_sequencer_event(priv->seq, UTSEQ_MSG_DISPOSITION_KNOWN, NULL);
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
@ -77,25 +87,35 @@ lws_expect_dispose(abs_unit_test_priv_t *priv, lws_expect_disposition disp,
|
|||
* start on the next step of the test
|
||||
*/
|
||||
|
||||
lws_expect_disposition
|
||||
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) {
|
||||
int f = priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE,
|
||||
s = priv->abs->ap->rx(priv->abs->api, priv->expect->buffer,
|
||||
priv->expect->len);
|
||||
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_expect_dispose(priv, LPE_FAILED,
|
||||
return lws_unit_test_packet_dispose(priv, LPE_FAILED,
|
||||
"rx unexpected return");
|
||||
}
|
||||
|
||||
if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END)
|
||||
return lws_expect_dispose(priv, LPE_SUCCEEDED, NULL);
|
||||
if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
|
||||
lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
priv->expect++;
|
||||
}
|
||||
|
@ -103,112 +123,164 @@ process_expect(abs_unit_test_priv_t *priv)
|
|||
return LPE_CONTINUE;
|
||||
}
|
||||
|
||||
static int
|
||||
heartbeat_cb(struct lws_dll2 *d, void *user)
|
||||
static lws_seq_cb_return_t
|
||||
unit_test_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
|
||||
void *data)
|
||||
{
|
||||
abs_unit_test_priv_t *priv = lws_container_of(d, abs_unit_test_priv_t,
|
||||
same_abs_transport_list);
|
||||
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;
|
||||
|
||||
if (priv->abs->ap->heartbeat)
|
||||
priv->abs->ap->heartbeat(priv->abs->api);
|
||||
switch ((int)event) {
|
||||
case LWSSEQ_CREATED: /* our sequencer just got started */
|
||||
lwsl_notice("%s: %s: created\n", __func__,
|
||||
lws_sequencer_name(seq));
|
||||
if (s->ai->at->client_conn(s->ai)) {
|
||||
lwsl_notice("%s: %s: abstract client conn failed\n",
|
||||
__func__, lws_sequencer_name(seq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
callback_abs_client_unit_test(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)user;
|
||||
struct vhd *vhd = (struct vhd *)
|
||||
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi));
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi), sizeof(struct vhd));
|
||||
if (!vhd)
|
||||
return 1;
|
||||
|
||||
lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi),
|
||||
LWS_CALLBACK_USER, 1);
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_USER:
|
||||
case LWSSEQ_DESTROYED:
|
||||
/*
|
||||
* This comes at 1Hz without a wsi context, so there is no
|
||||
* valid priv. We need to track the live abstract objects that
|
||||
* are using our abstract protocol, and pass the heartbeat
|
||||
* through to the ones that care.
|
||||
* This sequencer is about to be destroyed. If we have any
|
||||
* other assets in play, detach them from us.
|
||||
*/
|
||||
if (!vhd)
|
||||
break;
|
||||
|
||||
lws_dll2_foreach_safe(&vhd->owner, NULL, heartbeat_cb);
|
||||
if (priv->abs)
|
||||
lws_abs_destroy_instance(&priv->abs);
|
||||
|
||||
lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi),
|
||||
LWS_CALLBACK_USER, 1);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_ADOPT_FILE:
|
||||
lwsl_debug("LWS_CALLBACK_RAW_ADOPT_FILE\n");
|
||||
priv->connecting = 0;
|
||||
priv->established = 1;
|
||||
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);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_CLOSE_FILE:
|
||||
if (!user)
|
||||
break;
|
||||
lwsl_debug("LWS_CALLBACK_RAW_CLOSE_FILE\n");
|
||||
priv->established = 0;
|
||||
priv->connecting = 0;
|
||||
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);
|
||||
if (priv->filefd != -1)
|
||||
close(priv->filefd);
|
||||
priv->filefd = -1;
|
||||
lws_set_wsi_user(wsi, NULL);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RAW_WRITEABLE_FILE:
|
||||
lwsl_debug("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
|
||||
priv->abs->ap->writeable(priv->abs->api,
|
||||
lws_get_peer_write_allowance(priv->wsi));
|
||||
break;
|
||||
goto done;
|
||||
|
||||
case LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL:
|
||||
lws_dll2_add_tail(&priv->same_abs_transport_list, &vhd->owner);
|
||||
break;
|
||||
case UTSEQ_MSG_TIMEOUT: /* current step timed out */
|
||||
|
||||
case LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL:
|
||||
lws_dll2_remove(&priv->same_abs_transport_list);
|
||||
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_sequencer_timeout(lws_sequencer_from_user(s), 0);
|
||||
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 0;
|
||||
return LWSSEQ_RET_CONTINUE;
|
||||
}
|
||||
|
||||
const struct lws_protocols protocol_abs_client_unit_test = {
|
||||
"lws-abs-cli-unit-test", callback_abs_client_unit_test,
|
||||
0, 1024, 1024, NULL, 0
|
||||
};
|
||||
|
||||
static int
|
||||
lws_atcut_close(lws_abs_transport_inst_t *ati)
|
||||
{
|
||||
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
|
||||
|
||||
lws_set_timeout(priv->wsi, 1, LWS_TO_KILL_SYNC);
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
/* priv is destroyed in the CLOSE callback */
|
||||
lws_sequencer_event(priv->seq, UTSEQ_MSG_CLOSING, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -220,10 +292,15 @@ lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
|
|||
|
||||
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_expect_dispose(priv, LPE_FAILED, "unexpected tx");
|
||||
lws_unit_test_packet_dispose(priv, LPE_FAILED, "unexpected tx");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -231,28 +308,32 @@ lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
|
|||
if (len != priv->expect->len) {
|
||||
lwsl_notice("%s: unexpected tx len %zu, expected %zu\n",
|
||||
__func__, len, priv->expect->len);
|
||||
lws_expect_dispose(priv, LPE_FAILED, "tx len mismatch");
|
||||
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_notice(priv->expect->buffer, len);
|
||||
lwsl_hexdump_notice(buf, len);
|
||||
lws_expect_dispose(priv, LPE_FAILED, "tx data mismatch");
|
||||
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_expect_dispose(priv, LPE_SUCCEEDED, NULL);
|
||||
lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
priv->expect++;
|
||||
|
||||
lws_sequencer_event(priv->seq, UTSEQ_MSG_POST_TX_KICK, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -262,32 +343,13 @@ 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;
|
||||
lws_sock_file_fd_type u;
|
||||
|
||||
/*
|
||||
* we do this fresh for each test
|
||||
*/
|
||||
|
||||
if (priv->connecting || priv->established)
|
||||
return 0;
|
||||
|
||||
priv->filefd = lws_open("/dev/null", O_RDWR);
|
||||
if (priv->filefd == -1) {
|
||||
lwsl_err("%s: Unable to open /dev/null\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
u.filefd = (lws_filefd_type)(long long)priv->filefd;
|
||||
if (!lws_adopt_descriptor_vhost(priv->abs->vh, LWS_ADOPT_RAW_FILE_DESC,
|
||||
u, "unit-test", NULL)) {
|
||||
lwsl_err("Failed to adopt file descriptor\n");
|
||||
close(priv->filefd);
|
||||
priv->filefd = -1;
|
||||
|
||||
if (priv->established) {
|
||||
lwsl_err("%s: already established\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* set up the test start pieces */
|
||||
/* 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) {
|
||||
|
@ -296,17 +358,31 @@ lws_atcut_client_conn(const lws_abs_t *abs)
|
|||
|
||||
return 1;
|
||||
}
|
||||
priv->current_test = (lws_expect_test_t *)tm->u.value;
|
||||
priv->expect = priv->current_test->expect;
|
||||
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';
|
||||
|
||||
lwsl_notice("%s: %s: %s: start\n", __func__, abs->ap->name,
|
||||
lws_sequencer_timeout(priv->seq, priv->current_test->max_secs);
|
||||
|
||||
lwsl_notice("%s: %s: test '%s': start\n", __func__, abs->ap->name,
|
||||
priv->current_test->name);
|
||||
|
||||
process_expect(priv);
|
||||
|
||||
priv->connecting = 1;
|
||||
lws_sequencer_event(priv->seq, UTSEQ_MSG_CONNECTING, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -320,18 +396,48 @@ lws_atcut_ask_for_writeable(lws_abs_transport_inst_t *ati)
|
|||
if (!priv->established)
|
||||
return 1;
|
||||
|
||||
lws_callback_on_writable(priv->wsi);
|
||||
/*
|
||||
* 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_sequencer_event(priv->seq, UTSEQ_MSG_WRITEABLE, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_atcut_create(struct lws_abs *ai)
|
||||
{
|
||||
abs_unit_test_priv_t *at = (abs_unit_test_priv_t *)ai->ati;
|
||||
/*
|
||||
* An abstract protocol + transport has been instantiated
|
||||
*/
|
||||
|
||||
memset(at, 0, sizeof(*at));
|
||||
at->abs = ai;
|
||||
static int
|
||||
lws_atcut_create(lws_abs_t *ai)
|
||||
{
|
||||
abs_unit_test_priv_t *priv;
|
||||
lws_sequencer_t *seq;
|
||||
seq_priv_t *s;
|
||||
|
||||
/*
|
||||
* Create the sequencer for the steps in a single unit test
|
||||
*/
|
||||
|
||||
seq = lws_sequencer_create(ai->vh->context, 0, sizeof(*s),
|
||||
(void **)&s, unit_test_sequencer_cb,
|
||||
"unit-test-seq");
|
||||
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;
|
||||
}
|
||||
|
@ -351,8 +457,14 @@ 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;
|
||||
|
||||
lws_set_timeout(priv->wsi, reason, secs);
|
||||
time(&now);
|
||||
|
||||
if (secs)
|
||||
priv->timeout = now + secs;
|
||||
else
|
||||
priv->timeout = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -368,6 +480,27 @@ lws_atcut_state(lws_abs_transport_inst_t *ati)
|
|||
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];
|
||||
}
|
||||
|
||||
const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
|
||||
.name = "unit_test",
|
||||
|
@ -387,33 +520,3 @@ const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
|
|||
.set_timeout = lws_atcut_set_timeout,
|
||||
.state = lws_atcut_state,
|
||||
};
|
||||
|
||||
/*
|
||||
* This goes through the test array instantiating a new protocol + transport
|
||||
* for each test and keeping track of the results
|
||||
*/
|
||||
|
||||
int
|
||||
lws_abs_transport_unit_test_helper(lws_abs_t *abs)
|
||||
{
|
||||
lws_abs_t *instance;
|
||||
const lws_token_map_t *tm;
|
||||
|
||||
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_TEST_ARRAY);
|
||||
if (!tm) {
|
||||
lwsl_err("%s: LTMI_PEER_V_EXPECT_TEST_ARRAY is required\n",
|
||||
__func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//wh
|
||||
|
||||
instance = lws_abs_bind_and_create_instance(abs);
|
||||
if (!instance) {
|
||||
lwsl_err("%s: failed to create SMTP client\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ typedef struct lws_sequencer {
|
|||
struct lws_dll2_owner seq_event_owner;
|
||||
struct lws_context_per_thread *pt;
|
||||
lws_seq_event_cb cb;
|
||||
const char *name;
|
||||
|
||||
time_t time_created;
|
||||
time_t timeout; /* 0 or time we timeout */
|
||||
|
@ -53,7 +54,7 @@ typedef struct lws_sequencer {
|
|||
|
||||
lws_sequencer_t *
|
||||
lws_sequencer_create(struct lws_context *context, int tsi, size_t user_size,
|
||||
void **puser, lws_seq_event_cb cb)
|
||||
void **puser, lws_seq_event_cb cb, const char *name)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
lws_sequencer_t *seq = lws_zalloc(sizeof(*seq) + user_size, __func__);
|
||||
|
@ -63,6 +64,7 @@ lws_sequencer_create(struct lws_context *context, int tsi, size_t user_size,
|
|||
|
||||
seq->cb = cb;
|
||||
seq->pt = pt;
|
||||
seq->name = name;
|
||||
|
||||
*puser = (void *)&seq[1];
|
||||
|
||||
|
@ -108,8 +110,11 @@ lws_sequencer_destroy(lws_sequencer_t **pseq)
|
|||
/* defeat another thread racing to add events while we are destroying */
|
||||
seq->going_down = 1;
|
||||
|
||||
seq->cb(seq, (void *)&seq[1], LWSSEQ_DESTROYED, NULL);
|
||||
|
||||
lws_pt_lock(seq->pt, __func__); /* -------------------------- pt { */
|
||||
|
||||
lws_dll2_remove(&seq->seq_list);
|
||||
lws_dll2_remove(&seq->seq_to_list);
|
||||
lws_dll2_remove(&seq->seq_pend_list);
|
||||
/* remove and destroy any pending events */
|
||||
|
@ -117,7 +122,6 @@ lws_sequencer_destroy(lws_sequencer_t **pseq)
|
|||
|
||||
lws_pt_unlock(seq->pt); /* } pt ---------------------------------- */
|
||||
|
||||
seq->cb(seq, (void *)&seq[1], LWSSEQ_DESTROYED, NULL);
|
||||
|
||||
lws_free_set_NULL(seq);
|
||||
}
|
||||
|
@ -125,14 +129,20 @@ lws_sequencer_destroy(lws_sequencer_t **pseq)
|
|||
int
|
||||
lws_sequencer_event(lws_sequencer_t *seq, lws_seq_events_t e, void *data)
|
||||
{
|
||||
lws_seq_event_t *seqe = lws_zalloc(sizeof(*seqe), __func__);
|
||||
lws_seq_event_t *seqe;
|
||||
|
||||
if (!seqe || seq->going_down)
|
||||
if (!seq || seq->going_down)
|
||||
return 1;
|
||||
|
||||
seqe = lws_zalloc(sizeof(*seqe), __func__);
|
||||
if (!seqe)
|
||||
return 1;
|
||||
|
||||
seqe->e = e;
|
||||
seqe->data = data;
|
||||
|
||||
// lwsl_notice("%s: seq %s: event %d\n", __func__, seq->name, e);
|
||||
|
||||
lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */
|
||||
|
||||
if (seq->seq_event_owner.count > QUEUE_SANITY_LIMIT) {
|
||||
|
@ -193,7 +203,8 @@ lws_sequencer_next_event(struct lws_dll2 *d, void *user)
|
|||
lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */
|
||||
|
||||
if (n) {
|
||||
lwsl_info("%s: destroying seq by request\n", __func__);
|
||||
lwsl_info("%s: destroying seq '%s' by request\n", __func__,
|
||||
seq->name);
|
||||
lws_sequencer_destroy(&seq);
|
||||
|
||||
return LWSSEQ_RET_DESTROY;
|
||||
|
@ -291,6 +302,18 @@ lws_sequencer_timeout_check(struct lws_context_per_thread *pt, time_t now)
|
|||
|
||||
} lws_end_foreach_dll_safe(p, tp);
|
||||
|
||||
/* send every sequencer a heartbeat message... it can ignore it */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
|
||||
pt->seq_owner.head) {
|
||||
lws_sequencer_t *s = lws_container_of(p, lws_sequencer_t,
|
||||
seq_list);
|
||||
|
||||
/* queue the message to inform the sequencer */
|
||||
lws_sequencer_event(s, LWSSEQ_HEARTBEAT, NULL);
|
||||
|
||||
} lws_end_foreach_dll_safe(p, tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -300,6 +323,12 @@ lws_sequencer_from_user(void *u)
|
|||
return &((lws_sequencer_t *)u)[-1];
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_sequencer_name(lws_sequencer_t *seq)
|
||||
{
|
||||
return seq->name;
|
||||
}
|
||||
|
||||
int
|
||||
lws_sequencer_secs_since_creation(lws_sequencer_t *seq)
|
||||
{
|
||||
|
|
|
@ -60,9 +60,6 @@ const struct lws_event_loop_ops *available_event_libs[] = {
|
|||
const struct lws_protocols *available_abstract_protocols[] = {
|
||||
#if defined(LWS_ROLE_RAW)
|
||||
&protocol_abs_client_raw_skt,
|
||||
#endif
|
||||
#if defined(LWS_WITH_ABSTRACT)
|
||||
&protocol_abs_client_unit_test,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-unit-tests-smtp-client)
|
||||
set(SAMP lws-api-test-smtp_client)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* lws-unit-tests-smtp-client
|
||||
* lws-api-test-smtp_client
|
||||
*
|
||||
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
|
@ -14,66 +14,6 @@
|
|||
static int interrupted, result = 1;
|
||||
static const char *recip;
|
||||
|
||||
/*
|
||||
* from https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
|
||||
*/
|
||||
|
||||
static lws_expect_t test_send1[] = {
|
||||
{
|
||||
"220 smtp.example.com ESMTP Postfix",
|
||||
34, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"HELO lws-test-client",
|
||||
20, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 smtp.example.com, I am glad to meet you",
|
||||
43, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"MAIL FROM:<noreply@warmcat.com>",
|
||||
31, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok",
|
||||
6, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"RCPT TO:andy@warmcat.com",
|
||||
24, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok",
|
||||
6, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"DATA",
|
||||
4, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"354 End data with <CR><LF>.<CR><LF>",
|
||||
35, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"From: noreply@example.com\n"
|
||||
"To: andy@warmcat.com\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n",
|
||||
27 + 21 + 39 + 1 + 46 + 5, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok: queued as 12345",
|
||||
23, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"QUIT",
|
||||
4, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"221 Bye",
|
||||
7, LWS_AUT_EXPECT_RX |
|
||||
LWS_AUT_EXPECT_LOCAL_CLOSE |
|
||||
LWS_AUT_EXPECT_DO_REMOTE_CLOSE |
|
||||
LWS_AUT_EXPECT_TEST_END
|
||||
}
|
||||
};
|
||||
|
||||
static lws_expect_test_t tests[] = {
|
||||
{ "sending", test_send1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
|
@ -99,43 +39,6 @@ email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The test helper calls this on the instance it created to prepare it for
|
||||
* the test.
|
||||
*/
|
||||
|
||||
static int
|
||||
smtp_test_instance_init(lws_abs_t *instance)
|
||||
{
|
||||
lws_smtp_email_t email;
|
||||
|
||||
/* attach an email to it */
|
||||
|
||||
memset(&email, 0, sizeof(email));
|
||||
email.data = NULL /* email specific user data */;
|
||||
email.email_from = "noreply@warmcat.com";
|
||||
email.email_to = "andy@warmcat.com";
|
||||
email.payload = malloc(2048);
|
||||
if (!email.payload)
|
||||
return 1;
|
||||
|
||||
lws_snprintf((char *)email.payload, 2048,
|
||||
"From: noreply@example.com\n"
|
||||
"To: %s\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n", recip);
|
||||
email.done = email_sent_or_failed;
|
||||
|
||||
if (lws_smtp_client_add_email(instance, &email)) {
|
||||
lwsl_err("%s: failed to add email\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're going to bind to the raw-skt transport, so tell that what we want it
|
||||
* to connect to
|
||||
|
@ -143,8 +46,11 @@ smtp_test_instance_init(lws_abs_t *instance)
|
|||
|
||||
static const lws_token_map_t smtp_raw_skt_transport_tokens[] = {
|
||||
{
|
||||
.u = { .value = (const char *)tests },
|
||||
.name_index = LTMI_PEER_V_EXPECT_TEST_ARRAY,
|
||||
.u = { .value = "127.0.0.1" },
|
||||
.name_index = LTMI_PEER_V_DNS_ADDRESS,
|
||||
}, {
|
||||
.u = { .lvalue = 25 },
|
||||
.name_index = LTMI_PEER_LV_PORT,
|
||||
}, {
|
||||
}
|
||||
};
|
||||
|
@ -153,7 +59,6 @@ static const lws_token_map_t smtp_protocol_tokens[] = {
|
|||
{
|
||||
.u = { .value = "lws-test-client" },
|
||||
.name_index = LTMI_PSMTP_V_HELO,
|
||||
.init = smtp_test_instance_init,
|
||||
}, {
|
||||
}
|
||||
};
|
||||
|
@ -164,6 +69,8 @@ int main(int argc, const char **argv)
|
|||
int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
lws_abs_t abs, *instance;
|
||||
lws_smtp_email_t email;
|
||||
struct lws_vhost *vh;
|
||||
const char *p;
|
||||
|
||||
|
@ -200,7 +107,9 @@ int main(int argc, const char **argv)
|
|||
goto bail1;
|
||||
}
|
||||
|
||||
/* create the smtp client */
|
||||
/*
|
||||
* create an smtp client that's hooked up to real sockets
|
||||
*/
|
||||
|
||||
memset(&abs, 0, sizeof(abs));
|
||||
abs.vh = vh;
|
||||
|
@ -214,22 +123,41 @@ int main(int argc, const char **argv)
|
|||
|
||||
/* select the transport and bind its tokens */
|
||||
|
||||
abs.at = lws_abs_transport_get_by_name("unit_tests");
|
||||
abs.at = lws_abs_transport_get_by_name("raw_skt");
|
||||
if (!abs.at)
|
||||
goto bail1;
|
||||
|
||||
/*
|
||||
* The transport token we pass here to the test helper is the array
|
||||
* of tests. The helper will iterate through it instantiating test
|
||||
* connections with one test each.
|
||||
*/
|
||||
abs.at_tokens = smtp_raw_skt_transport_tokens;
|
||||
|
||||
if (lws_abs_transport_unit_test_helper(&abs)) {
|
||||
instance = lws_abs_bind_and_create_instance(&abs);
|
||||
if (!instance) {
|
||||
lwsl_err("%s: failed to create SMTP client\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/* attach an email to it */
|
||||
|
||||
memset(&email, 0, sizeof(email));
|
||||
email.data = NULL /* email specific user data */;
|
||||
email.email_from = "andy@warmcat.com";
|
||||
email.email_to = recip;
|
||||
email.payload = malloc(2048);
|
||||
if (!email.payload) {
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_snprintf((char *)email.payload, 2048,
|
||||
"From: noreply@example.com\n"
|
||||
"To: %s\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n", recip);
|
||||
email.done = email_sent_or_failed;
|
||||
|
||||
if (lws_smtp_client_add_email(instance, &email)) {
|
||||
lwsl_err("%s: failed to add email\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* the usual lws event loop */
|
||||
|
||||
|
|
|
@ -370,7 +370,7 @@ main(int argc, const char **argv)
|
|||
*/
|
||||
|
||||
seq = lws_sequencer_create(context, 0, sizeof(struct myseq),
|
||||
(void **)&s, sequencer_cb);
|
||||
(void **)&s, sequencer_cb, "seq");
|
||||
if (!seq) {
|
||||
lwsl_err("%s: unable to create sequencer\n", __func__);
|
||||
goto bail1;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-api-test-smtp_client)
|
||||
set(SAMP lws-unit-tests-smtp-client)
|
||||
set(SRCS main.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
# lws api test smtp client
|
||||
|
||||
Demonstrates how to send email through your local MTA
|
||||
Performs unit tests on the lws SMTP client abstract protocol
|
||||
implementation.
|
||||
|
||||
The first test "sends mail to a server" (actually is prompted by
|
||||
test vectors that look like a server) and the second test
|
||||
confirm it can handle rejection by the "server" cleanly.
|
||||
|
||||
## build
|
||||
|
||||
|
@ -19,11 +24,18 @@ Commandline option|Meaning
|
|||
|
||||
|
||||
```
|
||||
$ ./lws-api-test-smtp_client -r andy@warmcat.com
|
||||
[2019/04/17 05:12:06:5293] USER: LWS API selftest: SMTP client
|
||||
[2019/04/17 05:12:06:5635] NOTICE: LGSSMTP_IDLE: connecting to 127.0.0.1:25
|
||||
[2019/04/17 05:12:06:6238] NOTICE: email_sent_or_failed: sent OK
|
||||
[2019/04/17 05:12:06:6394] USER: Completed: PASS
|
||||
|
||||
$ ./lws-api-test-smtp_client
|
||||
[2019/06/28 21:56:41:0711] USER: LWS API selftest: SMTP client unit tests
|
||||
[2019/06/28 21:56:41:1114] NOTICE: test_sequencer_cb: test-seq: created
|
||||
[2019/06/28 21:56:41:1259] NOTICE: unit_test_sequencer_cb: unit-test-seq: created
|
||||
[2019/06/28 21:56:41:1272] NOTICE: lws_atcut_client_conn: smtp: test 'sending': start
|
||||
[2019/06/28 21:56:41:1441] NOTICE: unit_test_sequencer_cb: unit-test-seq: created
|
||||
[2019/06/28 21:56:41:1442] NOTICE: lws_atcut_client_conn: smtp: test 'rejected': start
|
||||
[2019/06/28 21:56:41:1453] NOTICE: lws_smtp_client_abs_rx: bad response from server: 500 (state 4) 500 Service Unavailable
|
||||
[2019/06/28 21:56:41:1467] USER: test_sequencer_cb: sequence completed OK
|
||||
[2019/06/28 21:56:41:1474] USER: main: 2 tests 0 fail
|
||||
[2019/06/28 21:56:41:1476] USER: test 0: PASS
|
||||
[2019/06/28 21:56:41:1478] USER: test 1: PASS
|
||||
[2019/06/28 21:56:41:1480] USER: Completed: PASS
|
||||
```
|
||||
|
||||
|
|
|
@ -1,18 +1,157 @@
|
|||
/*
|
||||
* lws-api-test-smtp_client
|
||||
* lws-unit-tests-smtp-client
|
||||
*
|
||||
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This performs unit tests for the SMTP client abstract protocol
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
static int interrupted, result = 1;
|
||||
static const char *recip;
|
||||
static int interrupted, results[10], count_tests, count_passes;
|
||||
|
||||
static int
|
||||
email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len)
|
||||
{
|
||||
free(email);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The test helper calls this on the instance it created to prepare it for
|
||||
* the test. In our case, we need to queue up a test email to send on the
|
||||
* smtp client abstract protocol.
|
||||
*/
|
||||
|
||||
static int
|
||||
smtp_test_instance_init(lws_abs_t *instance)
|
||||
{
|
||||
lws_smtp_email_t *email = (lws_smtp_email_t *)
|
||||
malloc(sizeof(*email) + 2048);
|
||||
|
||||
if (!email)
|
||||
return 1;
|
||||
|
||||
/* attach an email to it */
|
||||
|
||||
memset(email, 0, sizeof(*email));
|
||||
email->data = NULL /* email specific user data */;
|
||||
email->email_from = "noreply@warmcat.com";
|
||||
email->email_to = "andy@warmcat.com";
|
||||
email->payload = (void *)&email[1];
|
||||
|
||||
lws_snprintf((char *)email->payload, 2048,
|
||||
"From: noreply@example.com\n"
|
||||
"To: %s\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n", "andy@warmcat.com");
|
||||
email->done = email_sent_or_failed;
|
||||
|
||||
if (lws_smtp_client_add_email(instance, email)) {
|
||||
lwsl_err("%s: failed to add email\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* from https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
|
||||
*
|
||||
* test vector sent to protocol
|
||||
* test vector received from protocol
|
||||
*/
|
||||
|
||||
static lws_unit_test_packet_t test_send1[] = {
|
||||
{
|
||||
"220 smtp.example.com ESMTP Postfix",
|
||||
smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"HELO lws-test-client\x0a",
|
||||
NULL, 21, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 smtp.example.com, I am glad to meet you",
|
||||
NULL, 43, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"MAIL FROM: <noreply@warmcat.com>\x0a",
|
||||
NULL, 33, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok",
|
||||
NULL, 6, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"RCPT TO: <andy@warmcat.com>\x0a",
|
||||
NULL, 28, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok",
|
||||
NULL, 6, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"DATA\x0a",
|
||||
NULL, 5, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"354 End data with <CR><LF>.<CR><LF>\x0a",
|
||||
NULL, 35, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"From: noreply@example.com\n"
|
||||
"To: andy@warmcat.com\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n",
|
||||
NULL, 27 + 21 + 39 + 1 + 47 + 5, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 Ok: queued as 12345\x0a",
|
||||
NULL, 23, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"quit\x0a",
|
||||
NULL, 5, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"221 Bye\x0a",
|
||||
NULL, 7, LWS_AUT_EXPECT_RX |
|
||||
LWS_AUT_EXPECT_LOCAL_CLOSE |
|
||||
LWS_AUT_EXPECT_DO_REMOTE_CLOSE |
|
||||
LWS_AUT_EXPECT_TEST_END
|
||||
}, { /* sentinel */
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static lws_unit_test_packet_t test_send2[] = {
|
||||
{
|
||||
"220 smtp.example.com ESMTP Postfix",
|
||||
smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"HELO lws-test-client\x0a",
|
||||
NULL, 21, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"250 smtp.example.com, I am glad to meet you",
|
||||
NULL, 43, LWS_AUT_EXPECT_RX
|
||||
}, {
|
||||
"MAIL FROM: <noreply@warmcat.com>\x0a",
|
||||
NULL, 33, LWS_AUT_EXPECT_TX
|
||||
}, {
|
||||
"500 Service Unavailable",
|
||||
NULL, 23, LWS_AUT_EXPECT_RX |
|
||||
LWS_AUT_EXPECT_DO_REMOTE_CLOSE |
|
||||
LWS_AUT_EXPECT_TEST_END
|
||||
}, { /* sentinel */
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
static lws_unit_test_t tests[] = {
|
||||
{ "sending", test_send1, 3 },
|
||||
{ "rejected", test_send2, 3 },
|
||||
{ } /* sentinel */
|
||||
};
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
|
@ -20,58 +159,32 @@ sigint_handler(int sig)
|
|||
interrupted = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len)
|
||||
{
|
||||
/* you could examine email->data here */
|
||||
if (buf)
|
||||
lwsl_notice("%s: %.*s\n", __func__, (int)len, (const char *)buf);
|
||||
else
|
||||
lwsl_notice("%s:\n", __func__);
|
||||
|
||||
/* destroy any allocations in email */
|
||||
|
||||
free((char *)email->payload);
|
||||
|
||||
result = 0;
|
||||
interrupted = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're going to bind to the raw-skt transport, so tell that what we want it
|
||||
* to connect to
|
||||
* set the HELO our SMTP client will use
|
||||
*/
|
||||
|
||||
static const lws_token_map_t smtp_raw_skt_transport_tokens[] = {
|
||||
{
|
||||
.u = { .value = "127.0.0.1" },
|
||||
.name_index = LTMI_PEER_V_DNS_ADDRESS,
|
||||
}, {
|
||||
.u = { .lvalue = 25 },
|
||||
.name_index = LTMI_PEER_LV_PORT,
|
||||
}, {
|
||||
}
|
||||
};
|
||||
|
||||
static const lws_token_map_t smtp_protocol_tokens[] = {
|
||||
{
|
||||
.u = { .value = "lws-test-client" },
|
||||
.name_index = LTMI_PSMTP_V_HELO,
|
||||
}, {
|
||||
}, { /* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
tests_completion_cb(const void *cb_user)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
lws_test_sequencer_args_t args;
|
||||
struct lws_context *context;
|
||||
lws_abs_t abs, *instance;
|
||||
lws_smtp_email_t email;
|
||||
struct lws_vhost *vh;
|
||||
lws_abs_t abs, *instance;
|
||||
const char *p;
|
||||
|
||||
/* the normal lws init */
|
||||
|
@ -81,15 +194,8 @@ int main(int argc, const char **argv)
|
|||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
p = lws_cmdline_option(argc, argv, "-r");
|
||||
if (!p) {
|
||||
lwsl_err("-r <recipient email> is required\n");
|
||||
return 1;
|
||||
}
|
||||
recip = p;
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS API selftest: SMTP client\n");
|
||||
lwsl_user("LWS API selftest: SMTP client unit tests\n");
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
|
@ -107,9 +213,7 @@ int main(int argc, const char **argv)
|
|||
goto bail1;
|
||||
}
|
||||
|
||||
/*
|
||||
* create an smtp client that's hooked up to real sockets
|
||||
*/
|
||||
/* create the smtp client */
|
||||
|
||||
memset(&abs, 0, sizeof(abs));
|
||||
abs.vh = vh;
|
||||
|
@ -124,52 +228,48 @@ int main(int argc, const char **argv)
|
|||
|
||||
/* select the transport and bind its tokens */
|
||||
|
||||
abs.at = lws_abs_transport_get_by_name("raw_skt");
|
||||
abs.at = lws_abs_transport_get_by_name("unit_test");
|
||||
if (!abs.at)
|
||||
goto bail1;
|
||||
|
||||
abs.at_tokens = smtp_raw_skt_transport_tokens;
|
||||
|
||||
instance = lws_abs_bind_and_create_instance(&abs);
|
||||
if (!instance)
|
||||
goto bail1;
|
||||
|
||||
/* attach an email to it */
|
||||
/* configure the test sequencer */
|
||||
|
||||
memset(&email, 0, sizeof(email));
|
||||
email.data = NULL /* email specific user data */;
|
||||
email.email_from = "andy@warmcat.com";
|
||||
email.email_to = recip;
|
||||
email.payload = malloc(2048);
|
||||
if (!email.payload) {
|
||||
args.abs = &abs;
|
||||
args.tests = tests;
|
||||
args.results = results;
|
||||
args.results_max = LWS_ARRAY_SIZE(results);
|
||||
args.count_tests = &count_tests;
|
||||
args.count_passes = &count_passes;
|
||||
args.cb = tests_completion_cb;
|
||||
args.cb_user = NULL;
|
||||
|
||||
if (lws_abs_unit_test_sequencer(&args)) {
|
||||
lwsl_err("%s: failed to create test sequencer\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
lws_snprintf((char *)email.payload, 2048,
|
||||
"From: noreply@example.com\n"
|
||||
"To: %s\n"
|
||||
"Subject: Test email for lws smtp-client\n"
|
||||
"\n"
|
||||
"Hello this was an api test for lws smtp-client\n"
|
||||
"\r\n.\r\n", recip);
|
||||
email.done = email_sent_or_failed;
|
||||
|
||||
if (lws_smtp_client_add_email(instance, &email)) {
|
||||
lwsl_err("%s: failed to add email\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* the usual lws event loop */
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 1000);
|
||||
|
||||
bail:
|
||||
/* describe the overall test results */
|
||||
|
||||
lwsl_user("%s: %d tests %d fail\n", __func__, count_tests,
|
||||
count_tests - count_passes);
|
||||
for (n = 0; n < count_tests; n++)
|
||||
lwsl_user(" test %d: %s\n", n,
|
||||
lws_unit_test_result_name(results[n]));
|
||||
|
||||
bail1:
|
||||
lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");
|
||||
lwsl_user("Completed: %s\n",
|
||||
!count_tests || count_passes != count_tests ? "FAIL" : "PASS");
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
return result;
|
||||
return !count_tests || count_passes != count_tests;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue