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