Skip to content

Commit 789538c

Browse files
rmr167shuahkh
authored andcommitted
lib/hashtable_test.c: add test for the hashtable structure
Add a KUnit test for the kernel hashtable implementation in include/linux/hashtable.h. Note that this version does not yet test each of the rcu alternative versions of functions. Signed-off-by: Rae Moar <[email protected]> Reviewed-by: David Gow <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent 9ecc9cd commit 789538c

File tree

3 files changed

+331
-0
lines changed

3 files changed

+331
-0
lines changed

lib/Kconfig.debug

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,19 @@ config LIST_KUNIT_TEST
24972497

24982498
If unsure, say N.
24992499

2500+
config HASHTABLE_KUNIT_TEST
2501+
tristate "KUnit Test for Kernel Hashtable structures" if !KUNIT_ALL_TESTS
2502+
depends on KUNIT
2503+
default KUNIT_ALL_TESTS
2504+
help
2505+
This builds the hashtable KUnit test suite.
2506+
It tests the basic functionality of the API defined in
2507+
include/linux/hashtable.h. For more information on KUnit and
2508+
unit tests in general please refer to the KUnit documentation
2509+
in Documentation/dev-tools/kunit/.
2510+
2511+
If unsure, say N.
2512+
25002513
config LINEAR_RANGES_TEST
25012514
tristate "KUnit test for linear_ranges"
25022515
depends on KUNIT

lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ obj-$(CONFIG_PLDMFW) += pldmfw/
377377
CFLAGS_bitfield_kunit.o := $(DISABLE_STRUCTLEAK_PLUGIN)
378378
obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o
379379
obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
380+
obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o
380381
obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
381382
obj-$(CONFIG_BITS_TEST) += test_bits.o
382383
obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o

lib/hashtable_test.c

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* KUnit test for the Kernel Hashtable structures.
4+
*
5+
* Copyright (C) 2022, Google LLC.
6+
* Author: Rae Moar <[email protected]>
7+
*/
8+
#include <kunit/test.h>
9+
10+
#include <linux/hashtable.h>
11+
12+
struct hashtable_test_entry {
13+
int key;
14+
int data;
15+
struct hlist_node node;
16+
int visited;
17+
};
18+
19+
static void hashtable_test_hash_init(struct kunit *test)
20+
{
21+
/* Test the different ways of initialising a hashtable. */
22+
DEFINE_HASHTABLE(hash1, 2);
23+
DECLARE_HASHTABLE(hash2, 3);
24+
25+
/* When using DECLARE_HASHTABLE, must use hash_init to
26+
* initialize the hashtable.
27+
*/
28+
hash_init(hash2);
29+
30+
KUNIT_EXPECT_TRUE(test, hash_empty(hash1));
31+
KUNIT_EXPECT_TRUE(test, hash_empty(hash2));
32+
}
33+
34+
static void hashtable_test_hash_empty(struct kunit *test)
35+
{
36+
struct hashtable_test_entry a;
37+
DEFINE_HASHTABLE(hash, 1);
38+
39+
KUNIT_EXPECT_TRUE(test, hash_empty(hash));
40+
41+
a.key = 1;
42+
a.data = 13;
43+
hash_add(hash, &a.node, a.key);
44+
45+
/* Hashtable should no longer be empty. */
46+
KUNIT_EXPECT_FALSE(test, hash_empty(hash));
47+
}
48+
49+
static void hashtable_test_hash_hashed(struct kunit *test)
50+
{
51+
struct hashtable_test_entry a, b;
52+
DEFINE_HASHTABLE(hash, 4);
53+
54+
a.key = 1;
55+
a.data = 13;
56+
hash_add(hash, &a.node, a.key);
57+
b.key = 1;
58+
b.data = 2;
59+
hash_add(hash, &b.node, b.key);
60+
61+
KUNIT_EXPECT_TRUE(test, hash_hashed(&a.node));
62+
KUNIT_EXPECT_TRUE(test, hash_hashed(&b.node));
63+
}
64+
65+
static void hashtable_test_hash_add(struct kunit *test)
66+
{
67+
struct hashtable_test_entry a, b, *x;
68+
int bkt;
69+
DEFINE_HASHTABLE(hash, 3);
70+
71+
a.key = 1;
72+
a.data = 13;
73+
a.visited = 0;
74+
hash_add(hash, &a.node, a.key);
75+
b.key = 2;
76+
b.data = 10;
77+
b.visited = 0;
78+
hash_add(hash, &b.node, b.key);
79+
80+
hash_for_each(hash, bkt, x, node) {
81+
x->visited++;
82+
if (x->key == a.key)
83+
KUNIT_EXPECT_EQ(test, x->data, 13);
84+
else if (x->key == b.key)
85+
KUNIT_EXPECT_EQ(test, x->data, 10);
86+
else
87+
KUNIT_FAIL(test, "Unexpected key in hashtable.");
88+
}
89+
90+
/* Both entries should have been visited exactly once. */
91+
KUNIT_EXPECT_EQ(test, a.visited, 1);
92+
KUNIT_EXPECT_EQ(test, b.visited, 1);
93+
}
94+
95+
static void hashtable_test_hash_del(struct kunit *test)
96+
{
97+
struct hashtable_test_entry a, b, *x;
98+
DEFINE_HASHTABLE(hash, 6);
99+
100+
a.key = 1;
101+
a.data = 13;
102+
hash_add(hash, &a.node, a.key);
103+
b.key = 2;
104+
b.data = 10;
105+
b.visited = 0;
106+
hash_add(hash, &b.node, b.key);
107+
108+
hash_del(&b.node);
109+
hash_for_each_possible(hash, x, node, b.key) {
110+
x->visited++;
111+
KUNIT_EXPECT_NE(test, x->key, b.key);
112+
}
113+
114+
/* The deleted entry should not have been visited. */
115+
KUNIT_EXPECT_EQ(test, b.visited, 0);
116+
117+
hash_del(&a.node);
118+
119+
/* The hashtable should be empty. */
120+
KUNIT_EXPECT_TRUE(test, hash_empty(hash));
121+
}
122+
123+
static void hashtable_test_hash_for_each(struct kunit *test)
124+
{
125+
struct hashtable_test_entry entries[3];
126+
struct hashtable_test_entry *x;
127+
int bkt, i, j, count;
128+
DEFINE_HASHTABLE(hash, 3);
129+
130+
/* Add three entries to the hashtable. */
131+
for (i = 0; i < 3; i++) {
132+
entries[i].key = i;
133+
entries[i].data = i + 10;
134+
entries[i].visited = 0;
135+
hash_add(hash, &entries[i].node, entries[i].key);
136+
}
137+
138+
count = 0;
139+
hash_for_each(hash, bkt, x, node) {
140+
x->visited += 1;
141+
KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable.");
142+
KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable.");
143+
count++;
144+
}
145+
146+
/* Should have visited each entry exactly once. */
147+
KUNIT_EXPECT_EQ(test, count, 3);
148+
for (j = 0; j < 3; j++)
149+
KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
150+
}
151+
152+
static void hashtable_test_hash_for_each_safe(struct kunit *test)
153+
{
154+
struct hashtable_test_entry entries[3];
155+
struct hashtable_test_entry *x;
156+
struct hlist_node *tmp;
157+
int bkt, i, j, count;
158+
DEFINE_HASHTABLE(hash, 3);
159+
160+
/* Add three entries to the hashtable. */
161+
for (i = 0; i < 3; i++) {
162+
entries[i].key = i;
163+
entries[i].data = i + 10;
164+
entries[i].visited = 0;
165+
hash_add(hash, &entries[i].node, entries[i].key);
166+
}
167+
168+
count = 0;
169+
hash_for_each_safe(hash, bkt, tmp, x, node) {
170+
x->visited += 1;
171+
KUNIT_ASSERT_GE_MSG(test, x->key, 0, "Unexpected key in hashtable.");
172+
KUNIT_ASSERT_LT_MSG(test, x->key, 3, "Unexpected key in hashtable.");
173+
count++;
174+
175+
/* Delete entry during loop. */
176+
hash_del(&x->node);
177+
}
178+
179+
/* Should have visited each entry exactly once. */
180+
KUNIT_EXPECT_EQ(test, count, 3);
181+
for (j = 0; j < 3; j++)
182+
KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
183+
}
184+
185+
static void hashtable_test_hash_for_each_possible(struct kunit *test)
186+
{
187+
struct hashtable_test_entry entries[4];
188+
struct hashtable_test_entry *x, *y;
189+
int buckets[2];
190+
int bkt, i, j, count;
191+
DEFINE_HASHTABLE(hash, 5);
192+
193+
/* Add three entries with key = 0 to the hashtable. */
194+
for (i = 0; i < 3; i++) {
195+
entries[i].key = 0;
196+
entries[i].data = i;
197+
entries[i].visited = 0;
198+
hash_add(hash, &entries[i].node, entries[i].key);
199+
}
200+
201+
/* Add an entry with key = 1. */
202+
entries[3].key = 1;
203+
entries[3].data = 3;
204+
entries[3].visited = 0;
205+
hash_add(hash, &entries[3].node, entries[3].key);
206+
207+
count = 0;
208+
hash_for_each_possible(hash, x, node, 0) {
209+
x->visited += 1;
210+
KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable.");
211+
KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable.");
212+
count++;
213+
}
214+
215+
/* Should have visited each entry with key = 0 exactly once. */
216+
for (j = 0; j < 3; j++)
217+
KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
218+
219+
/* Save the buckets for the different keys. */
220+
hash_for_each(hash, bkt, y, node) {
221+
KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable.");
222+
KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable.");
223+
buckets[y->key] = bkt;
224+
}
225+
226+
/* If entry with key = 1 is in the same bucket as the entries with
227+
* key = 0, check it was visited. Otherwise ensure that only three
228+
* entries were visited.
229+
*/
230+
if (buckets[0] == buckets[1]) {
231+
KUNIT_EXPECT_EQ(test, count, 4);
232+
KUNIT_EXPECT_EQ(test, entries[3].visited, 1);
233+
} else {
234+
KUNIT_EXPECT_EQ(test, count, 3);
235+
KUNIT_EXPECT_EQ(test, entries[3].visited, 0);
236+
}
237+
}
238+
239+
static void hashtable_test_hash_for_each_possible_safe(struct kunit *test)
240+
{
241+
struct hashtable_test_entry entries[4];
242+
struct hashtable_test_entry *x, *y;
243+
struct hlist_node *tmp;
244+
int buckets[2];
245+
int bkt, i, j, count;
246+
DEFINE_HASHTABLE(hash, 5);
247+
248+
/* Add three entries with key = 0 to the hashtable. */
249+
for (i = 0; i < 3; i++) {
250+
entries[i].key = 0;
251+
entries[i].data = i;
252+
entries[i].visited = 0;
253+
hash_add(hash, &entries[i].node, entries[i].key);
254+
}
255+
256+
/* Add an entry with key = 1. */
257+
entries[3].key = 1;
258+
entries[3].data = 3;
259+
entries[3].visited = 0;
260+
hash_add(hash, &entries[3].node, entries[3].key);
261+
262+
count = 0;
263+
hash_for_each_possible_safe(hash, x, tmp, node, 0) {
264+
x->visited += 1;
265+
KUNIT_ASSERT_GE_MSG(test, x->data, 0, "Unexpected data in hashtable.");
266+
KUNIT_ASSERT_LT_MSG(test, x->data, 4, "Unexpected data in hashtable.");
267+
count++;
268+
269+
/* Delete entry during loop. */
270+
hash_del(&x->node);
271+
}
272+
273+
/* Should have visited each entry with key = 0 exactly once. */
274+
for (j = 0; j < 3; j++)
275+
KUNIT_EXPECT_EQ(test, entries[j].visited, 1);
276+
277+
/* Save the buckets for the different keys. */
278+
hash_for_each(hash, bkt, y, node) {
279+
KUNIT_ASSERT_GE_MSG(test, y->key, 0, "Unexpected key in hashtable.");
280+
KUNIT_ASSERT_LE_MSG(test, y->key, 1, "Unexpected key in hashtable.");
281+
buckets[y->key] = bkt;
282+
}
283+
284+
/* If entry with key = 1 is in the same bucket as the entries with
285+
* key = 0, check it was visited. Otherwise ensure that only three
286+
* entries were visited.
287+
*/
288+
if (buckets[0] == buckets[1]) {
289+
KUNIT_EXPECT_EQ(test, count, 4);
290+
KUNIT_EXPECT_EQ(test, entries[3].visited, 1);
291+
} else {
292+
KUNIT_EXPECT_EQ(test, count, 3);
293+
KUNIT_EXPECT_EQ(test, entries[3].visited, 0);
294+
}
295+
}
296+
297+
static struct kunit_case hashtable_test_cases[] = {
298+
KUNIT_CASE(hashtable_test_hash_init),
299+
KUNIT_CASE(hashtable_test_hash_empty),
300+
KUNIT_CASE(hashtable_test_hash_hashed),
301+
KUNIT_CASE(hashtable_test_hash_add),
302+
KUNIT_CASE(hashtable_test_hash_del),
303+
KUNIT_CASE(hashtable_test_hash_for_each),
304+
KUNIT_CASE(hashtable_test_hash_for_each_safe),
305+
KUNIT_CASE(hashtable_test_hash_for_each_possible),
306+
KUNIT_CASE(hashtable_test_hash_for_each_possible_safe),
307+
{},
308+
};
309+
310+
static struct kunit_suite hashtable_test_module = {
311+
.name = "hashtable",
312+
.test_cases = hashtable_test_cases,
313+
};
314+
315+
kunit_test_suites(&hashtable_test_module);
316+
317+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)