From: Tobias Brunner Date: Fri, 13 May 2016 18:48:44 +0000 (+0200) Subject: unit-tests: Add asserts against hooks on listener_t and messages captured there X-Git-Tag: 5.5.0dr1~4^2~76 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=632ba2a21e28d80fadd14ccc1d910d8469f67979;p=thirdparty%2Fstrongswan.git unit-tests: Add asserts against hooks on listener_t and messages captured there --- diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index e51b73571c..451f95e6f9 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -24,6 +24,7 @@ libcharon_tests_LDADD = \ exchange_tests_SOURCES = \ + utils/exchange_test_asserts.h utils/exchange_test_asserts.c \ utils/exchange_test_helper.h utils/exchange_test_helper.c \ utils/mock_ipsec.h utils/mock_ipsec.c \ utils/mock_sender.h utils/mock_sender.c \ diff --git a/src/libcharon/tests/utils/exchange_test_asserts.c b/src/libcharon/tests/utils/exchange_test_asserts.c new file mode 100644 index 0000000000..4fd5df52a5 --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_asserts.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "exchange_test_asserts.h" + +/* + * Described in header + */ +bool exchange_test_asserts_hook(listener_t *listener) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_ike_updown(listener_t *listener, ike_sa_t *ike_sa, + bool up) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + assert_listener_msg(this->up == up, this, "IKE_SA not '%s'", + this->up ? "up" : "down"); + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_child_updown(listener_t *listener, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up) +{ + listener_hook_assert_t *this = (listener_hook_assert_t*)listener; + + this->count++; + assert_listener_msg(this->up == up, this, "CHILD_SA not '%s'", + this->up ? "up" : "down"); + return TRUE; +} + +/* + * Described in header + */ +bool exchange_test_asserts_message(listener_t *listener, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + listener_message_assert_t *this = (listener_message_assert_t*)listener; + + if (plain && this->incoming == incoming) + { + if (this->count >= 0) + { + enumerator_t *enumerator; + int count = 0; + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, NULL)) + { + count++; + } + enumerator->destroy(enumerator); + assert_listener_msg(this->count == count, this, "unexpected payload " + "count in message (%d != %d)", this->count, + count); + } + if (this->payload) + { + assert_listener_msg(message->get_payload(message, this->payload), + this, "expected payload (%N) not found", + payload_type_names, this->payload); + } + if (this->notify) + { + assert_listener_msg(message->get_notify(message, this->notify), + this, "expected notify payload (%N) not found", + notify_type_names, this->notify); + } + return FALSE; + } + return TRUE; +} diff --git a/src/libcharon/tests/utils/exchange_test_asserts.h b/src/libcharon/tests/utils/exchange_test_asserts.h new file mode 100644 index 0000000000..cdee76b0a6 --- /dev/null +++ b/src/libcharon/tests/utils/exchange_test_asserts.h @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * Special assertions using listener_t. + * + * @defgroup exchange_test_asserts exchange_test_asserts + * @{ @ingroup test_utils_c + */ + +#ifndef EXCHANGE_TEST_ASSERTS_H_ +#define EXCHANGE_TEST_ASSERTS_H_ + +#include + +typedef struct listener_hook_assert_t listener_hook_assert_t; +typedef struct listener_message_assert_t listener_message_assert_t; + +struct listener_hook_assert_t { + + /** + * Implemented interface + */ + listener_t listener; + + /** + * Original source file + */ + const char *file; + + /** + * Source line + */ + int line; + + /** + * Name of the hook + */ + const char *name; + + /** + * Expected number of calls (-1 to ignore) + */ + int expected; + + /** + * Number of times the hook was called + */ + int count; + + /** + * Expected updown result + */ + bool up; +}; + +/** + * Basic callback for methods on listener_t, counting the number of calls. + */ +bool exchange_test_asserts_hook(listener_t *this); + +/** + * Implementation of listener_t::ike_updown. + */ +bool exchange_test_asserts_ike_updown(listener_t *this, ike_sa_t *ike_sa, + bool up); + +/** + * Implementation of listener_t::child_updown. + */ +bool exchange_test_asserts_child_updown(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up); + +/** + * Check if a statement evaluates to TRUE, use original source file and line + * in the error message if not. + * + * @param x statement to evaluate + * @param l listener providing original source file and line + * @param fmt printf format string + * @param ... arguments for fmt + */ +#define assert_listener_msg(x, l, fmt, ...) ({ \ + test_fail_if_worker_failed(); \ + if (!(x)) \ + { \ + test_fail_msg((l)->file, (l)->line, "%s: " fmt, #x, ##__VA_ARGS__); \ + } \ +}) + +/** + * Initialize an assertion that enforces that the given hook was called. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + */ +#define assert_hook_called(name) \ + _assert_hook_init(name, exchange_test_asserts_hook, .expected = 1) + +/** + * Initialize an assertion that enforces that the given hook was not called. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + */ +#define assert_hook_not_called(name) \ + _assert_hook_init(name, exchange_test_asserts_hook, .expected = 0) + +/** + * Initialize an assertion that enforces that the given updown hook was called + * with the expected result. + * Must be matched by a call to assert_hook(). + * + * @param name name of the hook + * @param e whether to expect up in the hook to be TRUE or not + */ +#define assert_hook_updown(name, e) \ + _assert_hook_init(name, \ + streq(#name, "ike_updown") ? (void*)exchange_test_asserts_ike_updown \ + : (void*)exchange_test_asserts_child_updown, \ + .expected = 1, \ + .up = e, \ + ) + +/** + * Initialize assertions against invocations of listener_t hooks. Each call + * must be matched by a call to assert_hook(). + */ +#define _assert_hook_init(n, callback, ...) \ +do { \ + listener_hook_assert_t _hook_listener = { \ + .listener = { .n = (void*)callback, }, \ + .file = __FILE__, \ + .line = __LINE__, \ + .name = #n, \ + ##__VA_ARGS__ \ + }; \ + charon->bus->add_listener(charon->bus, &_hook_listener.listener) + +/** + * Enforce the most recently initialized hook assertion. + */ +#define assert_hook() \ + charon->bus->remove_listener(charon->bus, &_hook_listener.listener); \ + if (_hook_listener.expected > 0) { \ + if (_hook_listener.count > 0) { \ + assert_listener_msg(_hook_listener.expected == _hook_listener.count, \ + &_hook_listener, "hook '%s' was called %d times " \ + "instead of %d", _hook_listener.name, \ + _hook_listener.count, _hook_listener.expected); \ + } else { \ + assert_listener_msg(_hook_listener.count, &_hook_listener, \ + "hook '%s' was not called (expected %d)", _hook_listener.name, \ + _hook_listener.expected); \ + } \ + } else if (_hook_listener.expected == 0) { \ + assert_listener_msg(_hook_listener.count == 0, &_hook_listener, \ + "hook '%s' was called unexpectedly", _hook_listener.name); \ + } \ +} while(FALSE) + +/** + * Data used to check plaintext messages via listener_t + */ +struct listener_message_assert_t { + + /** + * Implemented interface + */ + listener_t listener; + + /** + * Original source file + */ + const char *file; + + /** + * Source line + */ + int line; + + /** + * Whether to check the next inbound or outbound message + */ + bool incoming; + + /** + * Payload count to expect + */ + int count; + + /** + * Payload type to look for + */ + payload_type_t payload; + + /** + * Notify type to look for + */ + notify_type_t notify; +}; + +/** + * Implementation of listener_t::message collecting data and asserting + * certain things. + */ +bool exchange_test_asserts_message(listener_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain); + +/** + * Assert that the next in- or outbound plaintext message is empty. + * + * @param dir IN or OUT to check the next in- or outbound message + */ +#define assert_message_empty(dir) ({ \ + _assert_payload(dir, 0, 0, 0); \ +}) + +/** + * Assert that the next in- or outbound plaintext message contains exactly + * one payload of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param expected expected payload type + */ +#define assert_single_payload(dir, expected) ({ \ + _assert_payload(dir, 1, expected, 0); \ +}) + +/** + * Assert that the next in- or outbound plaintext message contains exactly + * one notify of the given type. + * + * @param dir IN or OUT to check the next in- or outbound message + * @param expected expected notify type + */ +#define assert_single_notify(dir, expected) ({ \ + _assert_payload(dir, 1, 0, expected); \ +}) + +#define _assert_payload(dir, c, p, n) ({ \ + listener_message_assert_t _listener = { \ + .listener = { .message = exchange_test_asserts_message, }, \ + .file = __FILE__, \ + .line = __LINE__, \ + .incoming = streq(#dir, "IN") ? TRUE : FALSE, \ + .count = c, \ + .payload = p, \ + }; \ + charon->bus->add_listener(charon->bus, &_listener.listener); \ +}) + +#endif /** EXCHANGE_TEST_ASSERTS_H_ @}*/