config for string node
[protos/libecoli.git] / lib / ecoli_config.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/queue.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <inttypes.h>
10
11 #include <ecoli_string.h>
12 #include <ecoli_malloc.h>
13 #include <ecoli_keyval.h>
14 #include <ecoli_node.h>
15 #include <ecoli_log.h>
16 #include <ecoli_test.h>
17 #include <ecoli_config.h>
18
19 EC_LOG_TYPE_REGISTER(config);
20
21 static int
22 __ec_config_dump(FILE *out, const char *key, const struct ec_config *config,
23         size_t indent);
24 static int
25 ec_config_dict_validate(const struct ec_keyval *dict,
26                         const struct ec_config_schema *schema,
27                         size_t schema_len);
28
29 /* return ec_value type as a string */
30 static const char *
31 ec_config_type_str(enum ec_config_type type)
32 {
33         switch (type) {
34         case EC_CONFIG_TYPE_BOOL: return "bool";
35         case EC_CONFIG_TYPE_INT64: return "int64";
36         case EC_CONFIG_TYPE_UINT64: return "uint64";
37         case EC_CONFIG_TYPE_STRING: return "string";
38         case EC_CONFIG_TYPE_NODE: return "node";
39         case EC_CONFIG_TYPE_LIST: return "list";
40         case EC_CONFIG_TYPE_DICT: return "dict";
41         default: return "unknown";
42         }
43 }
44
45 static int
46 __ec_config_schema_validate(const struct ec_config_schema *schema,
47                         size_t schema_len, enum ec_config_type type)
48 {
49         size_t i, j;
50         int ret;
51
52         if (type == EC_CONFIG_TYPE_LIST) {
53                 if (schema[0].key != NULL) {
54                         errno = EINVAL;
55                         EC_LOG(EC_LOG_ERR, "list schema key must be NULL\n");
56                         return -1;
57                 }
58         } else if (type == EC_CONFIG_TYPE_DICT) {
59                 for (i = 0; i < schema_len; i++) {
60                         if (schema[i].key == NULL) {
61                                 errno = EINVAL;
62                                 EC_LOG(EC_LOG_ERR,
63                                         "dict schema key should not be NULL\n");
64                                 return -1;
65                         }
66                 }
67         } else {
68                 errno = EINVAL;
69                 EC_LOG(EC_LOG_ERR, "invalid schema type\n");
70                 return -1;
71         }
72
73         for (i = 0; i < schema_len; i++) {
74                 /* check for duplicate name if more than one element */
75                 for (j = i + 1; j < schema_len; j++) {
76                         if (!strcmp(schema[i].key, schema[j].key)) {
77                                 errno = EEXIST;
78                                 EC_LOG(EC_LOG_ERR,
79                                         "duplicate key <%s> in schema\n",
80                                         schema[i].key);
81                                 return -1;
82                         }
83                 }
84
85                 switch (schema[i].type) {
86                 case EC_CONFIG_TYPE_BOOL:
87                 case EC_CONFIG_TYPE_INT64:
88                 case EC_CONFIG_TYPE_UINT64:
89                 case EC_CONFIG_TYPE_STRING:
90                 case EC_CONFIG_TYPE_NODE:
91                         if (schema[i].subschema != NULL ||
92                                         schema[i].subschema_len != 0) {
93                                 errno = EINVAL;
94                                 EC_LOG(EC_LOG_ERR,
95                                         "key <%s> should not have subtype/subschema\n",
96                                         schema[i].key);
97                                 return -1;
98                         }
99                         break;
100                 case EC_CONFIG_TYPE_LIST:
101                         if (schema[i].subschema == NULL ||
102                                         schema[i].subschema_len != 1) {
103                                 errno = EINVAL;
104                                 EC_LOG(EC_LOG_ERR,
105                                         "key <%s> must have subschema of length 1\n",
106                                         schema[i].key);
107                                 return -1;
108                         }
109                         break;
110                 case EC_CONFIG_TYPE_DICT:
111                         if (schema[i].subschema == NULL ||
112                                         schema[i].subschema_len == 0) {
113                                 errno = EINVAL;
114                                 EC_LOG(EC_LOG_ERR,
115                                         "key <%s> must have subschema\n",
116                                         schema[i].key);
117                                 return -1;
118                         }
119                         break;
120                 default:
121                         EC_LOG(EC_LOG_ERR, "invalid type for key <%s>\n",
122                                 schema[i].key);
123                         errno = EINVAL;
124                         return -1;
125                 }
126
127                 if (schema[i].subschema == NULL)
128                         continue;
129
130                 ret = __ec_config_schema_validate(schema[i].subschema,
131                                                 schema[i].subschema_len,
132                                                 schema[i].type);
133                 if (ret < 0) {
134                         EC_LOG(EC_LOG_ERR, "cannot parse subschema %s%s\n",
135                                 schema[i].key ? "key=" : "",
136                                 schema[i].key ? : "");
137                         return ret;
138                 }
139         }
140
141         return 0;
142 }
143
144 int
145 ec_config_schema_validate(const struct ec_config_schema *schema,
146                         size_t schema_len)
147 {
148         return __ec_config_schema_validate(schema, schema_len,
149                                         EC_CONFIG_TYPE_DICT);
150 }
151
152 static void
153 __ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
154                         size_t schema_len, size_t indent)
155 {
156         size_t i;
157
158         for (i = 0; i < schema_len; i++) {
159                 fprintf(out, "%*s" "%s%s%stype=%s desc='%s'\n",
160                         (int)indent * 4, "",
161                         schema[i].key ? "key=": "",
162                         schema[i].key ? : "",
163                         schema[i].key ? " ": "",
164                         ec_config_type_str(schema[i].type),
165                         schema[i].desc);
166                 if (schema[i].subschema == NULL)
167                         continue;
168                 __ec_config_schema_dump(out, schema[i].subschema,
169                                         schema[i].subschema_len, indent + 1);
170         }
171 }
172
173 void
174 ec_config_schema_dump(FILE *out, const struct ec_config_schema *schema,
175                 size_t schema_len)
176 {
177         fprintf(out, "------------------- schema dump:\n");
178
179         if (schema == NULL || schema_len == 0) {
180                 fprintf(out, "no schema\n");
181                 return;
182         }
183
184         __ec_config_schema_dump(out, schema, schema_len, 0);
185 }
186
187 struct ec_config *
188 ec_config_bool(bool boolean)
189 {
190         struct ec_config *value = NULL;
191
192         value = ec_calloc(1, sizeof(*value));
193         if (value == NULL)
194                 return NULL;
195
196         value->type = EC_CONFIG_TYPE_BOOL;
197         value->boolean = boolean;
198
199         return value;
200 }
201
202 struct ec_config *
203 ec_config_i64(int64_t i64)
204 {
205         struct ec_config *value = NULL;
206
207         value = ec_calloc(1, sizeof(*value));
208         if (value == NULL)
209                 return NULL;
210
211         value->type = EC_CONFIG_TYPE_INT64;
212         value->i64 = i64;
213
214         return value;
215 }
216
217 struct ec_config *
218 ec_config_u64(uint64_t u64)
219 {
220         struct ec_config *value = NULL;
221
222         value = ec_calloc(1, sizeof(*value));
223         if (value == NULL)
224                 return NULL;
225
226         value->type = EC_CONFIG_TYPE_UINT64;
227         value->u64 = u64;
228
229         return value;
230 }
231
232 /* duplicate string */
233 struct ec_config *
234 ec_config_string(const char *string)
235 {
236         struct ec_config *value = NULL;
237         char *s = NULL;
238
239         if (string == NULL)
240                 goto fail;
241
242         s = ec_strdup(string);
243         if (s == NULL)
244                 goto fail;
245
246         value = ec_calloc(1, sizeof(*value));
247         if (value == NULL)
248                 goto fail;
249
250         value->type = EC_CONFIG_TYPE_STRING;
251         value->string = s;
252
253         return value;
254
255 fail:
256         ec_free(value);
257         ec_free(s);
258         return NULL;
259 }
260
261 /* "consume" the node */
262 struct ec_config *
263 ec_config_node(struct ec_node *node)
264 {
265         struct ec_config *value = NULL;
266
267         if (node == NULL)
268                 goto fail;
269
270         value = ec_calloc(1, sizeof(*value));
271         if (value == NULL)
272                 goto fail;
273
274         value->type = EC_CONFIG_TYPE_NODE;
275         value->node = node;
276
277         return value;
278
279 fail:
280         ec_node_free(node);
281         ec_free(value);
282         return NULL;
283 }
284
285 struct ec_config *
286 ec_config_dict(void)
287 {
288         struct ec_config *value = NULL;
289         struct ec_keyval *dict = NULL;
290
291         dict = ec_keyval();
292         if (dict == NULL)
293                 goto fail;
294
295         value = ec_calloc(1, sizeof(*value));
296         if (value == NULL)
297                 goto fail;
298
299         value->type = EC_CONFIG_TYPE_DICT;
300         value->dict = dict;
301
302         return value;
303
304 fail:
305         ec_keyval_free(dict);
306         ec_free(value);
307         return NULL;
308 }
309
310 struct ec_config *
311 ec_config_list(void)
312 {
313         struct ec_config *value = NULL;
314
315         value = ec_calloc(1, sizeof(*value));
316         if (value == NULL)
317                 return NULL;
318
319         value->type = EC_CONFIG_TYPE_LIST;
320         TAILQ_INIT(&value->list);
321
322         return value;
323 }
324
325 static const struct ec_config_schema *
326 ec_config_schema_lookup(const struct ec_config_schema *schema,
327                         size_t schema_len, const char *key,
328                         enum ec_config_type type)
329 {
330         size_t i;
331
332         for (i = 0; i < schema_len; i++) {
333                 if (!strcmp(key, schema[i].key) &&
334                                 type == schema[i].type)
335                         return &schema[i];
336         }
337
338         errno = ENOENT;
339         return NULL;
340 }
341
342 void
343 ec_config_free(struct ec_config *value)
344 {
345         if (value == NULL)
346                 return;
347
348         switch (value->type) {
349         case EC_CONFIG_TYPE_STRING:
350                 ec_free(value->string);
351                 break;
352         case EC_CONFIG_TYPE_NODE:
353                 ec_node_free(value->node);
354                 break;
355         case EC_CONFIG_TYPE_LIST:
356                 while (!TAILQ_EMPTY(&value->list)) {
357                         struct ec_config *v;
358                         v = TAILQ_FIRST(&value->list);
359                         TAILQ_REMOVE(&value->list, v, next);
360                         ec_config_free(v);
361                 }
362                 break;
363         case EC_CONFIG_TYPE_DICT:
364                 ec_keyval_free(value->dict);
365                 break;
366         default:
367                 break;
368         }
369
370         ec_free(value);
371 }
372
373 static int
374 ec_config_list_cmp(const struct ec_config_list *list1,
375                 const struct ec_config_list *list2)
376 {
377         const struct ec_config *v1, *v2;
378
379         for (v1 = TAILQ_FIRST(list1), v2 = TAILQ_FIRST(list2);
380              v1 != NULL && v2 != NULL;
381              v1 = TAILQ_NEXT(v1, next), v2 = TAILQ_NEXT(v2, next)) {
382                 if (ec_config_cmp(v1, v2))
383                         return -1;
384         }
385         if (v1 != NULL || v2 != NULL)
386                 return -1;
387
388         return 0;
389 }
390
391 /* XXX -> ec_keyval_cmp() */
392 static int
393 ec_config_dict_cmp(const struct ec_keyval *d1,
394                 const struct ec_keyval *d2)
395 {
396         const struct ec_config *v1, *v2;
397         struct ec_keyval_iter *iter = NULL;
398         const char *key;
399
400         if (ec_keyval_len(d1) != ec_keyval_len(d2))
401                 return -1;
402
403         for (iter = ec_keyval_iter(d1);
404              ec_keyval_iter_valid(iter);
405              ec_keyval_iter_next(iter)) {
406                 key = ec_keyval_iter_get_key(iter);
407                 v1 = ec_keyval_iter_get_val(iter);
408                 v2 = ec_keyval_get(d2, key);
409
410                 if (ec_config_cmp(v1, v2))
411                         goto fail;
412         }
413
414         ec_keyval_iter_free(iter);
415         return 0;
416
417 fail:
418         ec_keyval_iter_free(iter);
419         return -1;
420 }
421
422 int
423 ec_config_cmp(const struct ec_config *value1,
424                 const struct ec_config *value2)
425 {
426         if (value1->type != value2->type)
427                 return -1;
428
429         switch (value1->type) {
430         case EC_CONFIG_TYPE_BOOL:
431                 if (value1->boolean == value2->boolean)
432                         return 0;
433         case EC_CONFIG_TYPE_INT64:
434                 if (value1->i64 == value2->i64)
435                         return 0;
436         case EC_CONFIG_TYPE_UINT64:
437                 if (value1->u64 == value2->u64)
438                         return 0;
439         case EC_CONFIG_TYPE_STRING:
440                 if (!strcmp(value1->string, value2->string))
441                         return 0;
442         case EC_CONFIG_TYPE_NODE:
443                 if (value1->node == value2->node)
444                         return 0;
445         case EC_CONFIG_TYPE_LIST:
446                 return ec_config_list_cmp(&value1->list, &value2->list);
447         case EC_CONFIG_TYPE_DICT:
448                 return ec_config_dict_cmp(value1->dict, value2->dict);
449         default:
450                 break;
451         }
452
453         return -1;
454 }
455
456 static int
457 ec_config_list_validate(const struct ec_config_list *list,
458                         const struct ec_config_schema *sch)
459 {
460         const struct ec_config *value;
461
462         TAILQ_FOREACH(value, list, next) {
463                 if (value->type != sch->type) {
464                         errno = EBADMSG;
465                         return -1;
466                 }
467
468                 if (value->type == EC_CONFIG_TYPE_LIST) {
469                         if (ec_config_list_validate(&value->list,
470                                                         sch->subschema) < 0)
471                                 return -1;
472                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
473                         if (ec_config_dict_validate(value->dict,
474                                         sch->subschema, sch->subschema_len) < 0)
475                                 return -1;
476                 }
477         }
478
479         return 0;
480 }
481
482 static int
483 ec_config_dict_validate(const struct ec_keyval *dict,
484                         const struct ec_config_schema *schema,
485                         size_t schema_len)
486 {
487         const struct ec_config *value;
488         struct ec_keyval_iter *iter = NULL;
489         const struct ec_config_schema *sch;
490         const char *key;
491
492         for (iter = ec_keyval_iter(dict);
493              ec_keyval_iter_valid(iter);
494              ec_keyval_iter_next(iter)) {
495
496                 key = ec_keyval_iter_get_key(iter);
497                 value = ec_keyval_iter_get_val(iter);
498                 sch = ec_config_schema_lookup(schema, schema_len,
499                                         key, value->type);
500                 if (sch == NULL) {
501                         errno = EBADMSG;
502                         goto fail;
503                 }
504
505                 if (value->type == EC_CONFIG_TYPE_LIST) {
506                         if (ec_config_list_validate(&value->list,
507                                                         sch->subschema) < 0)
508                                 goto fail;
509                 } else if (value->type == EC_CONFIG_TYPE_DICT) {
510                         if (ec_config_dict_validate(value->dict,
511                                         sch->subschema, sch->subschema_len) < 0)
512                                 goto fail;
513                 }
514         }
515
516         ec_keyval_iter_free(iter);
517         return 0;
518
519 fail:
520         ec_keyval_iter_free(iter);
521         return -1;
522 }
523
524 int
525 ec_config_validate(const struct ec_config *dict,
526                 const struct ec_config_schema *schema,
527                 size_t schema_len)
528 {
529         if (dict->type != EC_CONFIG_TYPE_DICT || schema == NULL) {
530                 errno = EINVAL;
531                 goto fail;
532         }
533
534         if (ec_config_dict_validate(dict->dict, schema, schema_len) < 0)
535                 goto fail;
536
537         return 0
538 ;
539 fail:
540         return -1;
541 }
542
543 struct ec_config *
544 ec_config_get(const struct ec_config *config, const char *key)
545 {
546         if (config == NULL)
547                 return NULL;
548
549         return ec_keyval_get(config->dict, key);
550 }
551
552 struct ec_config *
553 ec_config_list_first(struct ec_config *list)
554 {
555         if (list  == NULL || list->type != EC_CONFIG_TYPE_LIST) {
556                 errno = EINVAL;
557                 return NULL;
558         }
559
560         return TAILQ_FIRST(&list->list);
561 }
562
563 struct ec_config *
564 ec_config_list_next(struct ec_config *list, struct ec_config *config)
565 {
566         (void)list;
567         return TAILQ_NEXT(config, next);
568 }
569
570 /* value is consumed */
571 int ec_config_dict_set(struct ec_config *config, const char *key,
572                 struct ec_config *value)
573 {
574         void (*free_cb)(struct ec_config *) = ec_config_free;
575
576         if (config == NULL || key == NULL || value == NULL) {
577                 errno = EINVAL;
578                 goto fail;
579         }
580         if (config->type != EC_CONFIG_TYPE_DICT) {
581                 errno = EINVAL;
582                 goto fail;
583         }
584
585         return ec_keyval_set(config->dict, key, value,
586                         (void (*)(void *))free_cb);
587
588 fail:
589         ec_config_free(value);
590         return -1;
591 }
592
593 int ec_config_dict_del(struct ec_config *config, const char *key)
594 {
595         if (config == NULL || key == NULL) {
596                 errno = EINVAL;
597                 return -1;
598         }
599         if (config->type != EC_CONFIG_TYPE_DICT) {
600                 errno = EINVAL;
601                 return -1;
602         }
603
604         return ec_keyval_del(config->dict, key);
605 }
606
607 /* value is consumed */
608 int
609 ec_config_list_add(struct ec_config *list,
610                 struct ec_config *value)
611 {
612         if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) {
613                 errno = EINVAL;
614                 goto fail;
615         }
616
617         TAILQ_INSERT_TAIL(&list->list, value, next);
618
619         return 0;
620
621 fail:
622         ec_config_free(value);
623         return -1;
624 }
625
626 int ec_config_list_del(struct ec_config *list, struct ec_config *config)
627 {
628         if (list == NULL || list->type != EC_CONFIG_TYPE_LIST) {
629                 errno = EINVAL;
630                 return -1;
631         }
632
633         TAILQ_REMOVE(&list->list, config, next);
634         ec_config_free(config);
635         return 0;
636 }
637
638 static int
639 ec_config_list_dump(FILE *out, const struct ec_config_list *list,
640                 size_t indent)
641 {
642         const struct ec_config *v;
643
644         fprintf(out, "%*s" "type=list:\n", (int)indent * 4, "");
645
646         TAILQ_FOREACH(v, list, next) {
647                 if (__ec_config_dump(out, NULL, v, indent + 1) < 0)
648                         return -1;
649         }
650
651         return 0;
652 }
653
654 static int
655 ec_config_dict_dump(FILE *out, const struct ec_keyval *dict,
656                 size_t indent)
657 {
658         const struct ec_config *value;
659         struct ec_keyval_iter *iter;
660         const char *key;
661
662         fprintf(out, "%*s" "type=dict:\n", (int)indent * 4, "");
663         for (iter = ec_keyval_iter(dict);
664              ec_keyval_iter_valid(iter);
665              ec_keyval_iter_next(iter)) {
666                 key = ec_keyval_iter_get_key(iter);
667                 value = ec_keyval_iter_get_val(iter);
668                 if (__ec_config_dump(out, key, value, indent + 1) < 0)
669                         goto fail;
670         }
671         ec_keyval_iter_free(iter);
672         return 0;
673
674 fail:
675         ec_keyval_iter_free(iter);
676         return -1;
677 }
678
679 static int
680 __ec_config_dump(FILE *out, const char *key, const struct ec_config *value,
681                 size_t indent)
682 {
683         char *val_str = NULL;
684
685         switch (value->type) {
686         case EC_CONFIG_TYPE_BOOL:
687                 if (value->boolean)
688                         ec_asprintf(&val_str, "true");
689                 else
690                         ec_asprintf(&val_str, "false");
691                 break;
692         case EC_CONFIG_TYPE_INT64:
693                 ec_asprintf(&val_str, "%"PRIu64, value->u64);
694                 break;
695         case EC_CONFIG_TYPE_UINT64:
696                 ec_asprintf(&val_str, "%"PRIi64, value->i64);
697                 break;
698         case EC_CONFIG_TYPE_STRING:
699                 ec_asprintf(&val_str, "%s", value->string);
700                 break;
701         case EC_CONFIG_TYPE_NODE:
702                 ec_asprintf(&val_str, "%p", value->node);
703                 break;
704         case EC_CONFIG_TYPE_LIST:
705                 return ec_config_list_dump(out, &value->list, indent);
706         case EC_CONFIG_TYPE_DICT:
707                 return ec_config_dict_dump(out, value->dict, indent);
708         default:
709                 errno = EINVAL;
710                 break;
711         }
712
713         /* errno is already set on error */
714         if (val_str == NULL)
715                 goto fail;
716
717         fprintf(out, "%*s" "%s%s%stype=%s val=%s\n", (int)indent * 4, "",
718                 key ? "key=": "",
719                 key ? key: "",
720                 key ? " ": "",
721                 ec_config_type_str(value->type), val_str);
722
723         ec_free(val_str);
724         return 0;
725
726 fail:
727         ec_free(val_str);
728         return -1;
729 }
730
731 void
732 ec_config_dump(FILE *out, const struct ec_config *config)
733 {
734         fprintf(out, "------------------- config dump:\n");
735
736         if (config == NULL) {
737                 fprintf(out, "no config\n");
738                 return;
739         }
740
741         if (__ec_config_dump(out, NULL, config, 0) < 0)
742                 fprintf(out, "error while dumping\n");
743 }
744
745 /* LCOV_EXCL_START */
746 static const struct ec_config_schema sch_intlist_elt[] = {
747         {
748                 .desc = "This is a description for int",
749                 .type = EC_CONFIG_TYPE_INT64,
750         },
751 };
752
753 static const struct ec_config_schema sch_dict[] = {
754         {
755                 .key = "my_int",
756                 .desc = "This is a description for int",
757                 .type = EC_CONFIG_TYPE_INT64,
758         },
759         {
760                 .key = "my_int2",
761                 .desc = "This is a description for int2",
762                 .type = EC_CONFIG_TYPE_INT64,
763         },
764 };
765
766 static const struct ec_config_schema sch_dictlist_elt[] = {
767         {
768                 .desc = "This is a description for dict",
769                 .type = EC_CONFIG_TYPE_DICT,
770                 .subschema = sch_dict,
771                 .subschema_len = EC_COUNT_OF(sch_dict),
772         },
773 };
774
775 static const struct ec_config_schema sch_baseconfig[] = {
776         {
777                 .key = "my_bool",
778                 .desc = "This is a description for bool",
779                 .type = EC_CONFIG_TYPE_BOOL,
780         },
781         {
782                 .key = "my_int",
783                 .desc = "This is a description for int",
784                 .type = EC_CONFIG_TYPE_INT64,
785         },
786         {
787                 .key = "my_string",
788                 .desc = "This is a description for string",
789                 .type = EC_CONFIG_TYPE_STRING,
790         },
791         {
792                 .key = "my_node",
793                 .desc = "This is a description for node",
794                 .type = EC_CONFIG_TYPE_NODE,
795         },
796         {
797                 .key = "my_intlist",
798                 .desc = "This is a description for list",
799                 .type = EC_CONFIG_TYPE_LIST,
800                 .subschema = sch_intlist_elt,
801                 .subschema_len = EC_COUNT_OF(sch_intlist_elt),
802         },
803         {
804                 .key = "my_dictlist",
805                 .desc = "This is a description for list",
806                 .type = EC_CONFIG_TYPE_LIST,
807                 .subschema = sch_dictlist_elt,
808                 .subschema_len = EC_COUNT_OF(sch_dictlist_elt),
809         },
810 };
811
812 static int ec_config_testcase(void)
813 {
814         struct ec_node *node = NULL;
815         struct ec_keyval *dict = NULL;
816         const struct ec_config *value = NULL;
817         struct ec_config *config = NULL, *list = NULL, *subconfig = NULL;
818         struct ec_config *list_, *config_;
819         int testres = 0;
820         int ret;
821
822         node = ec_node("empty", EC_NO_ID);
823         if (node == NULL)
824                 goto fail;
825
826         if (ec_config_schema_validate(sch_baseconfig,
827                                         EC_COUNT_OF(sch_baseconfig)) < 0) {
828                 EC_LOG(EC_LOG_ERR, "invalid config schema\n");
829                 goto fail;
830         }
831
832         ec_config_schema_dump(stdout, sch_baseconfig,
833                         EC_COUNT_OF(sch_baseconfig));
834
835         config = ec_config_dict();
836         if (config == NULL)
837                 goto fail;
838
839         ret = ec_config_dict_set(config, "my_bool", ec_config_bool(true));
840         testres |= EC_TEST_CHECK(ret == 0, "cannot set boolean");
841         value = ec_config_get(config, "my_bool");
842         testres |= EC_TEST_CHECK(
843                 value != NULL &&
844                 value->type == EC_CONFIG_TYPE_BOOL &&
845                 value->boolean == true,
846                 "unexpected boolean value");
847
848         ret = ec_config_dict_set(config, "my_int", ec_config_i64(1234));
849         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
850         value = ec_config_get(config, "my_int");
851         testres |= EC_TEST_CHECK(
852                 value != NULL &&
853                 value->type == EC_CONFIG_TYPE_INT64 &&
854                 value->i64 == 1234,
855                 "unexpected int value");
856
857         testres |= EC_TEST_CHECK(
858                 ec_config_validate(config, sch_baseconfig,
859                                 EC_COUNT_OF(sch_baseconfig)) == 0,
860                 "cannot validate config\n");
861
862         ret = ec_config_dict_set(config, "my_string", ec_config_string("toto"));
863         testres |= EC_TEST_CHECK(ret == 0, "cannot set string");
864         value = ec_config_get(config, "my_string");
865         testres |= EC_TEST_CHECK(
866                 value != NULL &&
867                 value->type == EC_CONFIG_TYPE_STRING &&
868                 !strcmp(value->string, "toto"),
869                 "unexpected string value");
870
871         list = ec_config_list();
872         if (list == NULL)
873                 goto fail;
874
875         subconfig = ec_config_dict();
876         if (subconfig == NULL)
877                 goto fail;
878
879         ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(1));
880         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
881         value = ec_config_get(subconfig, "my_int");
882         testres |= EC_TEST_CHECK(
883                 value != NULL &&
884                 value->type == EC_CONFIG_TYPE_INT64 &&
885                 value->i64 == 1,
886                 "unexpected int value");
887
888         ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(2));
889         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
890         value = ec_config_get(subconfig, "my_int2");
891         testres |= EC_TEST_CHECK(
892                 value != NULL &&
893                 value->type == EC_CONFIG_TYPE_INT64 &&
894                 value->i64 == 2,
895                 "unexpected int value");
896
897         testres |= EC_TEST_CHECK(
898                 ec_config_validate(subconfig, sch_dict,
899                                 EC_COUNT_OF(sch_dict)) == 0,
900                 "cannot validate subconfig\n");
901
902         ret = ec_config_list_add(list, subconfig);
903         subconfig = NULL; /* freed */
904         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
905
906         subconfig = ec_config_dict();
907         if (subconfig == NULL)
908                 goto fail;
909
910         ret = ec_config_dict_set(subconfig, "my_int", ec_config_i64(3));
911         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
912         value = ec_config_get(subconfig, "my_int");
913         testres |= EC_TEST_CHECK(
914                 value != NULL &&
915                 value->type == EC_CONFIG_TYPE_INT64 &&
916                 value->i64 == 3,
917                 "unexpected int value");
918
919         ret = ec_config_dict_set(subconfig, "my_int2", ec_config_i64(4));
920         testres |= EC_TEST_CHECK(ret == 0, "cannot set int");
921         value = ec_config_get(subconfig, "my_int2");
922         testres |= EC_TEST_CHECK(
923                 value != NULL &&
924                 value->type == EC_CONFIG_TYPE_INT64 &&
925                 value->i64 == 4,
926                 "unexpected int value");
927
928         testres |= EC_TEST_CHECK(
929                 ec_config_validate(subconfig, sch_dict,
930                                 EC_COUNT_OF(sch_dict)) == 0,
931                 "cannot validate subconfig\n");
932
933         ret = ec_config_list_add(list, subconfig);
934         subconfig = NULL; /* freed */
935         testres |= EC_TEST_CHECK(ret == 0, "cannot add in list");
936
937         ret = ec_config_dict_set(config, "my_dictlist", list);
938         list = NULL;
939         testres |= EC_TEST_CHECK(ret == 0, "cannot set list");
940
941         testres |= EC_TEST_CHECK(
942                 ec_config_validate(config, sch_baseconfig,
943                                 EC_COUNT_OF(sch_baseconfig)) == 0,
944                 "cannot validate config\n");
945
946         list_ = ec_config_get(config, "my_dictlist");
947         printf("list = %p\n", list_);
948         for (config_ = ec_config_list_first(list_); config_ != NULL;
949              config_ = ec_config_list_next(list_, config_)) {
950                 ec_config_dump(stdout, config_);
951         }
952
953         ec_config_dump(stdout, config);
954
955         /* remove the first element */
956         ec_config_list_del(list_, ec_config_list_first(list_));
957         testres |= EC_TEST_CHECK(
958                 ec_config_validate(config, sch_baseconfig,
959                                 EC_COUNT_OF(sch_baseconfig)) == 0,
960                 "cannot validate config\n");
961
962         ec_config_dump(stdout, config);
963
964         ec_config_free(list);
965         ec_config_free(subconfig);
966         ec_config_free(config);
967         ec_keyval_free(dict);
968         ec_node_free(node);
969
970         return testres;
971
972 fail:
973         ec_config_free(list);
974         ec_config_free(subconfig);
975         ec_config_free(config);
976         ec_keyval_free(dict);
977         ec_node_free(node);
978
979         return -1;
980 }
981 /* LCOV_EXCL_STOP */
982
983 static struct ec_test ec_config_test = {
984         .name = "config",
985         .test = ec_config_testcase,
986 };
987
988 EC_TEST_REGISTER(ec_config_test);