1 /** |
|
2 * The main test code entry point |
|
3 */ |
|
4 #include "transport_test.h" |
|
5 #include "line_proto.h" |
|
6 #include "irc_queue.h" |
|
7 #include "irc_conn.h" |
|
8 #include "irc_net.h" |
|
9 #include "fifo.h" |
|
10 #include "log.h" |
|
11 #include "str.h" |
|
12 #include "error.h" |
|
13 |
|
14 #include <stdlib.h> |
|
15 #include <string.h> |
|
16 #include <getopt.h> |
|
17 #include <assert.h> |
|
18 #include <ctype.h> |
|
19 |
|
20 #define DUMP_STR_BUF 1024 |
|
21 #define DUMP_STR_COUNT 8 |
|
22 #define DUMP_STR_TAIL 10 |
|
23 |
|
24 /** |
|
25 * Global test-running state |
|
26 */ |
|
27 struct test_ctx { |
|
28 /** The event_base that we have setup */ |
|
29 struct event_base *ev_base; |
|
30 |
|
31 } _test_ctx; |
|
32 |
|
33 |
|
34 /** |
|
35 * This re-formats the given string to escape values, and returns a pointer to an internal static buffer. |
|
36 * |
|
37 * If len is given as >= 0, only the given number of chars will be dumped from str. |
|
38 * |
|
39 * The buffer cycles a bit, so the returned pointers remain valid across DUMP_STR_COUNT calls. |
|
40 * |
|
41 * The resulting string is truncated to (DUMP_STR_BUF - DUMP_STR_TAIL) bytes, not including the ending "...'\0". |
|
42 * |
|
43 * @param str the string to dump, should be NUL-terminated unless len is given |
|
44 * @param len if negative, ignored, otherwise, only this many bytes are dumped from str |
|
45 * @param return a pointer to a static buffer that remains valid across DUMP_STR_COUNT calls to this function |
|
46 */ |
|
47 const char *dump_strn (const char *str, ssize_t len) |
|
48 { |
|
49 static char dump_buf[DUMP_STR_COUNT][DUMP_STR_BUF]; |
|
50 static size_t dump_idx = 0; |
|
51 |
|
52 // pick a buffer to use |
|
53 char *buf = dump_buf[dump_idx++]; |
|
54 |
|
55 // cycle |
|
56 if (dump_idx >= DUMP_STR_COUNT) |
|
57 dump_idx = 0; |
|
58 |
|
59 str_quote(buf, DUMP_STR_BUF, str, len); |
|
60 |
|
61 // ok |
|
62 return buf; |
|
63 } |
|
64 |
|
65 const char *dump_str (const char *str) |
|
66 { |
|
67 return dump_strn(str, -1); |
|
68 } |
|
69 |
|
70 void assert_null (const void *ptr) |
|
71 { |
|
72 if (ptr) |
|
73 FATAL("%p != NULL", ptr); |
|
74 } |
|
75 |
|
76 void assert_strcmp (const char *is, const char *should_be) |
|
77 { |
|
78 if (!is || strcmp(is, should_be)) |
|
79 FATAL("%s != %s", dump_str(is), dump_str(should_be)); |
|
80 } |
|
81 |
|
82 void assert_strncmp (const char *is, const char *should_be, size_t n) |
|
83 { |
|
84 if (!is || strncmp(is, should_be, n)) |
|
85 FATAL("%s:%u != %s", dump_strn(is, n), (unsigned) n, dump_strn(should_be, n)); |
|
86 } |
|
87 |
|
88 void assert_strlen (const char *str, size_t n) |
|
89 { |
|
90 if (!str || strlen(str) != n) |
|
91 FATAL("strlen(%s) != %u", dump_str(str), (unsigned) n); |
|
92 } |
|
93 |
|
94 void assert_strnull (const char *str) |
|
95 { |
|
96 if (str != NULL) |
|
97 FATAL("%s != NULL", dump_str(str)); |
|
98 } |
|
99 |
|
100 void assert_success (err_t err) |
|
101 { |
|
102 if (err != SUCCESS) |
|
103 FATAL("error: %s", error_name(err)); |
|
104 } |
|
105 |
|
106 void assert_err (err_t err, err_t target) |
|
107 { |
|
108 if (err != target) |
|
109 FATAL("error: <%s> != <%s>", error_name(err), error_name(target)); |
|
110 } |
|
111 |
|
112 void assert_error_info (struct error_info *is, struct error_info *should_be) |
|
113 { |
|
114 if (ERROR_CODE(is) != ERROR_CODE(should_be) || ERROR_EXTRA(is) != ERROR_EXTRA(should_be)) |
|
115 FATAL("error: <%s> != <%s>", error_msg(is), error_msg(should_be)); |
|
116 } |
|
117 |
|
118 void assert_transport_read (transport_t *transport, const char *str) |
|
119 { |
|
120 size_t len = strlen(str); |
|
121 char buf[len]; |
|
122 error_t err; |
|
123 |
|
124 log_debug("read: %p: %s", transport, dump_str(str)); |
|
125 |
|
126 // read it |
|
127 assert(transport_read(transport, buf, len, &err) == (int) len); |
|
128 |
|
129 // cmp |
|
130 assert_strncmp(buf, str, len); |
|
131 } |
|
132 |
|
133 void assert_transport_write (transport_t *transport, const char *str) |
|
134 { |
|
135 size_t len = strlen(str); |
|
136 error_t err; |
|
137 |
|
138 log_debug("write: %p: %s", transport, dump_str(str)); |
|
139 |
|
140 // write it |
|
141 assert(transport_write(transport, str, len, &err) == (int) len); |
|
142 } |
|
143 |
|
144 void assert_transport_eof (transport_t *transport) |
|
145 { |
|
146 char buf; |
|
147 error_t err; |
|
148 |
|
149 log_debug("eof: %p", transport); |
|
150 |
|
151 assert_err(-transport_read(transport, &buf, 1, &err), ERR_EOF); |
|
152 } |
|
153 |
|
154 void assert_transport_data (struct transport_test *tp, const char *fmt, ...) |
|
155 { |
|
156 char buf[TRANSPORT_TEST_FMT_MAX]; |
|
157 va_list vargs; |
|
158 size_t len; |
|
159 |
|
160 va_start(vargs, fmt); |
|
161 |
|
162 if ((len = vsnprintf(buf, TRANSPORT_TEST_FMT_MAX, fmt, vargs)) >= TRANSPORT_TEST_FMT_MAX) |
|
163 FATAL("input too long: %zu bytes", len); |
|
164 |
|
165 va_end(vargs); |
|
166 |
|
167 // get the data out |
|
168 char *out; |
|
169 |
|
170 transport_test_pull_buf(tp, &out, &len); |
|
171 |
|
172 log_debug("pull_buf: %s", dump_strn(out, len)); |
|
173 |
|
174 // should be the same |
|
175 assert_strncmp(out, buf, len); |
|
176 assert_strlen(buf, len); |
|
177 |
|
178 // cleanup |
|
179 free(out); |
|
180 } |
|
181 |
|
182 /** |
|
183 * Setup the global sock_stream state |
|
184 */ |
|
185 struct event_base* setup_sock (void) |
|
186 { |
|
187 struct event_base *ev_base; |
|
188 struct error_info err; |
|
189 |
|
190 assert((ev_base = event_base_new())); |
|
191 assert_success(sock_init(ev_base, &err)); |
|
192 |
|
193 return ev_base; |
|
194 } |
|
195 |
|
196 /** |
|
197 * Create an empty transport_test |
|
198 */ |
|
199 struct transport_test* setup_transport_test () |
|
200 { |
|
201 struct transport_test *tp; |
|
202 |
|
203 assert ((tp = transport_test_create(NULL)) != NULL); |
|
204 |
|
205 return tp; |
|
206 } |
|
207 |
|
208 void assert_str_quote (size_t buf_size, const char *data, ssize_t len, const char *target, size_t out) |
|
209 { |
|
210 char buf[buf_size]; |
|
211 |
|
212 size_t ret = str_quote(buf, buf_size, data, len); |
|
213 |
|
214 log_debug("str_quote(%zu, %zd) -> %s:%zu / %s:%zu", buf_size, len, buf, ret, target, out); |
|
215 |
|
216 assert_strcmp(buf, target); |
|
217 assert(ret == out); |
|
218 } |
|
219 |
|
220 void test_str_quote (void) |
|
221 { |
|
222 log_info("testing str_quote()"); |
|
223 |
|
224 assert_str_quote(5, NULL, -1, "NULL", 4 ); |
|
225 assert_str_quote(16, "foo", -1, "'foo'", 5 ); |
|
226 assert_str_quote(16, "foobar", 3, "'foo'", 5 ); |
|
227 assert_str_quote(16, "\r\n", -1, "'\\r\\n'", 6 ); |
|
228 assert_str_quote(16, "\x13", -1, "'\\x13'", 6 ); |
|
229 assert_str_quote(16, "x'y", -1, "'x\\'y'", 6 ); |
|
230 assert_str_quote(7, "1234567890", -1, "'1'...", 12 ); |
|
231 assert_str_quote(9, "1234567890", -1, "'123'...", 12 ); |
|
232 } |
|
233 |
|
234 struct str_format_ctx { |
|
235 const char *name; |
|
236 |
|
237 const char *value; |
|
238 }; |
|
239 |
|
240 err_t test_str_format_cb (const char *name, const char **value, ssize_t *value_len, void *arg) |
|
241 { |
|
242 struct str_format_ctx *ctx = arg; |
|
243 |
|
244 assert_strcmp(name, ctx->name); |
|
245 |
|
246 *value = ctx->value; |
|
247 *value_len = -1; |
|
248 |
|
249 return SUCCESS; |
|
250 } |
|
251 |
|
252 void assert_str_format (const char *format, const char *name, const char *value, const char *out) |
|
253 { |
|
254 struct str_format_ctx ctx = { name, value }; |
|
255 char buf[512]; |
|
256 |
|
257 assert_success(str_format(buf, sizeof(buf), format, test_str_format_cb, &ctx)); |
|
258 |
|
259 log_debug("str_format(%s), { %s:%s } -> %s / %s", format, name, value, buf, out); |
|
260 |
|
261 assert_strcmp(buf, out); |
|
262 } |
|
263 |
|
264 void test_str_format (void) |
|
265 { |
|
266 log_info("test str_format()"); |
|
267 |
|
268 assert_str_format("foo", NULL, NULL, "foo"); |
|
269 assert_str_format("foo {bar} quux", "bar", "XXX", "foo XXX quux"); |
|
270 } |
|
271 |
|
272 void test_dump_str (void) |
|
273 { |
|
274 log_info("dumping example strings on stdout:"); |
|
275 |
|
276 log_debug("normal: %s", dump_str("Hello World")); |
|
277 log_debug("escapes: %s", dump_str("foo\r\nbar\a\001")); |
|
278 log_debug("length: %s", dump_strn("<-->**", 4)); |
|
279 log_debug("overflow: %s", dump_str( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); |
|
280 log_debug("null: %s", dump_str(NULL)); |
|
281 log_debug("quote: %s", dump_str("foo\\bar'quux")); |
|
282 } |
|
283 |
|
284 void test_transport_test (void) |
|
285 { |
|
286 struct transport_info info = { NULL, NULL, 0 }; |
|
287 struct transport_test *tp = transport_test_create(&info); |
|
288 transport_t *transport = transport_test_cast(tp); |
|
289 |
|
290 // put the read data |
|
291 log_info("test transport_test_push_*"); |
|
292 transport_test_push_buf(tp, "foo", 3); |
|
293 transport_test_push_str(tp, "barx"); |
|
294 transport_test_push_fmt(tp, "xx %s xx", "quux"); |
|
295 transport_test_push_eof(tp); |
|
296 |
|
297 // read it out |
|
298 log_info("test transport_test_read"); |
|
299 |
|
300 assert_transport_read(transport, "foo"); |
|
301 assert_transport_read(transport, "ba"); |
|
302 assert_transport_read(transport, "rx"); |
|
303 assert_transport_read(transport, "xx quux xx"); |
|
304 assert_transport_eof(transport); |
|
305 |
|
306 // write some data in |
|
307 log_info("test transport_test_write"); |
|
308 |
|
309 assert_transport_write(transport, "test "); |
|
310 assert_transport_write(transport, "data"); |
|
311 |
|
312 // check output |
|
313 log_info("test transport_test_pull_*"); |
|
314 |
|
315 assert_transport_data(tp, "test data"); |
|
316 assert_transport_data(tp, ""); |
|
317 |
|
318 // cleanup |
|
319 transport_test_destroy(tp); |
|
320 } |
|
321 |
|
322 void assert_read_line (struct line_proto *lp, const char *line_str) |
|
323 { |
|
324 char *line_buf; |
|
325 |
|
326 log_debug("expect: %s", dump_str(line_str)); |
|
327 |
|
328 assert_success(line_proto_recv(lp, &line_buf)); |
|
329 |
|
330 if (line_str) { |
|
331 assert_strcmp(line_buf, line_str); |
|
332 |
|
333 } else { |
|
334 assert_strnull(line_buf); |
|
335 |
|
336 } |
|
337 } |
|
338 |
|
339 /** |
|
340 * Context info for test_line_proto callbacks |
|
341 */ |
|
342 struct _lp_test_ctx { |
|
343 /** Expected line */ |
|
344 const char *line; |
|
345 |
|
346 /** Expected error */ |
|
347 struct error_info err; |
|
348 }; |
|
349 |
|
350 static void _lp_on_line (char *line, void *arg) |
|
351 { |
|
352 struct _lp_test_ctx *ctx = arg; |
|
353 |
|
354 log_debug("%s", dump_str(line)); |
|
355 |
|
356 assert_strcmp(line, ctx->line); |
|
357 |
|
358 ctx->line = NULL; |
|
359 } |
|
360 |
|
361 static void _lp_on_error (struct error_info *err, void *arg) |
|
362 { |
|
363 struct _lp_test_ctx *ctx = arg; |
|
364 |
|
365 assert_error_info(err, &ctx->err); |
|
366 } |
|
367 |
|
368 static struct line_proto_callbacks _lp_callbacks = { |
|
369 .on_line = &_lp_on_line, |
|
370 .on_error = &_lp_on_error, |
|
371 }; |
|
372 |
|
373 void test_line_proto (void) |
|
374 { |
|
375 struct transport_test *tp = transport_test_create(NULL); |
|
376 transport_t *transport = transport_test_cast(tp); |
|
377 struct line_proto *lp; |
|
378 struct _lp_test_ctx ctx; |
|
379 struct error_info err; |
|
380 |
|
381 // put the read data |
|
382 log_debug("transport_test_push_*"); |
|
383 transport_test_push_str(tp, "hello\r\n"); |
|
384 transport_test_push_str(tp, "world\n"); |
|
385 transport_test_push_str(tp, "this "); |
|
386 transport_test_push_str(tp, "is a line\r"); |
|
387 transport_test_push_str(tp, "\nfragment"); |
|
388 |
|
389 // create the lp |
|
390 assert_success(line_proto_create(&lp, transport, 128, &_lp_callbacks, &ctx, &err)); |
|
391 |
|
392 log_info("test line_proto_recv"); |
|
393 |
|
394 // then read some lines from it |
|
395 assert_read_line(lp, "hello"); |
|
396 assert_read_line(lp, "world"); |
|
397 assert_read_line(lp, "this is a line"); |
|
398 assert_read_line(lp, NULL); |
|
399 |
|
400 // then add a final bit to trigger on_line |
|
401 log_info("test on_line"); |
|
402 |
|
403 ctx.line = "fragment"; |
|
404 transport_test_push_str(tp, "\r\n"); |
|
405 assert_strnull(ctx.line); |
|
406 |
|
407 // test writing |
|
408 log_info("test line_proto_send"); |
|
409 assert_success(-line_proto_send(lp, "foobar\r\n")); |
|
410 assert_success(-line_proto_send(lp, "quux\r\n")); |
|
411 assert_transport_data(tp, "foobar\r\nquux\r\n"); |
|
412 |
|
413 // XXX: test partial writes |
|
414 |
|
415 // cleanup |
|
416 line_proto_destroy(lp); |
|
417 } |
|
418 |
|
419 void test_irc_queue (void) |
|
420 { |
|
421 struct transport_test *tp = transport_test_create(NULL); |
|
422 transport_t *transport = transport_test_cast(tp); |
|
423 struct line_proto *lp; |
|
424 struct irc_queue *queue; |
|
425 struct irc_queue_entry *queue_entry; |
|
426 struct error_info err; |
|
427 |
|
428 // create the lp |
|
429 assert_success(line_proto_create(&lp, transport, 128, &_lp_callbacks, NULL, &err)); |
|
430 |
|
431 // create the queue |
|
432 assert_success(irc_queue_create(&queue, _test_ctx.ev_base, lp, &err)); |
|
433 |
|
434 struct irc_line line = { |
|
435 NULL, "TEST", { "fooX" } |
|
436 }; |
|
437 |
|
438 // then test simple writes, we should be able to push five lines directly |
|
439 log_info("test irc_queue_process (irc_queue_send_direct)"); |
|
440 line.args[0] = "foo0"; assert_success(irc_queue_process(queue, &line)); |
|
441 line.args[0] = "foo1"; assert_success(irc_queue_process(queue, &line)); |
|
442 line.args[0] = "foo2"; assert_success(irc_queue_process(queue, &line)); |
|
443 line.args[0] = "foo3"; assert_success(irc_queue_process(queue, &line)); |
|
444 line.args[0] = "foo4"; assert_success(irc_queue_process(queue, &line)); |
|
445 |
|
446 // they should all be output |
|
447 assert_transport_data(tp, |
|
448 "TEST foo0\r\n" |
|
449 "TEST foo1\r\n" |
|
450 "TEST foo2\r\n" |
|
451 "TEST foo3\r\n" |
|
452 "TEST foo4\r\n" |
|
453 ); |
|
454 |
|
455 // then enqueue |
|
456 log_info("test irc_queue_process (irc_queue_put)"); |
|
457 line.args[0] = "foo5"; assert_success(irc_queue_process(queue, &line)); |
|
458 |
|
459 // ensure it was enqueued |
|
460 assert((queue_entry = TAILQ_FIRST(&queue->list)) != NULL); |
|
461 assert_strcmp(queue_entry->line_buf, "TEST foo5\r\n"); |
|
462 |
|
463 // ensure timer is set |
|
464 assert(event_pending(queue->ev, EV_TIMEOUT, NULL)); |
|
465 |
|
466 // run the event loop to let the timer run |
|
467 log_info("running the event loop once..."); |
|
468 assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0); |
|
469 |
|
470 // test to check that the line was now sent |
|
471 log_info("checking that the delayed line was sent..."); |
|
472 assert_transport_data(tp, "TEST foo5\r\n"); |
|
473 assert(TAILQ_EMPTY(&queue->list)); |
|
474 assert(!event_pending(queue->ev, EV_TIMEOUT, NULL)); |
|
475 |
|
476 // cleanup |
|
477 irc_queue_destroy(queue); |
|
478 } |
|
479 |
|
480 struct test_conn_ctx { |
|
481 /** Callback flags */ |
|
482 bool on_registered, on_TEST, on_error, on_quit; |
|
483 }; |
|
484 |
|
485 static void _conn_on_registered (struct irc_conn *conn, void *arg) |
|
486 { |
|
487 struct test_conn_ctx *ctx = arg; |
|
488 |
|
489 (void) conn; |
|
490 |
|
491 if (ctx) ctx->on_registered = true; |
|
492 |
|
493 log_debug("registered"); |
|
494 } |
|
495 |
|
496 static void _conn_on_error (struct irc_conn *conn, struct error_info *err, void *arg) |
|
497 { |
|
498 struct test_conn_ctx *ctx = arg; |
|
499 |
|
500 (void) conn; |
|
501 (void) err; |
|
502 |
|
503 if (ctx) ctx->on_error = true; |
|
504 |
|
505 log_debug("on_error"); |
|
506 } |
|
507 |
|
508 static void _conn_on_quit (struct irc_conn *conn, void *arg) |
|
509 { |
|
510 struct test_conn_ctx *ctx = arg; |
|
511 |
|
512 (void) conn; |
|
513 |
|
514 if (ctx) ctx->on_quit = true; |
|
515 |
|
516 log_debug("on_quit"); |
|
517 } |
|
518 |
|
519 static void _conn_on_TEST (const struct irc_line *line, void *arg) |
|
520 { |
|
521 struct test_conn_ctx *ctx = arg; |
|
522 |
|
523 assert(line->source); |
|
524 assert(!line->source->nickname && !line->source->username && line->source->hostname); |
|
525 assert_strcmp(line->command, "TEST"); |
|
526 assert_strcmp(line->args[0], "arg0"); |
|
527 assert_strnull(line->args[1]); |
|
528 |
|
529 if (ctx) ctx->on_TEST = true; |
|
530 |
|
531 log_debug("on_TEST"); |
|
532 } |
|
533 |
|
534 static struct irc_conn_callbacks _conn_callbacks = { |
|
535 .on_registered = &_conn_on_registered, |
|
536 .on_error = &_conn_on_error, |
|
537 .on_quit = &_conn_on_quit, |
|
538 }; |
|
539 |
|
540 static struct irc_cmd_handler _conn_handlers[] = { |
|
541 { "TEST", &_conn_on_TEST }, |
|
542 { NULL, NULL } |
|
543 }; |
|
544 |
|
545 /** |
|
546 * Create and return a new irc_conn with the given ctx (will be initialized to zero). |
|
547 */ |
|
548 struct irc_conn* setup_irc_conn (struct transport_test *tp, bool noisy, struct test_conn_ctx *ctx) |
|
549 { |
|
550 struct irc_conn *conn; |
|
551 struct error_info err; |
|
552 struct irc_conn_register_info register_info = { |
|
553 "nick", "user", "realname" |
|
554 }; |
|
555 |
|
556 // init the ctx |
|
557 memset(ctx, 0, sizeof(*ctx)); |
|
558 |
|
559 // create the irc_conn |
|
560 assert_success(irc_conn_create(&conn, transport_test_cast(tp), &_conn_callbacks, ctx, &err)); |
|
561 |
|
562 // test register |
|
563 if (noisy) log_info("test irc_conn_register"); |
|
564 assert_success(irc_conn_register(conn, ®ister_info)); |
|
565 assert_transport_data(tp, "NICK nick\r\nUSER user 0 * realname\r\n"); |
|
566 |
|
567 // test on_register callback |
|
568 if (noisy) log_info("test irc_conn_callbacks.on_register"); |
|
569 transport_test_push_str(tp, "001 mynick :Blaa blaa blaa\r\n"); |
|
570 if (ctx) assert(ctx->on_registered); |
|
571 assert_strcmp(conn->nickname, "mynick"); |
|
572 |
|
573 // ok |
|
574 return conn; |
|
575 } |
|
576 |
|
577 void test_irc_conn (void) |
|
578 { |
|
579 struct test_conn_ctx ctx; |
|
580 struct transport_test *tp = setup_transport_test(); |
|
581 struct irc_conn *conn = setup_irc_conn(tp, true, &ctx); |
|
582 |
|
583 // add our test handlers |
|
584 assert_success(irc_conn_add_cmd_handlers(conn, _conn_handlers, &ctx)); |
|
585 |
|
586 // test on_TEST handler |
|
587 // XXX: come up with a better prefix |
|
588 log_info("test irc_conn.handlers"); |
|
589 transport_test_push_str(tp, ":foobar-prefix TEST arg0\r\n"); |
|
590 assert(ctx.on_TEST); |
|
591 |
|
592 // test PING/PONG |
|
593 log_info("test PING/PONG"); |
|
594 transport_test_push_str(tp, "PING foo\r\n"); |
|
595 assert_transport_data(tp, "PONG foo\r\n"); |
|
596 |
|
597 // quit nicely |
|
598 log_info("test QUIT"); |
|
599 assert_success(irc_conn_QUIT(conn, "bye now")); |
|
600 assert_transport_data(tp, "QUIT :bye now\r\n"); |
|
601 assert(conn->quitting); |
|
602 |
|
603 transport_test_push_str(tp, "ERROR :Closing Link: Quit\r\n"); |
|
604 transport_test_push_eof(tp); |
|
605 assert(conn->quit && !conn->quitting && !conn->registered); |
|
606 assert(ctx.on_quit); |
|
607 assert(!ctx.on_error); |
|
608 |
|
609 // destroy it |
|
610 irc_conn_destroy(conn); |
|
611 } |
|
612 |
|
613 void test_irc_conn_self_nick (void) |
|
614 { |
|
615 struct test_conn_ctx ctx; |
|
616 struct transport_test *tp = setup_transport_test(); |
|
617 struct irc_conn *conn = setup_irc_conn(tp, false, &ctx); |
|
618 |
|
619 log_info("test irc_conn_on_NICK"); |
|
620 transport_test_push_fmt(tp, ":mynick!user@somehost NICK mynick2\r\n"); |
|
621 assert_strcmp(conn->nickname, "mynick2"); |
|
622 |
|
623 // cleanup |
|
624 irc_conn_destroy(conn); |
|
625 } |
|
626 |
|
627 struct test_chan_ctx { |
|
628 /** The channel name */ |
|
629 const char *channel; |
|
630 |
|
631 /** The channel we're supposed to be testing */ |
|
632 struct irc_chan *chan; |
|
633 |
|
634 /** Flags for callbacks called*/ |
|
635 bool on_chan_self_join, on_chan_self_part, on_chan_join, on_chan_part; |
|
636 |
|
637 }; |
|
638 |
|
639 void _on_chan_self_join (struct irc_chan *chan, void *arg) |
|
640 { |
|
641 struct test_chan_ctx *ctx = arg; |
|
642 |
|
643 assert(chan == ctx->chan); |
|
644 |
|
645 ctx->on_chan_self_join = true; |
|
646 |
|
647 log_debug("on_self_join"); |
|
648 } |
|
649 |
|
650 void _on_chan_join (struct irc_chan *chan, const struct irc_nm *source, void *arg) |
|
651 { |
|
652 struct test_chan_ctx *ctx = arg; |
|
653 |
|
654 assert(chan == ctx->chan); |
|
655 |
|
656 // XXX: verify source |
|
657 |
|
658 ctx->on_chan_join = true; |
|
659 |
|
660 log_debug("on_join"); |
|
661 } |
|
662 |
|
663 void _on_chan_part (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg) |
|
664 { |
|
665 struct test_chan_ctx *ctx = arg; |
|
666 |
|
667 assert(chan == ctx->chan); |
|
668 |
|
669 // XXX: verify source |
|
670 // XXX: verify msg |
|
671 |
|
672 ctx->on_chan_part = true; |
|
673 |
|
674 log_debug("on_part"); |
|
675 } |
|
676 |
|
677 |
|
678 struct irc_chan_callbacks _chan_callbacks = { |
|
679 .on_self_join = &_on_chan_self_join, |
|
680 .on_join = &_on_chan_join, |
|
681 .on_part = &_on_chan_part, |
|
682 }; |
|
683 |
|
684 /** |
|
685 * Setup an irc_net using the given socket, and consume the register request output, but do not push the RPL_WELCOME |
|
686 */ |
|
687 struct irc_net* setup_irc_net_unregistered (struct transport_test *tp) |
|
688 { |
|
689 struct irc_net *net; |
|
690 struct irc_net_info net_info = { |
|
691 .register_info = { |
|
692 "nick", "user", "realname" |
|
693 }, |
|
694 }; |
|
695 struct error_info err; |
|
696 |
|
697 // create the irc_net |
|
698 net_info.transport = transport_test_cast(tp); |
|
699 assert_success(irc_net_create(&net, &net_info, &err)); |
|
700 |
|
701 // test register output |
|
702 assert_transport_data(tp, "NICK nick\r\nUSER user 0 * realname\r\n"); |
|
703 |
|
704 // ok |
|
705 return net; |
|
706 } |
|
707 |
|
708 /** |
|
709 * Push to RPL_WELCOME reply, and test state |
|
710 */ |
|
711 void do_irc_net_welcome (struct transport_test *tp, struct irc_net *net) |
|
712 { |
|
713 // registration reply |
|
714 transport_test_push_fmt(tp, "001 mynick :Blaa blaa blaa\r\n"); |
|
715 assert(net->conn->registered); |
|
716 assert_strcmp(net->conn->nickname, "mynick"); |
|
717 |
|
718 } |
|
719 |
|
720 /** |
|
721 * Creates an irc_net and puts it into the registered state |
|
722 */ |
|
723 struct irc_net* setup_irc_net (struct transport_test *tp) |
|
724 { |
|
725 struct irc_net *net; |
|
726 |
|
727 net = setup_irc_net_unregistered(tp); |
|
728 do_irc_net_welcome(tp, net); |
|
729 |
|
730 // ok |
|
731 return net; |
|
732 } |
|
733 |
|
734 /** |
|
735 * General test for irc_net to handle startup |
|
736 */ |
|
737 void test_irc_net (void) |
|
738 { |
|
739 struct transport_test *tp = setup_transport_test(); |
|
740 |
|
741 // create the network |
|
742 log_info("test irc_net_create"); |
|
743 struct irc_net *net = setup_irc_net_unregistered(tp); |
|
744 |
|
745 // send the registration reply |
|
746 log_info("test irc_conn_on_RPL_WELCOME"); |
|
747 do_irc_net_welcome(tp, net); |
|
748 |
|
749 // test errors by setting EOF |
|
750 log_info("test irc_net_error"); |
|
751 transport_test_push_eof(tp); |
|
752 assert(net->conn == NULL); |
|
753 |
|
754 // cleanup |
|
755 irc_net_destroy(net); |
|
756 } |
|
757 |
|
758 /** |
|
759 * Ensure that an irc_chan_user exists/doesn't exist for the given channel/nickname, and return it |
|
760 */ |
|
761 struct irc_chan_user* check_chan_user (struct irc_chan *chan, const char *nickname, bool exists) |
|
762 { |
|
763 struct irc_chan_user *chan_user = irc_chan_get_user(chan, nickname); |
|
764 |
|
765 if (exists && chan_user == NULL) |
|
766 FATAL("user %s not found in channel %s", dump_str(nickname), dump_str(irc_chan_name(chan))); |
|
767 |
|
768 if (!exists && chan_user) |
|
769 FATAL("user %s should not be on channel %s anymore", dump_str(nickname), dump_str(irc_chan_name(chan))); |
|
770 |
|
771 log_debug("%s, exists=%d -> %p: user=%p, nickname=%s", |
|
772 nickname, exists, chan_user, chan_user ? chan_user->user : NULL, chan_user ? chan_user->user->nickname : NULL); |
|
773 |
|
774 if (chan_user) |
|
775 assert_strcmp(chan_user->user->nickname, nickname); |
|
776 |
|
777 return chan_user; |
|
778 } |
|
779 |
|
780 /** |
|
781 * Creates an irc_chan on the given irc_net, but does not check any output (useful for testing offline add). |
|
782 * |
|
783 * You must pass a test_chan_ctx for use with later operations, this will be initialized for you. |
|
784 */ |
|
785 struct irc_chan* setup_irc_chan_raw (struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
786 { |
|
787 struct irc_chan *chan; |
|
788 struct irc_chan_info chan_info = { |
|
789 .channel = channel, |
|
790 }; |
|
791 struct error_info err; |
|
792 |
|
793 // initialize the given ctx |
|
794 memset(ctx, 0, sizeof(*ctx)); |
|
795 ctx->channel = channel; |
|
796 |
|
797 // add a channel |
|
798 assert_success(irc_net_add_chan(net, &chan, &chan_info, &err)); |
|
799 assert(!chan->joined); |
|
800 assert_success(irc_chan_add_callbacks(chan, &_chan_callbacks, ctx)); |
|
801 ctx->chan = chan; |
|
802 |
|
803 // ok |
|
804 return chan; |
|
805 } |
|
806 |
|
807 /** |
|
808 * Checks that the JOIN request for a channel was sent, and sends the basic JOIN reply |
|
809 */ |
|
810 void do_irc_chan_join (struct transport_test *tp, struct test_chan_ctx *ctx) |
|
811 { |
|
812 // JOIN request |
|
813 assert(ctx->chan->joining); |
|
814 assert_transport_data(tp, "JOIN %s\r\n", ctx->channel); |
|
815 |
|
816 // JOIN reply |
|
817 transport_test_push_fmt(tp, ":mynick!user@host JOIN %s\r\n", ctx->channel); |
|
818 assert(!ctx->chan->joining && ctx->chan->joined); |
|
819 assert(ctx->on_chan_self_join); |
|
820 } |
|
821 |
|
822 /** |
|
823 * Sends a short RPL_NAMREPLY/RPL_ENDOFNAMES reply and checks that the users list matches |
|
824 */ |
|
825 void do_irc_chan_namreply (struct transport_test *tp, struct test_chan_ctx *ctx) |
|
826 { |
|
827 // RPL_NAMREPLY |
|
828 transport_test_push_fmt(tp, "353 mynick = %s :mynick userA +userB @userC\r\n", ctx->channel); |
|
829 transport_test_push_fmt(tp, "353 mynick = %s :trailingspace \r\n", ctx->channel); |
|
830 transport_test_push_fmt(tp, "366 mynick %s :End of NAMES\r\n", ctx->channel); |
|
831 |
|
832 // XXX: this should be an exclusive test, i.e. these should be the only ones... |
|
833 check_chan_user(ctx->chan, "mynick", true); |
|
834 check_chan_user(ctx->chan, "userA", true); |
|
835 check_chan_user(ctx->chan, "userB", true); |
|
836 check_chan_user(ctx->chan, "userC", true); |
|
837 } |
|
838 |
|
839 /** |
|
840 * Creates an irc_chan on the given irc_net, and checks up to the JOIN reply |
|
841 */ |
|
842 struct irc_chan* setup_irc_chan_join (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
843 { |
|
844 setup_irc_chan_raw(net, channel, ctx); |
|
845 do_irc_chan_join(tp, ctx); |
|
846 |
|
847 // ok |
|
848 return ctx->chan; |
|
849 } |
|
850 |
|
851 /** |
|
852 * Creates an irc_chan on the given irc_net, sends the JOIN stuff plus RPL_NAMREPLY |
|
853 */ |
|
854 struct irc_chan* setup_irc_chan (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx) |
|
855 { |
|
856 setup_irc_chan_raw(net, channel, ctx); |
|
857 do_irc_chan_join(tp, ctx); |
|
858 do_irc_chan_namreply(tp, ctx); |
|
859 |
|
860 // ok |
|
861 return ctx->chan; |
|
862 } |
|
863 |
|
864 |
|
865 /** |
|
866 * Call irc_net_add_chan while offline, and ensure that we send the JOIN request after RPL_WELCOME, and handle the join |
|
867 * reply OK. |
|
868 */ |
|
869 void test_irc_chan_add_offline (void) |
|
870 { |
|
871 struct test_chan_ctx ctx; |
|
872 |
|
873 struct transport_test *tp = setup_transport_test(); |
|
874 |
|
875 log_info("test irc_net_create"); |
|
876 struct irc_net *net = setup_irc_net_unregistered(tp); |
|
877 |
|
878 // add an offline channel |
|
879 log_info("test offline irc_net_add_chan"); |
|
880 struct irc_chan *chan = setup_irc_chan_raw(net, "#test", &ctx); |
|
881 assert(!chan->joining && !chan->joined); |
|
882 |
|
883 // send the registration reply |
|
884 log_info("test irc_conn_on_RPL_WELCOME"); |
|
885 do_irc_net_welcome(tp, net); |
|
886 |
|
887 // test the join sequence |
|
888 log_info("test irc_chan_join/irc_chan_on_JOIN"); |
|
889 do_irc_chan_join(tp, &ctx); |
|
890 |
|
891 // cleanup |
|
892 irc_net_destroy(net); |
|
893 } |
|
894 |
|
895 void test_irc_chan_namreply (void) |
|
896 { |
|
897 struct test_chan_ctx ctx; |
|
898 struct transport_test *tp = setup_transport_test(); |
|
899 struct irc_net *net = setup_irc_net(tp); |
|
900 setup_irc_chan_join(tp, net, "#test", &ctx); |
|
901 |
|
902 log_info("test irc_chan_on_RPL_NAMREPLY"); |
|
903 do_irc_chan_namreply(tp, &ctx); |
|
904 |
|
905 // cleanup |
|
906 irc_net_destroy(net); |
|
907 } |
|
908 |
|
909 void test_irc_chan_user_join (void) |
|
910 { |
|
911 struct test_chan_ctx ctx; |
|
912 struct transport_test *tp = setup_transport_test(); |
|
913 struct irc_net *net = setup_irc_net(tp); |
|
914 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
915 |
|
916 // have a user join |
|
917 log_info("test irc_chan_on_JOIN"); |
|
918 transport_test_push_fmt(tp, ":newuser!someone@somewhere JOIN %s\r\n", "#test"); |
|
919 assert(ctx.on_chan_join); |
|
920 check_chan_user(chan, "newuser", true); |
|
921 |
|
922 // cleanup |
|
923 irc_net_destroy(net); |
|
924 } |
|
925 |
|
926 void test_irc_chan_user_part (void) |
|
927 { |
|
928 struct test_chan_ctx ctx; |
|
929 struct transport_test *tp = setup_transport_test(); |
|
930 struct irc_net *net = setup_irc_net(tp); |
|
931 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
932 |
|
933 // have a user join |
|
934 log_info("test irc_chan_on_PART"); |
|
935 transport_test_push_fmt(tp, ":userA!someone@somewhere PART %s\r\n", "#test"); |
|
936 assert(ctx.on_chan_part); ctx.on_chan_part = NULL; |
|
937 check_chan_user(chan, "userA", false); |
|
938 |
|
939 // cleanup |
|
940 irc_net_destroy(net); |
|
941 } |
|
942 |
|
943 void test_irc_chan_user_kick (void) |
|
944 { |
|
945 struct test_chan_ctx ctx; |
|
946 struct transport_test *tp = setup_transport_test(); |
|
947 struct irc_net *net = setup_irc_net(tp); |
|
948 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
949 |
|
950 // kick a user |
|
951 log_info("test irc_chan_on_KICK (other)"); |
|
952 transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s userB\r\n", "#test"); |
|
953 check_chan_user(chan, "userA", true); |
|
954 check_chan_user(chan, "userB", false); |
|
955 |
|
956 // cleanup |
|
957 irc_net_destroy(net); |
|
958 } |
|
959 |
|
960 void test_irc_chan_self_kick (void) |
|
961 { |
|
962 struct test_chan_ctx ctx; |
|
963 struct transport_test *tp = setup_transport_test(); |
|
964 struct irc_net *net = setup_irc_net(tp); |
|
965 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
966 |
|
967 // kick a user |
|
968 log_info("test irc_chan_on_KICK (self)"); |
|
969 transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s mynick foobar\r\n", "#test"); |
|
970 assert(!chan->joined); |
|
971 assert(chan->kicked); |
|
972 |
|
973 // cleanup |
|
974 irc_net_destroy(net); |
|
975 } |
|
976 |
|
977 void test_irc_chan_user_nick (void) |
|
978 { |
|
979 struct test_chan_ctx ctx; |
|
980 struct transport_test *tp = setup_transport_test(); |
|
981 struct irc_net *net = setup_irc_net(tp); |
|
982 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
983 |
|
984 // rename one of the users |
|
985 log_info("test irc_net_on_chanuser"); |
|
986 transport_test_push_fmt(tp, ":userA!someone@somewhere NICK userA2\r\n"); |
|
987 check_chan_user(chan, "userA", false); |
|
988 check_chan_user(chan, "userB", true); |
|
989 |
|
990 // cleanup |
|
991 irc_net_destroy(net); |
|
992 } |
|
993 |
|
994 void test_irc_chan_user_quit (void) |
|
995 { |
|
996 struct test_chan_ctx ctx; |
|
997 struct transport_test *tp = setup_transport_test(); |
|
998 struct irc_net *net = setup_irc_net(tp); |
|
999 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
1000 |
|
1001 // rename one of the users |
|
1002 log_info("test irc_net_on_chanuser"); |
|
1003 transport_test_push_fmt(tp, ":userA!someone@somewhere QUIT foo\r\n"); |
|
1004 check_chan_user(chan, "userA", false); |
|
1005 |
|
1006 // cleanup |
|
1007 irc_net_destroy(net); |
|
1008 } |
|
1009 |
|
1010 void _test_irc_chan_on_CTCP_ACTION (const struct irc_line *line, void *arg) |
|
1011 { |
|
1012 bool *flag = arg; |
|
1013 |
|
1014 log_debug("CTCP ACTION"); |
|
1015 |
|
1016 *flag = true; |
|
1017 } |
|
1018 |
|
1019 static struct irc_cmd_handler _test_irc_chan_handlers[] = { |
|
1020 { "CTCP ACTION", &_test_irc_chan_on_CTCP_ACTION }, |
|
1021 { NULL, NULL } |
|
1022 }; |
|
1023 |
|
1024 void test_irc_chan_CTCP_ACTION (void) |
|
1025 { |
|
1026 struct test_chan_ctx ctx; |
|
1027 struct transport_test *tp = setup_transport_test(); |
|
1028 struct irc_net *net = setup_irc_net(tp); |
|
1029 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
1030 bool cb_ok = false; |
|
1031 |
|
1032 // add our handler |
|
1033 assert_success(irc_cmd_add(&chan->handlers, _test_irc_chan_handlers, &cb_ok)); |
|
1034 |
|
1035 // rename one of the users |
|
1036 log_info("test irc_conn_on_CTCP_ACTION"); |
|
1037 transport_test_push_fmt(tp, ":userA!someone@somewhere PRIVMSG #test \001ACTION hello world\001\r\n"); |
|
1038 assert(cb_ok); |
|
1039 |
|
1040 // cleanup |
|
1041 irc_net_destroy(net); |
|
1042 } |
|
1043 |
|
1044 void test_irc_chan_privmsg (void) |
|
1045 { |
|
1046 struct test_chan_ctx ctx; |
|
1047 struct transport_test *tp = setup_transport_test(); |
|
1048 struct irc_net *net = setup_irc_net(tp); |
|
1049 struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx); |
|
1050 |
|
1051 // rename one of the users |
|
1052 log_info("test irc_chan_PRIVMSG"); |
|
1053 assert_success(irc_chan_PRIVMSG(chan, "foobar quux")); |
|
1054 assert_transport_data(tp, "PRIVMSG #test :foobar quux\r\n"); |
|
1055 |
|
1056 // cleanup |
|
1057 irc_net_destroy(net); |
|
1058 } |
|
1059 |
|
1060 // XXX: needs to be split off into its own test_fifo.c |
|
1061 #include <sys/types.h> |
|
1062 #include <sys/stat.h> |
|
1063 #include <fcntl.h> |
|
1064 #include <unistd.h> |
|
1065 struct test_fifo_ctx { |
|
1066 /** Path to the fifo */ |
|
1067 const char *path; |
|
1068 |
|
1069 /** The write end */ |
|
1070 int fd; |
|
1071 |
|
1072 /** callback invoked? */ |
|
1073 bool on_read; |
|
1074 |
|
1075 /** Still running? */ |
|
1076 bool run; |
|
1077 }; |
|
1078 |
|
1079 /** |
|
1080 * Open the FIFO and write the test string to it |
|
1081 */ |
|
1082 static void test_fifo_open_write (struct test_fifo_ctx *ctx) |
|
1083 { |
|
1084 // ...raw, for writing |
|
1085 if ((ctx->fd = open(ctx->path, O_WRONLY)) < 0) |
|
1086 FATAL_PERROR("open"); |
|
1087 |
|
1088 // write something into it |
|
1089 if (write(ctx->fd, "test", 4) != 4) |
|
1090 FATAL_PERROR("write"); |
|
1091 |
|
1092 } |
|
1093 |
|
1094 static void test_fifo_close (struct test_fifo_ctx *ctx) |
|
1095 { |
|
1096 close(ctx->fd); |
|
1097 ctx->fd = -1; |
|
1098 } |
|
1099 |
|
1100 static void test_fifo_on_read (transport_t *fifo, void *arg) |
|
1101 { |
|
1102 int ret; |
|
1103 char buf[16]; |
|
1104 struct test_fifo_ctx *ctx = arg; |
|
1105 error_t err; |
|
1106 |
|
1107 // read it back out |
|
1108 log_info("test fifo_read"); |
|
1109 if ((ret = transport_read(fifo, buf, 16, &err)) < 0) |
|
1110 assert_success(-ret); |
|
1111 |
|
1112 assert(ret == 4); |
|
1113 assert_strncmp(buf, "test", 4); |
|
1114 |
|
1115 if (ctx->on_read) { |
|
1116 test_fifo_close(ctx); |
|
1117 ctx->run = false; |
|
1118 return; |
|
1119 } |
|
1120 |
|
1121 // re-open the fifo |
|
1122 log_info("test fifo-re-open"); |
|
1123 test_fifo_close(ctx); |
|
1124 test_fifo_open_write(ctx); |
|
1125 |
|
1126 ctx->on_read = true; |
|
1127 } |
|
1128 |
|
1129 static struct transport_callbacks test_fifo_callbacks = { |
|
1130 .on_read = test_fifo_on_read, |
|
1131 }; |
|
1132 |
|
1133 void test_fifo (void) |
|
1134 { |
|
1135 transport_t *fifo; |
|
1136 struct error_info err; |
|
1137 struct test_fifo_ctx _ctx, *ctx = &_ctx; memset(ctx, 0, sizeof(*ctx)); |
|
1138 struct transport_info info = { &test_fifo_callbacks, ctx, TRANSPORT_READ }; |
|
1139 |
|
1140 // XXX: requires that this be run in a suitable CWD |
|
1141 ctx->path = "test.fifo"; |
|
1142 |
|
1143 // create the fifo |
|
1144 if ((mkfifo(ctx->path, 0600) < 0) && (errno != EEXIST)) |
|
1145 FATAL_PERROR("mkfifo"); |
|
1146 |
|
1147 // open it |
|
1148 log_info("test fifo_open_read"); |
|
1149 assert_success(fifo_open_read(&info, &fifo, _test_ctx.ev_base, ctx->path, &err)); |
|
1150 |
|
1151 // put some data into it |
|
1152 test_fifo_open_write(ctx); |
|
1153 |
|
1154 // run the event loop |
|
1155 log_debug("running the event loop..."); |
|
1156 ctx->run = true; |
|
1157 |
|
1158 while (ctx->run) |
|
1159 assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0); |
|
1160 |
|
1161 // check |
|
1162 assert(ctx->fd < 0); |
|
1163 |
|
1164 // cleanup |
|
1165 transport_destroy(fifo); |
|
1166 } |
|
1167 |
|
1168 /** |
|
1169 * Test definition |
|
1170 */ |
|
1171 struct test { |
|
1172 /** Test name */ |
|
1173 const char *name; |
|
1174 |
|
1175 /** Test func */ |
|
1176 void (*func) (void); |
|
1177 |
|
1178 bool optional; |
|
1179 }; |
|
1180 |
|
1181 #define DEF_TEST(name) { #name, &test_ ## name, false } |
|
1182 #define DEF_TEST_OPTIONAL(name) { #name, &test_ ## name, true } |
|
1183 #define DEF_TEST_END { NULL, NULL, false } |
|
1184 |
|
1185 static struct test _tests[] = { |
|
1186 DEF_TEST( str_quote ), |
|
1187 DEF_TEST( str_format ), |
|
1188 DEF_TEST( dump_str ), |
|
1189 DEF_TEST( transport_test ), |
|
1190 DEF_TEST( line_proto ), |
|
1191 DEF_TEST( irc_queue ), |
|
1192 // XXX: irc_line_parse_invalid_prefix |
|
1193 DEF_TEST( irc_conn ), |
|
1194 DEF_TEST( irc_conn_self_nick ), |
|
1195 DEF_TEST( irc_net ), |
|
1196 DEF_TEST( irc_chan_add_offline ), |
|
1197 DEF_TEST( irc_chan_namreply ), |
|
1198 DEF_TEST( irc_chan_user_join ), |
|
1199 DEF_TEST( irc_chan_user_part ), |
|
1200 DEF_TEST( irc_chan_user_kick ), |
|
1201 DEF_TEST( irc_chan_self_kick ), |
|
1202 DEF_TEST( irc_chan_user_nick ), |
|
1203 DEF_TEST( irc_chan_user_quit ), |
|
1204 DEF_TEST( irc_chan_CTCP_ACTION ), |
|
1205 DEF_TEST( irc_chan_privmsg ), |
|
1206 DEF_TEST_OPTIONAL( fifo ), |
|
1207 DEF_TEST_END |
|
1208 }; |
|
1209 |
|
1210 /** |
|
1211 * Command-line option codes |
|
1212 */ |
|
1213 enum option_code { |
|
1214 OPT_HELP = 'h', |
|
1215 OPT_DEBUG = 'd', |
|
1216 OPT_QUIET = 'q', |
|
1217 OPT_LIST = 'l', |
|
1218 |
|
1219 /** Options without short names */ |
|
1220 _OPT_EXT_BEGIN = 0x00ff, |
|
1221 }; |
|
1222 |
|
1223 /** |
|
1224 * Command-line option definitions |
|
1225 */ |
|
1226 static struct option options[] = { |
|
1227 {"help", 0, NULL, OPT_HELP }, |
|
1228 {"debug", 0, NULL, OPT_DEBUG }, |
|
1229 {"quiet", 0, NULL, OPT_QUIET }, |
|
1230 {"list", 0, NULL, OPT_LIST }, |
|
1231 {0, 0, 0, 0 }, |
|
1232 }; |
|
1233 |
|
1234 /** |
|
1235 * Display --help output on stdout |
|
1236 */ |
|
1237 static void usage (const char *exe) |
|
1238 { |
|
1239 printf("Usage: %s [OPTIONS]\n", exe); |
|
1240 printf("\n"); |
|
1241 printf(" --help / -h display this message\n"); |
|
1242 printf(" --debug / -d display DEBUG log messages\n"); |
|
1243 printf(" --quiet / -q supress INFO log messages\n"); |
|
1244 printf(" --list / -l list all tests\n"); |
|
1245 } |
|
1246 |
|
1247 static void list_tests (struct test *tests) |
|
1248 { |
|
1249 struct test *test; |
|
1250 |
|
1251 printf("Available tests:\n"); |
|
1252 |
|
1253 for (test = tests; test->name; test++) { |
|
1254 printf("\t%s\n", test->name); |
|
1255 } |
|
1256 } |
|
1257 |
|
1258 int main (int argc, char **argv) |
|
1259 { |
|
1260 struct test *test; |
|
1261 size_t test_count = 0; |
|
1262 |
|
1263 int opt, option_index; |
|
1264 const char *filter = NULL; |
|
1265 |
|
1266 // parse options |
|
1267 while ((opt = getopt_long(argc, argv, "hdql", options, &option_index)) != -1) { |
|
1268 switch (opt) { |
|
1269 case OPT_HELP: |
|
1270 usage(argv[0]); |
|
1271 exit(EXIT_SUCCESS); |
|
1272 |
|
1273 case OPT_DEBUG: |
|
1274 set_log_level(LOG_DEBUG); |
|
1275 break; |
|
1276 |
|
1277 case OPT_QUIET: |
|
1278 set_log_level(LOG_WARN); |
|
1279 break; |
|
1280 |
|
1281 case OPT_LIST: |
|
1282 list_tests(_tests); |
|
1283 exit(EXIT_SUCCESS); |
|
1284 |
|
1285 case '?': |
|
1286 usage(argv[0]); |
|
1287 exit(EXIT_FAILURE); |
|
1288 } |
|
1289 } |
|
1290 |
|
1291 if (optind < argc) { |
|
1292 if (optind == argc - 1) { |
|
1293 // filter |
|
1294 filter = argv[optind]; |
|
1295 |
|
1296 log_info("only running tests: %s", filter); |
|
1297 } else { |
|
1298 FATAL("too many arguments"); |
|
1299 } |
|
1300 } |
|
1301 |
|
1302 // setup the sockets stuff |
|
1303 _test_ctx.ev_base = setup_sock(); |
|
1304 |
|
1305 // run tests |
|
1306 for (test = _tests; test->name; test++) { |
|
1307 if ((filter && strcmp(test->name, filter)) || (!filter && test->optional)) |
|
1308 continue; |
|
1309 |
|
1310 log_info("Running test: %s", test->name); |
|
1311 |
|
1312 test_count++; |
|
1313 test->func(); |
|
1314 } |
|
1315 |
|
1316 // no tests run? |
|
1317 if (test_count == 0) |
|
1318 FATAL("no tests run"); |
|
1319 |
|
1320 log_info("done, ran %zu tests", test_count); |
|
1321 } |
|