test coverage for ec_node
[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         if (node->id == NULL)
244                 return "None";
245         return node->id;
246 }
247
248 static void __ec_node_dump(FILE *out,
249         const struct ec_node *node, size_t indent)
250 {
251         const char *id, *typename;
252         struct ec_node *child;
253         size_t i, n;
254
255         id = ec_node_id(node);
256         typename = node->type->name;
257
258         fprintf(out, "%*s" "type=%s id=%s %p\n",
259                 (int)indent * 4, "", typename, id, node);
260         n = node->n_children;
261         for (i = 0; i < n; i++) {
262                 child = node->children[i];
263                 __ec_node_dump(out, child, indent + 1);
264         }
265 }
266
267 void ec_node_dump(FILE *out, const struct ec_node *node)
268 {
269         fprintf(out, "------------------- node dump:\n");
270
271         if (node == NULL) {
272                 fprintf(out, "node is NULL\n");
273                 return;
274         }
275
276         __ec_node_dump(out, node, 0);
277 }
278
279 const char *ec_node_desc(const struct ec_node *node)
280 {
281         if (node->type->desc != NULL)
282                 return node->type->desc(node);
283
284         return node->desc;
285 }
286
287 int ec_node_check_type(const struct ec_node *node,
288                 const struct ec_node_type *type)
289 {
290         if (strcmp(node->type->name, type->name)) {
291                 errno = EINVAL;
292                 return -1;
293         }
294         return 0;
295 }
296
297 /* LCOV_EXCL_START */
298 static int ec_node_testcase(void)
299 {
300         struct ec_node *node = NULL;
301         const struct ec_node *child;
302         const struct ec_node_type *type;
303         FILE *f = NULL;
304         char *buf = NULL;
305         size_t buflen = 0;
306         int testres = 0;
307         int ret;
308
309         f = open_memstream(&buf, &buflen);
310         if (f == NULL)
311                 goto fail;
312
313         node = EC_NODE_SEQ(EC_NO_ID,
314                         ec_node_str("id_x", "x"),
315                         ec_node_str("id_y", "y"));
316         if (node == NULL)
317                 goto fail;
318
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
334         testres |= EC_TEST_CHECK(
335                 ec_node_get_children_count(node) == 2,
336                 "bad children count\n");
337         child = ec_node_get_child(node, 0);
338         testres |= EC_TEST_CHECK(child != NULL &&
339                 !strcmp(ec_node_type(child)->name, "str") &&
340                 !strcmp(ec_node_id(child), "id_x"),
341                 "bad child 0");
342         child = ec_node_get_child(node, 1);
343         testres |= EC_TEST_CHECK(child != NULL &&
344                 !strcmp(ec_node_type(child)->name, "str") &&
345                 !strcmp(ec_node_id(child), "id_y"),
346                 "bad child 1");
347         child = ec_node_get_child(node, 2);
348         testres |= EC_TEST_CHECK(child == NULL,
349                 "child 2 should be NULL");
350
351         child = ec_node_find(node, "id_x");
352         testres |= EC_TEST_CHECK(child != NULL &&
353                 !strcmp(ec_node_type(child)->name, "str") &&
354                 !strcmp(ec_node_id(child), "id_x"),
355                 "bad child id_x");
356         child = ec_node_find(node, "id_dezdex");
357         testres |= EC_TEST_CHECK(child == NULL,
358                 "child with wrong id should be NULL");
359
360         ret = ec_keyval_set(ec_node_attrs(node), "key", "val", NULL);
361         testres |= EC_TEST_CHECK(ret == 0,
362                 "cannot set node attribute\n");
363
364         type = ec_node_type_lookup("seq");
365         testres |= EC_TEST_CHECK(type != NULL &&
366                 ec_node_check_type(node, type) == 0,
367                 "cannot get seq node type");
368         type = ec_node_type_lookup("str");
369         testres |= EC_TEST_CHECK(type != NULL &&
370                 ec_node_check_type(node, type) < 0,
371                 "node type should not be str");
372
373         ec_node_free(node);
374         node = NULL;
375
376         node = ec_node("deznuindez", EC_NO_ID);
377         testres |= EC_TEST_CHECK(node == NULL,
378                         "should not be able to create node\n");
379
380         return testres;
381
382 fail:
383         ec_node_free(node);
384         if (f != NULL)
385                 fclose(f);
386
387         return -1;
388 }
389 /* LCOV_EXCL_STOP */
390
391 static struct ec_test ec_node_test = {
392         .name = "node",
393         .test = ec_node_testcase,
394 };
395
396 EC_TEST_REGISTER(ec_node_test);