]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
unit-tests: Add asserts against hooks on listener_t and messages captured there
authorTobias Brunner <tobias@strongswan.org>
Fri, 13 May 2016 18:48:44 +0000 (20:48 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 17 Jun 2016 16:48:01 +0000 (18:48 +0200)
src/libcharon/tests/Makefile.am
src/libcharon/tests/utils/exchange_test_asserts.c [new file with mode: 0644]
src/libcharon/tests/utils/exchange_test_asserts.h [new file with mode: 0644]

index e51b73571c3c813983c88617f0bad2230176a0e6..451f95e6f9fc4aa6239b1e2be00ed3415ef380d5 100644 (file)
@@ -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 (file)
index 0000000..4fd5df5
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <test_suite.h>
+
+#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 (file)
index 0000000..cdee76b
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <bus/listeners/listener.h>
+
+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_ @}*/