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