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