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 * A helper for running multiple unit tests against abstract protocols. 25 * 26 * An lws_seq_t is used to base its actions in the event loop and manage 27 * the sequencing of multiple tests. A new abstract connection is instantiated 28 * for each test using te 29 */ 30 31#include <private-lib-core.h> 32 33struct lws_seq_test_sequencer { 34 lws_abs_t original_abs; 35 36 lws_test_sequencer_args_t args; 37 38 struct lws_context *context; 39 struct lws_vhost *vhost; 40 struct lws_sequencer *unit_test_seq; 41 42 /* holds the per-test token for the unit-test transport to consume */ 43 lws_token_map_t uttt[4]; 44 45 lws_abs_t *instance; 46 47 int state; 48}; 49 50/* sequencer messages specific to this sequencer */ 51 52enum { 53 SEQ_MSG_PASS = LWSSEQ_USER_BASE, 54 SEQ_MSG_FAIL, 55 SEQ_MSG_FAIL_TIMEOUT, 56}; 57 58/* 59 * We get called back when the unit test transport has decided if the test 60 * passed or failed. We get the priv, and report to the sequencer message queue 61 * what the result was. 62 */ 63 64static int 65unit_test_result_cb(const void *cb_user, int disposition) 66{ 67 const struct lws_seq_test_sequencer *s = 68 (const struct lws_seq_test_sequencer *)cb_user; 69 int r; 70 71 lwsl_debug("%s: disp %d\n", __func__, disposition); 72 73 switch (disposition) { 74 case LPE_FAILED_UNEXPECTED_PASS: 75 case LPE_FAILED_UNEXPECTED_CLOSE: 76 case LPE_FAILED: 77 r = SEQ_MSG_FAIL; 78 break; 79 80 case LPE_FAILED_UNEXPECTED_TIMEOUT: 81 r = SEQ_MSG_FAIL_TIMEOUT; 82 break; 83 84 case LPE_SUCCEEDED: 85 r = SEQ_MSG_PASS; 86 break; 87 88 default: 89 assert(0); 90 return -1; 91 } 92 93 lws_seq_queue_event(s->unit_test_seq, r, NULL, NULL); 94 95 ((struct lws_seq_test_sequencer *)s)->instance = NULL; 96 97 return 0; 98} 99 100/* 101 * We receive the unit test result callback's messages via the message queue. 102 * 103 * We log the results and always move on to the next test until there are no 104 * more tests. 105 */ 106 107static lws_seq_cb_return_t 108test_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data, 109 void *aux) 110{ 111 struct lws_seq_test_sequencer *s = 112 (struct lws_seq_test_sequencer *)user; 113 lws_unit_test_packet_t *exp = (lws_unit_test_packet_t *) 114 s->args.tests[s->state].expect_array; 115 lws_abs_t test_abs; 116 117 switch ((int)event) { 118 case LWSSEQ_CREATED: /* our sequencer just got started */ 119 lwsl_notice("%s: %s: created\n", __func__, 120 lws_seq_name(seq)); 121 s->state = 0; /* first thing we'll do is the first url */ 122 goto step; 123 124 case LWSSEQ_DESTROYED: 125 /* 126 * We are going down... if we have a child unit test sequencer 127 * still around inform and destroy it 128 */ 129 if (s->instance) { 130 s->instance->at->close(s->instance); 131 s->instance = NULL; 132 } 133 break; 134 135 case SEQ_MSG_FAIL_TIMEOUT: /* current step timed out */ 136 if (exp->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) { 137 lwsl_user("%s: test %d got expected timeout\n", 138 __func__, s->state); 139 140 goto pass; 141 } 142 lwsl_user("%s: seq timed out at step %d\n", __func__, s->state); 143 144 s->args.results[s->state] = LPE_FAILED_UNEXPECTED_TIMEOUT; 145 goto done; /* always move on to the next test */ 146 147 case SEQ_MSG_FAIL: 148 if (exp->flags & LWS_AUT_EXPECT_SHOULD_FAIL) { 149 /* 150 * in this case, we expected to fail like this, it's OK 151 */ 152 lwsl_user("%s: test %d failed as expected\n", 153 __func__, s->state); 154 155 goto pass; /* always move on to the next test */ 156 } 157 158 lwsl_user("%s: seq failed at step %d\n", __func__, s->state); 159 160 s->args.results[s->state] = LPE_FAILED; 161 goto done; /* always move on to the next test */ 162 163 case SEQ_MSG_PASS: 164 if (exp->flags & (LWS_AUT_EXPECT_SHOULD_FAIL | 165 LWS_AUT_EXPECT_SHOULD_TIMEOUT)) { 166 /* 167 * In these specific cases, done would be a failure, 168 * we expected to timeout or fail 169 */ 170 lwsl_user("%s: seq failed at step %d\n", __func__, 171 s->state); 172 173 s->args.results[s->state] = LPE_FAILED_UNEXPECTED_PASS; 174 175 goto done; /* always move on to the next test */ 176 } 177 lwsl_info("%s: seq done test %d\n", __func__, s->state); 178pass: 179 (*s->args.count_passes)++; 180 s->args.results[s->state] = LPE_SUCCEEDED; 181 182done: 183 lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE); 184 s->state++; 185step: 186 if (!s->args.tests[s->state].name) { 187 /* the sequence has completed */ 188 lwsl_user("%s: sequence completed OK\n", __func__); 189 190 if (s->args.cb) 191 s->args.cb(s->args.cb_user); 192 193 return LWSSEQ_RET_DESTROY; 194 } 195 lwsl_info("%s: starting test %d\n", __func__, s->state); 196 197 if (s->state >= s->args.results_max) { 198 lwsl_err("%s: results array is too small\n", __func__); 199 200 return LWSSEQ_RET_DESTROY; 201 } 202 test_abs = s->original_abs; 203 s->uttt[0].name_index = LTMI_PEER_V_EXPECT_TEST; 204 s->uttt[0].u.value = (void *)&s->args.tests[s->state]; 205 s->uttt[1].name_index = LTMI_PEER_V_EXPECT_RESULT_CB; 206 s->uttt[1].u.value = (void *)unit_test_result_cb; 207 s->uttt[2].name_index = LTMI_PEER_V_EXPECT_RESULT_CB_ARG; 208 s->uttt[2].u.value = (void *)s; 209 /* give the unit test transport the test tokens */ 210 test_abs.at_tokens = s->uttt; 211 212 s->instance = lws_abs_bind_and_create_instance(&test_abs); 213 if (!s->instance) { 214 lwsl_notice("%s: failed to create step %d unit test\n", 215 __func__, s->state); 216 217 return LWSSEQ_RET_DESTROY; 218 } 219 (*s->args.count_tests)++; 220 break; 221 222 default: 223 break; 224 } 225 226 return LWSSEQ_RET_CONTINUE; 227} 228 229 230/* 231 * Creates an lws_sequencer to manage the test sequence 232 */ 233 234int 235lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args) 236{ 237 struct lws_seq_test_sequencer *s; 238 struct lws_sequencer *seq; 239 lws_seq_info_t i; 240 241 memset(&i, 0, sizeof(i)); 242 i.context = args->abs->vh->context; 243 i.user_size = sizeof(struct lws_seq_test_sequencer); 244 i.puser = (void **)&s; 245 i.cb = test_sequencer_cb; 246 i.name = "test-seq"; 247 248 /* 249 * Create a sequencer in the event loop to manage the tests 250 */ 251 252 seq = lws_seq_create(&i); 253 if (!seq) { 254 lwsl_err("%s: unable to create sequencer\n", __func__); 255 return 1; 256 } 257 258 /* 259 * Take a copy of the original lws_abs_t we were passed so we can use 260 * it as the basis of the lws_abs_t we create the individual tests with 261 */ 262 s->original_abs = *args->abs; 263 264 s->args = *args; 265 266 s->context = args->abs->vh->context; 267 s->vhost = args->abs->vh; 268 s->unit_test_seq = seq; 269 270 *s->args.count_tests = 0; 271 *s->args.count_passes = 0; 272 273 return 0; 274} 275