convert-fcrontab
doc/stylesheets/fcron-doc.dsl
doc/fcron-doc.mod
-exe_list_test
MANIFEST
VERSION
PREVIOUS_VERSION
doc/fr/man/*
doc/fr/txt/*
configure
-test/mailbox_addr
-tests/test-open*
-tests/test-types*
-tests/test-uidgid*
+tests/*_test
debug/
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)
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
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}\"" \
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)
$(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 {} \;
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
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
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
+++ /dev/null
-#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;
-
-}
}
void
-install_signal_handler(int signal, void(*handler)(int))
+install_signal_handler(int signal, void (*handler)(int))
{
set_signal_handler(signal, handler, true);
}
#include "fcron.h"
#include "fcrondyn_svr.h"
+#include "fcrondyn_svr_priv.h"
#include "select.h"
#include "getloadavg.h"
#include "database.h"
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 */
--- /dev/null
+/*
+ * FCRON - periodic command scheduler
+ *
+ * Copyright 2000-2025 Thibault Godouet <fcron@free.fr>
+ *
+ * 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__ */
+++ /dev/null
-# 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)
+++ /dev/null
-/* Prototype test harness, while waiting for integration of a test framework */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#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 B<ar", "\"Foo B<ar\"");
- _test_format_displayname("displayname with special char",
- "Fo>o 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 <baz@quux>");
-
-
- /*
- * 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: */
--- /dev/null
+#include "../exe_list.h"
+#include "../global.h"
+#include "../fcron.h"
+
+#include <setjmp.h> /* setjmp.h is needed by cmocka.h */
+#include <cmocka.h>
+
+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);
+}
--- /dev/null
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h> /* setjmp.h is needed by cmocka.h */
+#include <cmocka.h>
+
+#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);
+}
--- /dev/null
+/* Prototype test harness, while waiting for integration of a test framework */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <setjmp.h> /* setjmp.h is needed by cmocka.h */
+#include <cmocka.h>
+
+#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 B<ar", "\"Foo B<ar\"");
+ TestFormatDisplayname("displayname with special char",
+ "Fo>o 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 <baz@quux>");
+
+}
+
+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: */