| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include <stdio.h> |
| 4 | #include <string.h> |
| 5 | |
| 6 | #include "c_rhash.h" |
| 7 | |
| 8 | // terminal color codes |
| 9 | #define KNRM "\x1B[0m" |
| 10 | #define KRED "\x1B[31m" |
| 11 | #define KGRN "\x1B[32m" |
| 12 | #define KYEL "\x1B[33m" |
| 13 | #define KBLU "\x1B[34m" |
| 14 | #define KMAG "\x1B[35m" |
| 15 | #define KCYN "\x1B[36m" |
| 16 | #define KWHT "\x1B[37m" |
| 17 | |
| 18 | #define KEY_1 "key1" |
| 19 | #define KEY_2 "keya" |
| 20 | |
| 21 | #define PRINT_ERR(str, ...) fprintf(stderr, "└─╼ ❌ " KRED str KNRM "\n" __VA_OPT__(,) __VA_ARGS__) |
| 22 | |
| 23 | #define ASSERT_RETVAL(fnc, comparator, expected_retval, ...) \ |
| 24 | { int rval; \ |
| 25 | if(!((rval = fnc(__VA_ARGS__)) comparator expected_retval)) { \ |
| 26 | PRINT_ERR("Failed test. Value returned by \"%s\" in fnc:\"%s\",line:%d is not equal to expected value. Expected:%d, Got:%d", #fnc, __FUNCTION__, __LINE__, expected_retval, rval); \ |
| 27 | rc = 1; \ |
| 28 | goto test_cleanup; \ |
| 29 | } passed_subtest_count++;}; |
| 30 | |
| 31 | #define ASSERT_VAL_UINT8(returned, expected) \ |
| 32 | if(returned != expected) { \ |
| 33 | PRINT_ERR("Failed test. Value returned (%d) doesn't match expected (%d)! fnc:\"%s\",line:%d", returned, expected, __FUNCTION__, __LINE__); \ |
| 34 | rc = 1; \ |
| 35 | goto test_cleanup; \ |
| 36 | } passed_subtest_count++; |
| 37 | |
| 38 | #define ASSERT_VAL_PTR(returned, expected) \ |
| 39 | if((void*)returned != (void*)expected) { \ |
| 40 | PRINT_ERR("Failed test. Value returned(%p) doesn't match expected(%p)! fnc:\"%s\",line:%d", (void*)returned, (void*)expected, __FUNCTION__, __LINE__); \ |
| 41 | rc = 1; \ |
| 42 | goto test_cleanup; \ |
| 43 | } passed_subtest_count++; |
| 44 | |
| 45 | #define ALL_SUBTESTS_PASS() printf("└─╼ ✅" KGRN " Test \"%s\" DONE. All of %zu subtests PASS. (line:%d)\n" KNRM, __FUNCTION__, passed_subtest_count, __LINE__); |
| 46 | |
| 47 | #define TEST_START() size_t passed_subtest_count = 0; int rc = 0; printf("╒═ Starting test \"%s\"\n", __FUNCTION__); |
| 48 | |
| 49 | int test_str_uint8() { |
| 50 | c_rhash hash = c_rhash_new(100); |
| 51 | uint8_t val; |
| 52 | |
| 53 | TEST_START(); |
| 54 | // function should fail on empty hash |
| 55 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, !=, 0, hash, KEY_1, &val); |
| 56 | |
| 57 | ASSERT_RETVAL(c_rhash_insert_str_uint8, ==, 0, hash, KEY_1, 5); |
| 58 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, ==, 0, hash, KEY_1, &val); |
| 59 | ASSERT_VAL_UINT8(5, val); |
| 60 | |
| 61 | ASSERT_RETVAL(c_rhash_insert_str_uint8, ==, 0, hash, KEY_2, 8); |
| 62 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, ==, 0, hash, KEY_1, &val); |
| 63 | ASSERT_VAL_UINT8(5, val); |
| 64 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, ==, 0, hash, KEY_2, &val); |
| 65 | ASSERT_VAL_UINT8(8, val); |
| 66 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, !=, 0, hash, "sndnskjdf", &val); |
| 67 | |
| 68 | // test update of key |
| 69 | ASSERT_RETVAL(c_rhash_insert_str_uint8, ==, 0, hash, KEY_1, 100); |
| 70 | ASSERT_RETVAL(c_rhash_get_uint8_by_str, ==, 0, hash, KEY_1, &val); |
| 71 | ASSERT_VAL_UINT8(100, val); |
| 72 | |
| 73 | ALL_SUBTESTS_PASS(); |
| 74 | test_cleanup: |
| 75 | c_rhash_destroy(hash); |
| 76 | return rc; |
| 77 | } |
| 78 | |
| 79 | int test_uint64_ptr() { |
| 80 | c_rhash hash = c_rhash_new(100); |
| 81 | void *val; |
| 82 | |
| 83 | TEST_START(); |
| 84 | |
| 85 | // function should fail on empty hash |
| 86 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, !=, 0, hash, 0, &val); |
| 87 | |
| 88 | ASSERT_RETVAL(c_rhash_insert_uint64_ptr, ==, 0, hash, 0, &hash); |
| 89 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, ==, 0, hash, 0, &val); |
| 90 | ASSERT_VAL_PTR(&hash, val); |
| 91 | |
| 92 | ASSERT_RETVAL(c_rhash_insert_uint64_ptr, ==, 0, hash, 1, &val); |
| 93 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, ==, 0, hash, 0, &val); |
| 94 | ASSERT_VAL_PTR(&hash, val); |
| 95 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, ==, 0, hash, 1, &val); |
| 96 | ASSERT_VAL_PTR(&val, val); |
| 97 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, !=, 0, hash, 2, &val); |
| 98 | |
| 99 | ALL_SUBTESTS_PASS(); |
| 100 | test_cleanup: |
| 101 | c_rhash_destroy(hash); |
| 102 | return rc; |
| 103 | } |
| 104 | |
| 105 | #define UINT64_PTR_INC_ITERATION_COUNT 5000 |
| 106 | int test_uint64_ptr_incremental() { |
| 107 | c_rhash hash = c_rhash_new(100); |
| 108 | void *val; |
| 109 | |
| 110 | TEST_START(); |
| 111 | |
| 112 | char a = 0x20; |
| 113 | char *ptr = &a; |
| 114 | while(ptr < &a + UINT64_PTR_INC_ITERATION_COUNT) { |
| 115 | ASSERT_RETVAL(c_rhash_insert_uint64_ptr, ==, 0, hash, (ptr-&a), ptr); |
| 116 | ptr++; |
| 117 | } |
| 118 | |
| 119 | ptr = &a; |
| 120 | char *retptr; |
| 121 | for(int i = 0; i < UINT64_PTR_INC_ITERATION_COUNT; i++) { |
| 122 | ASSERT_RETVAL(c_rhash_get_ptr_by_uint64, ==, 0, hash, i, (void**)&retptr); |
| 123 | ASSERT_VAL_PTR(retptr, (&a+i)); |
| 124 | } |
| 125 | |
| 126 | ALL_SUBTESTS_PASS(); |
| 127 | test_cleanup: |
| 128 | c_rhash_destroy(hash); |
| 129 | return rc; |
| 130 | } |
| 131 | |
| 132 | struct test_string { |
| 133 | const char *str; |
| 134 | int counter; |
| 135 | }; |
| 136 | |
| 137 | struct test_string test_strings[] = { |
| 138 | { .str = "Cillum reprehenderit eiusmod elit nisi aliquip esse exercitation commodo Lorem voluptate esse.", .counter = 0 }, |
| 139 | { .str = "Ullamco eiusmod tempor occaecat ad.", .counter = 0 }, |
| 140 | { .str = "Esse aliquip tempor sint tempor ullamco duis aute incididunt ad.", .counter = 0 }, |
| 141 | { .str = "Cillum Lorem labore cupidatat commodo proident adipisicing.", .counter = 0 }, |
| 142 | { .str = "Quis ad cillum officia exercitation.", .counter = 0 }, |
| 143 | { .str = "Ipsum enim dolor ullamco amet sint nisi ut occaecat sint non.", .counter = 0 }, |
| 144 | { .str = "Id duis officia ipsum cupidatat velit fugiat.", .counter = 0 }, |
| 145 | { .str = "Aliqua non occaecat voluptate reprehenderit reprehenderit veniam minim exercitation ea aliquip enim aliqua deserunt qui.", .counter = 0 }, |
| 146 | { .str = "Ullamco elit tempor laboris reprehenderit quis deserunt duis quis tempor reprehenderit magna dolore reprehenderit exercitation.", .counter = 0 }, |
| 147 | { .str = "Culpa do dolor quis incididunt et labore in ex.", .counter = 0 }, |
| 148 | { .str = "Aliquip velit cupidatat qui incididunt ipsum nostrud eiusmod ut proident nisi magna fugiat excepteur.", .counter = 0 }, |
| 149 | { .str = "Aliqua qui dolore tempor id proident ullamco sunt magna.", .counter = 0 }, |
| 150 | { .str = "Labore eiusmod ut fugiat dolore reprehenderit mollit magna.", .counter = 0 }, |
| 151 | { .str = "Veniam aliquip dolor excepteur minim nulla esse cupidatat esse.", .counter = 0 }, |
| 152 | { .str = "Do quis dolor irure nostrud occaecat aute proident anim.", .counter = 0 }, |
| 153 | { .str = "Enim veniam non nulla ad quis sit amet.", .counter = 0 }, |
| 154 | { .str = "Cillum reprehenderit do enim esse do ullamco consectetur ea.", .counter = 0 }, |
| 155 | { .str = "Sit et duis sint anim qui ad anim labore exercitation sunt cupidatat.", .counter = 0 }, |
| 156 | { .str = "Dolor officia adipisicing sint pariatur in dolor occaecat officia reprehenderit magna.", .counter = 0 }, |
| 157 | { .str = "Aliquip dolore qui occaecat eiusmod sunt incididunt reprehenderit minim et.", .counter = 0 }, |
| 158 | { .str = "Aute fugiat laboris cillum tempor consequat tempor do non laboris culpa officia nisi.", .counter = 0 }, |
| 159 | { .str = "Et excepteur do aliquip fugiat nisi velit tempor officia enim quis elit incididunt.", .counter = 0 }, |
| 160 | { .str = "Eu officia adipisicing incididunt occaecat officia cupidatat enim sit sit officia.", .counter = 0 }, |
| 161 | { .str = "Do amet cillum duis pariatur commodo nulla cillum magna nulla Lorem veniam cupidatat.", .counter = 0 }, |
| 162 | { .str = "Dolor adipisicing voluptate laboris occaecat culpa aliquip ipsum ut consequat aliqua aliquip commodo sunt velit.", .counter = 0 }, |
| 163 | { .str = "Nulla proident ipsum quis nulla.", .counter = 0 }, |
| 164 | { .str = "Laborum adipisicing nulla do aute aliqua est quis sint culpa pariatur laborum voluptate qui.", .counter = 0 }, |
| 165 | { .str = "Proident eiusmod sunt et nulla elit pariatur dolore irure ex voluptate excepteur adipisicing consectetur.", .counter = 0 }, |
| 166 | { .str = "Consequat ex voluptate officia excepteur aute deserunt proident commodo et.", .counter = 0 }, |
| 167 | { .str = "Velit sit cupidatat dolor dolore.", .counter = 0 }, |
| 168 | { .str = "Sunt enim do non anim nostrud exercitation ullamco ex proident commodo.", .counter = 0 }, |
| 169 | { .str = "Id ex officia cillum ad.", .counter = 0 }, |
| 170 | { .str = "Laboris in sunt eiusmod veniam laboris nostrud.", .counter = 0 }, |
| 171 | { .str = "Ex magna occaecat ea ea incididunt aliquip.", .counter = 0 }, |
| 172 | { .str = "Sunt eiusmod ex nostrud eu pariatur sit cupidatat ea adipisicing cillum culpa esse consequat aliquip.", .counter = 0 }, |
| 173 | { .str = "Excepteur commodo qui incididunt enim culpa sunt non excepteur Lorem adipisicing.", .counter = 0 }, |
| 174 | { .str = "Quis officia est ullamco reprehenderit incididunt occaecat pariatur ex reprehenderit nisi.", .counter = 0 }, |
| 175 | { .str = "Culpa irure proident proident et eiusmod irure aliqua ipsum cupidatat minim sit.", .counter = 0 }, |
| 176 | { .str = "Qui cupidatat aliquip est velit magna veniam.", .counter = 0 }, |
| 177 | { .str = "Pariatur ad ad mollit nostrud non irure minim veniam anim aliquip quis eu.", .counter = 0 }, |
| 178 | { .str = "Nisi ex minim eu adipisicing tempor Lorem nisi do ad exercitation est non eu.", .counter = 0 }, |
| 179 | { .str = "Cupidatat do mollit ad commodo cupidatat ut.", .counter = 0 }, |
| 180 | { .str = "Est non excepteur eiusmod nostrud et eu.", .counter = 0 }, |
| 181 | { .str = "Cupidatat mollit nisi magna officia ut elit eiusmod.", .counter = 0 }, |
| 182 | { .str = "Est aliqua consectetur laboris ex consequat est ut dolor.", .counter = 0 }, |
| 183 | { .str = "Duis eu laboris laborum ut id Lorem nostrud qui ad velit proident fugiat minim ullamco.", .counter = 0 }, |
| 184 | { .str = "Pariatur esse excepteur anim amet excepteur irure sint quis esse ex cupidatat ut.", .counter = 0 }, |
| 185 | { .str = "Esse reprehenderit amet qui excepteur aliquip amet.", .counter = 0 }, |
| 186 | { .str = "Ullamco laboris elit labore adipisicing aute nulla qui laborum tempor officia ut dolor aute.", .counter = 0 }, |
| 187 | { .str = "Commodo sunt cillum velit minim laborum Lorem aliqua tempor ad id eu.", .counter = 0 }, |
| 188 | { .str = NULL, .counter = 0 } |
| 189 | }; |
| 190 | |
| 191 | uint32_t test_strings_contain_element(const char *str) { |
| 192 | struct test_string *str_desc = test_strings; |
| 193 | while(str_desc->str) { |
| 194 | if (!strcmp(str, str_desc->str)) |
| 195 | return str_desc - test_strings; |
| 196 | str_desc++; |
| 197 | } |
| 198 | return -1; |
| 199 | } |
| 200 | |
| 201 | #define TEST_INCREMENT_STR_KEYS_HASH_SIZE 20 |
| 202 | int test_increment_str_keys() { |
| 203 | c_rhash hash; |
| 204 | const char *key; |
| 205 | |
| 206 | TEST_START(); |
| 207 | |
| 208 | hash = c_rhash_new(TEST_INCREMENT_STR_KEYS_HASH_SIZE); // less than element count of test_strings |
| 209 | |
| 210 | c_rhash_iter_t iter = C_RHASH_ITER_T_INITIALIZER; |
| 211 | |
| 212 | // check iter on empty hash |
| 213 | ASSERT_RETVAL(c_rhash_iter_str_keys, !=, 0, hash, &iter, &key); |
| 214 | |
| 215 | int32_t element_count = 0; |
| 216 | while (test_strings[element_count].str) { |
| 217 | ASSERT_RETVAL(c_rhash_insert_str_ptr, ==, 0, hash, test_strings[element_count].str, NULL); |
| 218 | test_strings[element_count].counter++; // we want to test we got each key exactly once |
| 219 | element_count++; |
| 220 | } |
| 221 | |
| 222 | if (element_count <= TEST_INCREMENT_STR_KEYS_HASH_SIZE * 2) { |
| 223 | // verify we are actually test also iteration trough single bin (when 2 keys have same hash pointing them to same bin) |
| 224 | PRINT_ERR("For this test to properly test all the hash size needs to be much smaller than all test key count."); |
| 225 | rc = 1; |
| 226 | goto test_cleanup; |
| 227 | } |
| 228 | |
| 229 | // we insert another type of key as iterator should skip it |
| 230 | // in case is another type |
| 231 | ASSERT_RETVAL(c_rhash_insert_uint64_ptr, ==, 0, hash, 5, NULL); |
| 232 | |
| 233 | c_rhash_iter_t_initialize(&iter); |
| 234 | while(!c_rhash_iter_str_keys(hash, &iter, &key)) { |
| 235 | element_count--; |
| 236 | int i; |
| 237 | if ( (i = test_strings_contain_element(key)) < 0) { |
| 238 | PRINT_ERR("Key \"%s\" is not present in test_strings array! (Fnc: %s, Line: %d)", key, __FUNCTION__, __LINE__); |
| 239 | rc = 1; |
| 240 | goto test_cleanup; |
| 241 | } |
| 242 | passed_subtest_count++; |
| 243 | |
| 244 | test_strings[i].counter--; |
| 245 | } |
| 246 | ASSERT_VAL_UINT8(element_count, 0); // we added also same non string keys |
| 247 | |
| 248 | // check each key was present exactly once |
| 249 | struct test_string *str_desc = test_strings; |
| 250 | while (str_desc->str) { |
| 251 | ASSERT_VAL_UINT8(str_desc->counter, 0); |
| 252 | str_desc++; |
| 253 | } |
| 254 | |
| 255 | ALL_SUBTESTS_PASS(); |
| 256 | test_cleanup: |
| 257 | c_rhash_destroy(hash); |
| 258 | return rc; |
| 259 | } |
| 260 | |
| 261 | #define RUN_TEST(fnc) \ |
| 262 | if(fnc()) \ |
| 263 | return 1; |
| 264 | |
| 265 | int main(int argc, char *argv[]) { |
| 266 | RUN_TEST(test_str_uint8); |
| 267 | RUN_TEST(test_uint64_ptr); |
| 268 | RUN_TEST(test_uint64_ptr_incremental); |
| 269 | RUN_TEST(test_increment_str_keys); |
| 270 | // TODO hash with mixed key tests |
| 271 | // TODO iterator test |
| 272 | return 0; |
| 273 | } |