[PATCH v5] Move schedule test to a unit test
Gert Doering <[email protected]> Thu, 30 Jul 2026 19:33:07 +0200
| Newsgroups | gmane.network.openvpn.devel |
|---|---|
| Message-ID | <[email protected]> |
From: Arne Schwabe <[email protected]> While this test might not be extremely useful this change removes the dead code from init.c and schedule.c and moves it to a unit test. Change-Id: Ie33aea26026e07c860da7d79880cef6b27b7b0e8 Signed-off-by: Arne Schwabe <[email protected]> Acked-by: Frank Lichtenheld <[email protected]> Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1828 --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1828 This mail reflects revision 5 of this Change. Acked-by according to Gerrit (reflected above): Frank Lichtenheld <[email protected]> diff --git a/CMakeLists.txt b/CMakeLists.txt index 7473f15..74c080e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -828,10 +828,14 @@ target_sources(test_misc PRIVATE tests/unit_tests/openvpn/mock_get_random.c + tests/unit_tests/openvpn/test_schedule.c src/openvpn/options_util.c + src/openvpn/otime.c src/openvpn/ssl_util.c src/openvpn/list.c - ) + src/openvpn/session_id.c + src/openvpn/schedule.c + ) target_sources(test_ncp PRIVATE src/openvpn/crypto_epoch.c diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 914d191..0236886 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -53,6 +53,7 @@ #include "mudp.h" #include "dco.h" #include "tun_afunix.h" +#include "schedule.h" #include "memdbg.h" @@ -876,11 +877,6 @@ init_ssl_lib(); -#ifdef SCHEDULE_TEST - schedule_test(); - return false; -#endif - #ifdef IFCONFIG_POOL_TEST ifconfig_pool_test(0x0A010004, 0x0A0100FF); return false; diff --git a/src/openvpn/otime.h b/src/openvpn/otime.h index ad79120..f02cc31 100644 --- a/src/openvpn/otime.h +++ b/src/openvpn/otime.h @@ -150,80 +150,6 @@ } } -static inline bool -tv_lt(const struct timeval *t1, const struct timeval *t2) -{ - if (t1->tv_sec < t2->tv_sec) - { - return true; - } - else if (t1->tv_sec > t2->tv_sec) - { - return false; - } - else - { - return t1->tv_usec < t2->tv_usec; - } -} - -static inline bool -tv_le(const struct timeval *t1, const struct timeval *t2) -{ - if (t1->tv_sec < t2->tv_sec) - { - return true; - } - else if (t1->tv_sec > t2->tv_sec) - { - return false; - } - else - { - return t1->tv_usec <= t2->tv_usec; - } -} - -static inline bool -tv_ge(const struct timeval *t1, const struct timeval *t2) -{ - if (t1->tv_sec > t2->tv_sec) - { - return true; - } - else if (t1->tv_sec < t2->tv_sec) - { - return false; - } - else - { - return t1->tv_usec >= t2->tv_usec; - } -} - -static inline bool -tv_gt(const struct timeval *t1, const struct timeval *t2) -{ - if (t1->tv_sec > t2->tv_sec) - { - return true; - } - else if (t1->tv_sec < t2->tv_sec) - { - return false; - } - else - { - return t1->tv_usec > t2->tv_usec; - } -} - -static inline bool -tv_eq(const struct timeval *t1, const struct timeval *t2) -{ - return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec; -} - static inline void tv_delta(struct timeval *dest, const struct timeval *t1, const struct timeval *t2) { diff --git a/src/openvpn/schedule.c b/src/openvpn/schedule.c index 6d9bb62..6772ad6 100644 --- a/src/openvpn/schedule.c +++ b/src/openvpn/schedule.c @@ -33,20 +33,6 @@ #include "memdbg.h" -#ifdef SCHEDULE_TEST - -struct status -{ - int sru; - int ins; - int coll; - int lsteps; -}; - -static struct status z; - -#endif - #ifdef ENABLE_DEBUG static void schedule_entry_debug_info(const char *caller, const struct schedule_entry *e) @@ -75,12 +61,7 @@ } } -/* This is the master key comparison routine. A key is - * simply a struct timeval containing the absolute time for - * an event. The unique treap priority (pri) is used to ensure - * that keys do not collide. - */ -static inline int +int schedule_entry_compare(const struct schedule_entry *e1, const struct schedule_entry *e2) { if (e1->tv.tv_sec < e2->tv.tv_sec) @@ -226,10 +207,6 @@ /* parent <-> child linkage is corrupted */ ASSERT(0); } - -#ifdef SCHEDULE_TEST - ++z.sru; -#endif } } @@ -284,10 +261,6 @@ { const int comp = schedule_entry_compare(e, c); -#ifdef SCHEDULE_TEST - ++z.ins; -#endif - if (comp == -1) { if (c->lt) @@ -320,9 +293,6 @@ { /* rare key/priority collision -- no big deal, * just choose another priority and retry */ -#ifdef SCHEDULE_TEST - ++z.coll; -#endif schedule_set_pri(e); /* msg (M_INFO, "PRI COLLISION pri=%u", e->pri); */ c = s->root; @@ -381,9 +351,6 @@ { while (e->lt) { -#ifdef SCHEDULE_TEST - ++z.lsteps; -#endif e = e->lt; } } @@ -422,280 +389,4 @@ { s->earliest_wakeup = NULL; /* invalidate cache */ schedule_remove_node(s, e); -} - -/* - * Debug functions below this point - */ - -#ifdef SCHEDULE_TEST - -static inline struct schedule_entry * -schedule_find_earliest_wakeup(struct schedule *s) -{ - return schedule_find_least(s->root); -} - -/* - * Recursively check that the treap (btree) is - * internally consistent. - */ -int -schedule_debug_entry(const struct schedule_entry *e, int depth, int *count, struct timeval *least, - const struct timeval *min, const struct timeval *max) -{ - struct gc_arena gc = gc_new(); - int maxdepth = depth; - if (e) - { - int d; - - ASSERT(e != e->lt); - ASSERT(e != e->gt); - ASSERT(e != e->parent); - ASSERT(!e->parent || e->parent != e->lt); - ASSERT(!e->parent || e->parent != e->gt); - ASSERT(!e->lt || e->lt != e->gt); - - if (e->lt) - { - ASSERT(e->lt->parent == e); - ASSERT(schedule_entry_compare(e->lt, e) == -1); - ASSERT(e->lt->pri >= e->pri); - } - - if (e->gt) - { - ASSERT(e->gt->parent == e); - ASSERT(schedule_entry_compare(e->gt, e)); - ASSERT(e->gt->pri >= e->pri); - } - - ASSERT(tv_le(min, &e->tv)); - ASSERT(tv_le(&e->tv, max)); - - if (count) - { - ++(*count); - } - - if (least && tv_lt(&e->tv, least)) - { - *least = e->tv; - } - - d = schedule_debug_entry(e->lt, depth + 1, count, least, min, &e->tv); - if (d > maxdepth) - { - maxdepth = d; - } - - d = schedule_debug_entry(e->gt, depth + 1, count, least, &e->tv, max); - if (d > maxdepth) - { - maxdepth = d; - } - } - gc_free(&gc); - return maxdepth; -} - -int -schedule_debug(struct schedule *s, int *count, struct timeval *least) -{ - struct timeval min; - struct timeval max; - - min.tv_sec = 0; - min.tv_usec = 0; - max.tv_sec = 0x7FFFFFFF; - max.tv_usec = 0x7FFFFFFF; - - if (s->root) - { - ASSERT(s->root->parent == NULL); - } - return schedule_debug_entry(s->root, 0, count, least, &min, &max); -} - -#if 1 - -void -tv_randomize(struct timeval *tv) -{ - tv->tv_sec += random() % 100; - tv->tv_usec = random() % 100; -} - -#else /* if 1 */ - -void -tv_randomize(struct timeval *tv) -{ - struct gc_arena gc = gc_new(); - long int choice = get_random(); - if ((choice & 0xFF) == 0) - { - tv->tv_usec += ((choice >> 8) & 0xFF); - } - else - { - prng_bytes((uint8_t *)tv, sizeof(struct timeval)); - } - gc_free(&gc); -} - -#endif /* if 1 */ - -void -schedule_verify(struct schedule *s) -{ - struct gc_arena gc = gc_new(); - struct timeval least; - int count; - int maxlev; - struct schedule_entry *e; - const struct status zz = z; - - least.tv_sec = least.tv_usec = 0x7FFFFFFF; - - count = 0; - - maxlev = schedule_debug(s, &count, &least); - - e = schedule_find_earliest_wakeup(s); - - if (e) - { - printf("Verification Phase count=%d maxlev=%d sru=%d ins=%d coll=%d ls=%d l=%s", count, - maxlev, zz.sru, zz.ins, zz.coll, zz.lsteps, tv_string(&e->tv, &gc)); - - if (!tv_eq(&least, &e->tv)) - { - printf(" [COMPUTED DIFFERENT MIN VALUES!]"); - } - - printf("\n"); - } - - CLEAR(z); - gc_free(&gc); -} - -void -schedule_randomize_array(struct schedule_entry **array, int size) -{ - int i; - for (i = 0; i < size; ++i) - { - const int src = get_random() % size; - struct schedule_entry *tmp = array[i]; - if (i != src) - { - array[i] = array[src]; - array[src] = tmp; - } - } -} - -void -schedule_print_work(struct schedule_entry *e, int indent) -{ - struct gc_arena gc = gc_new(); - int i; - for (i = 0; i < indent; ++i) - { - printf(" "); - } - if (e) - { - printf("%s [%u] e=" ptr_format ", p=" ptr_format " lt=" ptr_format " gt=" ptr_format "\n", - tv_string(&e->tv, &gc), e->pri, (ptr_type)e, (ptr_type)e->parent, (ptr_type)e->lt, - (ptr_type)e->gt); - schedule_print_work(e->lt, indent + 1); - schedule_print_work(e->gt, indent + 1); - } - else - { - printf("NULL\n"); - } - gc_free(&gc); -} - -void -schedule_print(struct schedule *s) -{ - printf("*************************\n"); - schedule_print_work(s->root, 0); -} - -void -schedule_test(void) -{ - struct gc_arena gc = gc_new(); - int n = 1000; - int n_mod = 25; - - int i, j; - struct schedule_entry **array; - struct schedule *s = schedule_init(); - struct schedule_entry *e; - - CLEAR(z); - ALLOC_ARRAY(array, struct schedule_entry *, n); - - printf("Creation/Insertion Phase\n"); - - for (i = 0; i < n; ++i) - { - ALLOC_OBJ_CLEAR(array[i], struct schedule_entry); - tv_randomize(&array[i]->tv); - /*schedule_print (s);*/ - /*schedule_verify (s);*/ - schedule_add_modify(s, array[i]); - } - - schedule_randomize_array(array, n); - - /*schedule_print (s);*/ - schedule_verify(s); - - for (j = 1; j <= n_mod; ++j) - { - printf("Modification Phase Pass %d\n", j); - - for (i = 0; i < n; ++i) - { - e = schedule_find_earliest_wakeup(s); - /*printf ("BEFORE %s\n", tv_string (&e->tv, &gc));*/ - tv_randomize(&e->tv); - /*printf ("AFTER %s\n", tv_string (&e->tv, &gc));*/ - schedule_add_modify(s, e); - /*schedule_verify (s);*/ - /*schedule_print (s);*/ - } - schedule_verify(s); - /*schedule_print (s);*/ - } - - /*printf ("INS=%d\n", z.ins);*/ - - while ((e = schedule_find_earliest_wakeup(s))) - { - schedule_remove_node(s, e); - /*schedule_verify (s);*/ - } - schedule_verify(s); - - printf("S->ROOT is %s\n", s->root ? "NOT NULL" : "NULL"); - - for (i = 0; i < n; ++i) - { - free(array[i]); - } - free(array); - free(s); - gc_free(&gc); -} - -#endif /* ifdef SCHEDULE_TEST */ +} \ No newline at end of file diff --git a/src/openvpn/schedule.h b/src/openvpn/schedule.h index 3847186..3c93208 100644 --- a/src/openvpn/schedule.h +++ b/src/openvpn/schedule.h @@ -34,9 +34,6 @@ * a ping or scheduling a TLS renegotiation. */ -/* define to enable a special test mode */ -/*#define SCHEDULE_TEST*/ - #include "otime.h" #include "error.h" @@ -63,11 +60,6 @@ void schedule_remove_entry(struct schedule *s, struct schedule_entry *e); -#ifdef SCHEDULE_TEST -void schedule_test(void); - -#endif - /* Private Functions */ /* is node already in tree? */ @@ -139,4 +131,14 @@ return ret; } +/** + * This method compares two schedule entries and return which one is + * earlier,later or equal. + * + * A key is simply a struct timeval containing the absolute time for + * an event. The unique treap priority (pri) is used to ensure + * that keys do not collide. + */ +int +schedule_entry_compare(const struct schedule_entry *e1, const struct schedule_entry *e2); #endif /* ifndef SCHEDULE_H */ diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am index d861ef9..4a76269 100644 --- a/tests/unit_tests/openvpn/Makefile.am +++ b/tests/unit_tests/openvpn/Makefile.am @@ -370,6 +370,7 @@ misc_testdriver_LDFLAGS = @TEST_LDFLAGS@ misc_testdriver_SOURCES = test_misc.c \ + test_schedule.c test_schedule.h \ mock_msg.c test_common.h \ mock_get_random.c \ $(top_srcdir)/src/openvpn/buffer.c \ @@ -377,7 +378,9 @@ $(top_srcdir)/src/openvpn/ssl_util.c \ $(top_srcdir)/src/openvpn/win32-util.c \ $(top_srcdir)/src/openvpn/platform.c \ - $(top_srcdir)/src/openvpn/list.c + $(top_srcdir)/src/openvpn/list.c \ + $(top_srcdir)/src/openvpn/otime.c \ + $(top_srcdir)/src/openvpn/schedule.c push_update_msg_testdriver_CFLAGS = -I$(top_srcdir)/src/openvpn \ -I$(top_srcdir)/src/compat \ diff --git a/tests/unit_tests/openvpn/test_common.h b/tests/unit_tests/openvpn/test_common.h index fb070aa..8db4ea6 100644 --- a/tests/unit_tests/openvpn/test_common.h +++ b/tests/unit_tests/openvpn/test_common.h @@ -78,7 +78,7 @@ * @param filename name of the filename to retrieve relative to the * unit test source directory */ -void +static inline void openvpn_test_get_srcdir_dir(char *buf, size_t bufsize, const char *filename) { const char *srcdir = getenv("srcdir"); diff --git a/tests/unit_tests/openvpn/test_misc.c b/tests/unit_tests/openvpn/test_misc.c index fc9840a..cd86fd2 100644 --- a/tests/unit_tests/openvpn/test_misc.c +++ b/tests/unit_tests/openvpn/test_misc.c @@ -41,6 +41,8 @@ #ifdef _WIN32 #include "win32-util.h" #endif +#include "test_schedule.h" + static void test_compat_lzo_string(void **state) @@ -488,7 +490,8 @@ cmocka_unit_test(test_auth_fail_temp_flags), cmocka_unit_test(test_auth_fail_temp_flags_msg), cmocka_unit_test(test_list), - cmocka_unit_test(test_atoi_variants) + cmocka_unit_test(test_atoi_variants), + cmocka_unit_test(schedule_test) }; int diff --git a/tests/unit_tests/openvpn/test_schedule.c b/tests/unit_tests/openvpn/test_schedule.c new file mode 100644 index 0000000..52cd415 --- /dev/null +++ b/tests/unit_tests/openvpn/test_schedule.c @@ -0,0 +1,310 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2026 OpenVPN Inc <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "syshead.h" +#include "schedule.h" +#include "test_common.h" + +static inline bool +tv_lt(const struct timeval *t1, const struct timeval *t2) +{ + if (t1->tv_sec < t2->tv_sec) + { + return true; + } + else if (t1->tv_sec > t2->tv_sec) + { + return false; + } + else + { + return t1->tv_usec < t2->tv_usec; + } +} + +static inline bool +tv_le(const struct timeval *t1, const struct timeval *t2) +{ + if (t1->tv_sec < t2->tv_sec) + { + return true; + } + else if (t1->tv_sec > t2->tv_sec) + { + return false; + } + else + { + return t1->tv_usec <= t2->tv_usec; + } +} + +static inline bool +tv_eq(const struct timeval *t1, const struct timeval *t2) +{ + return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec; +} + +static inline struct schedule_entry * +schedule_find_earliest_wakeup(struct schedule *s) +{ + return schedule_find_least(s->root); +} + +/* + * Recursively check that the treap (btree) is + * internally consistent. + */ +int +schedule_debug_entry(const struct schedule_entry *e, int depth, int *count, struct timeval *least, + const struct timeval *min, const struct timeval *max) +{ + struct gc_arena gc = gc_new(); + int maxdepth = depth; + if (e) + { + int d; + + assert_ptr_not_equal(e, e->lt); + assert_ptr_not_equal(e, e->gt); + assert_ptr_not_equal(e, e->parent); + assert_true(!e->parent || e->parent != e->lt); + assert_true(!e->parent || e->parent != e->gt); + assert_true(!e->lt || e->lt != e->gt); + + if (e->lt) + { + assert_ptr_equal(e->lt->parent, e); + assert_int_equal(schedule_entry_compare(e->lt, e), -1); + assert_true(e->lt->pri >= e->pri); + } + + if (e->gt) + { + assert_ptr_equal(e->gt->parent, e); + assert_int_equal(schedule_entry_compare(e->gt, e), 1); + assert_true(e->gt->pri >= e->pri); + } + + assert_true(tv_le(min, &e->tv)); + assert_true(tv_le(&e->tv, max)); + + if (count) + { + ++(*count); + } + + if (least && tv_lt(&e->tv, least)) + { + *least = e->tv; + } + + d = schedule_debug_entry(e->lt, depth + 1, count, least, min, &e->tv); + if (d > maxdepth) + { + maxdepth = d; + } + + d = schedule_debug_entry(e->gt, depth + 1, count, least, &e->tv, max); + if (d > maxdepth) + { + maxdepth = d; + } + } + gc_free(&gc); + return maxdepth; +} + +int +schedule_debug(struct schedule *s, int *count, struct timeval *least) +{ + struct timeval min; + struct timeval max; + + min.tv_sec = 0; + min.tv_usec = 0; + max.tv_sec = 0x7FFFFFFF; + max.tv_usec = 0x7FFFFFFF; + + if (s->root) + { + assert_null(s->root->parent); + } + return schedule_debug_entry(s->root, 0, count, least, &min, &max); +} + +void +tv_randomize(struct timeval *tv) +{ + tv->tv_sec += random() % 100; + tv->tv_usec = random() % 100; +} + +void +schedule_verify(struct schedule *s, int n) +{ + struct gc_arena gc = gc_new(); + struct timeval least; + + least.tv_sec = least.tv_usec = 0x7FFFFFFF; + + int count = 0; + int maxlev = schedule_debug(s, &count, &least); + + /* a stupid algorithm to do C23 stdc_bit_ceil_ui/stdc_bit_width + * calculate roundup(log2 n) */ + int bit_ceil_n = 1; + int log2n = 0; + while (bit_ceil_n < n) + { + bit_ceil_n <<= 1; + log2n++; + } + + /* Since this is a binary tree the maximum level needs to be at least + * log2(n) */ + assert_true(maxlev >= log2n); + struct schedule_entry *e = schedule_find_earliest_wakeup(s); + + if (e) + { + assert_true(tv_eq(&least, &e->tv)); + } + + gc_free(&gc); +} + +void +schedule_randomize_array(struct schedule_entry **array, int size) +{ + int i; + for (i = 0; i < size; ++i) + { + const int src = rand() % size; + struct schedule_entry *tmp = array[i]; + if (i != src) + { + array[i] = array[src]; + array[src] = tmp; + } + } +} + +void +schedule_print_work(struct schedule_entry *e, int indent) +{ + struct gc_arena gc = gc_new(); + int i; + for (i = 0; i < indent; ++i) + { + printf(" "); + } + if (e) + { + printf("%s [%u] e=" ptr_format ", p=" ptr_format " lt=" ptr_format " gt=" ptr_format "\n", + tv_string(&e->tv, &gc), e->pri, (ptr_type)e, (ptr_type)e->parent, (ptr_type)e->lt, + (ptr_type)e->gt); + schedule_print_work(e->lt, indent + 1); + schedule_print_work(e->gt, indent + 1); + } + else + { + printf("NULL\n"); + } + gc_free(&gc); +} + +void +schedule_print(struct schedule *s) +{ + printf("*************************\n"); + schedule_print_work(s->root, 0); +} + +void +schedule_test(void **state) +{ + struct gc_arena gc = gc_new(); + int n = 1000; + int n_mod = 25; + + int i, j; + struct schedule_entry **array; + struct schedule *s = schedule_init(); + struct schedule_entry *e; + + ALLOC_ARRAY(array, struct schedule_entry *, n); + + for (i = 0; i < n; ++i) + { + ALLOC_OBJ_CLEAR(array[i], struct schedule_entry); + tv_randomize(&array[i]->tv); + /*schedule_print (s);*/ + /*schedule_verify (s, n);*/ + schedule_add_modify(s, array[i]); + } + + schedule_randomize_array(array, n); + + /*schedule_print (s);*/ + schedule_verify(s, n); + + for (j = 1; j <= n_mod; ++j) + { + /*printf("Modification Phase Pass %d\n", j);*/ + + for (i = 0; i < n; ++i) + { + e = schedule_find_earliest_wakeup(s); + /*printf ("BEFORE %s\n", tv_string (&e->tv, &gc));*/ + tv_randomize(&e->tv); + /*printf ("AFTER %s\n", tv_string (&e->tv, &gc));*/ + schedule_add_modify(s, e); + /*schedule_verify (s, n);*/ + /*schedule_print (s);*/ + } + schedule_verify(s, n); + /*schedule_print (s);*/ + } + + /*printf ("INS=%d\n", z.ins);*/ + + while ((e = schedule_find_earliest_wakeup(s))) + { + schedule_remove_node(s, e); + /*schedule_verify (s, n);*/ + } + schedule_verify(s, 0); + assert_null(s->root); + + for (i = 0; i < n; ++i) + { + free(array[i]); + } + free(array); + schedule_free(s); + gc_free(&gc); +} diff --git a/tests/unit_tests/openvpn/test_schedule.h b/tests/unit_tests/openvpn/test_schedule.h new file mode 100644 index 0000000..55dc591 --- /dev/null +++ b/tests/unit_tests/openvpn/test_schedule.h @@ -0,0 +1,27 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2026 OpenVPN Inc <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <https://www.gnu.org/licenses/>. + */ +#ifndef SCHEDULE_TEST_H +#define SCHEDULE_TEST_H +/** Runs the schedule test */ +void +schedule_test(void **state); +#endif \ No newline at end of file