]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
DEBUG: init: add a way to register functions for unit tests
authorWilliam Lallemand <wlallemand@haproxy.com>
Fri, 28 Feb 2025 20:44:35 +0000 (21:44 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 3 Mar 2025 11:43:32 +0000 (12:43 +0100)
Doing unit tests with haproxy was always a bit difficult, some of the
function you want to test would depend on the buffer or trash buffer
initialisation of HAProxy, so building a separate main() for them is
quite hard.

This patch adds a way to register a function that can be called with the
"-U" parameter on the command line, will be executed just after
step_init_1() and will exit the process with its return value as an exit
code.

When using the -U option, every keywords after this option is passed to
the callback and could be used as a parameter, letting the capability to
handle complex arguments if required by the test.

HAProxy need to be built with DEBUG_UNIT to activate this feature.

Makefile
include/haproxy/debug.h
include/haproxy/init-t.h
include/haproxy/init.h
src/debug.c
src/haproxy.c

index 5486f07d539d905fb31110fd968e4ac1e89a2e64..669dea1a4dffee9021cbbbfedcba0121c7013ba4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -262,7 +262,7 @@ endif
 # DEBUG_NO_POOLS, DEBUG_FAIL_ALLOC, DEBUG_STRICT_ACTION=[0-3], DEBUG_HPACK,
 # DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF, DEBUG_THREAD, DEBUG_STRICT, DEBUG_DEV,
 # DEBUG_TASK, DEBUG_MEMORY_POOLS, DEBUG_POOL_TRACING, DEBUG_QPACK, DEBUG_LIST,
-# DEBUG_GLITCHES, DEBUG_STRESS.
+# DEBUG_GLITCHES, DEBUG_STRESS, DEBUG_UNIT.
 DEBUG =
 
 #### Trace options
index 9feed8a99d7afab933e6163445e318829d1eb398..3142185cd8c1f823de2ff67ac662281e254d70e3 100644 (file)
@@ -38,4 +38,8 @@ void post_mortem_add_component(const char *name, const char *version,
                               const char *toolchain, const char *toolchain_opts,
                               const char *build_settings, const char *path);
 
+#ifdef DEBUG_UNIT
+void list_unittests();
+#endif
+
 #endif /* _HAPROXY_DEBUG_H */
index 110171b053f4a6dbfbced28321dcc2147c097325..ad17112c0d560610101e9318c20c9ecc62b97c67 100644 (file)
@@ -61,4 +61,10 @@ struct per_thread_deinit_fct {
        void (*fct)();
 };
 
+struct unittest_fct {
+       struct list list;
+       const char *name;
+       int (*fct)(int argc, char **argv);
+};
+
 #endif /* _HAPROXY_INIT_T_H */
index 6e304756759bc133cc380bb4b6b1234ada514462..ca059e3760c3556b77ecc75be141b7ffcf2b5523 100644 (file)
@@ -32,6 +32,13 @@ void hap_register_per_thread_init(int (*fct)());
 void hap_register_per_thread_deinit(void (*fct)());
 void hap_register_per_thread_free(void (*fct)());
 
+
+#ifdef DEBUG_UNIT
+void hap_register_unittest(const char *name, int (*fct)());
+#else
+#define hap_register_unittest(a,b) ({})
+#endif
+
 /* simplified way to declare a pre-check callback in a file */
 #define REGISTER_PRE_CHECK(fct) \
        INITCALL1(STG_REGISTER, hap_register_pre_check, (fct))
index 147e73ea146a5345cdf440cc1fe43a85a13fedfc..d7ac90f478903d61225204462513199d4a5cb456 100644 (file)
@@ -2764,6 +2764,31 @@ static int feed_post_mortem_late()
 REGISTER_PER_THREAD_INIT(feed_post_mortem_late);
 #endif
 
+#ifdef DEBUG_UNIT
+
+extern struct list unittest_list;
+
+void list_unittests()
+{
+       struct unittest_fct *unit;
+       int found = 0;
+
+       fprintf(stdout, "Unit tests list :");
+
+       list_for_each_entry(unit, &unittest_list, list) {
+               fprintf(stdout, " %s", unit->name);
+               found = 1;
+       }
+
+       if (!found)
+               fprintf(stdout, " none");
+
+       fprintf(stdout, "\n");
+}
+
+#endif
+
+
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
 #if !defined(USE_OBSOLETE_LINKER)
index 31d20d55fb24b0996d0c5d7d98ab8d52b0c53abd..90b89f9b851e4120a44033d251ebf11a2ab2d78d 100644 (file)
@@ -250,6 +250,11 @@ char **old_argv = NULL; /* previous argv but cleaned up */
 
 struct list proc_list = LIST_HEAD_INIT(proc_list);
 
+#ifdef DEBUG_UNIT
+struct list unittest_list = LIST_HEAD_INIT(unittest_list);
+static int unittest_argc = -1;
+#endif
+
 int master = 0; /* 1 if in master, 0 if in child */
 
 /* per-boot randomness */
@@ -396,6 +401,22 @@ void hap_register_feature(const char *name)
        must_free = 1;
 }
 
+#ifdef DEBUG_UNIT
+/* register a function that could be registered in "-U" argument */
+void hap_register_unittest(const char *name, int (*fct)(int argc, char **argv))
+{
+       struct unittest_fct *unit;
+
+       if (!name || !fct)
+               return;
+
+       unit = calloc(1, sizeof(*unit));
+       unit->fct = fct;
+       unit->name = name;
+       LIST_APPEND(&unittest_list, &unit->list);
+}
+#endif
+
 #define VERSION_MAX_ELTS  7
 
 /* This function splits an haproxy version string into an array of integers.
@@ -602,6 +623,10 @@ static void display_build_opts()
 
        putchar('\n');
 
+#ifdef DEBUG_UNIT
+       list_unittests();
+       putchar('\n');
+#endif
        list_pollers(stdout);
        putchar('\n');
        list_mux_proto(stdout);
@@ -1629,6 +1654,19 @@ static void init_args(int argc, char **argv)
                                        nb_oldpids++;
                                }
                        }
+#ifdef DEBUG_UNIT
+                       else if (*flag == 'U')  {
+                               if (argc <= 1) {
+                                       ha_alert("-U takes a least a unittest name in argument, and must be the last option\n");
+                                       usage(progname);
+                               }
+                               /* this is the last option, we keep the option position */
+                               argv++;
+                               argc--;
+                               unittest_argc = argc;
+                               break;
+                       }
+#endif
                        else if (flag[0] == '-' && flag[1] == 0) { /* "--" */
                                /* now that's a cfgfile list */
                                argv++; argc--;
@@ -1912,7 +1950,6 @@ static void bind_listeners()
                select(0, NULL, NULL, NULL, &w);
                retry--;
        }
-
        /* Note: protocol_bind_all() sends an alert when it fails. */
        if ((err & ~ERR_WARN) != ERR_NONE) {
                ha_alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", progname);
@@ -3133,6 +3170,26 @@ int main(int argc, char **argv)
         */
        step_init_1();
 
+       /* call a function to be called with -U in order to make some tests */
+#ifdef DEBUG_UNIT
+       if (unittest_argc > -1) {
+               struct unittest_fct *unit;
+               int ret = 1;
+               int argc_start = argc - unittest_argc;
+
+               list_for_each_entry(unit, &unittest_list, list) {
+
+                       if (strcmp(unit->name, argv[argc_start]) == 0) {
+                               ret = unit->fct(unittest_argc, argv + argc_start);
+                               break;
+                       }
+               }
+
+               exit(ret);
+       }
+#endif
+
+
        /* deserialize processes list, if we do reload in master-worker mode */
        if ((getenv("HAPROXY_MWORKER_REEXEC") != NULL)) {
                if (mworker_env_to_proc_list() < 0) {