From: Rusty Russell Date: Mon, 14 Oct 2013 10:58:36 +0000 (+1030) Subject: ccan/io: test custom io functions. X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=e2ce04eac30ec613c858bd4cd2ca12e1c464edb8;hp=34776d3e9ad7de78778306a2d09c2c95df06c902 ccan/io: test custom io functions. And rename debug_io_plan() to io_plan_debug() so it can be exposed. Signed-off-by: Rusty Russell --- diff --git a/ccan/io/io.c b/ccan/io/io.c index 5ecd8241..b3c4c760 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -18,7 +18,7 @@ struct io_conn *current; bool (*io_debug)(struct io_conn *conn); bool io_debug_wakeup; -static void debug_io_plan(struct io_plan *plan) +void io_plan_debug(struct io_plan *plan) { if (io_plan_for_other) { io_plan_for_other = false; @@ -46,9 +46,6 @@ static void debug_io_wake(struct io_conn *conn) io_debug_wakeup = true; } #else -static void debug_io_plan(struct io_plan *plan) -{ -} static void debug_io_wake(struct io_conn *conn) { } @@ -181,7 +178,7 @@ struct io_plan io_write_(const void *data, size_t len, plan.next_arg = arg; plan.pollflag = POLLOUT; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } @@ -214,7 +211,7 @@ struct io_plan io_read_(void *data, size_t len, plan.next_arg = arg; plan.pollflag = POLLIN; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } @@ -246,7 +243,7 @@ struct io_plan io_read_partial_(void *data, size_t *len, plan.next_arg = arg; plan.pollflag = POLLIN; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } @@ -278,7 +275,7 @@ struct io_plan io_write_partial_(const void *data, size_t *len, plan.next_arg = arg; plan.pollflag = POLLOUT; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } @@ -291,7 +288,7 @@ struct io_plan io_idle(void) /* Never called (overridded by io_wake), but NULL means closing */ plan.next = io_close; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } @@ -331,7 +328,7 @@ struct io_plan io_close(struct io_conn *conn, void *arg) /* This means we're closing. */ plan.next = NULL; - debug_io_plan(&plan); + io_plan_debug(&plan); return plan; } diff --git a/ccan/io/io.h b/ccan/io/io.h index b8bf643d..72f80524 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -8,34 +8,6 @@ struct io_conn; -#ifdef DEBUG -extern bool io_plan_for_other; -extern bool (*io_debug)(struct io_conn *conn); -#define io_plan_other() ((io_plan_for_other = true)) -#else -#define io_plan_other() (void)0 -#endif - -struct io_state_read { - char *buf; - size_t len; -}; - -struct io_state_write { - const char *buf; - size_t len; -}; - -struct io_state_readpart { - char *buf; - size_t *lenp; -}; - -struct io_state_writepart { - const char *buf; - size_t *lenp; -}; - /** * struct io_plan - returned from a setup function. * @@ -50,13 +22,47 @@ struct io_plan { void *next_arg; union { - struct io_state_read read; - struct io_state_write write; - struct io_state_readpart readpart; - struct io_state_writepart writepart; + struct { + char *buf; + size_t len; + } read; + struct { + const char *buf; + size_t len; + } write; + struct { + char *buf; + size_t *lenp; + } readpart; + struct { + const char *buf; + size_t *lenp; + } writepart; + struct { + void *p; + size_t len; + } ptr_len; + struct { + void *p1; + void *p2; + } ptr_ptr; + struct { + size_t len1; + size_t len2; + } len_len; } u; }; +#ifdef DEBUG +extern bool io_plan_for_other; +extern bool (*io_debug)(struct io_conn *conn); +#define io_plan_other() ((io_plan_for_other = true)) +void io_plan_debug(struct io_plan *plan); +#else +#define io_plan_other() (void)0 +static inline void io_plan_debug(struct io_plan *plan) { } +#endif + /** * io_new_conn - create a new connection. * @fd: the file descriptor. diff --git a/ccan/io/test/run-17-homemade-io-DEBUG.c b/ccan/io/test/run-17-homemade-io-DEBUG.c new file mode 100644 index 00000000..40105d6f --- /dev/null +++ b/ccan/io/test/run-17-homemade-io-DEBUG.c @@ -0,0 +1,8 @@ +#define DEBUG +#define PORT "64017" +#define main real_main +int real_main(void); +#include "run-17-homemade-io.c" +#undef main +static bool always_debug(struct io_conn *conn) { return true; } +int main(void) { io_debug = always_debug; return real_main(); } diff --git a/ccan/io/test/run-17-homemade-io.c b/ccan/io/test/run-17-homemade-io.c new file mode 100644 index 00000000..08c081ec --- /dev/null +++ b/ccan/io/test/run-17-homemade-io.c @@ -0,0 +1,186 @@ +#include +/* Include the C files directly. */ +#include +#include +#include +#include +#include + +#ifndef PORT +#define PORT "65017" +#endif + +struct packet { + int state; + size_t len; + void *contents; +}; + +static void finish_ok(struct io_conn *conn, struct packet *pkt) +{ + ok1(pkt->state == 3); + pkt->state++; + io_break(pkt, io_idle()); +} + +static bool do_read_packet(int fd, struct io_plan *plan) +{ + struct packet *pkt = plan->u.ptr_len.p; + char *dest; + ssize_t ret; + size_t off, totlen; + + /* Reading len? */ + if (plan->u.ptr_len.len < sizeof(size_t)) { + ok1(pkt->state == 1); + pkt->state++; + dest = (char *)&pkt->len; + off = plan->u.ptr_len.len; + totlen = sizeof(pkt->len); + } else { + ok1(pkt->state == 2); + pkt->state++; + if (pkt->len == 0) + return true; + if (!pkt->contents && !(pkt->contents = malloc(pkt->len))) + goto fail; + else { + dest = pkt->contents; + off = plan->u.ptr_len.len - sizeof(pkt->len); + totlen = pkt->len; + } + } + + ret = read(fd, dest + off, totlen - off); + if (ret <= 0) + goto fail; + + plan->u.ptr_len.len += ret; + + /* Finished? */ + return (plan->u.ptr_len.len >= sizeof(pkt->len) + && plan->u.ptr_len.len == pkt->len + sizeof(pkt->len)); + +fail: + free(pkt->contents); + /* Override next function to close us. */ + plan->next = io_close; + return true; +} + +static struct io_plan io_read_packet(struct packet *pkt, + struct io_plan (*cb)(struct io_conn *, void *), + void *arg) +{ + struct io_plan plan; + + assert(cb); + pkt->contents = NULL; + plan.u.ptr_len.p = pkt; + plan.u.ptr_len.len = 0; + plan.io = do_read_packet; + plan.next = cb; + plan.next_arg = arg; + plan.pollflag = POLLIN; + + io_plan_debug(&plan); + return plan; +} + +static void init_conn(int fd, struct packet *pkt) +{ + ok1(pkt->state == 0); + pkt->state++; + + if (!io_new_conn(fd, io_read_packet(pkt, io_close, pkt), finish_ok, pkt)) + abort(); +} + +static int make_listen_fd(const char *port, struct addrinfo **info) +{ + int fd, on = 1; + struct addrinfo *addrinfo, hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + + if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0) + return -1; + + fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, + addrinfo->ai_protocol); + if (fd < 0) + return -1; + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { + close(fd); + return -1; + } + if (listen(fd, 1) != 0) { + close(fd); + return -1; + } + *info = addrinfo; + return fd; +} + +int main(void) +{ + struct packet *pkt = malloc(sizeof(*pkt)); + struct addrinfo *addrinfo; + struct io_listener *l; + int fd, status; + + /* This is how many tests you plan to run */ + plan_tests(13); + pkt->state = 0; + fd = make_listen_fd(PORT, &addrinfo); + ok1(fd >= 0); + l = io_new_listener(fd, init_conn, pkt); + ok1(l); + fflush(stdout); + if (!fork()) { + struct { + size_t len; + char data[8]; + } data; + + io_close_listener(l); + fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, + addrinfo->ai_protocol); + if (fd < 0) + exit(1); + if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) + exit(2); + signal(SIGPIPE, SIG_IGN); + + data.len = sizeof(data.data); + memcpy(data.data, "hithere!", sizeof(data.data)); + if (write(fd, &data, sizeof(data)) != sizeof(data)) + exit(3); + + close(fd); + freeaddrinfo(addrinfo); + free(pkt); + exit(0); + } + freeaddrinfo(addrinfo); + ok1(io_loop() == pkt); + ok1(pkt->state == 4); + ok1(pkt->len == 8); + ok1(memcmp(pkt->contents, "hithere!", 8) == 0); + free(pkt->contents); + free(pkt); + io_close_listener(l); + + ok1(wait(&status)); + ok1(WIFEXITED(status)); + ok1(WEXITSTATUS(status) == 0); + + /* This exits depending on whether all tests passed */ + return exit_status(); +}