| #ifndef TESTS_TEST_DRIVER_H |
| #define TESTS_TEST_DRIVER_H |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "test_tools.h" |
| #include <proton/ssl.h> |
| |
| /* C event handlers for tests */ |
| |
| #define MAX_EVENT_LOG 2048 /* Max number of event types stored per proactor_test */ |
| |
| struct test_handler_t; |
| |
| /* Returns 0 if the test should continue processing events, non-0 if processing should pause here */ |
| typedef pn_event_type_t (*test_handler_fn)(struct test_handler_t*, pn_event_t*); |
| |
| /* A handler that logs event types and delegates to another handler */ |
| typedef struct test_handler_t { |
| test_t *t; |
| test_handler_fn f; |
| pn_event_type_t log[MAX_EVENT_LOG]; /* Log of event types */ |
| size_t log_size; /* Number of events in the log */ |
| void *context; /* Generic test context */ |
| /* Specific context slots for proton objects commonly used by handlers */ |
| pn_connection_t *connection; |
| pn_session_t *session; |
| pn_link_t *link; |
| pn_link_t *sender; |
| pn_link_t *receiver; |
| pn_delivery_t *delivery; |
| pn_message_t *message; |
| pn_ssl_domain_t *ssl_domain; |
| } test_handler_t; |
| |
| void test_handler_init(test_handler_t *th, test_t *t, test_handler_fn f) { |
| memset(th, 0, sizeof(*th)); |
| th->t = t; |
| th->f = f; |
| } |
| |
| /* Save event type in the handler log */ |
| void test_handler_log(test_handler_t *th, pn_event_t *e) { |
| TEST_ASSERT(th->log_size < MAX_EVENT_LOG); |
| th->log[th->log_size++] = pn_event_type(e); |
| } |
| |
| /* Keep at most n events in the handler's log, remove old events if necessary */ |
| void test_handler_clear(test_handler_t *th, size_t n) { |
| if (n == 0) { |
| th->log_size = 0; |
| } else if (n < th->log_size) { |
| memmove(th->log, th->log + th->log_size - n, n * sizeof(pn_event_type_t)); |
| th->log_size = n; |
| } |
| } |
| |
| void test_etypes_expect_(test_t *t, pn_event_type_t *etypes, size_t size, const char* file, int line, ...) { |
| va_list ap; |
| va_start(ap, line); /* ap is null terminated */ |
| pn_event_type_t want = (pn_event_type_t)va_arg(ap, int); |
| size_t i = 0; |
| while (want && i < size && want == etypes[i]) { |
| ++i; |
| want = (pn_event_type_t)va_arg(ap, int); |
| } |
| if (i < size || want) { |
| test_errorf_(t, NULL, file, line, "event mismatch"); |
| if (!t->inverted) { |
| fprintf(stderr, "after:"); |
| for (size_t j = 0; j < i; ++j) { /* These events matched */ |
| fprintf(stderr, " %s", pn_event_type_name(etypes[j])); |
| } |
| fprintf(stderr, "\n want:"); |
| for (; want; want = (pn_event_type_t)va_arg(ap, int)) { |
| fprintf(stderr, " %s", pn_event_type_name(want)); |
| } |
| fprintf(stderr, "\n got:"); |
| for (; i < size; ++i) { |
| fprintf(stderr, " %s", pn_event_type_name(etypes[i])); |
| } |
| fprintf(stderr, "\n"); |
| } |
| } |
| va_end(ap); |
| } |
| |
| #define TEST_HANDLER_EXPECT(TH, ...) do { \ |
| test_etypes_expect_((TH)->t, (TH)->log, (TH)->log_size, __FILE__, __LINE__, __VA_ARGS__); \ |
| test_handler_clear((TH), 0); \ |
| } while(0) |
| |
| #define TEST_HANDLER_EXPECT_LAST(TH, ETYPE) do { \ |
| test_handler_clear((TH), 1); \ |
| test_etypes_expect_((TH)->t, (TH)->log, (TH)->log_size, __FILE__, __LINE__, ETYPE, 0); \ |
| test_handler_clear((TH), 0); \ |
| } while(0) |
| |
| /* A pn_connection_driver_t with a test_handler */ |
| typedef struct test_connection_driver_t { |
| test_handler_t handler; |
| pn_connection_driver_t driver; |
| } test_connection_driver_t; |
| |
| void test_connection_driver_init(test_connection_driver_t *d, test_t *t, test_handler_fn f, void* context) |
| { |
| test_handler_init(&d->handler, t, f); |
| d->handler.context = context; |
| pn_connection_driver_init(&d->driver, NULL, NULL); |
| } |
| |
| void test_connection_driver_destroy(test_connection_driver_t *d) { |
| pn_connection_driver_destroy(&d->driver); |
| } |
| |
| pn_event_type_t test_connection_driver_handle(test_connection_driver_t *d) { |
| for (pn_event_t *e = pn_connection_driver_next_event(&d->driver); |
| e; |
| e = pn_connection_driver_next_event(&d->driver)) |
| { |
| test_handler_log(&d->handler, e); |
| pn_event_type_t et = d->handler.f ? d->handler.f(&d->handler, e) : PN_EVENT_NONE; |
| if (et) return et; |
| } |
| return PN_EVENT_NONE; |
| } |
| |
| /* Transfer data from one driver to another in memory */ |
| static int test_connection_drivers_xfer(test_connection_driver_t *dst, test_connection_driver_t *src) |
| { |
| pn_bytes_t wb = pn_connection_driver_write_buffer(&src->driver); |
| pn_rwbytes_t rb = pn_connection_driver_read_buffer(&dst->driver); |
| size_t size = rb.size < wb.size ? rb.size : wb.size; |
| if (size) { |
| memcpy(rb.start, wb.start, size); |
| pn_connection_driver_write_done(&src->driver, size); |
| pn_connection_driver_read_done(&dst->driver, size); |
| } |
| return size; |
| } |
| |
| /* Run a pair of test drivers till there is nothing to do or one of their handlers returns non-0 |
| In that case return that driver |
| */ |
| test_connection_driver_t* test_connection_drivers_run(test_connection_driver_t *a, test_connection_driver_t *b) |
| { |
| int data = 0; |
| do { |
| if (test_connection_driver_handle(a)) return a; |
| if (test_connection_driver_handle(b)) return b; |
| data = test_connection_drivers_xfer(a, b) + test_connection_drivers_xfer(b, a); |
| } while (data || pn_connection_driver_has_event(&a->driver) || pn_connection_driver_has_event(&b->driver)); |
| return NULL; |
| } |
| |
| /* Initialize a client-server driver pair */ |
| void test_connection_drivers_init(test_t *t, test_connection_driver_t *a, test_handler_fn fa, test_connection_driver_t *b, test_handler_fn fb) { |
| test_connection_driver_init(a, t, fa, NULL); |
| test_connection_driver_init(b, t, fb, NULL); |
| pn_transport_set_server(b->driver.transport); |
| } |
| |
| void test_connection_drivers_destroy(test_connection_driver_t *a, test_connection_driver_t *b) { |
| test_connection_driver_destroy(a); |
| test_connection_driver_destroy(b); |
| } |
| #endif // TESTS_TEST_DRIVER_H |