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