From: Thibault Godouet Date: Sun, 16 Jun 2024 14:53:52 +0000 (+0100) Subject: Convert existing adhoc unit tests to cmocka. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d6dccec157f78eec9356f71d28aa65c4b36a3b26;p=thirdparty%2Ffcron.git Convert existing adhoc unit tests to cmocka. --- diff --git a/.gitignore b/.gitignore index 73e8181..b426a00 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ files/fcron.conf convert-fcrontab doc/stylesheets/fcron-doc.dsl doc/fcron-doc.mod -exe_list_test MANIFEST VERSION PREVIOUS_VERSION @@ -27,8 +26,5 @@ doc/fr/HTML/* doc/fr/man/* doc/fr/txt/* configure -test/mailbox_addr -tests/test-open* -tests/test-types* -tests/test-uidgid* +tests/*_test debug/ diff --git a/Makefile.in b/Makefile.in index c82582f..ade2fb6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -76,12 +76,16 @@ CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS) $(LDFLAGS) ifeq ($(FCRONDYN), 1) LIBOBJS := $(LIBOBJS) endif -OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o mail.o filesubs.o select.o fcrondyn_svr.o suspend.o $(LIBOBJS) -OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o mail.o filesubs.o +OBJSD_MAIN := fcron.o +OBJSD_NO_MAIN := cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o mail.o filesubs.o select.o fcrondyn_svr.o suspend.o $(LIBOBJS) +OBJSD := $(OBJSD_MAIN) $(OBJSD_NO_MAIN) +OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o mail.o filesubs.o OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o mail.o filesubs.o OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o mail.o filesubs.o OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o mail.o filesubs.o HEADERSALL := config.h $(SRCDIR)/global.h $(SRCDIR)/cl.h $(SRCDIR)/log.h $(SRCDIR)/subs.h $(SRCDIR)/mem.h $(SRCDIR)/save.h $(SRCDIR)/option.h $(SRCDIR)/dyncom.h $(SRCDIR)/mail.h +TESTS_DIR := tests +TESTS := $(addprefix ${TESTS_DIR}/,fcrondyn_svr_test exe_list_test mail_test) # this is a regular expression : # do not ci automaticaly generated files and doc (done by doc's Makefile) @@ -96,6 +100,8 @@ else BINMODESIGHUP := 4710 endif +.PHONY: all tests initscripts documentation install install-staged perms strip install-boot install-restart uninstall clean ciclean vclean updatedoc indent ci tar + ifeq ($(FCRONDYN), 1) all: fcron fcrontab fcrondyn convert-fcrontab files/fcron.conf initscripts documentation else @@ -117,8 +123,26 @@ fcronsighup: $(OBJSIG) convert-fcrontab: $(OBJCONV) $(CC) $(CFLAGS) -o $@ $(OBJCONV) $(LIBS) -exe_list_test: exe_list.o u_list.o exe_list_test.o log.o subs.o - $(CC) $(CFLAGS) -o $@ exe_list.o u_list.o exe_list_test.o log.o subs.o $(LIBS) +$(TESTS_DIR)/%.test.o: %.o + # All for main() to be replaced by cmocka: + objcopy $< --globalize-symbol=main --weaken-symbol=main $@ + +$(TESTS_DIR)/fcrondyn_svr_test: $(OBJSD_NO_MAIN) $(TESTS_DIR)/fcron.test.o $(TESTS_DIR)/fcrondyn_svr_test.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lcmocka -Wno-implicit-function-declaration -Wl,--wrap=send + $@ + +$(TESTS_DIR)/exe_list_test: $(OBJSD_NO_MAIN) $(TESTS_DIR)/fcron.test.o $(TESTS_DIR)/exe_list_test.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lcmocka -Wno-implicit-function-declaration + $@ + +$(TESTS_DIR)/mail_test: $(OBJSD_NO_MAIN) $(TESTS_DIR)/fcron.test.o $(TESTS_DIR)/mail_test.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lcmocka -Wno-implicit-function-declaration + $@ + +tests: $(TESTS) + +$(TESTS_DIR)/%.o: $(TESTS_DIR)/%.c + $(CC) $(CFLAGS) -o $@ -c $< -Wno-implicit-function-declaration -Wno-unused-variable %.o: $(SRCDIR)/%.c $(HEADERSALL) $(SRCDIR)/%.h $(CC) $(CFLAGS) -DPIDFILE="\"${PIDFILE}\"" -DREBOOT_LOCK="\"${REBOOT_LOCK}\"" \ @@ -139,11 +163,6 @@ initscripts: documentation: $(MAKE) -C doc doc-if-none -.PHONY: test -test: fcron - $(MAKE) -C test tests - - install: install-staged strip perms ifeq ($(BOOTINSTALL), 1) $(SRCDIR)/script/boot-install "$(INSTALL) -o $(ROOTNAME)" $(DESTSBIN) $(DEBUG) $(FCRONTABS) $(ANSWERALL) $(SRCDIR) @@ -269,10 +288,10 @@ uninstall: $(SRCDIR)/script/boot-uninstall clean: - rm -f *.o core + rm -f *.o core $(TESTS_DIR)/*.o rm -f fcron fcrontab fcrondyn fcronsighup convert-fcrontab files/fcron.conf + rm -f $(TESTS_DIR)/*_test $(MAKE) -C doc clean - $(MAKE) -C test clean ciclean: clean find ./ -name "*~" -exec rm -f {} \; @@ -283,7 +302,6 @@ vclean: ciclean files/fcron.conf script/fcron.init.suse script/fcron.init.systemd script/fcron.init.systemd.reboot \ script/fcron.sh script/sysVinit-launcher $(MAKE) -C doc clean - $(MAKE) -C test clean files/fcron.conf: $(SRCDIR)/files/fcron.conf.in config.h @@ -296,7 +314,7 @@ updatedoc: indent: indent -linux -i4 --no-tabs --leave-optional-blank-lines \ --start-left-side-of-comments --procnames-start-lines \ - --dont-cuddle-else *.c *.h + --dont-cuddle-else *.c *.h tests/*.c configure: configure.in # update configure script, then Makefile and config.h, and finally diff --git a/configure.in b/configure.in index 174e1a5..c74462d 100644 --- a/configure.in +++ b/configure.in @@ -1077,7 +1077,7 @@ dnl Final settings dnl --------------------------------------------------------------------- -AC_CONFIG_FILES([Makefile doc/Makefile test/Makefile doc/stylesheets/fcron-doc.dsl]) +AC_CONFIG_FILES([Makefile doc/Makefile doc/stylesheets/fcron-doc.dsl]) AC_OUTPUT diff --git a/exe_list_test.c b/exe_list_test.c deleted file mode 100644 index 08ed930..0000000 --- a/exe_list_test.c +++ /dev/null @@ -1,157 +0,0 @@ -#include "exe_list.h" -#include "global.h" -#include "fcron.h" - -/* required by log.c */ -char debug_opt = 1; -char *prog_name = NULL; -char foreground = 1; -pid_t daemon_pid = 0; -uid_t rootuid = 0; - -void -print_cur(exe_t *e) -{ - printf("Current entry's shell command: %s\n", - e ? e->e_line->cl_shell : "NULL"); -} - -void -print_list(exe_list_t *list) -{ - exe_t *e = NULL; - printf("Current list:\n"); - for (e = exe_list_first(list); e != NULL; e = exe_list_next(list)) - printf(" Shell command: %s\n", e ? e->e_line->cl_shell : "NULL"); -} - -int -main(int argc, char *argv[]) -{ - exe_list_t *list = NULL; - exe_t *e = NULL; - cl_t l1, l2, l3, l4, l5, l6, l7; - - l1.cl_shell = "line 1"; - l2.cl_shell = "line 2"; - l3.cl_shell = "line 3"; - l4.cl_shell = "line 4"; - l5.cl_shell = "line 5"; - l6.cl_shell = "line 6"; - l7.cl_shell = "line 7"; - - list = exe_list_init(); - - /* trigger a resize of the list during an iteration */ - printf("Adding l1...\n"); - exe_list_add_line(list, &l1); - printf("Adding l2...\n"); - exe_list_add_line(list, &l2); - printf("Adding l3...\n"); - exe_list_add_line(list, &l3); - e = exe_list_first(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - printf("Adding l4...\n"); - exe_list_add_line(list, &l4); - printf("Adding l5...\n"); - exe_list_add_line(list, &l5); - printf("Adding l6...\n"); - exe_list_add_line(list, &l6); - printf("Adding l7...\n"); - exe_list_add_line(list, &l7); - - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - - /* "standard" iteration: */ - print_list(list); - - /* remove item at the beginning and middle of the list + add an item which is already in there */ - printf("removing l1, l3, adding l2\n"); - e = exe_list_first(list); - print_cur(e); - printf("Removing l1...\n"); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); /* this one will be the item replacing l1 */ - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - printf("Removing l3...\n"); - exe_list_remove_cur(list); - exe_list_end_iteration(list); - printf("Adding l2...\n"); - exe_list_add_line(list, &l2); /* we suppose the list array won't be reallocated() */ - - print_list(list); - - /* remove an item at the end of the list: */ - printf("removing l5, l2\n"); - e = exe_list_first(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - printf("Removing l5...\n"); - exe_list_remove_cur(list); - exe_list_end_iteration(list); - e = exe_list_first(list); - print_cur(e); - e = exe_list_next(list); - print_cur(e); - printf("Removing l2...\n"); - exe_list_remove_cur(list); - exe_list_end_iteration(list); - - print_list(list); - - printf("Adding l1...\n"); - exe_list_add_line(list, &l1); - - print_list(list); - - printf("empty the list\n"); - e = exe_list_first(list); - print_cur(e); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); - exe_list_remove_cur(list); - e = exe_list_next(list); - print_cur(e); - - print_list(list); - - printf("Destroying the list...\n"); - list = exe_list_destroy(list); - - return 0; - -} diff --git a/fcron.c b/fcron.c index 2bc641e..0be2604 100644 --- a/fcron.c +++ b/fcron.c @@ -584,7 +584,7 @@ set_signal_handler(int signal, void (*handler)(int), bool first_install) } void -install_signal_handler(int signal, void(*handler)(int)) +install_signal_handler(int signal, void (*handler)(int)) { set_signal_handler(signal, handler, true); } diff --git a/fcrondyn_svr.c b/fcrondyn_svr.c index 7a94247..2599254 100644 --- a/fcrondyn_svr.c +++ b/fcrondyn_svr.c @@ -26,6 +26,7 @@ #include "fcron.h" #include "fcrondyn_svr.h" +#include "fcrondyn_svr_priv.h" #include "select.h" #include "getloadavg.h" #include "database.h" @@ -91,19 +92,6 @@ char err_others_nallowed_str[] = Tell_no_more_data(FD); \ } -/* which bit corresponds to which field ? */ -#define FIELD_USER 0 -#define FIELD_RQ 1 -#define FIELD_PID 2 -#define FIELD_SCHEDULE 3 -#define FIELD_LAVG 4 -#define FIELD_INDEX 5 -#define FIELD_OPTIONS 6 - -/* the number of char we need (8 bits (i.e. fields) per char) */ -#define FIELD_NUM_SIZE 1 - - void fcrondyn_socket_init(select_instance *si) /* do everything needed to get a working listening socket */ diff --git a/fcrondyn_svr_priv.h b/fcrondyn_svr_priv.h new file mode 100644 index 0000000..31d55d4 --- /dev/null +++ b/fcrondyn_svr_priv.h @@ -0,0 +1,42 @@ +/* + * FCRON - periodic command scheduler + * + * Copyright 2000-2025 Thibault Godouet + * + * 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. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The GNU General Public License can also be found in the file + * `LICENSE' that comes with the fcron source distribution. + */ + + +/* Private declarations for fcrondyn_svr.c */ + +#ifndef __FCRONDYN_SVR_PRIV_H__ +#define __FCRONDYN_SVR_PRIV_H__ + +/* which bit corresponds to which field ? */ +#define FIELD_USER 0 +#define FIELD_RQ 1 +#define FIELD_PID 2 +#define FIELD_SCHEDULE 3 +#define FIELD_LAVG 4 +#define FIELD_INDEX 5 +#define FIELD_OPTIONS 6 + +/* the number of char we need (8 bits (i.e. fields) per char) */ +#define FIELD_NUM_SIZE 1 + +#endif /* __FCRONDYN_SVR_PRIV_H__ */ diff --git a/test/Makefile.in b/test/Makefile.in deleted file mode 100644 index 38c5476..0000000 --- a/test/Makefile.in +++ /dev/null @@ -1,19 +0,0 @@ -# Prototype makefile, while waiting for integration of a test framework -SRCDIR := .. -LIBS := @LIBS@ -SRCST := mailbox_addr.c -OBJST := $(patsubst %.c,%.o,$(SRCST)) -EXEST := $(patsubst %.c,%,$(SRCST)) -OBJSD := $(addprefix $(SRCDIR)/,log.o job.o mem.o filesubs.o subs.o fcronconf.o mail.o temp_file.o env_list.o u_list.o) - -CFLAGS += -I$(SRCDIR) -CFLAGS += @CFLAGS@ - -.PHONY: clean tests - -tests: $(OBJSD) - $(CC) $(CFLAGS) $(OBJSD) $(LIBS) $(SRCST) -o $(EXEST) - ./$(EXEST) - -clean: - rm -f *.o core $(EXEST) diff --git a/test/mailbox_addr.c b/test/mailbox_addr.c deleted file mode 100644 index c529b7f..0000000 --- a/test/mailbox_addr.c +++ /dev/null @@ -1,172 +0,0 @@ -/* Prototype test harness, while waiting for integration of a test framework */ -#include -#include -#include -#include - -#include "fcron.h" -#include "job.h" -#include "fcronconf.h" -#include "mail.h" - -#define AssertEq(DESCR, RETURNED, EXPECTED) \ -{ \ - printf("%s: check %ld == %ld\n", (DESCR), (long)(RETURNED), (long)(EXPECTED)); \ - assert((RETURNED) == (EXPECTED)); \ -} - -#define AssertNeq(DESCR, RETURNED, EXPECTED) \ -{ \ - printf("%s: check %ld != %ld\n", (DESCR), (long)(RETURNED), (long)(EXPECTED)); \ - assert((RETURNED) != (EXPECTED)); \ -} - -/* These globals should maybe come from a lib instead of the various exes? */ -char *prog_name = NULL; -pid_t daemon_pid = 0; -char foreground = 1; -char default_mail_charset[TERM_LEN] = ""; -pam_handle_t *pamh = NULL; -const struct pam_conv apamconv = { NULL }; -mode_t saved_umask; -uid_t rootuid = 0; -char *tmp_path = ""; - -char *format_maildisplayname(char *displayname); -char *make_mailbox_addr(char *displayname, char *mail_from, char *hostname); - - -void _test_format_displayname(char *test_desc, char *arg, char *expected) -{ - char *output = NULL; - - output = format_maildisplayname(arg); - printf("%s: format_displayname('%s'): '%s' ?= '%s'\n", - test_desc, arg, output, expected); - assert(strcmp(output, expected) == 0); - Free_safe(output); -} - - -void _test_make_mailbox_addr(char *test_desc, char *displayname, - char *mailfrom, char *hostname, char *expected) -{ - char *output = NULL; - - output = make_mailbox_addr(displayname, mailfrom, hostname); - printf("%s: make_mailbox_addr('%s', '%s', '%s'): '%s' ?= '%s'\n", - test_desc, displayname, mailfrom, hostname, output, expected); - assert(strcmp(output, expected) == 0); - Free_safe(output); -} - -int main(int argc, char* argv[]) -{ - char *displayname = NULL; - char *output = NULL; - - /* Mind that format_maildisplayname() might modify input displayname, thus all - special characters must be tested */ - _test_format_displayname("empty displayname", "", ""); - _test_format_displayname("displayname with no special char", - "Foo Bar", "Foo Bar"); - _test_format_displayname("displayname with no special char", - "Foo'Bar", "Foo'Bar"); - - /* Make sure to extend this series, should SPECIAL_MBOX_CHARS change. All - those require quoting */ - _test_format_displayname("displayname with special char", - "(Foo Bar", "\"(Foo Bar\""); - _test_format_displayname("displayname with special char", - "Foo Bar)", "\"Foo Bar)\""); - _test_format_displayname("displayname with special char", - "Foo Bo Bar", "\"Fo>o Bar\""); - _test_format_displayname("displayname with special char", - "F[oo Bar", "\"F[oo Bar\""); - _test_format_displayname("displayname with special char", - "Foo] Bar", "\"Foo] Bar\""); - _test_format_displayname("displayname with special char", - "Foo . Bar", "\"Foo . Bar\""); - _test_format_displayname("displayname with special char", - "Foo, Bar", "\"Foo, Bar\""); - _test_format_displayname("displayname with special char", - "Foo B:ar", "\"Foo B:ar\""); - _test_format_displayname("displayname with special char", - "Foo ;Bar", "\"Foo ;Bar\""); - _test_format_displayname("displayname with special char", - "Foo@Bar", "\"Foo@Bar\""); - _test_format_displayname("displayname with double quote", - "Foo \" Bar", "\"Foo \\\" Bar\""); - _test_format_displayname("displayname with backslash", - "Foo \\Bar", "\"Foo \\Bar\""); - - /* make_mailbox_addr() only composes the "mailbox" address header: it's - agnostic of any special character */ - _test_make_mailbox_addr("empty displayname => no angle brackets", - "", "baz", "@quux", "baz@quux"); - _test_make_mailbox_addr("displayname => needs angle brackets", - "Foo Bar", "baz", "@quux", "Foo Bar "); - - - /* - * format_displayname() overflow tests - */ - - displayname = (char *)realloc_safe(displayname, - MAIL_FROM_VALUE_LEN_MAX * 2 * sizeof(char), - "displayname buffer"); - - memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX*2); - - printf("=== format_maildisplayname: one-char overflow with no expansion...\n"); - displayname[MAIL_FROM_VALUE_LEN_MAX+1] = '\0'; - output = format_maildisplayname(displayname); - AssertEq("format_displayname: overflow with no expansion", output, NULL); - Free_safe(output); - - - printf("=== format_maildisplayname: max size with no special char...\n"); - displayname[MAIL_FROM_VALUE_LEN_MAX] = '\0'; - output = format_maildisplayname(displayname); - _test_format_displayname("format_displayname: max size with no special chars", - output, displayname); - printf("format_displayname: max size with no special char: len(output)=%lu, MAIL_FROM_VALUE_LEN_MAX=%lu\n", strlen(output), MAIL_FROM_VALUE_LEN_MAX); - Free_safe(output); - - printf("=== format_maildisplayname: overflow on max size with special char...\n"); - displayname[0] = DQUOTE; - output = format_maildisplayname(displayname); - AssertEq("format_displayname: overflow on max size with special char", output, NULL); - Free_safe(output); - - /* - * make_mailbox_addr() overflow tests - */ - - printf("=== make_mailbox_addr: overflow on displayname at max size + 1...\n"); - /* make_mailbox_addr() adds 3 chars: space, <, > */ - displayname[MAIL_FROM_VALUE_LEN_MAX-strlen("baz")-strlen("@quux")-2] = '\0'; - output = make_mailbox_addr(displayname, "baz", "@quux"); - AssertEq("make_mailbox_addr: overflow", output, NULL); - Free_safe(output); - - printf("=== make_mailbox_addr: displayname at max size...\n"); - /* make_mailbox_addr() adds 3 chars: space, <, > */ - displayname[MAIL_FROM_VALUE_LEN_MAX-strlen("baz")-strlen("@quux")-3] = '\0'; - output = make_mailbox_addr(displayname, "baz", "@quux"); - printf("make_mailbox_addr: max size with no special char: len(output)=%lu, MAIL_FROM_VALUE_LEN_MAX=%lu\n", strlen(output), MAIL_FROM_VALUE_LEN_MAX); - AssertNeq("make_mailbox_addr: max size", output, NULL); - Free_safe(output); - - - Free_safe(displayname); - - return 0; -} - -/* Local Variables: */ -/* mode: c */ -/* indent-tabs-mode: nil */ -/* End: */ diff --git a/tests/exe_list_test.c b/tests/exe_list_test.c new file mode 100644 index 0000000..d54cfdd --- /dev/null +++ b/tests/exe_list_test.c @@ -0,0 +1,202 @@ +#include "../exe_list.h" +#include "../global.h" +#include "../fcron.h" + +#include /* setjmp.h is needed by cmocka.h */ +#include + +static void +test_list_init_iterate_destroy(int list_size) +{ + exe_list_t *list = NULL; + exe_t *e = NULL; + int i = 0; + cl_t l1 = {.cl_shell = "line 1" }; + + list = exe_list_init(); + assert_non_null(list); + + for (i = 0; i < list_size; i++) { + exe_list_add_line(list, &l1); + } + + for (e = exe_list_first(list), i = 0; e != NULL; + e = exe_list_next(list), i++) { + assert_string_equal(e->e_line->cl_shell, l1.cl_shell); + } + assert_int_equal(i, list_size); + + list = exe_list_destroy(list); + assert_null(list); +} + +static void +test_list_init_iterate_destroy_0(void **state) +{ + test_list_init_iterate_destroy(0); +} + +static void +test_list_init_iterate_destroy_1(void **state) +{ + test_list_init_iterate_destroy(1); +} + +static void +test_list_init_iterate_destroy_7(void **state) +{ + test_list_init_iterate_destroy(7); +} + +static void +test_list_init_iterate_destroy_100(void **state) +{ + test_list_init_iterate_destroy(100); +} + +static void +test_list_init_add_remove(void **state) +{ + exe_list_t *list = NULL; + exe_t *e = NULL; + int i = 0; + char *removed_cl_shells[10]; + int removed_cl_shell_count = 0; + + cl_t l1 = {.cl_shell = "line 1" }; + cl_t l2 = {.cl_shell = "line 2" }; + cl_t l3 = {.cl_shell = "line 3" }; + cl_t l4 = {.cl_shell = "line 4" }; + cl_t l5 = {.cl_shell = "line 5" }; + cl_t l6 = {.cl_shell = "line 6" }; + cl_t l7 = {.cl_shell = "line 7" }; + + printf("Init the list and populate it...\n"); + list = exe_list_init(); + assert_non_null(list); + + /* trigger a resize of the list during an iteration */ + exe_list_add_line(list, &l1); + exe_list_add_line(list, &l2); + exe_list_add_line(list, &l3); + assert_int_equal(list->num_entries, 3); + + e = exe_list_first(list); + assert_non_null(e->e_line->cl_shell); + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + exe_list_add_line(list, &l4); + exe_list_add_line(list, &l5); + exe_list_add_line(list, &l6); + exe_list_add_line(list, &l7); + assert_int_equal(list->num_entries, 7); + + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + e = exe_list_next(list); + assert_non_null(e->e_line->cl_shell); + /* we reached the end of the list: */ + e = exe_list_next(list); + assert_null(e); + + /* remove item at the beginning and middle of the list + add an item which is already in there */ + printf("Remove two items...\n"); + e = exe_list_first(list); + assert_non_null(e->e_line->cl_shell); + removed_cl_shells[removed_cl_shell_count++] = e->e_line->cl_shell; + exe_list_remove_cur(list); + assert_int_equal(list->num_entries, 6); + e = exe_list_next(list); + e = exe_list_next(list); + e = exe_list_next(list); + removed_cl_shells[removed_cl_shell_count++] = e->e_line->cl_shell; + exe_list_remove_cur(list); + assert_int_equal(list->num_entries, 5); + exe_list_end_iteration(list); + assert_int_equal(list->num_entries, 5); + for (e = exe_list_first(list), i = 0; e != NULL; + e = exe_list_next(list), i++) { + assert_string_not_equal(e->e_line->cl_shell, removed_cl_shells[0]); + assert_string_not_equal(e->e_line->cl_shell, removed_cl_shells[1]); + } + assert_int_equal(i, 5); + exe_list_end_iteration(list); + assert_int_equal(list->num_entries, 5); + + printf("Add a duplicate item...\n"); + e = exe_list_first(list); + exe_list_add_line(list, e->e_line); + exe_list_end_iteration(list); + assert_int_equal(list->num_entries, 6); + + for (e = exe_list_first(list); e != NULL; e = exe_list_next(list)) { + assert_non_null(e->e_line->cl_shell); + } + + printf("Remove last item...\n"); + for (i = 0, e = exe_list_first(list); i < list->num_entries - 1; + i++, e = exe_list_next(list)) ; + exe_list_remove_cur(list); + exe_list_end_iteration(list); + assert_int_equal(list->num_entries, 5); + + printf("Remove an item...\n"); + e = exe_list_first(list); + e = exe_list_next(list); + exe_list_remove_cur(list); + exe_list_end_iteration(list); + assert_int_equal(list->num_entries, 4); + + for (e = exe_list_first(list); e != NULL; e = exe_list_next(list)) { + assert_non_null(e->e_line->cl_shell); + } + + printf("Add an item...\n"); + exe_list_add_line(list, &l1); + assert_int_equal(list->num_entries, 5); + + for (e = exe_list_first(list); e != NULL; e = exe_list_next(list)) { + assert_non_null(e->e_line->cl_shell); + } + + printf("Empty the list...\n"); + e = exe_list_first(list); + exe_list_remove_cur(list); + e = exe_list_next(list); + exe_list_remove_cur(list); + e = exe_list_next(list); + exe_list_remove_cur(list); + e = exe_list_next(list); + exe_list_remove_cur(list); + e = exe_list_next(list); + exe_list_remove_cur(list); + e = exe_list_next(list); + assert_int_equal(list->num_entries, 0); + + for (e = exe_list_first(list); e != NULL; e = exe_list_next(list)) { + assert_non_null(e->e_line->cl_shell); + } + + printf("Destroying the list...\n"); + list = exe_list_destroy(list); + assert_null(list); +} + +int +main(void) +{ + const struct CMUnitTest mytests[] = { + cmocka_unit_test(test_list_init_iterate_destroy_0), + cmocka_unit_test(test_list_init_iterate_destroy_1), + cmocka_unit_test(test_list_init_iterate_destroy_7), + cmocka_unit_test(test_list_init_iterate_destroy_100), + cmocka_unit_test(test_list_init_add_remove), + }; + return cmocka_run_group_tests(mytests, NULL, NULL); +} diff --git a/tests/fcrondyn_svr_test.c b/tests/fcrondyn_svr_test.c new file mode 100644 index 0000000..fc9aceb --- /dev/null +++ b/tests/fcrondyn_svr_test.c @@ -0,0 +1,75 @@ +#include +#include +#include /* setjmp.h is needed by cmocka.h */ +#include + +#include "../fcron.h" +#include "../fcrondyn_svr_priv.h" + +ssize_t +__wrap_send(int sockfd, const void *buf, size_t len, int flags) +{ + double up; + double idle; + + /* Verify the passed value of the argument is correct */ + check_expected_ptr(buf); + + return len; +} + +static void +test_print_line(void **state) +{ + int i; + unsigned char fields[FIELD_NUM_SIZE]; + + for (i = 0; i < 1; i++) + fields[i] = 0; + + bit_set(fields, FIELD_SCHEDULE); + bit_set(fields, FIELD_USER); + + cf_t cf; + cl_t cl; + cl.cl_id = 33; + cf.cf_user = "myuser"; + cl.cl_file = &cf; + setenv("TZ", "Europe/London", 1); + cl.cl_nextexe = 1717337700; /* Sun 2 Jun 15:15:00 BST 2024 */ + time_t until = 1717234200; /* Sat 1 Jun 10:30:00 BST 2024 */ + + cl.cl_shell = "echo blah"; + expect_string(__wrap_send, buf, + "33 |myuser |2024-06-02 15:15:00|echo blah\n"); + print_line(1, &cl, fields, 123, 456, 1717234200); + +#define S10 "0123456789" +#define S50 S10 S10 S10 S10 S10 + /* shell command longer than TERM_LEN which will get truncated: */ + cl.cl_shell = "echo " S50 S50 S50 S50 S50; + char expected_output_truncated[] = + "33 |myuser |2024-06-02 15:15:00|echo " S50 S50 S10 S10 S10 S10 + "0 (truncated)\n"; + assert_int_equal(sizeof(expected_output_truncated), TERM_LEN); + expect_string(__wrap_send, buf, expected_output_truncated); + print_line(1, &cl, fields, 123, 456, 1717234200); + + /* shell command exactly long enough to use the TERM_LEN length without truncation: */ +#define SHELL_CMD "echo " S50 S50 S10 S10 S10 S10 S10 "012" + cl.cl_shell = SHELL_CMD; + char expected_output[] = + "33 |myuser |2024-06-02 15:15:00|" SHELL_CMD "\n"; + assert_int_equal(sizeof(expected_output), TERM_LEN); + expect_string(__wrap_send, buf, expected_output); + print_line(1, &cl, fields, 123, 456, 1717234200); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_print_line), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/mail_test.c b/tests/mail_test.c new file mode 100644 index 0000000..a5598d2 --- /dev/null +++ b/tests/mail_test.c @@ -0,0 +1,214 @@ +/* Prototype test harness, while waiting for integration of a test framework */ +#include +#include +#include +#include +#include /* setjmp.h is needed by cmocka.h */ +#include + +#include "../fcron.h" +#include "../job.h" +#include "../fcronconf.h" +#include "../mail.h" + +#define TestFormatDisplayname(DESC, ARG, EXPECTED) \ +{ \ + char *_local_output = NULL; \ + _local_output = format_maildisplayname((ARG)); \ + assert_string_equal(_local_output, (EXPECTED)); \ + Free_safe(_local_output); \ +} + +#define TestMakeMailboxAddr(DESC, DISPLAYNAME, MAILFROM, HOSTNAME, EXPECTED) \ +{ \ + char *_local_output = NULL; \ + _local_output = make_mailbox_addr((DISPLAYNAME), (MAILFROM), (HOSTNAME)); \ + assert_string_equal(_local_output, (EXPECTED)); \ + Free_safe(_local_output); \ +} + +static void +test_format_displayname_no_special_char(void **state) +{ + /* Mind that format_maildisplayname() might modify input displayname, thus all + * special characters must be tested */ + TestFormatDisplayname("empty displayname", "", ""); + TestFormatDisplayname("displayname with no special char", + "Foo Bar", "Foo Bar"); + TestFormatDisplayname("displayname with no special char", + "Foo'Bar", "Foo'Bar"); +} + +static void +test_format_displayname_quoted_special_char(void **state) +{ + /* Make sure to extend this series, should SPECIAL_MBOX_CHARS change. All + * those require quoting */ + TestFormatDisplayname("displayname with special char", + "(Foo Bar", "\"(Foo Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo Bar)", "\"Foo Bar)\""); + TestFormatDisplayname("displayname with special char", + "Foo Bo Bar", "\"Fo>o Bar\""); + TestFormatDisplayname("displayname with special char", + "F[oo Bar", "\"F[oo Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo] Bar", "\"Foo] Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo . Bar", "\"Foo . Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo, Bar", "\"Foo, Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo B:ar", "\"Foo B:ar\""); + TestFormatDisplayname("displayname with special char", + "Foo ;Bar", "\"Foo ;Bar\""); + TestFormatDisplayname("displayname with special char", + "Foo@Bar", "\"Foo@Bar\""); + TestFormatDisplayname("displayname with double quote", + "Foo \" Bar", "\"Foo \\\" Bar\""); + TestFormatDisplayname("displayname with backslash", + "Foo \\Bar", "\"Foo \\Bar\""); +} + +static void +test_format_displayname_overflow_no_expansion(void **state) +{ + char *displayname = NULL; + char *output = NULL; + + displayname = (char *)realloc_safe(displayname, + MAIL_FROM_VALUE_LEN_MAX * 2 * + sizeof(char), "displayname buffer"); + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX * 2); + + displayname[MAIL_FROM_VALUE_LEN_MAX + 1] = '\0'; + output = format_maildisplayname(displayname); + assert_null(output); + + Free_safe(output); + Free_safe(displayname); +} + +static void +test_format_displayname_max_size_no_special_char(void **state) +{ + char *displayname = NULL; + char *output = NULL; + + displayname = (char *)realloc_safe(displayname, + MAIL_FROM_VALUE_LEN_MAX * 2 * + sizeof(char), "displayname buffer"); + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX * 2); + + displayname[MAIL_FROM_VALUE_LEN_MAX] = '\0'; + output = format_maildisplayname(displayname); + TestFormatDisplayname("format_displayname: max size with no special chars", + output, displayname); + assert_int_equal(strlen(output), MAIL_FROM_VALUE_LEN_MAX); + + Free_safe(output); + Free_safe(displayname); +} + + +static void +test_format_displayname_max_size_special_char_overflow(void **state) +{ + char *displayname = NULL; + char *output = NULL; + + displayname = (char *)realloc_safe(displayname, + MAIL_FROM_VALUE_LEN_MAX * 2 * + sizeof(char), "displayname buffer"); + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX * 2); + + displayname[0] = DQUOTE; + output = format_maildisplayname(displayname); + assert_null(output); /* format_displayname: overflow on max size with special char */ + Free_safe(output); + Free_safe(displayname); +} + +static void +test_make_mailbox_addr(void **state) +{ + /* make_mailbox_addr() only composes the "mailbox" address header: it's + * agnostic of any special character */ + TestMakeMailboxAddr("empty displayname => no angle brackets", + "", "baz", "@quux", "baz@quux"); + TestMakeMailboxAddr("displayname => needs angle brackets", + "Foo Bar", "baz", "@quux", "Foo Bar "); + +} + +static void +test_make_mailbox_addr_max_size(void **state) +{ + char *displayname = NULL; + char *output = NULL; + + displayname = (char *)realloc_safe(displayname, + MAIL_FROM_VALUE_LEN_MAX * 2 * + sizeof(char), "displayname buffer"); + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX * 2); + + /* make_mailbox_addr() adds 3 chars: space, <, > */ + displayname[MAIL_FROM_VALUE_LEN_MAX - strlen("baz") - strlen("@quux") - 3] = + '\0'; + output = make_mailbox_addr(displayname, "baz", "@quux"); + assert_int_equal(strlen(output), MAIL_FROM_VALUE_LEN_MAX); + assert_non_null(output); /* make_mailbox_addr: max size */ + + Free_safe(output); + Free_safe(displayname); +} + +static void +test_make_mailbox_addr_overflow(void **state) +{ + char *displayname = NULL; + char *output = NULL; + + displayname = (char *)realloc_safe(displayname, + MAIL_FROM_VALUE_LEN_MAX * 2 * + sizeof(char), "displayname buffer"); + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX * 2); + + /* make_mailbox_addr() adds 3 chars: space, <, > */ + displayname[MAIL_FROM_VALUE_LEN_MAX - strlen("baz") - strlen("@quux") - 2] = + '\0'; + output = make_mailbox_addr(displayname, "baz", "@quux"); + assert_null(output); /* make_mailbox_addr: overflow */ + + Free_safe(output); + Free_safe(displayname); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_format_displayname_no_special_char), + cmocka_unit_test(test_format_displayname_quoted_special_char), + cmocka_unit_test(test_format_displayname_overflow_no_expansion), + cmocka_unit_test(test_format_displayname_max_size_no_special_char), + cmocka_unit_test + (test_format_displayname_max_size_special_char_overflow), + cmocka_unit_test(test_make_mailbox_addr), + cmocka_unit_test(test_make_mailbox_addr_max_size), + cmocka_unit_test(test_make_mailbox_addr_overflow), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} + +/* Local Variables: */ +/* mode: c */ +/* indent-tabs-mode: nil */ +/* End: */