]> git.ipfire.org Git - thirdparty/fcron.git/commitdiff
Convert existing adhoc unit tests to cmocka.
authorThibault Godouet <yo8192@users.noreply.github.com>
Sun, 16 Jun 2024 14:53:52 +0000 (15:53 +0100)
committerThibault Godouet <yo8192@users.noreply.github.com>
Sat, 19 Apr 2025 20:51:21 +0000 (21:51 +0100)
12 files changed:
.gitignore
Makefile.in
configure.in
exe_list_test.c [deleted file]
fcron.c
fcrondyn_svr.c
fcrondyn_svr_priv.h [new file with mode: 0644]
test/Makefile.in [deleted file]
test/mailbox_addr.c [deleted file]
tests/exe_list_test.c [new file with mode: 0644]
tests/fcrondyn_svr_test.c [new file with mode: 0644]
tests/mail_test.c [new file with mode: 0644]

index 73e818188a9259336d6f5de4414431a4e9f6c3ec..b426a000497b5ef5d5f832c074472768a25dc9aa 100644 (file)
@@ -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/
index c82582fda4146f6d8d518ed8118e74873af98b68..ade2fb69fe26e5f5bac30701cba8a312cc65fe77 100644 (file)
@@ -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
index 174e1a54958774184bf53e61cb31f9cec9fabc03..c74462d8ebc904f1a3d91b79cceff513ddcb96a7 100644 (file)
@@ -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 (file)
index 08ed930..0000000
+++ /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 2bc641eab2d78dbb1cd0656bb50aa49ce2106f8b..0be26047ca46138e07795af49bd2e0934ddf471f 100644 (file)
--- 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);
 }
index 7a94247ecbefa615e7a3e41615b08f01a601ee5a..2599254b8b8b4f4f0815ee953dc228c32df69e8b 100644 (file)
@@ -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 (file)
index 0000000..31d55d4
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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__ */
diff --git a/test/Makefile.in b/test/Makefile.in
deleted file mode 100644 (file)
index 38c5476..0000000
+++ /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 (file)
index c529b7f..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/* 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:              */
diff --git a/tests/exe_list_test.c b/tests/exe_list_test.c
new file mode 100644 (file)
index 0000000..d54cfdd
--- /dev/null
@@ -0,0 +1,202 @@
+#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);
+}
diff --git a/tests/fcrondyn_svr_test.c b/tests/fcrondyn_svr_test.c
new file mode 100644 (file)
index 0000000..fc9aceb
--- /dev/null
@@ -0,0 +1,75 @@
+#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);
+}
diff --git a/tests/mail_test.c b/tests/mail_test.c
new file mode 100644 (file)
index 0000000..a5598d2
--- /dev/null
@@ -0,0 +1,214 @@
+/* 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:              */