From: Artur Alves <arturacb@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>,
linux-kernel@vger.kernel.org,
Brendan Higgins <brendan.higgins@linux.dev>,
David Gow <davidgow@google.com>, Rae Moar <rmoar@google.com>,
linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com
Cc: n@nfraprado.net, andrealmeid@riseup.net, vinicius@nukelet.com,
diego.daniel.professional@gmail.com
Subject: [PATCH v3 1/1] lib/llist_kunit.c: add KUnit tests for llist
Date: Mon, 16 Sep 2024 21:51:16 -0300 [thread overview]
Message-ID: <20240917005116.304090-2-arturacb@gmail.com> (raw)
In-Reply-To: <20240917005116.304090-1-arturacb@gmail.com>
Add KUnit tests for the llist data structure. They test the vast
majority of methods and macros defined in include/linux/llist.h.
These are inspired by the existing tests for the 'list' doubly
linked in lib/list-test.c. Each test case (llist_test_x) tests
the behaviour of the llist function/macro 'x'.
Signed-off-by: Artur Alves <arturacb@gmail.com>
---
lib/Kconfig.debug | 11 ++
lib/tests/Makefile | 1 +
lib/tests/llist_kunit.c | 358 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 370 insertions(+)
create mode 100644 lib/tests/llist_kunit.c
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index b5696659f027..f6bd98f8ce2b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2813,6 +2813,17 @@ config USERCOPY_KUNIT_TEST
on the copy_to/from_user infrastructure, making sure basic
user/kernel boundary testing is working.
+config LLIST_KUNIT_TEST
+ tristate "KUnit tests for lib/llist" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This option builds the llist (lock-less list) KUnit test suite.
+ It tests the API and basic functionality of the macros and
+ functions defined in <linux/llist.h>.
+
+ If unsure, say N.
+
config TEST_UDELAY
tristate "udelay test driver"
help
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index c6a14cc8663e..8d7c40a73110 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -34,4 +34,5 @@ CFLAGS_stackinit_kunit.o += $(call cc-disable-warning, switch-unreachable)
obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o
obj-$(CONFIG_STRING_KUNIT_TEST) += string_kunit.o
obj-$(CONFIG_STRING_HELPERS_KUNIT_TEST) += string_helpers_kunit.o
+obj-$(CONFIG_LLIST_KUNIT_TEST) += llist_kunit.o
diff --git a/lib/tests/llist_kunit.c b/lib/tests/llist_kunit.c
new file mode 100644
index 000000000000..817bb2948697
--- /dev/null
+++ b/lib/tests/llist_kunit.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for the Kernel lock-less linked-list structure.
+ *
+ * Author: Artur Alves <arturacb@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/llist.h>
+
+#define ENTRIES_SIZE 3
+
+struct llist_test_struct {
+ int data;
+ struct llist_node node;
+};
+
+static void llist_test_init_llist(struct kunit *test)
+{
+ /* test if the llist is correctly initialized */
+ struct llist_head llist1 = LLIST_HEAD_INIT(llist1);
+ LLIST_HEAD(llist2);
+ struct llist_head llist3, *llist4, *llist5;
+
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist1));
+
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist2));
+
+ init_llist_head(&llist3);
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist3));
+
+ llist4 = kzalloc(sizeof(*llist4), GFP_KERNEL | __GFP_NOFAIL);
+ init_llist_head(llist4);
+ KUNIT_EXPECT_TRUE(test, llist_empty(llist4));
+ kfree(llist4);
+
+ llist5 = kmalloc(sizeof(*llist5), GFP_KERNEL | __GFP_NOFAIL);
+ memset(llist5, 0xFF, sizeof(*llist5));
+ init_llist_head(llist5);
+ KUNIT_EXPECT_TRUE(test, llist_empty(llist5));
+ kfree(llist5);
+}
+
+static void llist_test_init_llist_node(struct kunit *test)
+{
+ struct llist_node a;
+
+ init_llist_node(&a);
+
+ KUNIT_EXPECT_PTR_EQ(test, a.next, &a);
+}
+
+static void llist_test_llist_entry(struct kunit *test)
+{
+ struct llist_test_struct test_struct, *aux;
+ struct llist_node *llist = &test_struct.node;
+
+ aux = llist_entry(llist, struct llist_test_struct, node);
+ KUNIT_EXPECT_PTR_EQ(test, &test_struct, aux);
+}
+
+static void llist_test_add(struct kunit *test)
+{
+ struct llist_node a, b;
+ LLIST_HEAD(llist);
+
+ init_llist_node(&a);
+ init_llist_node(&b);
+
+ /* The first assertion must be true, given that llist is empty */
+ KUNIT_EXPECT_TRUE(test, llist_add(&a, &llist));
+ KUNIT_EXPECT_FALSE(test, llist_add(&b, &llist));
+
+ /* Should be [List] -> b -> a */
+ KUNIT_EXPECT_PTR_EQ(test, llist.first, &b);
+ KUNIT_EXPECT_PTR_EQ(test, b.next, &a);
+}
+
+static void llist_test_add_batch(struct kunit *test)
+{
+ struct llist_node a, b, c;
+ LLIST_HEAD(llist);
+ LLIST_HEAD(llist2);
+
+ init_llist_node(&a);
+ init_llist_node(&b);
+ init_llist_node(&c);
+
+ llist_add(&a, &llist2);
+ llist_add(&b, &llist2);
+ llist_add(&c, &llist2);
+
+ /* This assertion must be true, given that llist is empty */
+ KUNIT_EXPECT_TRUE(test, llist_add_batch(&c, &a, &llist));
+
+ /* should be [List] -> c -> b -> a */
+ KUNIT_EXPECT_PTR_EQ(test, llist.first, &c);
+ KUNIT_EXPECT_PTR_EQ(test, c.next, &b);
+ KUNIT_EXPECT_PTR_EQ(test, b.next, &a);
+}
+
+static void llist_test_llist_next(struct kunit *test)
+{
+ struct llist_node a, b;
+ LLIST_HEAD(llist);
+
+ init_llist_node(&a);
+ init_llist_node(&b);
+
+ llist_add(&a, &llist);
+ llist_add(&b, &llist);
+
+ /* should be [List] -> b -> a */
+ KUNIT_EXPECT_PTR_EQ(test, llist_next(&b), &a);
+ KUNIT_EXPECT_NULL(test, llist_next(&a));
+}
+
+static void llist_test_empty_llist(struct kunit *test)
+{
+ struct llist_head llist = LLIST_HEAD_INIT(llist);
+ struct llist_node a;
+
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist));
+
+ llist_add(&a, &llist);
+
+ KUNIT_EXPECT_FALSE(test, llist_empty(&llist));
+}
+
+static void llist_test_llist_on_list(struct kunit *test)
+{
+ struct llist_node a, b;
+ LLIST_HEAD(llist);
+
+ init_llist_node(&a);
+ init_llist_node(&b);
+
+ llist_add(&a, &llist);
+
+ /* should be [List] -> a */
+ KUNIT_EXPECT_TRUE(test, llist_on_list(&a));
+ KUNIT_EXPECT_FALSE(test, llist_on_list(&b));
+}
+
+static void llist_test_del_first(struct kunit *test)
+{
+ struct llist_node a, b, *c;
+ LLIST_HEAD(llist);
+
+ llist_add(&a, &llist);
+ llist_add(&b, &llist);
+
+ /* before: [List] -> b -> a */
+ c = llist_del_first(&llist);
+
+ /* should be [List] -> a */
+ KUNIT_EXPECT_PTR_EQ(test, llist.first, &a);
+
+ /* del must return a pointer to llist_node b
+ * the returned pointer must be marked on list
+ */
+ KUNIT_EXPECT_PTR_EQ(test, c, &b);
+ KUNIT_EXPECT_TRUE(test, llist_on_list(c));
+}
+
+static void llist_test_del_first_init(struct kunit *test)
+{
+ struct llist_node a, *b;
+ LLIST_HEAD(llist);
+
+ llist_add(&a, &llist);
+
+ b = llist_del_first_init(&llist);
+
+ /* should be [List] */
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist));
+
+ /* the returned pointer must be marked out of the list */
+ KUNIT_EXPECT_FALSE(test, llist_on_list(b));
+}
+
+static void llist_test_del_first_this(struct kunit *test)
+{
+ struct llist_node a, b;
+ LLIST_HEAD(llist);
+
+ llist_add(&a, &llist);
+ llist_add(&b, &llist);
+
+ llist_del_first_this(&llist, &a);
+
+ /* before: [List] -> b -> a */
+
+ // should remove only if is the first node in the llist
+ KUNIT_EXPECT_FALSE(test, llist_del_first_this(&llist, &a));
+
+ KUNIT_EXPECT_TRUE(test, llist_del_first_this(&llist, &b));
+
+ /* should be [List] -> a */
+ KUNIT_EXPECT_PTR_EQ(test, llist.first, &a);
+}
+
+static void llist_test_del_all(struct kunit *test)
+{
+ struct llist_node a, b;
+ LLIST_HEAD(llist);
+ LLIST_HEAD(empty_llist);
+
+ llist_add(&a, &llist);
+ llist_add(&b, &llist);
+
+ /* deleting from a empty llist should return NULL */
+ KUNIT_EXPECT_NULL(test, llist_del_all(&empty_llist));
+
+ llist_del_all(&llist);
+
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist));
+}
+
+static void llist_test_for_each(struct kunit *test)
+{
+ struct llist_node entries[ENTRIES_SIZE] = { 0 };
+ struct llist_node *pos, *deleted_nodes;
+ LLIST_HEAD(llist);
+ int i = 0, j = 0;
+
+ for (int i = ENTRIES_SIZE - 1; i >= 0; i--)
+ llist_add(&entries[i], &llist);
+
+ /* before [List] -> entries[0] -> ... -> entries[ENTRIES_SIZE - 1] */
+ llist_for_each(pos, llist.first) {
+ KUNIT_EXPECT_PTR_EQ(test, pos, &entries[i++]);
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, i);
+
+ /* traversing deleted nodes */
+ deleted_nodes = llist_del_all(&llist);
+
+ llist_for_each(pos, deleted_nodes) {
+ KUNIT_EXPECT_PTR_EQ(test, pos, &entries[j++]);
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, j);
+}
+
+static void llist_test_safe_for_each(struct kunit *test)
+{
+ struct llist_node entries[ENTRIES_SIZE];
+ struct llist_node *pos, *n;
+ LLIST_HEAD(llist);
+ int i = 0;
+
+ for (int i = ENTRIES_SIZE - 1; i >= 0; i--)
+ llist_add(&entries[i], &llist);
+
+ llist_for_each_safe(pos, n, llist.first) {
+ KUNIT_EXPECT_PTR_EQ(test, pos, &entries[i++]);
+ llist_del_first(&llist);
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, i);
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist));
+}
+
+static void llist_test_entry_for_each(struct kunit *test)
+{
+ struct llist_test_struct entries[ENTRIES_SIZE], *pos;
+ LLIST_HEAD(llist);
+ int i = 0;
+
+ for (int i = ENTRIES_SIZE - 1; i >= 0; --i) {
+ entries[i].data = i;
+ llist_add(&entries[i].node, &llist);
+ }
+
+ i = 0;
+
+ llist_for_each_entry(pos, llist.first, node) {
+ KUNIT_EXPECT_EQ(test, pos->data, i);
+ i++;
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, i);
+}
+
+static void llist_test_entry_safe_for_each(struct kunit *test)
+{
+ struct llist_test_struct entries[ENTRIES_SIZE], *pos, *n;
+ LLIST_HEAD(llist);
+ int i = 0;
+
+ for (int i = ENTRIES_SIZE - 1; i >= 0; --i) {
+ entries[i].data = i;
+ llist_add(&entries[i].node, &llist);
+ }
+
+ i = 0;
+
+ llist_for_each_entry_safe(pos, n, llist.first, node) {
+ KUNIT_EXPECT_EQ(test, pos->data, i++);
+ llist_del_first(&llist);
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, i);
+ KUNIT_EXPECT_TRUE(test, llist_empty(&llist));
+}
+
+static void llist_test_reverse_order(struct kunit *test)
+{
+ struct llist_node entries[ENTRIES_SIZE], *pos, *reversed_llist;
+ LLIST_HEAD(llist);
+ int i = 0;
+
+ for (int i = 0; i < ENTRIES_SIZE; i++)
+ llist_add(&entries[i], &llist);
+
+ /* before [List] -> entries[2] -> entries[1] -> entries[0] */
+ reversed_llist = llist_reverse_order(llist_del_first(&llist));
+
+ /* should be [List] -> entries[0] -> entries[1] -> entries[2] */
+ llist_for_each(pos, reversed_llist) {
+ KUNIT_EXPECT_PTR_EQ(test, pos, &entries[i++]);
+ }
+
+ KUNIT_EXPECT_EQ(test, ENTRIES_SIZE, i);
+}
+
+static struct kunit_case llist_test_cases[] = {
+ KUNIT_CASE(llist_test_init_llist),
+ KUNIT_CASE(llist_test_init_llist_node),
+ KUNIT_CASE(llist_test_llist_entry),
+ KUNIT_CASE(llist_test_add),
+ KUNIT_CASE(llist_test_add_batch),
+ KUNIT_CASE(llist_test_llist_next),
+ KUNIT_CASE(llist_test_empty_llist),
+ KUNIT_CASE(llist_test_llist_on_list),
+ KUNIT_CASE(llist_test_del_first),
+ KUNIT_CASE(llist_test_del_first_init),
+ KUNIT_CASE(llist_test_del_first_this),
+ KUNIT_CASE(llist_test_del_all),
+ KUNIT_CASE(llist_test_for_each),
+ KUNIT_CASE(llist_test_safe_for_each),
+ KUNIT_CASE(llist_test_entry_for_each),
+ KUNIT_CASE(llist_test_entry_safe_for_each),
+ KUNIT_CASE(llist_test_reverse_order),
+ {}
+};
+
+static struct kunit_suite llist_test_suite = {
+ .name = "llist",
+ .test_cases = llist_test_cases,
+};
+
+kunit_test_suite(llist_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for the llist data structure.");
--
2.46.0
next prev parent reply other threads:[~2024-09-17 0:51 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-17 0:51 [PATCH v3 0/1] Add KUnit tests for llist Artur Alves
2024-09-17 0:51 ` Artur Alves [this message]
2024-10-02 20:27 ` [PATCH v3 1/1] lib/llist_kunit.c: add " Rae Moar
2024-10-03 6:56 ` David Gow
2024-09-19 16:01 ` [PATCH v3 0/1] Add " Shuah Khan
2024-09-19 22:27 ` Artur Alves Cavalcante de Barros
2024-09-20 7:10 ` David Gow
2024-09-20 7:10 ` David Gow
2024-09-20 15:10 ` Shuah Khan
2024-09-21 3:07 ` Artur Alves Cavalcante de Barros
2024-09-21 2:49 ` Artur Alves Cavalcante de Barros
2024-09-23 15:48 ` Shuah Khan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240917005116.304090-2-arturacb@gmail.com \
--to=arturacb@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=andrealmeid@riseup.net \
--cc=brendan.higgins@linux.dev \
--cc=davidgow@google.com \
--cc=diego.daniel.professional@gmail.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=n@nfraprado.net \
--cc=rmoar@google.com \
--cc=vinicius@nukelet.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.