test coverage
[protos/libecoli.git] / lib / ecoli_node.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <errno.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_string.h>
14 #include <ecoli_strvec.h>
15 #include <ecoli_keyval.h>
16 #include <ecoli_log.h>
17 #include <ecoli_test.h>
18 #include <ecoli_node.h>
19
20 #include <ecoli_node_str.h>
21 #include <ecoli_node_seq.h>
22
23 EC_LOG_TYPE_REGISTER(node);
24
25 static struct ec_node_type_list node_type_list =
26         TAILQ_HEAD_INITIALIZER(node_type_list);
27
28 const struct ec_node_type *
29 ec_node_type_lookup(const char *name)
30 {
31         struct ec_node_type *type;
32
33         TAILQ_FOREACH(type, &node_type_list, next) {
34                 if (!strcmp(name, type->name))
35                         return type;
36         }
37
38         return NULL;
39 }
40
41 int ec_node_type_register(struct ec_node_type *type)
42 {
43         if (ec_node_type_lookup(type->name) != NULL)
44                 return -EEXIST;
45         if (type->size < sizeof(struct ec_node))
46                 return -EINVAL;
47
48         TAILQ_INSERT_TAIL(&node_type_list, type, next);
49
50         return 0;
51 }
52
53 void ec_node_type_dump(FILE *out)
54 {
55         struct ec_node_type *type;
56
57         TAILQ_FOREACH(type, &node_type_list, next)
58                 fprintf(out, "%s\n", type->name);
59 }
60
61 struct ec_node *__ec_node(const struct ec_node_type *type, const char *id)
62 {
63         struct ec_node *node = NULL;
64
65         EC_LOG(EC_LOG_DEBUG, "create node type=%s id=%s\n",
66                 type->name, id);
67         if (id == NULL) {
68                 errno = EINVAL;
69                 goto fail;
70         }
71
72         node = ec_calloc(1, type->size);
73         if (node == NULL)
74                 goto fail;
75
76         node->type = type;
77         node->refcnt = 1;
78
79         node->id = ec_strdup(id);
80         if (node->id == NULL)
81                 goto fail;
82
83         if (ec_asprintf(&node->desc, "<%s>", type->name) < 0)
84                 goto fail;
85
86         node->attrs = ec_keyval();
87         if (node->attrs == NULL)
88                 goto fail;
89
90         if (type->init_priv != NULL) {
91                 if (type->init_priv(node) < 0)
92                         goto fail;
93         }
94
95         return node;
96
97  fail:
98         if (node != NULL) {
99                 ec_keyval_free(node->attrs);
100                 ec_free(node->desc);
101                 ec_free(node->id);
102         }
103         ec_free(node);
104
105         return NULL;
106 }
107
108 struct ec_node *ec_node(const char *typename, const char *id)
109 {
110         const struct ec_node_type *type;
111
112         type = ec_node_type_lookup(typename);
113         if (type == NULL) {
114                 EC_LOG(EC_LOG_ERR, "type=%s does not exist\n",
115                         typename);
116                 return NULL;
117         }
118
119         return __ec_node(type, id);
120 }
121
122 void ec_node_free(struct ec_node *node)
123 {
124         if (node == NULL)
125                 return;
126
127         assert(node->refcnt > 0);
128
129         if (--node->refcnt > 0)
130                 return;
131
132         if (node->type != NULL && node->type->free_priv != NULL)
133                 node->type->free_priv(node);
134         ec_free(node->children);
135         ec_free(node->id);
136         ec_free(node->desc);
137         ec_keyval_free(node->attrs);
138         ec_free(node);
139 }
140
141 struct ec_node *ec_node_clone(struct ec_node *node)
142 {
143         if (node != NULL)
144                 node->refcnt++;
145         return node;
146 }
147
148 size_t ec_node_get_children_count(const struct ec_node *node)
149 {
150         return node->n_children;
151 }
152
153 struct ec_node *
154 ec_node_get_child(const struct ec_node *node, size_t i)
155 {
156         if (i >= ec_node_get_children_count(node))
157                 return NULL;
158         return node->children[i];
159 }
160
161 int ec_node_add_child(struct ec_node *node, struct ec_node *child)
162 {
163         struct ec_node **children = NULL;
164         size_t n;
165
166         if (node == NULL || child == NULL) {
167                 errno = EINVAL;
168                 goto fail;
169         }
170
171         n = node->n_children;
172         children = ec_realloc(node->children,
173                         (n + 1) * sizeof(child));
174         if (children == NULL)
175                 goto fail;
176
177         children[n] = child;
178         node->children = children;
179         node->n_children = n + 1;
180
181         return 0;
182
183 fail:
184         ec_free(children);
185         return -1;
186 }
187
188 #if 0 /* later */
189 int ec_node_del_child(struct ec_node *node, struct ec_node *child)
190 {
191         size_t i, n;
192
193         if (node == NULL || child == NULL)
194                 goto fail;
195
196         n = node->n_children;
197         for (i = 0; i < n; i++) {
198                 if (node->children[i] != child)
199                         continue;
200                 memcpy(&node->children[i], &node->children[i+1],
201                         (n - i - 1) * sizeof(child));
202                 return 0;
203         }
204
205 fail:
206         errno = EINVAL;
207         return -1;
208 }
209 #endif
210
211 struct ec_node *ec_node_find(struct ec_node *node, const char *id)
212 {
213         struct ec_node *child, *ret;
214         const char *node_id = ec_node_id(node);
215         size_t i, n;
216
217         if (id != NULL && node_id != NULL && !strcmp(node_id, id))
218                 return node;
219
220         n = node->n_children;
221         for (i = 0; i < n; i++) {
222                 child = node->children[i];
223                 ret = ec_node_find(child, id);
224                 if (ret != NULL)
225                         return ret;
226         }
227
228         return NULL;
229 }
230
231 const struct ec_node_type *ec_node_type(const struct ec_node *node)
232 {
233         return node->type;
234 }
235
236 struct ec_keyval *ec_node_attrs(const struct ec_node *node)
237 {
238         return node->attrs;
239 }
240
241 const char *ec_node_id(const struct ec_node *node)
242 {
243         return node->id;
244 }
245
246 static void __ec_node_dump(FILE *out,
247         const struct ec_node *node, size_t indent)
248 {
249         const char *id, *typename;
250         struct ec_node *child;
251         size_t i, n;
252
253         id = ec_node_id(node);
254         typename = node->type->name;
255
256         fprintf(out, "%*s" "type=%s id=%s %p\n",
257                 (int)indent * 4, "", typename, id, node);
258         n = node->n_children;
259         for (i = 0; i < n; i++) {
260                 child = node->children[i];
261                 __ec_node_dump(out, child, indent + 1);
262         }
263 }
264
265 void ec_node_dump(FILE *out, const struct ec_node *node)
266 {
267         fprintf(out, "------------------- node dump:\n");
268
269         if (node == NULL) {
270                 fprintf(out, "node is NULL\n");
271                 return;
272         }
273
274         __ec_node_dump(out, node, 0);
275 }
276
277 const char *ec_node_desc(const struct ec_node *node)
278 {
279         if (node->type->desc != NULL)
280                 return node->type->desc(node);
281
282         return node->desc;
283 }
284
285 int ec_node_check_type(const struct ec_node *node,
286                 const struct ec_node_type *type)
287 {
288         if (strcmp(node->type->name, type->name)) {
289                 errno = EINVAL;
290                 return -1;
291         }
292         return 0;
293 }
294
295 /* LCOV_EXCL_START */
296 static int ec_node_testcase(void)
297 {
298         struct ec_node *node = NULL;
299         const struct ec_node *child;
300         const struct ec_node_type *type;
301         FILE *f = NULL;
302         char *buf = NULL;
303         size_t buflen = 0;
304         int testres = 0;
305         int ret;
306
307         node = EC_NODE_SEQ(EC_NO_ID,
308                         ec_node_str("id_x", "x"),
309                         ec_node_str("id_y", "y"));
310         if (node == NULL)
311                 goto fail;
312
313         ec_node_clone(node);
314         ec_node_free(node);
315
316         f = open_memstream(&buf, &buflen);
317         if (f == NULL)
318                 goto fail;
319         ec_node_dump(f, node);
320         ec_node_type_dump(f);
321         ec_node_dump(f, NULL);
322         fclose(f);
323         f = NULL;
324
325         testres |= EC_TEST_CHECK(
326                 strstr(buf, "type=seq id=no-id"), "bad dump\n");
327         testres |= EC_TEST_CHECK(
328                 strstr(buf, "type=str id=id_x") &&
329                 strstr(strstr(buf, "type=str id=id_x") + 1,
330                         "type=str id=id_y"),
331                 "bad dump\n");
332         free(buf);
333         buf = NULL;
334
335         testres |= EC_TEST_CHECK(
336                 !strcmp(ec_node_type(node)->name, "seq") &&
337                 !strcmp(ec_node_id(node), EC_NO_ID) &&
338                 !strcmp(ec_node_desc(node), "<seq>"),
339                 "bad child 0");
340
341         testres |= EC_TEST_CHECK(
342                 ec_node_get_children_count(node) == 2,
343                 "bad children count\n");
344         child = ec_node_get_child(node, 0);
345         testres |= EC_TEST_CHECK(child != NULL &&
346                 !strcmp(ec_node_type(child)->name, "str") &&
347                 !strcmp(ec_node_id(child), "id_x"),
348                 "bad child 0");
349         child = ec_node_get_child(node, 1);
350         testres |= EC_TEST_CHECK(child != NULL &&
351                 !strcmp(ec_node_type(child)->name, "str") &&
352                 !strcmp(ec_node_id(child), "id_y"),
353                 "bad child 1");
354         child = ec_node_get_child(node, 2);
355         testres |= EC_TEST_CHECK(child == NULL,
356                 "child 2 should be NULL");
357
358         child = ec_node_find(node, "id_x");
359         testres |= EC_TEST_CHECK(child != NULL &&
360                 !strcmp(ec_node_type(child)->name, "str") &&
361                 !strcmp(ec_node_id(child), "id_x") &&
362                 !strcmp(ec_node_desc(child), "x"),
363                 "bad child id_x");
364         child = ec_node_find(node, "id_dezdex");
365         testres |= EC_TEST_CHECK(child == NULL,
366                 "child with wrong id should be NULL");
367
368         ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL);
369         testres |= EC_TEST_CHECK(ret == 0,
370                 "cannot set node attribute\n");
371
372         type = ec_node_type_lookup("seq");
373         testres |= EC_TEST_CHECK(type != NULL &&
374                 ec_node_check_type(node, type) == 0,
375                 "cannot get seq node type");
376         type = ec_node_type_lookup("str");
377         testres |= EC_TEST_CHECK(type != NULL &&
378                 ec_node_check_type(node, type) < 0,
379                 "node type should not be str");
380
381         ec_node_free(node);
382         node = NULL;
383
384         node = ec_node("deznuindez", EC_NO_ID);
385         testres |= EC_TEST_CHECK(node == NULL,
386                         "should not be able to create node\n");
387
388         return testres;
389
390 fail:
391         ec_node_free(node);
392         if (f != NULL)
393                 fclose(f);
394         free(buf);
395
396         return -1;
397 }
398 /* LCOV_EXCL_STOP */
399
400 static struct ec_test ec_node_test = {
401         .name = "node",
402         .test = ec_node_testcase,
403 };
404
405 EC_TEST_REGISTER(ec_node_test);