2023-01-26 01:54:49 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for the Kernel Hashtable structures .
*
* Copyright ( C ) 2022 , Google LLC .
* Author : Rae Moar < rmoar @ google . com >
*/
# include <kunit/test.h>
# include <linux/hashtable.h>
struct hashtable_test_entry {
int key ;
int data ;
struct hlist_node node ;
int visited ;
} ;
static void hashtable_test_hash_init ( struct kunit * test )
{
/* Test the different ways of initialising a hashtable. */
DEFINE_HASHTABLE ( hash1 , 2 ) ;
DECLARE_HASHTABLE ( hash2 , 3 ) ;
/* When using DECLARE_HASHTABLE, must use hash_init to
* initialize the hashtable .
*/
hash_init ( hash2 ) ;
KUNIT_EXPECT_TRUE ( test , hash_empty ( hash1 ) ) ;
KUNIT_EXPECT_TRUE ( test , hash_empty ( hash2 ) ) ;
}
static void hashtable_test_hash_empty ( struct kunit * test )
{
struct hashtable_test_entry a ;
DEFINE_HASHTABLE ( hash , 1 ) ;
KUNIT_EXPECT_TRUE ( test , hash_empty ( hash ) ) ;
a . key = 1 ;
a . data = 13 ;
hash_add ( hash , & a . node , a . key ) ;
/* Hashtable should no longer be empty. */
KUNIT_EXPECT_FALSE ( test , hash_empty ( hash ) ) ;
}
static void hashtable_test_hash_hashed ( struct kunit * test )
{
struct hashtable_test_entry a , b ;
DEFINE_HASHTABLE ( hash , 4 ) ;
a . key = 1 ;
a . data = 13 ;
hash_add ( hash , & a . node , a . key ) ;
b . key = 1 ;
b . data = 2 ;
hash_add ( hash , & b . node , b . key ) ;
KUNIT_EXPECT_TRUE ( test , hash_hashed ( & a . node ) ) ;
KUNIT_EXPECT_TRUE ( test , hash_hashed ( & b . node ) ) ;
}
static void hashtable_test_hash_add ( struct kunit * test )
{
struct hashtable_test_entry a , b , * x ;
int bkt ;
DEFINE_HASHTABLE ( hash , 3 ) ;
a . key = 1 ;
a . data = 13 ;
a . visited = 0 ;
hash_add ( hash , & a . node , a . key ) ;
b . key = 2 ;
b . data = 10 ;
b . visited = 0 ;
hash_add ( hash , & b . node , b . key ) ;
hash_for_each ( hash , bkt , x , node ) {
x - > visited + + ;
if ( x - > key = = a . key )
KUNIT_EXPECT_EQ ( test , x - > data , 13 ) ;
else if ( x - > key = = b . key )
KUNIT_EXPECT_EQ ( test , x - > data , 10 ) ;
else
KUNIT_FAIL ( test , " Unexpected key in hashtable. " ) ;
}
/* Both entries should have been visited exactly once. */
KUNIT_EXPECT_EQ ( test , a . visited , 1 ) ;
KUNIT_EXPECT_EQ ( test , b . visited , 1 ) ;
}
static void hashtable_test_hash_del ( struct kunit * test )
{
struct hashtable_test_entry a , b , * x ;
DEFINE_HASHTABLE ( hash , 6 ) ;
a . key = 1 ;
a . data = 13 ;
hash_add ( hash , & a . node , a . key ) ;
b . key = 2 ;
b . data = 10 ;
b . visited = 0 ;
hash_add ( hash , & b . node , b . key ) ;
hash_del ( & b . node ) ;
hash_for_each_possible ( hash , x , node , b . key ) {
x - > visited + + ;
KUNIT_EXPECT_NE ( test , x - > key , b . key ) ;
}
/* The deleted entry should not have been visited. */
KUNIT_EXPECT_EQ ( test , b . visited , 0 ) ;
hash_del ( & a . node ) ;
/* The hashtable should be empty. */
KUNIT_EXPECT_TRUE ( test , hash_empty ( hash ) ) ;
}
static void hashtable_test_hash_for_each ( struct kunit * test )
{
struct hashtable_test_entry entries [ 3 ] ;
struct hashtable_test_entry * x ;
int bkt , i , j , count ;
DEFINE_HASHTABLE ( hash , 3 ) ;
/* Add three entries to the hashtable. */
for ( i = 0 ; i < 3 ; i + + ) {
entries [ i ] . key = i ;
entries [ i ] . data = i + 10 ;
entries [ i ] . visited = 0 ;
hash_add ( hash , & entries [ i ] . node , entries [ i ] . key ) ;
}
count = 0 ;
hash_for_each ( hash , bkt , x , node ) {
x - > visited + = 1 ;
KUNIT_ASSERT_GE_MSG ( test , x - > key , 0 , " Unexpected key in hashtable. " ) ;
KUNIT_ASSERT_LT_MSG ( test , x - > key , 3 , " Unexpected key in hashtable. " ) ;
count + + ;
}
/* Should have visited each entry exactly once. */
KUNIT_EXPECT_EQ ( test , count , 3 ) ;
for ( j = 0 ; j < 3 ; j + + )
KUNIT_EXPECT_EQ ( test , entries [ j ] . visited , 1 ) ;
}
static void hashtable_test_hash_for_each_safe ( struct kunit * test )
{
struct hashtable_test_entry entries [ 3 ] ;
struct hashtable_test_entry * x ;
struct hlist_node * tmp ;
int bkt , i , j , count ;
DEFINE_HASHTABLE ( hash , 3 ) ;
/* Add three entries to the hashtable. */
for ( i = 0 ; i < 3 ; i + + ) {
entries [ i ] . key = i ;
entries [ i ] . data = i + 10 ;
entries [ i ] . visited = 0 ;
hash_add ( hash , & entries [ i ] . node , entries [ i ] . key ) ;
}
count = 0 ;
hash_for_each_safe ( hash , bkt , tmp , x , node ) {
x - > visited + = 1 ;
KUNIT_ASSERT_GE_MSG ( test , x - > key , 0 , " Unexpected key in hashtable. " ) ;
KUNIT_ASSERT_LT_MSG ( test , x - > key , 3 , " Unexpected key in hashtable. " ) ;
count + + ;
/* Delete entry during loop. */
hash_del ( & x - > node ) ;
}
/* Should have visited each entry exactly once. */
KUNIT_EXPECT_EQ ( test , count , 3 ) ;
for ( j = 0 ; j < 3 ; j + + )
KUNIT_EXPECT_EQ ( test , entries [ j ] . visited , 1 ) ;
}
static void hashtable_test_hash_for_each_possible ( struct kunit * test )
{
struct hashtable_test_entry entries [ 4 ] ;
struct hashtable_test_entry * x , * y ;
int buckets [ 2 ] ;
int bkt , i , j , count ;
DEFINE_HASHTABLE ( hash , 5 ) ;
/* Add three entries with key = 0 to the hashtable. */
for ( i = 0 ; i < 3 ; i + + ) {
entries [ i ] . key = 0 ;
entries [ i ] . data = i ;
entries [ i ] . visited = 0 ;
hash_add ( hash , & entries [ i ] . node , entries [ i ] . key ) ;
}
/* Add an entry with key = 1. */
entries [ 3 ] . key = 1 ;
entries [ 3 ] . data = 3 ;
entries [ 3 ] . visited = 0 ;
hash_add ( hash , & entries [ 3 ] . node , entries [ 3 ] . key ) ;
count = 0 ;
hash_for_each_possible ( hash , x , node , 0 ) {
x - > visited + = 1 ;
KUNIT_ASSERT_GE_MSG ( test , x - > data , 0 , " Unexpected data in hashtable. " ) ;
KUNIT_ASSERT_LT_MSG ( test , x - > data , 4 , " Unexpected data in hashtable. " ) ;
count + + ;
}
/* Should have visited each entry with key = 0 exactly once. */
for ( j = 0 ; j < 3 ; j + + )
KUNIT_EXPECT_EQ ( test , entries [ j ] . visited , 1 ) ;
/* Save the buckets for the different keys. */
hash_for_each ( hash , bkt , y , node ) {
KUNIT_ASSERT_GE_MSG ( test , y - > key , 0 , " Unexpected key in hashtable. " ) ;
KUNIT_ASSERT_LE_MSG ( test , y - > key , 1 , " Unexpected key in hashtable. " ) ;
buckets [ y - > key ] = bkt ;
}
/* If entry with key = 1 is in the same bucket as the entries with
* key = 0 , check it was visited . Otherwise ensure that only three
* entries were visited .
*/
if ( buckets [ 0 ] = = buckets [ 1 ] ) {
KUNIT_EXPECT_EQ ( test , count , 4 ) ;
KUNIT_EXPECT_EQ ( test , entries [ 3 ] . visited , 1 ) ;
} else {
KUNIT_EXPECT_EQ ( test , count , 3 ) ;
KUNIT_EXPECT_EQ ( test , entries [ 3 ] . visited , 0 ) ;
}
}
static void hashtable_test_hash_for_each_possible_safe ( struct kunit * test )
{
struct hashtable_test_entry entries [ 4 ] ;
struct hashtable_test_entry * x , * y ;
struct hlist_node * tmp ;
int buckets [ 2 ] ;
int bkt , i , j , count ;
DEFINE_HASHTABLE ( hash , 5 ) ;
/* Add three entries with key = 0 to the hashtable. */
for ( i = 0 ; i < 3 ; i + + ) {
entries [ i ] . key = 0 ;
entries [ i ] . data = i ;
entries [ i ] . visited = 0 ;
hash_add ( hash , & entries [ i ] . node , entries [ i ] . key ) ;
}
/* Add an entry with key = 1. */
entries [ 3 ] . key = 1 ;
entries [ 3 ] . data = 3 ;
entries [ 3 ] . visited = 0 ;
hash_add ( hash , & entries [ 3 ] . node , entries [ 3 ] . key ) ;
count = 0 ;
hash_for_each_possible_safe ( hash , x , tmp , node , 0 ) {
x - > visited + = 1 ;
KUNIT_ASSERT_GE_MSG ( test , x - > data , 0 , " Unexpected data in hashtable. " ) ;
KUNIT_ASSERT_LT_MSG ( test , x - > data , 4 , " Unexpected data in hashtable. " ) ;
count + + ;
/* Delete entry during loop. */
hash_del ( & x - > node ) ;
}
/* Should have visited each entry with key = 0 exactly once. */
for ( j = 0 ; j < 3 ; j + + )
KUNIT_EXPECT_EQ ( test , entries [ j ] . visited , 1 ) ;
/* Save the buckets for the different keys. */
hash_for_each ( hash , bkt , y , node ) {
KUNIT_ASSERT_GE_MSG ( test , y - > key , 0 , " Unexpected key in hashtable. " ) ;
KUNIT_ASSERT_LE_MSG ( test , y - > key , 1 , " Unexpected key in hashtable. " ) ;
buckets [ y - > key ] = bkt ;
}
/* If entry with key = 1 is in the same bucket as the entries with
* key = 0 , check it was visited . Otherwise ensure that only three
* entries were visited .
*/
if ( buckets [ 0 ] = = buckets [ 1 ] ) {
KUNIT_EXPECT_EQ ( test , count , 4 ) ;
KUNIT_EXPECT_EQ ( test , entries [ 3 ] . visited , 1 ) ;
} else {
KUNIT_EXPECT_EQ ( test , count , 3 ) ;
KUNIT_EXPECT_EQ ( test , entries [ 3 ] . visited , 0 ) ;
}
}
static struct kunit_case hashtable_test_cases [ ] = {
KUNIT_CASE ( hashtable_test_hash_init ) ,
KUNIT_CASE ( hashtable_test_hash_empty ) ,
KUNIT_CASE ( hashtable_test_hash_hashed ) ,
KUNIT_CASE ( hashtable_test_hash_add ) ,
KUNIT_CASE ( hashtable_test_hash_del ) ,
KUNIT_CASE ( hashtable_test_hash_for_each ) ,
KUNIT_CASE ( hashtable_test_hash_for_each_safe ) ,
KUNIT_CASE ( hashtable_test_hash_for_each_possible ) ,
KUNIT_CASE ( hashtable_test_hash_for_each_possible_safe ) ,
{ } ,
} ;
static struct kunit_suite hashtable_test_module = {
. name = " hashtable " ,
. test_cases = hashtable_test_cases ,
} ;
kunit_test_suites ( & hashtable_test_module ) ;
2024-06-02 02:33:23 +03:00
MODULE_DESCRIPTION ( " KUnit test for the Kernel Hashtable structures " ) ;
2023-01-26 01:54:49 +03:00
MODULE_LICENSE ( " GPL " ) ;