blob: 3e1755251625b03b61c2880ea6d8b597eed9ca0c [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
16
Radek Krejcib7db73a2018-10-24 14:18:40 +020017#include "common.h"
Radek Krejciaaf6d402018-09-20 15:14:47 +020018
19#include "tests/config.h"
Radek Krejciaaf6d402018-09-20 15:14:47 +020020
21#include <stdarg.h>
22#include <stddef.h>
23#include <setjmp.h>
24#include <cmocka.h>
25
26#include <string.h>
Radek Krejcica376bd2020-06-11 16:04:06 +020027#include <stdlib.h>
Radek Krejciaaf6d402018-09-20 15:14:47 +020028#include <stdio.h>
29
Radek Krejci2d7a47b2019-05-16 13:34:10 +020030#include "../../src/hash_table.h"
31
32struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
Radek Krejciaaf6d402018-09-20 15:14:47 +020033
34#define BUFSIZE 1024
35char logbuf[BUFSIZE] = {0};
36
37/* set to 0 to printing error messages to stderr instead of checking them in code */
38#define ENABLE_LOGGER_CHECKING 1
39
40static void
41logger(LY_LOG_LEVEL level, const char *msg, const char *path)
42{
43 (void) level; /* unused */
44 (void) path; /* unused */
45
46 strncpy(logbuf, msg, BUFSIZE - 1);
47}
48
49static int
50logger_setup(void **state)
51{
52 (void) state; /* unused */
53#if ENABLE_LOGGER_CHECKING
54 ly_set_log_clb(logger, 0);
55#endif
56 return 0;
57}
58
59void
60logbuf_clean(void)
61{
62 logbuf[0] = '\0';
63}
64
65#if ENABLE_LOGGER_CHECKING
66# define logbuf_assert(str) assert_string_equal(logbuf, str)
67#else
68# define logbuf_assert(str)
69#endif
70
71static void
72test_invalid_arguments(void **state)
73{
74 (void) state; /* unused */
75 struct ly_ctx *ctx;
76
77 assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
78
79 assert_null(lydict_insert(NULL, NULL, 0));
80 logbuf_assert("Invalid argument ctx (lydict_insert()).");
81
82 assert_null(lydict_insert_zc(NULL, NULL));
83 logbuf_assert("Invalid argument ctx (lydict_insert_zc()).");
Radek Krejciaaf6d402018-09-20 15:14:47 +020084 assert_null(lydict_insert_zc(ctx, NULL));
Radek Krejci0ae092d2018-09-20 16:43:19 +020085 logbuf_assert("Invalid argument value (lydict_insert_zc()).");
Radek Krejciaaf6d402018-09-20 15:14:47 +020086
87 ly_ctx_destroy(ctx, NULL);
88}
89
90static void
91test_dict_hit(void **state)
92{
93 (void) state; /* unused */
94
95 const char *str1, *str2;
96 struct ly_ctx *ctx;
97
98 assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
99
100 /* insert 2 strings, one of them repeatedly */
101 str1 = lydict_insert(ctx, "test1", 0);
102 assert_non_null(str1);
103 /* via zerocopy we have to get the same pointer as provided */
104 assert_non_null(str2 = strdup("test2"));
105 assert_true(str2 == lydict_insert_zc(ctx, (char *)str2));
106 /* here we get the same pointer as in case the string was inserted first time */
107 str2 = lydict_insert(ctx, "test1", 0);
108 assert_non_null(str2);
109 assert_ptr_equal(str1, str2);
110
111 /* remove strings, but the repeatedly inserted only once */
112 lydict_remove(ctx, "test1");
113 lydict_remove(ctx, "test2");
114
115 /* destroy dictionary - should raise warning about data presence */
116 ly_ctx_destroy(ctx, NULL);
117 logbuf_assert("String \"test1\" not freed from the dictionary, refcount 1");
118
119#ifndef NDEBUG
120 /* cleanup */
121 free((char*)str1);
122#endif
123}
124
Radek Krejci0ae092d2018-09-20 16:43:19 +0200125static int
126ht_equal_clb(void *val1, void *val2, int mod, void *cb_data)
127{
128 int *v1, *v2;
129 (void)mod;
130 (void)cb_data;
131
132 v1 = (int *)val1;
133 v2 = (int *)val2;
134
135 return *v1 == *v2;
136}
137
138static void
139test_ht_basic(void **state)
140{
141 (void) state; /* unused */
142
143 uint32_t i;
144 struct hash_table *ht;
145
146 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0));
147
148 i = 2;
149 assert_int_equal(1, lyht_find(ht, &i, i, NULL));
150 assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
151 assert_int_equal(0, lyht_find(ht, &i, i, NULL));
152 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
153 assert_int_equal(1, lyht_find(ht, &i, i, NULL));
154 assert_int_equal(LY_EINVAL, lyht_remove(ht, &i, i));
155 logbuf_assert("Invalid argument hash (lyht_remove()).");
156
157 lyht_free(ht);
158}
159
160static void
161test_ht_resize(void **state)
162{
163 (void) state; /* unused */
164
165 uint32_t i;
166 struct ht_rec *rec;
167 struct hash_table *ht;
168
169 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
170 assert_int_equal(8, ht->size);
171
172 /* insert records into indexes 2-7 */
173 for (i = 2; i < 8; ++i) {
174 assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
175 }
176 /* check that table resized */
177 assert_int_equal(16, ht->size);
178
179 /* check expected content of the table */
180 for (i = 0; i < 16; ++i) {
181 if (i >=2 && i < 8) {
182 /* inserted data on indexes 2-7 */
183 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
184 assert_int_equal(1, rec->hits);
185 assert_int_equal(i, rec->hash);
186 } else {
187 /* nothing otherwise */
188 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
189 assert_int_equal(0, rec->hits);
190 }
191 }
192
193 /* removing not present data should fail */
194 for (i = 0; i < 2; ++i) {
195 logbuf_clean();
196 assert_int_equal(LY_EINVAL, lyht_remove(ht, &i, i));
197 logbuf_assert("Invalid argument hash (lyht_remove()).");
198 }
199 /* removing present data, resize should happened
200 * when we are below 25% of the table filled, so with 3 records left */
201 for (; i < 5; ++i) {
202 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
203 }
204 assert_int_equal(8, ht->size);
205
206 /* remove the rest */
207 for (; i < 8; ++i) {
208 assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
209 }
210
211 for (i = 0; i < 8; ++i) {
212 assert_int_equal(1, lyht_find(ht, &i, i, NULL));
213 }
214
215 /* cleanup */
216 lyht_free(ht);
217}
218
219
220static void
221test_ht_collisions(void **state)
222{
223 (void) state; /* unused */
224#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
225
226 uint32_t i;
227 struct ht_rec *rec;
228 struct hash_table *ht;
229
230 assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
231
232 for (i = 2; i < 6; ++i) {
233 assert_int_equal(lyht_insert(ht, &i, 2, NULL), 0);
234 }
235
236 /* check all records */
237 for (i = 0; i < 2; ++i) {
238 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
239 assert_int_equal(rec->hits, 0);
240 }
241 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
242 assert_int_equal(rec->hits, 4);
243 assert_int_equal(GET_REC_INT(rec), i);
244 ++i;
245 for (; i < 6; ++i) {
246 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
247 assert_int_equal(rec->hits, 1);
248 assert_int_equal(GET_REC_INT(rec), i);
249 }
250 for (; i < 8; ++i) {
251 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
252 assert_int_equal(rec->hits, 0);
253 }
254
255 i = 4;
256 assert_int_equal(lyht_remove(ht, &i, 2), 0);
257
258 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
259 assert_int_equal(rec->hits, -1);
260
261 i = 2;
262 assert_int_equal(lyht_remove(ht, &i, 2), 0);
263
264 /* check all records */
265 for (i = 0; i < 2; ++i) {
266 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
267 assert_int_equal(rec->hits, 0);
268 }
269 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
270 assert_int_equal(rec->hits, 2);
271 assert_int_equal(GET_REC_INT(rec), 5);
272 ++i;
273 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
274 assert_int_equal(rec->hits, 1);
275 assert_int_equal(GET_REC_INT(rec), 3);
276 ++i;
277 for (; i < 6; ++i) {
278 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
279 assert_int_equal(rec->hits, -1);
280 }
281 for (; i < 8; ++i) {
282 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
283 assert_int_equal(rec->hits, 0);
284 }
285
286 for (i = 0; i < 3; ++i) {
287 assert_int_equal(lyht_find(ht, &i, 2, NULL), 1);
288 }
289 assert_int_equal(lyht_find(ht, &i, 2, NULL), 0);
290 ++i;
291 assert_int_equal(lyht_find(ht, &i, 2, NULL), 1);
292 ++i;
293 assert_int_equal(lyht_find(ht, &i, 2, NULL), 0);
294 ++i;
295 for (; i < 8; ++i) {
296 assert_int_equal(lyht_find(ht, &i, 2, NULL), 1);
297 }
298
299 i = 3;
300 assert_int_equal(lyht_remove(ht, &i, 2), 0);
301 i = 5;
302 assert_int_equal(lyht_remove(ht, &i, 2), 0);
303
304 /* check all records */
305 for (i = 0; i < 2; ++i) {
306 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
307 assert_int_equal(rec->hits, 0);
308 }
309 for (; i < 6; ++i) {
310 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
311 assert_int_equal(rec->hits, -1);
312 }
313 for (; i < 8; ++i) {
314 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
315 assert_int_equal(rec->hits, 0);
316 }
317
318 lyht_free(ht);
319}
320
Radek Krejciaaf6d402018-09-20 15:14:47 +0200321int main(void)
322{
323 const struct CMUnitTest tests[] = {
324 cmocka_unit_test_setup(test_invalid_arguments, logger_setup),
325 cmocka_unit_test_setup(test_dict_hit, logger_setup),
Radek Krejci0ae092d2018-09-20 16:43:19 +0200326 cmocka_unit_test_setup(test_ht_basic, logger_setup),
327 cmocka_unit_test_setup(test_ht_resize, logger_setup),
328 cmocka_unit_test_setup(test_ht_collisions, logger_setup),
Radek Krejciaaf6d402018-09-20 15:14:47 +0200329 };
330
331 return cmocka_run_group_tests(tests, NULL, NULL);
332}