git: 4fa275a5f357 - main - queue(3): Add simple tests for some macros for all list/tailq types

From: Olivier Certner <olce_at_FreeBSD.org>
Date: Tue, 29 Apr 2025 07:47:24 UTC
The branch main has been updated by olce:

URL: https://cgit.FreeBSD.org/src/commit/?id=4fa275a5f35742a5d662db7106d20819329dc8f2

commit 4fa275a5f35742a5d662db7106d20819329dc8f2
Author:     Olivier Certner <olce@FreeBSD.org>
AuthorDate: 2025-04-08 11:42:27 +0000
Commit:     Olivier Certner <olce@FreeBSD.org>
CommitDate: 2025-04-29 07:47:02 +0000

    queue(3): Add simple tests for some macros for all list/tailq types
    
    These essentially test building a list/tailq of 100 elements with
    _INSERT_HEAD(), iterating over them with _FOREACH(), splitting it with
    _SPLIT_AFTER() and concatenating back the results with _CONCAT(),
    checking that the lists/tailqs at each step have the right number of
    elements and at the expected positions.
    
    Reviewed by:    markj
    MFC after:      2 days
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D49975
---
 tests/sys/sys/Makefile     |   1 +
 tests/sys/sys/queue_test.c | 235 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 236 insertions(+)

diff --git a/tests/sys/sys/Makefile b/tests/sys/sys/Makefile
index 40060911856f..a1b4e3234e1c 100644
--- a/tests/sys/sys/Makefile
+++ b/tests/sys/sys/Makefile
@@ -7,6 +7,7 @@ ATF_TESTS_C=	arb_test \
 		bitstring_test \
 		buf_ring_test \
 		qmath_test \
+		queue_test \
 		rb_test \
 		splay_test \
 		time_test
diff --git a/tests/sys/sys/queue_test.c b/tests/sys/sys/queue_test.c
new file mode 100644
index 000000000000..75974ad8d792
--- /dev/null
+++ b/tests/sys/sys/queue_test.c
@@ -0,0 +1,235 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * This software was developed by Olivier Certner <olce@FreeBSD.org> at
+ * Kumacom SARL under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/types.h>
+#define QUEUE_MACRO_DEBUG_ASSERTIONS
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+/*
+ * General utilities.
+ */
+#define DIAG(fmt, ...)	do {						\
+	fprintf(stderr, "%s(): " fmt "\n", __func__, ##__VA_ARGS__);	\
+} while (0)
+
+/*
+ * Common definitions and utilities.
+ *
+ * 'type' should be tailq, stailq, list or slist.  'TYPE' is 'type' in
+ * uppercase.
+ */
+
+#define QUEUE_TESTS_COMMON(type, TYPE)					\
+/*									\
+ * Definitions and utilities.						\
+ */									\
+									\
+struct type ## _id_elem {						\
+	TYPE ## _ENTRY(type ## _id_elem) ie_entry;			\
+	u_int ie_id;							\
+};									\
+									\
+TYPE ## _HEAD(type ## _ids, type ## _id_elem);				\
+									\
+static void								\
+type ## _check(const struct type ## _ids *const type,			\
+    const u_int nb, const u_int id_shift);				\
+									\
+/*									\
+ * Creates a tailq/list with 'nb' elements with contiguous IDs		\
+ * in ascending order starting at 'id_shift'.				\
+ */									\
+static struct type ## _ids *						\
+type ## _create(const u_int nb, const u_int id_shift)			\
+{									\
+	struct type ## _ids *const type =				\
+	    malloc(sizeof(*type));					\
+									\
+	ATF_REQUIRE_MSG(type != NULL,					\
+	    "Cannot malloc " #type " head");				\
+									\
+	TYPE ## _INIT(type);						\
+	for (u_int i = 0; i < nb; ++i) {				\
+		struct type ## _id_elem *const e =			\
+		    malloc(sizeof(*e));					\
+									\
+		ATF_REQUIRE_MSG(e != NULL,				\
+		    "Cannot malloc " #type " element %u", i);		\
+		e->ie_id = nb - 1 - i + id_shift;			\
+		TYPE ## _INSERT_HEAD(type, e, ie_entry);		\
+	}								\
+									\
+	DIAG("Created " #type " %p with %u elements",			\
+	    type, nb);							\
+	type ## _check(type, nb, id_shift);				\
+	return (type);							\
+}									\
+									\
+/* Performs no check. */						\
+static void								\
+type ## _destroy(struct type ## _ids *const type)			\
+{									\
+	struct type ## _id_elem *e, *tmp_e;				\
+									\
+	DIAG("Destroying " #type" %p", type);				\
+	TYPE ## _FOREACH_SAFE(e, type, ie_entry,			\
+	    tmp_e) {							\
+		free(e);						\
+	}								\
+	free(type);							\
+}									\
+									\
+									\
+/* Checks that some tailq/list is as produced by *_create(). */		\
+static void								\
+type ## _check(const struct type ## _ids *const type,			\
+    const u_int nb, const u_int id_shift)				\
+{									\
+	struct type ## _id_elem *e;					\
+	u_int i = 0;							\
+									\
+	TYPE ## _FOREACH(e, type, ie_entry) {				\
+		ATF_REQUIRE_MSG(i + 1 <= nb,				\
+		    #type " %p has more than %u elements",		\
+		    type, nb);						\
+		ATF_REQUIRE_MSG(e->ie_id == i + id_shift,		\
+		    #type " %p element %p: Found ID %u, "		\
+		    "expected %u",					\
+		    type, e, e->ie_id, i + id_shift);			\
+		++i;							\
+	}								\
+	ATF_REQUIRE_MSG(i == nb,					\
+	    #type " %p has only %u elements, expected %u",		\
+	    type, i, nb);						\
+}									\
+									\
+/* Returns NULL if not enough elements. */				\
+static struct type ## _id_elem *					\
+type ## _nth(const struct type ## _ids *const type,			\
+    const u_int idx)							\
+{									\
+	struct type ## _id_elem *e;					\
+	u_int i = 0;							\
+									\
+	TYPE ## _FOREACH(e, type, ie_entry) {				\
+		if (i == idx) {						\
+			DIAG(#type " %p has element %p "		\
+			    "(ID %u) at index %u",			\
+			    type, e, e->ie_id, idx);			\
+			return (e);					\
+		}							\
+		++i;							\
+	}								\
+	DIAG(#type " %p: Only %u elements, no index %u",		\
+	    type, i, idx);						\
+	return (NULL);							\
+}									\
+									\
+/*									\
+ * Tests.								\
+ */									\
+									\
+ATF_TC(type ## _split_after_and_concat);				\
+ATF_TC_HEAD(type ## _split_after_and_concat, tc)			\
+{									\
+	atf_tc_set_md_var(tc, "descr",					\
+	    "Test " #TYPE "_SPLIT_AFTER() followed by "			\
+	    #TYPE "_CONCAT()");						\
+}									\
+ATF_TC_BODY(type ## _split_after_and_concat, tc)			\
+{									\
+	struct type ## _ids *const type =				\
+	    type ## _create(100, 0);					\
+	struct type ## _ids rest;					\
+	struct type ## _id_elem *e;					\
+									\
+	e = type ## _nth(type, 49);					\
+	TYPE ## _SPLIT_AFTER(type, e, &rest, ie_entry);			\
+	type ## _check(type, 50, 0);					\
+	type ## _check(&rest, 50, 50);					\
+	QUEUE_TESTS_ ## TYPE ## _CONCAT(type, &rest);			\
+	ATF_REQUIRE_MSG(TYPE ## _EMPTY(&rest),				\
+	    "'rest' not empty after concat");				\
+	type ## _check(type, 100, 0);					\
+	type ## _destroy(type);						\
+}
+
+/*
+ * Paper over the *_CONCAT() signature differences.
+ */
+
+#define QUEUE_TESTS_TAILQ_CONCAT(first, second)				\
+	TAILQ_CONCAT(first, second, ie_entry)
+
+#define QUEUE_TESTS_LIST_CONCAT(first, second)				\
+	LIST_CONCAT(first, second, list_id_elem, ie_entry)
+
+#define QUEUE_TESTS_STAILQ_CONCAT(first, second)			\
+	STAILQ_CONCAT(first, second)
+
+#define QUEUE_TESTS_SLIST_CONCAT(first, second)				\
+	SLIST_CONCAT(first, second, slist_id_elem, ie_entry)
+
+/*
+ * ATF test registration.
+ */
+
+#define QUEUE_TESTS_REGISTRATION(tp, type)				\
+	ATF_TP_ADD_TC(tp, type ## _split_after_and_concat)
+
+/*
+ * Macros defining print functions.
+ *
+ * They are currently not used in the tests above, but are useful for debugging.
+ */
+
+#define QUEUE_TESTS_TQ_PRINT(type, hfp)					\
+	static void							\
+	type ## _print(const struct type ## _ids *const type)		\
+	{								\
+		printf(#type " %p: " __STRING(hfp ## _first)		\
+		    " = %p, " __STRING(hfp ## _last) " = %p\n",		\
+		    type, type->hfp ## _first, type->hfp ## _last);	\
+	}
+
+#define QUEUE_TESTS_L_PRINT(type, hfp)					\
+	static void							\
+	type ## _print(const struct type ## _ids *const type)		\
+	{								\
+		printf(#type " %p: " __STRING(hfp ## _first) " = %p\n",	\
+		    type, type->hfp ## _first);				\
+	}
+
+
+/*
+ * Meat.
+ */
+
+QUEUE_TESTS_COMMON(tailq, TAILQ);
+QUEUE_TESTS_COMMON(list, LIST);
+QUEUE_TESTS_COMMON(stailq, STAILQ);
+QUEUE_TESTS_COMMON(slist, SLIST);
+
+/*
+ * Main.
+ */
+ATF_TP_ADD_TCS(tp)
+{
+	QUEUE_TESTS_REGISTRATION(tp, tailq);
+	QUEUE_TESTS_REGISTRATION(tp, list);
+	QUEUE_TESTS_REGISTRATION(tp, stailq);
+	QUEUE_TESTS_REGISTRATION(tp, slist);
+
+	return (atf_no_error());
+}