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