blob: 8799fb3edacf7e1c99d1e5cc401733dd4c901583 [file] [log] [blame]
#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