PROTON-1445: Change proactor ownership model Changed ownership model for pn_connection_t and pn_listener_t managed by the proactor: instead of proactor freeing automatically after the final event, the user must free on or after the final event.
There are 2 basic use cases: 1. Free connection/listener immediately on the last event. 2. Keep connection/listener in memory until all application pointers are cleaned up. Proactor ownership does 1. very well, but makes 2. very difficult without exposing reference-counts. User ownership supports both reasonably easily: 1. is implemented by calling pn_connection_free() or pn_listener_free() in the event handler on the last event. Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/a85c89ac Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/a85c89ac Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/a85c89ac Branch: refs/heads/master Commit: a85c89ac8c1b8a19fae0b32cab6549e8d9d1ce24 Parents: 432fcb5 Author: Alan Conway <[email protected]> Authored: Thu Mar 23 19:13:15 2017 -0400 Committer: Alan Conway <[email protected]> Committed: Fri Mar 24 15:17:58 2017 -0400 ---------------------------------------------------------------------- proton-c/include/proton/listener.h | 12 ++- proton-c/include/proton/proactor.h | 4 +- proton-c/src/proactor/libuv.c | 20 +++- proton-c/src/tests/proactor.c | 157 +++++++++++++++++--------------- proton-c/src/tests/test_tools.h | 19 +--- 5 files changed, 115 insertions(+), 97 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/a85c89ac/proton-c/include/proton/listener.h ---------------------------------------------------------------------- diff --git a/proton-c/include/proton/listener.h b/proton-c/include/proton/listener.h index 6646dd1..bf52150 100644 --- a/proton-c/include/proton/listener.h +++ b/proton-c/include/proton/listener.h @@ -39,17 +39,21 @@ extern "C" { */ /** - * Create a listener. + * Create a listener to pass to pn_proactor_listen() + * + * Must be freed with pn_listener_free() * * You can use pn_listener_set_context() or pn_listener_attachments() to set * application data that can be accessed when accepting connections. - * - * You must pass the returned listener to pn_proactor_listen(), the proactor - * will free the listener when it is no longer active. */ PNP_EXTERN pn_listener_t *pn_listener(void); /** + * Free a listener. Must not be in use, see pn_proactor_listen() + */ +PNP_EXTERN void pn_listener_free(pn_listener_t *l); + +/** * Asynchronously accept a connection using the listener. * * @param[in] connection the listener takes ownership, do not free. http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/a85c89ac/proton-c/include/proton/proactor.h ---------------------------------------------------------------------- diff --git a/proton-c/include/proton/proactor.h b/proton-c/include/proton/proactor.h index 345e3fc..974b432 100644 --- a/proton-c/include/proton/proactor.h +++ b/proton-c/include/proton/proactor.h @@ -65,7 +65,7 @@ PNP_EXTERN void pn_proactor_free(pn_proactor_t *proactor); * returned by pn_proactor_wait() * * @param[in] proactor the proactor object - * @param[in] connection the proactor takes ownership, do not free + * @param[in] connection must not be freed until after the final PN_TRANSPORT_CLOSED event or pn_proactor_free() * @param[in] addr the network address (not AMQP address) to connect to. May * be in the form "host:port" or an "amqp://" or "amqps://" URL. The `/path` part of * the URL is ignored. @@ -86,7 +86,7 @@ PNP_EXTERN int pn_proactor_connect( * * * @param[in] proactor the proactor object - * @param[in] listener proactor takes ownership of listener, do not free + * @param[in] listener must not be freed until after the final PN_LISTENER_CLOSE event or pn_proactor_free() * @param[in] addr the network address (not AMQP address) to connect to in "host:port" * * The host can be a host name, IPV4 or IPV6 literal, or the empty string. The empty http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/a85c89ac/proton-c/src/proactor/libuv.c ---------------------------------------------------------------------- diff --git a/proton-c/src/proactor/libuv.c b/proton-c/src/proactor/libuv.c index 1ba840a..728ba7d 100644 --- a/proton-c/src/proactor/libuv.c +++ b/proton-c/src/proactor/libuv.c @@ -215,6 +215,7 @@ struct pn_listener_t { pn_collector_t *collector; pconnection_queue_t accept; /* pconnection_t list for accepting */ listener_state state; + int refcount; /* Free when proactor and user are done */ }; typedef enum { TM_NONE, TM_REQUEST, TM_PENDING, TM_FIRED } timeout_state_t; @@ -304,6 +305,7 @@ static pconnection_t *pconnection(pn_proactor_t *p, pn_connection_t *c, bool ser if (!pc || pn_connection_driver_init(&pc->driver, c, NULL) != 0) { return NULL; } + pn_incref(c); /* User owns original ref, make one for the proactor */ work_init(&pc->work, p, T_CONNECTION); pc->next = pconnection_unqueued; pc->write.data = &pc->work; @@ -366,10 +368,16 @@ static void pconnection_free(pconnection_t *pc) { if (pc->addr.getaddrinfo.addrinfo) { uv_freeaddrinfo(pc->addr.getaddrinfo.addrinfo); /* Interrupted after resolve */ } - pn_incref(pc); /* Make sure we don't do a circular free */ + /* Don't let driver_destroy call pn_connection_free(), the user does that */ + pn_connection_t *c = pc->driver.connection; + pc->driver.connection = NULL; + pn_collector_t *collector = pn_connection_collector(c); + if (collector) { + pn_collector_release(collector); /* Break circular refs */ + } pn_connection_driver_destroy(&pc->driver); - pn_decref(pc); - /* Now pc is freed iff the connection is, otherwise remains till the pn_connection_t is freed. */ + /* The user either has or will call pn_connection_free(), drop our ref. */ + pn_decref(c); } /* Final close event for for a pconnection_t, disconnects from proactor */ @@ -628,8 +636,8 @@ static void leader_listen_lh(pn_listener_t *l) { } } -static void pn_listener_free(pn_listener_t *l) { - if (l) { +void pn_listener_free(pn_listener_t *l) { + if (l && --l->refcount == 0) { if (l->addr.getaddrinfo.addrinfo) { /* Interrupted after resolve */ uv_freeaddrinfo(l->addr.getaddrinfo.addrinfo); } @@ -1042,6 +1050,7 @@ int pn_proactor_listen(pn_proactor_t *p, pn_listener_t *l, const char *addr, int work_init(&l->work, p, T_LISTENER); parse_addr(&l->addr, addr); l->backlog = backlog; + ++l->refcount; work_start(&l->work); return 0; } @@ -1128,6 +1137,7 @@ void pn_connection_wake(pn_connection_t* c) { pn_listener_t *pn_listener(void) { pn_listener_t *l = (pn_listener_t*)calloc(1, sizeof(pn_listener_t)); if (l) { + l->refcount = 1; l->batch.next_event = listener_batch_next; l->collector = pn_collector(); l->condition = pn_condition(); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/a85c89ac/proton-c/src/tests/proactor.c ---------------------------------------------------------------------- diff --git a/proton-c/src/tests/proactor.c b/proton-c/src/tests/proactor.c index 34a1880..f0431cb 100644 --- a/proton-c/src/tests/proactor.c +++ b/proton-c/src/tests/proactor.c @@ -32,7 +32,10 @@ static pn_millis_t timeout = 7*1000; /* timeout for hanging tests */ static const char *localhost = "127.0.0.1"; /* host for connect/listen */ -typedef pn_event_t *(*test_handler_fn)(test_t *, pn_event_t*); +typedef pn_event_type_t (*test_handler_fn)(test_t *, pn_event_t*); + +/* Save the last condition description of a handled event here */ +char last_condition[1024] = {0}; /* Proactor and handler that take part in a test */ typedef struct proactor_test_t { @@ -61,18 +64,34 @@ static void proactor_test_free(proactor_test_t *pts, size_t n) { #define PROACTOR_TEST_FREE(A) proactor_test_free(A, sizeof(A)/sizeof(*A)) +static void save_condition(pn_event_t *e) { + /* FIXME aconway 2017-03-23: extend pn_event_condition to include listener */ + last_condition[0] = '\0'; + pn_condition_t *cond = NULL; + if (pn_event_listener(e)) { + cond = pn_listener_condition(pn_event_listener(e)); + } else { + cond = pn_event_condition(e); + } + if (cond && pn_condition_is_set(cond)) { + const char *desc = pn_condition_get_description(cond); + strncpy(last_condition, desc, sizeof(last_condition)); + } +} + /* Process events on a proactor array until a handler returns an event, or * all proactors return NULL */ -static pn_event_t *proactor_test_get(proactor_test_t *pts, size_t n) { +static pn_event_type_t proactor_test_get(proactor_test_t *pts, size_t n) { while (true) { bool busy = false; for (proactor_test_t *pt = pts; pt < pts + n; ++pt) { pn_event_batch_t *eb = pn_proactor_get(pt->proactor); if (eb) { busy = true; - pn_event_t *ret = NULL; + pn_event_type_t ret = PN_EVENT_NONE; for (pn_event_t* e = pn_event_batch_next(eb); e; e = pn_event_batch_next(eb)) { + save_condition(e); ret = pt->handler(pt->t, e); if (ret) break; } @@ -81,15 +100,15 @@ static pn_event_t *proactor_test_get(proactor_test_t *pts, size_t n) { } } if (!busy) { - return NULL; + return PN_EVENT_NONE; } } } /* Run an array of proactors till a handler returns an event. */ -static pn_event_t *proactor_test_run(proactor_test_t *pts, size_t n) { - pn_event_t *e; - while ((e = proactor_test_get(pts, n)) == NULL) +static pn_event_type_t proactor_test_run(proactor_test_t *pts, size_t n) { + pn_event_type_t e; + while ((e = proactor_test_get(pts, n)) == PN_EVENT_NONE) ; return e; } @@ -139,31 +158,37 @@ static void test_interrupt_timeout(test_t *t) { } /* Common handler for simple client/server interactions, */ -static pn_event_t *common_handler(test_t *t, pn_event_t *e) { +static pn_event_type_t common_handler(test_t *t, pn_event_t *e) { pn_connection_t *c = pn_event_connection(e); pn_listener_t *l = pn_event_listener(e); switch (pn_event_type(e)) { - /* Stop on these events */ + /* Cleanup events */ case PN_LISTENER_CLOSE: + pn_listener_free(pn_event_listener(e)); + return PN_LISTENER_CLOSE; + case PN_TRANSPORT_CLOSED: + pn_connection_free(pn_event_connection(e)); + return PN_TRANSPORT_CLOSED; + + /* Stop on these events */ case PN_LISTENER_OPEN: case PN_PROACTOR_INACTIVE: case PN_PROACTOR_TIMEOUT: - case PN_TRANSPORT_CLOSED: - return e; + return pn_event_type(e); case PN_LISTENER_ACCEPT: pn_listener_accept(l, pn_connection()); - return NULL; + return PN_EVENT_NONE; case PN_CONNECTION_REMOTE_OPEN: pn_connection_open(c); /* Return the open (no-op if already open) */ - return NULL; + return PN_EVENT_NONE; case PN_CONNECTION_REMOTE_CLOSE: pn_connection_close(c); /* Return the close */ - return NULL; + return PN_EVENT_NONE; /* Ignored these events */ case PN_CONNECTION_INIT: @@ -174,20 +199,20 @@ static pn_event_t *common_handler(test_t *t, pn_event_t *e) { case PN_TRANSPORT_ERROR: case PN_TRANSPORT_HEAD_CLOSED: case PN_TRANSPORT_TAIL_CLOSED: - return NULL; + return PN_EVENT_NONE; default: TEST_ERRORF(t, "unexpected event %s", pn_event_type_name(pn_event_type(e))); - return NULL; /* Fail the test but keep going */ + return PN_EVENT_NONE; /* Fail the test but keep going */ } } /* close a connection when it is remote open */ -static pn_event_t *open_close_handler(test_t *t, pn_event_t *e) { +static pn_event_type_t open_close_handler(test_t *t, pn_event_t *e) { switch (pn_event_type(e)) { case PN_CONNECTION_REMOTE_OPEN: pn_connection_close(pn_event_connection(e)); - return NULL; /* common_handler will finish on TRANSPORT_CLOSED */ + return PN_EVENT_NONE; /* common_handler will finish on TRANSPORT_CLOSED */ default: return common_handler(t, e); } @@ -200,28 +225,28 @@ static void test_client_server(test_t *t) { pn_proactor_t *client = pts[0].proactor, *server = pts[1].proactor; test_port_t port = test_port(localhost); pn_proactor_listen(server, pn_listener(), port.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(port.sock); /* Connect and wait for close at both ends */ pn_proactor_connect(client, pn_connection(), port.host_port); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); /* Connect and wait for close at both ends */ pn_proactor_connect(client, pn_connection(), port.host_port); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); PROACTOR_TEST_FREE(pts); } /* Return on connection open, close and return on wake */ -static pn_event_t *open_wake_handler(test_t *t, pn_event_t *e) { +static pn_event_type_t open_wake_handler(test_t *t, pn_event_t *e) { switch (pn_event_type(e)) { case PN_CONNECTION_REMOTE_OPEN: - return e; + return pn_event_type(e); case PN_CONNECTION_WAKE: pn_connection_close(pn_event_connection(e)); - return e; + return pn_event_type(e); default: return common_handler(t, e); } @@ -234,17 +259,17 @@ static void test_connection_wake(test_t *t) { pn_proactor_t *client = pts[0].proactor, *server = pts[1].proactor; test_port_t port = test_port(localhost); /* Hold a port */ pn_proactor_listen(server, pn_listener(), port.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(port.sock); pn_connection_t *c = pn_connection(); pn_incref(c); /* Keep c alive after proactor frees it */ pn_proactor_connect(client, c, port.host_port); - TEST_EVENT_TYPE(t, PN_CONNECTION_REMOTE_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, PROACTOR_TEST_RUN(pts)); TEST_CHECK(t, pn_proactor_get(client) == NULL); /* Should be idle */ pn_connection_wake(c); - TEST_EVENT_TYPE(t, PN_CONNECTION_WAKE, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_CONNECTION_WAKE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); /* The pn_connection_t is still valid so wake is legal but a no-op */ pn_connection_wake(c); @@ -263,21 +288,21 @@ static void test_inactive(test_t *t) { pn_listener_t *l = pn_listener(); pn_proactor_listen(server, l, port.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); pn_connection_t *c = pn_connection(); pn_proactor_connect(client, c, port.host_port); - TEST_EVENT_TYPE(t, PN_CONNECTION_REMOTE_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, PROACTOR_TEST_RUN(pts)); pn_connection_wake(c); - TEST_EVENT_TYPE(t, PN_CONNECTION_WAKE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_CONNECTION_WAKE, PROACTOR_TEST_RUN(pts)); /* expect TRANSPORT_CLOSED from client and server, INACTIVE from client */ - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); /* server won't be INACTIVE until listener is closed */ TEST_CHECK(t, pn_proactor_get(server) == NULL); pn_listener_close(l); - TEST_EVENT_TYPE(t, PN_LISTENER_CLOSE, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); sock_close(port.sock); PROACTOR_TEST_FREE(pts); @@ -293,32 +318,28 @@ static void test_errors(test_t *t) { /* Invalid connect/listen parameters */ pn_connection_t *c = pn_connection(); pn_proactor_connect(client, c, "127.0.0.1:xxx"); - TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); - TEST_CHECK_COND(t, "xxx", pn_transport_condition(pn_connection_transport(c))); - TEST_EVENT_TYPE(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); + TEST_STR_IN(t, "xxx", last_condition); + TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); pn_listener_t *l = pn_listener(); pn_proactor_listen(server, l, "127.0.0.1:xxx", 1); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); - TEST_EVENT_TYPE(t, PN_LISTENER_CLOSE, PROACTOR_TEST_RUN(pts)); - TEST_CHECK_COND(t, "xxx", pn_listener_condition(l)); - TEST_EVENT_TYPE(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, PROACTOR_TEST_RUN(pts)); + TEST_STR_IN(t, "xxx", last_condition); + TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); /* Connect with no listener */ c = pn_connection(); pn_proactor_connect(client, c, port.host_port); - if (TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts))) { - TEST_CHECK_COND(t, "connection refused", pn_transport_condition(pn_connection_transport(c))); - TEST_EVENT_TYPE(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); + if (TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts))) { + TEST_STR_IN(t, "connection refused", last_condition); + TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, PROACTOR_TEST_RUN(pts)); sock_close(port.sock); PROACTOR_TEST_FREE(pts); } } -static inline const char *event_listener_desc(pn_event_t *e) { - return pn_condition_get_description(pn_listener_condition(pn_event_listener(e))); -} - /* Test that we can control listen/select on ipv6/v4 and listen on both by default */ static void test_ipv4_ipv6(test_t *t) { proactor_test_t pts[] ={ { open_close_handler }, { common_handler } }; @@ -328,48 +349,42 @@ static void test_ipv4_ipv6(test_t *t) { /* Listen on all interfaces for IPv6 only. If this fails, skip IPv6 tests */ test_port_t port6 = test_port("[::]"); pn_proactor_listen(server, pn_listener(), port6.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(port6.sock); - pn_event_t *e = PROACTOR_TEST_GET(pts); - bool has_ipv6 = (pn_event_type(e) != PN_LISTENER_CLOSE); + pn_event_type_t e = PROACTOR_TEST_GET(pts); + bool has_ipv6 = (e != PN_LISTENER_CLOSE); if (!has_ipv6) { - TEST_LOGF(t, "skip IPv6 tests: %s", event_listener_desc(e)); + TEST_LOGF(t, "skip IPv6 tests: %s", last_condition); } PROACTOR_TEST_DRAIN(pts); /* Listen on all interfaces for IPv4 only. */ test_port_t port4 = test_port("0.0.0.0"); pn_proactor_listen(server, pn_listener(), port4.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(port4.sock); - e = PROACTOR_TEST_GET(pts); - if (pn_event_type(e) == PN_LISTENER_CLOSE) { - TEST_ERRORF(t, "listener error: %s", event_listener_desc(e)); - } + TEST_CHECKF(t, PROACTOR_TEST_GET(pts) != PN_LISTENER_CLOSE, "listener error: %s", last_condition); PROACTOR_TEST_DRAIN(pts); /* Empty address listens on both IPv4 and IPv6 on all interfaces */ test_port_t port = test_port(""); pn_proactor_listen(server, pn_listener(), port.host_port, 4); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(port.sock); e = PROACTOR_TEST_GET(pts); - if (pn_event_type(e) == PN_LISTENER_CLOSE) { - TEST_ERRORF(t, "listener error: %s", event_listener_desc(e)); - } - PROACTOR_TEST_DRAIN(pts); + TEST_CHECKF(t, PROACTOR_TEST_GET(pts) != PN_LISTENER_CLOSE, "listener error: %s", last_condition); PROACTOR_TEST_DRAIN(pts); #define EXPECT_CONNECT(TP, HOST) do { \ pn_proactor_connect(client, pn_connection(), test_port_use_host(&(TP), (HOST))); \ - pn_event_t *e = TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); \ - if (e) TEST_CHECK_NO_COND(t, pn_transport_condition(pn_event_transport(e))); \ + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); \ + TEST_STR_EQUAL(t, "", last_condition); \ PROACTOR_TEST_DRAIN(pts); \ } while(0) #define EXPECT_FAIL(TP, HOST) do { \ pn_proactor_connect(client, pn_connection(), test_port_use_host(&(TP), (HOST))); \ - pn_event_t *e = TEST_EVENT_TYPE(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); \ - if (e) TEST_CHECK_COND(t, "refused", pn_transport_condition(pn_event_transport(e))); \ + TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, PROACTOR_TEST_RUN(pts)); \ + TEST_STR_IN(t, "refused", last_condition); \ PROACTOR_TEST_DRAIN(pts); \ } while(0) @@ -399,7 +414,7 @@ static void test_free_cleanup(test_t *t) { test_port_t ports[3] = { test_port(localhost), test_port(localhost), test_port(localhost) }; for (int i = 0; i < 3; ++i) { pn_proactor_listen(server, pn_listener(), ports[i].host_port, 2); - TEST_EVENT_TYPE(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); + TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, PROACTOR_TEST_RUN(pts)); sock_close(ports[i].sock); pn_proactor_connect(client, pn_connection(), ports[i].host_port); pn_proactor_connect(client, pn_connection(), ports[i].host_port); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/a85c89ac/proton-c/src/tests/test_tools.h ---------------------------------------------------------------------- diff --git a/proton-c/src/tests/test_tools.h b/proton-c/src/tests/test_tools.h index 26f3b35..0c913ff 100644 --- a/proton-c/src/tests/test_tools.h +++ b/proton-c/src/tests/test_tools.h @@ -130,22 +130,11 @@ static inline bool test_etype_equal_(test_t *t, int want, int got, const char *f pn_event_type_name((pn_event_type_t)got)); } -#define TEST_CHECK_COND(T, WANT, COND) do { \ - pn_condition_t *cond = (COND); \ - if (TEST_CHECKF((T), pn_condition_is_set(cond), "expecting error")) { \ - const char* description = pn_condition_get_description(cond); \ - if (!strstr(description, (WANT))) { \ - TEST_ERRORF((T), "expected '%s' in '%s'", (WANT), description); \ - } \ - } \ - } while(0) +#define TEST_STR_EQUAL(TEST, WANT, GOT) \ + TEST_CHECKF((TEST), !strcmp((WANT), (GOT)), " got '%s'", (GOT)) -#define TEST_CHECK_NO_COND(T, COND) do { \ - pn_condition_t *cond = (COND); \ - if (cond && pn_condition_is_set(cond)) { \ - TEST_ERRORF((T), "unexpected condition: %s", pn_condition_get_description(cond)); \ - } \ - } while(0) +#define TEST_STR_IN(TEST, WANT, GOT) \ + TEST_CHECKF((TEST), strstr((GOT), (WANT)), " got '%s'", (GOT)) #define TEST_ETYPE_EQUAL(TEST, WANT, GOT) \ test_etype_equal_((TEST), (WANT), (GOT), __FILE__, __LINE__) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
