blob: 2f0265f884446db306de2145fe0b4114ee8f79f9 [file] [log] [blame]
Radek Krejciaaf6d402018-09-20 15:14:47 +02001/*
2 * @file hash_table.c
3 * @author: Radek Krejci <rkrejci@cesnet.cz>
4 * @brief unit tests for functions from hash_table.c
5 *
6 * Copyright (c) 2018 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup */
Radek Krejci535ea9f2020-05-29 16:01:05 +020017
Radek Krejciaaf6d402018-09-20 15:14:47 +020018#include <stdarg.h>
19#include <stddef.h>
20#include <setjmp.h>
21#include <cmocka.h>
22
23#include <string.h>
Radek Krejcica376bd2020-06-11 16:04:06 +020024#include <stdlib.h>
Radek Krejciaaf6d402018-09-20 15:14:47 +020025#include <stdio.h>
26
Radek Krejci70593c12020-06-13 20:48:09 +020027#include "common.h"
28#include "hash_table.h"
Radek Krejci2d7a47b2019-05-16 13:34:10 +020029
30struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
Radek Krejciaaf6d402018-09-20 15:14:47 +020031
32#define BUFSIZE 1024
33char logbuf[BUFSIZE] = {0};
34
35/* set to 0 to printing error messages to stderr instead of checking them in code */
36#define ENABLE_LOGGER_CHECKING 1
37
38static void
39logger(LY_LOG_LEVEL level, const char *msg, const char *path)
40{
41 (void) level; /* unused */
42 (void) path; /* unused */
43
44 strncpy(logbuf, msg, BUFSIZE - 1);
45}
46
47static int
48logger_setup(void **state)
49{
50 (void) state; /* unused */
51#if ENABLE_LOGGER_CHECKING
52 ly_set_log_clb(logger, 0);
53#endif
54 return 0;
55}
56
57void
58logbuf_clean(void)
59{
60 logbuf[0] = '\0';
61}
62
63#if ENABLE_LOGGER_CHECKING
64# define logbuf_assert(str) assert_string_equal(logbuf, str)
65#else
66# define logbuf_assert(str)
67#endif
68
69static void
70test_invalid_arguments(void **state)
71{
72 (void) state; /* unused */
73 struct ly_ctx *ctx;
74
75 assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
76
Radek Krejci011e4aa2020-09-04 15:22:31 +020077 assert_int_equal(LY_EINVAL, lydict_insert(NULL, NULL, 0, NULL));
Radek Krejciaaf6d402018-09-20 15:14:47 +020078 logbuf_assert("Invalid argument ctx (lydict_insert()).");
79
Radek Krejci011e4aa2020-09-04 15:22:31 +020080 assert_int_equal(LY_EINVAL, lydict_insert_zc(NULL, NULL, NULL));
Radek Krejciaaf6d402018-09-20 15:14:47 +020081 logbuf_assert("Invalid argument ctx (lydict_insert_zc()).");
Radek Krejci011e4aa2020-09-04 15:22:31 +020082 assert_int_equal(LY_EINVAL, lydict_insert_zc(ctx, NULL, NULL));
Radek Krejci0ae092d2018-09-20 16:43:19 +020083 logbuf_assert("Invalid argument value (lydict_insert_zc()).");
Radek Krejciaaf6d402018-09-20 15:14:47 +020084
85 ly_ctx_destroy(ctx, NULL);
86}
87
88static void
89test_dict_hit(void **state)
90{
91 (void) state; /* unused */
92
Radek Krejci011e4aa2020-09-04 15:22:31 +020093 const char *str1, *str2, *str3;
Radek Krejciaaf6d402018-09-20 15:14:47 +020094 struct ly_ctx *ctx;
95
96 assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
97
98 /* insert 2 strings, one of them repeatedly */
Radek Krejci011e4aa2020-09-04 15:22:31 +020099 assert_int_equal(LY_SUCCESS, lydict_insert(ctx, "test1", 0, &str1));
Radek Krejciaaf6d402018-09-20 15:14:47 +0200100 assert_non_null(str1);
101 /* via zerocopy we have to get the same pointer as provided */
102 assert_non_null(str2 = strdup("test2"));
Radek Krejci011e4aa2020-09-04 15:22:31 +0200103 assert_int_equal(LY_SUCCESS, lydict_insert_zc(ctx, (char *)str2, &str3));
104 assert_ptr_equal(str2, str3);
Radek Krejciaaf6d402018-09-20 15:14:47 +0200105 /* here we get the same pointer as in case the string was inserted first time */
Radek Krejci011e4aa2020-09-04 15:22:31 +0200106 assert_int_equal(LY_SUCCESS, lydict_insert(ctx, "test1", 0, &str2));
Radek Krejciaaf6d402018-09-20 15:14:47 +0200107 assert_non_null(str2);
108 assert_ptr_equal(str1, str2);
109
110 /* remove strings, but the repeatedly inserted only once */
111 lydict_remove(ctx, "test1");
112 lydict_remove(ctx, "test2");
113
114 /* destroy dictionary - should raise warning about data presence */
115 ly_ctx_destroy(ctx, NULL);
116 logbuf_assert("String \"test1\" not freed from the dictionary, refcount 1");
117
118#ifndef NDEBUG
119 /* cleanup */
120 free((char*)str1);
121#endif
122}
123
Radek Krejci1deb5be2020-08-26 16:43:36 +0200124static uint8_t
125ht_equal_clb(void *val1, void *val2, uint8_t mod, void *cb_data)
Radek Krejci0ae092d2018-09-20 16:43:19 +0200126{
127 int *v1, *v2;
128 (void)mod;
129 (void)cb_data;
130
131 v1 = (int *)val1;
132 v2 = (int *)val2;
133
134 return *v1 == *v2;
135}
136
137static void
138test_ht_basic(void **state)
139{
140 (void) state; /* unused */
141
142 uint32_t i;
143 struct hash_table *ht;
144
145 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0));
146
147 i = 2;
Michal Vaskoda859032020-07-14 12:20:14 +0200148 assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
Radek Krejci0ae092d2018-09-20 16:43:19 +0200149 assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
Michal Vaskoda859032020-07-14 12:20:14 +0200150 assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL));
Radek Krejci0ae092d2018-09-20 16:43:19 +0200151 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
Michal Vaskoda859032020-07-14 12:20:14 +0200152 assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
Michal Vasko4a4c7ed2020-07-17 09:30:12 +0200153 assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200154 logbuf_assert("Invalid argument hash (lyht_remove_with_resize_cb()).");
Radek Krejci0ae092d2018-09-20 16:43:19 +0200155
156 lyht_free(ht);
157}
158
159static void
160test_ht_resize(void **state)
161{
162 (void) state; /* unused */
163
164 uint32_t i;
165 struct ht_rec *rec;
166 struct hash_table *ht;
167
168 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
169 assert_int_equal(8, ht->size);
170
171 /* insert records into indexes 2-7 */
172 for (i = 2; i < 8; ++i) {
173 assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
174 }
175 /* check that table resized */
176 assert_int_equal(16, ht->size);
177
178 /* check expected content of the table */
179 for (i = 0; i < 16; ++i) {
180 if (i >=2 && i < 8) {
181 /* inserted data on indexes 2-7 */
182 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
183 assert_int_equal(1, rec->hits);
184 assert_int_equal(i, rec->hash);
185 } else {
186 /* nothing otherwise */
187 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
188 assert_int_equal(0, rec->hits);
189 }
190 }
191
192 /* removing not present data should fail */
193 for (i = 0; i < 2; ++i) {
194 logbuf_clean();
Michal Vasko4a4c7ed2020-07-17 09:30:12 +0200195 assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200196 logbuf_assert("Invalid argument hash (lyht_remove_with_resize_cb()).");
Radek Krejci0ae092d2018-09-20 16:43:19 +0200197 }
198 /* removing present data, resize should happened
199 * when we are below 25% of the table filled, so with 3 records left */
200 for (; i < 5; ++i) {
201 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
202 }
203 assert_int_equal(8, ht->size);
204
205 /* remove the rest */
206 for (; i < 8; ++i) {
207 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
208 }
209
210 for (i = 0; i < 8; ++i) {
Michal Vaskoda859032020-07-14 12:20:14 +0200211 assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
Radek Krejci0ae092d2018-09-20 16:43:19 +0200212 }
213
214 /* cleanup */
215 lyht_free(ht);
216}
217
218
219static void
220test_ht_collisions(void **state)
221{
222 (void) state; /* unused */
223#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
224
225 uint32_t i;
226 struct ht_rec *rec;
227 struct hash_table *ht;
228
229 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
230
231 for (i = 2; i < 6; ++i) {
232 assert_int_equal(lyht_insert(ht, &i, 2, NULL), 0);
233 }
234
235 /* check all records */
236 for (i = 0; i < 2; ++i) {
237 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
238 assert_int_equal(rec->hits, 0);
239 }
240 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
241 assert_int_equal(rec->hits, 4);
242 assert_int_equal(GET_REC_INT(rec), i);
243 ++i;
244 for (; i < 6; ++i) {
245 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
246 assert_int_equal(rec->hits, 1);
247 assert_int_equal(GET_REC_INT(rec), i);
248 }
249 for (; i < 8; ++i) {
250 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
251 assert_int_equal(rec->hits, 0);
252 }
253
254 i = 4;
255 assert_int_equal(lyht_remove(ht, &i, 2), 0);
256
257 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
258 assert_int_equal(rec->hits, -1);
259
260 i = 2;
261 assert_int_equal(lyht_remove(ht, &i, 2), 0);
262
263 /* check all records */
264 for (i = 0; i < 2; ++i) {
265 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
266 assert_int_equal(rec->hits, 0);
267 }
268 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
269 assert_int_equal(rec->hits, 2);
270 assert_int_equal(GET_REC_INT(rec), 5);
271 ++i;
272 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
273 assert_int_equal(rec->hits, 1);
274 assert_int_equal(GET_REC_INT(rec), 3);
275 ++i;
276 for (; i < 6; ++i) {
277 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
278 assert_int_equal(rec->hits, -1);
279 }
280 for (; i < 8; ++i) {
281 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
282 assert_int_equal(rec->hits, 0);
283 }
284
285 for (i = 0; i < 3; ++i) {
Michal Vaskoda859032020-07-14 12:20:14 +0200286 assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
Radek Krejci0ae092d2018-09-20 16:43:19 +0200287 }
Michal Vaskoda859032020-07-14 12:20:14 +0200288 assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
Radek Krejci0ae092d2018-09-20 16:43:19 +0200289 ++i;
Michal Vaskoda859032020-07-14 12:20:14 +0200290 assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
Radek Krejci0ae092d2018-09-20 16:43:19 +0200291 ++i;
Michal Vaskoda859032020-07-14 12:20:14 +0200292 assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
Radek Krejci0ae092d2018-09-20 16:43:19 +0200293 ++i;
294 for (; i < 8; ++i) {
Michal Vaskoda859032020-07-14 12:20:14 +0200295 assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
Radek Krejci0ae092d2018-09-20 16:43:19 +0200296 }
297
298 i = 3;
299 assert_int_equal(lyht_remove(ht, &i, 2), 0);
300 i = 5;
301 assert_int_equal(lyht_remove(ht, &i, 2), 0);
302
303 /* check all records */
304 for (i = 0; i < 2; ++i) {
305 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
306 assert_int_equal(rec->hits, 0);
307 }
308 for (; i < 6; ++i) {
309 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
310 assert_int_equal(rec->hits, -1);
311 }
312 for (; i < 8; ++i) {
313 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
314 assert_int_equal(rec->hits, 0);
315 }
316
317 lyht_free(ht);
318}
319
Radek Krejciaaf6d402018-09-20 15:14:47 +0200320int main(void)
321{
322 const struct CMUnitTest tests[] = {
323 cmocka_unit_test_setup(test_invalid_arguments, logger_setup),
324 cmocka_unit_test_setup(test_dict_hit, logger_setup),
Radek Krejci0ae092d2018-09-20 16:43:19 +0200325 cmocka_unit_test_setup(test_ht_basic, logger_setup),
326 cmocka_unit_test_setup(test_ht_resize, logger_setup),
327 cmocka_unit_test_setup(test_ht_collisions, logger_setup),
Radek Krejciaaf6d402018-09-20 15:14:47 +0200328 };
329
330 return cmocka_run_group_tests(tests, NULL, NULL);
331}