57d509705e072b3fad50642e3a2720ab0793c34a
[protos/libecoli.git] / src / ecoli_node_cond.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/queue.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <stdarg.h>
11 #include <errno.h>
12 #include <limits.h>
13
14 #include <ecoli_init.h>
15 #include <ecoli_malloc.h>
16 #include <ecoli_log.h>
17 #include <ecoli_test.h>
18 #include <ecoli_strvec.h>
19 #include <ecoli_node.h>
20 #include <ecoli_config.h>
21 #include <ecoli_dict.h>
22 #include <ecoli_htable.h>
23 #include <ecoli_parse.h>
24 #include <ecoli_complete.h>
25 #include <ecoli_node_str.h>
26 #include <ecoli_node_re.h>
27 #include <ecoli_node_many.h>
28 #include <ecoli_node_subset.h>
29 #include <ecoli_node_seq.h>
30 #include <ecoli_node_or.h>
31 #include <ecoli_node_option.h>
32 #include <ecoli_node_any.h>
33 #include <ecoli_node_many.h>
34 #include <ecoli_node_bypass.h>
35 #include <ecoli_node_re_lex.h>
36 #include <ecoli_node_cond.h>
37
38 EC_LOG_TYPE_REGISTER(node_cond);
39
40 static struct ec_node *ec_node_cond_parser; /* the expression parser. */
41 static struct ec_dict *ec_node_cond_functions; /* functions dictionary */
42
43 struct ec_node_cond {
44         struct ec_node gen;
45         char *cond_str;                /* the condition string. */
46         struct ec_parse *parsed_cond;  /* the parsed condition. */
47         struct ec_node *child;         /* the child node. */
48 };
49
50 enum cond_result_type {
51         NODESET,
52         BOOLEAN,
53         INT,
54         STR,
55 };
56
57 /*
58   compare(eq|ne|gt|lt|ge|le, x, y)
59   find(nodeset, id)
60   count(nodeset)
61
62   find by attrs? get_attr ?
63 */
64
65 struct cond_result {
66         enum cond_result_type type;
67         union {
68                 struct ec_htable *htable;
69                 char *str;
70                 int64_t int64;
71                 bool boolean;
72         };
73 };
74
75 typedef struct cond_result *(cond_func_t)(
76         const struct ec_parse *state,
77         struct cond_result **in, size_t in_len);
78
79 void cond_result_free(struct cond_result *res)
80 {
81         if (res == NULL)
82                 return;
83
84         switch (res->type) {
85         case NODESET:
86                 ec_htable_free(res->htable);
87                 break;
88         case STR:
89                 ec_free(res->str);
90                 break;
91         case BOOLEAN:
92         case INT:
93                 break;
94         }
95
96         ec_free(res);
97 }
98
99 void cond_result_table_free(struct cond_result **table, size_t len)
100 {
101         size_t i;
102
103         for (i = 0; i < len; i++) {
104                 cond_result_free(table[i]);
105                 table[i] = NULL;
106         }
107         ec_free(table);
108 }
109
110 static struct ec_node *
111 ec_node_cond_build_parser(void)
112 {
113         struct ec_node *lex = NULL;
114         struct ec_node *expr = NULL;
115         int ret;
116
117         expr = ec_node("or", "id_arg");
118         if (expr == NULL)
119                 goto fail;
120
121         if (ec_node_or_add(expr,
122                 EC_NODE_SEQ("id_function",
123                         ec_node_any("id_function_name", "a_identifier"),
124                         ec_node_any(EC_NO_ID, "a_open"),
125                         ec_node_option("id_arg_list",
126                                 EC_NODE_SEQ(EC_NO_ID,
127                                         ec_node_clone(expr),
128                                         ec_node_many(EC_NO_ID,
129                                                 EC_NODE_SEQ(EC_NO_ID,
130                                                         ec_node_str(EC_NO_ID,
131                                                                 ","),
132                                                         ec_node_clone(expr)),
133                                         0, 0))),
134                         ec_node_any(EC_NO_ID, "a_close"))) < 0)
135                 goto fail;
136
137         if (ec_node_or_add(expr,
138                         ec_node_any("id_value", "a_identifier")) < 0)
139                 goto fail;
140
141         /* prepend a lexer to the expression node */
142         lex = ec_node_re_lex(EC_NO_ID, ec_node_clone(expr));
143         if (lex == NULL)
144                 goto fail;
145
146         ec_node_free(expr);
147         expr = NULL;
148
149         ret = ec_node_re_lex_add(lex, "[_a-zA-Z][._a-zA-Z0-9]*", 1,
150                                 "a_identifier");
151         if (ret < 0)
152                 goto fail;
153         ret = ec_node_re_lex_add(lex, "\\(", 1, "a_open");
154         if (ret < 0)
155                 goto fail;
156         ret = ec_node_re_lex_add(lex, "\\)", 1, "a_close");
157         if (ret < 0)
158                 goto fail;
159         ret = ec_node_re_lex_add(lex, ",", 1, NULL);
160         if (ret < 0)
161                 goto fail;
162         ret = ec_node_re_lex_add(lex, "[        ]", 0, NULL);
163         if (ret < 0)
164                 goto fail;
165
166         return lex;
167
168 fail:
169         ec_node_free(lex);
170         ec_node_free(expr);
171
172         return NULL;
173 }
174
175 static struct ec_parse *
176 ec_node_cond_build(const char *cond_str)
177 {
178         struct ec_parse *p = NULL;
179
180         /* parse the condition expression */
181         p = ec_node_parse(ec_node_cond_parser, cond_str);
182         if (p == NULL)
183                 goto fail;
184
185         if (!ec_parse_matches(p)) {
186                 errno = EINVAL;
187                 goto fail;
188         }
189         if (!ec_parse_has_child(p)) {
190                 errno = EINVAL;
191                 goto fail;
192         }
193
194         return p;
195
196 fail:
197         ec_parse_free(p);
198         return NULL;
199 }
200
201 static struct cond_result *
202 eval_root(const struct ec_parse *state, struct cond_result **in, size_t in_len)
203 {
204         struct cond_result *out = NULL;
205         const struct ec_parse *root = NULL;
206
207         (void)in;
208
209         if (in_len != 0) {
210                 EC_LOG(LOG_ERR, "root() does not take any argument\n");
211                 errno = EINVAL;
212                 goto fail;
213         }
214
215         out = ec_malloc(sizeof(*out));
216         if (out == NULL)
217                 goto fail;
218
219         out->type = NODESET;
220         out->htable = ec_htable();
221         if (out->htable == NULL)
222                 goto fail;
223
224         root = ec_parse_get_root(state);
225         if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
226                 goto fail;
227
228         cond_result_table_free(in, in_len);
229         return out;
230
231 fail:
232         cond_result_free(out);
233         cond_result_table_free(in, in_len);
234         return NULL;
235 }
236
237 static struct cond_result *
238 eval_current(const struct ec_parse *state, struct cond_result **in,
239         size_t in_len)
240 {
241         struct cond_result *out = NULL;
242
243         (void)in;
244
245         if (in_len != 0) {
246                 EC_LOG(LOG_ERR, "current() does not take any argument\n");
247                 errno = EINVAL;
248                 goto fail;
249         }
250
251         out = ec_malloc(sizeof(*out));
252         if (out == NULL)
253                 goto fail;
254
255         out->type = NODESET;
256         out->htable = ec_htable();
257         if (out->htable == NULL)
258                 goto fail;
259
260         if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
261                 goto fail;
262
263         cond_result_table_free(in, in_len);
264         return out;
265
266 fail:
267         cond_result_free(out);
268         cond_result_table_free(in, in_len);
269         return NULL;
270 }
271
272 static bool
273 boolean_value(const struct cond_result *res)
274 {
275         switch (res->type) {
276         case NODESET:
277                 return (ec_htable_len(res->htable) > 0);
278         case BOOLEAN:
279                 return res->boolean;
280         case INT:
281                 return (res->int64 != 0);
282         case STR:
283                 return (res->str[0] != 0);
284         }
285
286         return false;
287 }
288
289 static struct cond_result *
290 eval_bool(const struct ec_parse *state, struct cond_result **in, size_t in_len)
291 {
292         struct cond_result *out = NULL;
293
294         (void)state;
295
296         if (in_len != 1) {
297                 EC_LOG(LOG_ERR, "bool() takes one argument.\n");
298                 errno = EINVAL;
299                 goto fail;
300         }
301
302         out = ec_malloc(sizeof(*out));
303         if (out == NULL)
304                 goto fail;
305
306         out->type = BOOLEAN;
307         out->boolean = boolean_value(in[0]);
308
309         cond_result_table_free(in, in_len);
310         return out;
311
312 fail:
313         cond_result_free(out);
314         cond_result_table_free(in, in_len);
315         return NULL;
316 }
317
318 static struct cond_result *
319 eval_or(const struct ec_parse *state, struct cond_result **in, size_t in_len)
320 {
321         struct cond_result *out = NULL;
322         size_t i;
323
324         (void)state;
325
326         if (in_len < 2) {
327                 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
328                 errno = EINVAL;
329                 goto fail;
330         }
331
332         /* return the first true element, or the last one */
333         for (i = 0; i < in_len; i++) {
334                 if (boolean_value(in[i]))
335                         break;
336         }
337         if (i == in_len)
338                 i--;
339
340         out = in[i];
341         in[i] = NULL;
342
343         cond_result_table_free(in, in_len);
344         return out;
345
346 fail:
347         cond_result_free(out);
348         cond_result_table_free(in, in_len);
349         return NULL;
350 }
351
352 static struct cond_result *
353 eval_and(const struct ec_parse *state, struct cond_result **in, size_t in_len)
354 {
355         struct cond_result *out = NULL;
356         size_t i;
357
358         (void)state;
359
360         if (in_len < 2) {
361                 EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
362                 errno = EINVAL;
363                 goto fail;
364         }
365
366         /* return the first false element, or the last one */
367         for (i = 0; i < in_len; i++) {
368                 if (!boolean_value(in[i]))
369                         break;
370         }
371         if (i == in_len)
372                 i--;
373
374         out = in[i];
375         in[i] = NULL;
376
377         cond_result_table_free(in, in_len);
378         return out;
379
380 fail:
381         cond_result_free(out);
382         cond_result_table_free(in, in_len);
383         return NULL;
384 }
385
386 static struct cond_result *
387 eval_first_child(const struct ec_parse *state, struct cond_result **in,
388         size_t in_len)
389 {
390         struct cond_result *out = NULL;
391         struct ec_htable_elt_ref *iter;
392         const struct ec_parse * const *pparse;
393         struct ec_parse *parse;
394
395         (void)state;
396
397         if (in_len != 1 || in[0]->type != NODESET) {
398                 EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
399                 errno = EINVAL;
400                 goto fail;
401         }
402
403         out = ec_malloc(sizeof(*out));
404         if (out == NULL)
405                 goto fail;
406
407         out->type = NODESET;
408         out->htable = ec_htable();
409         if (out->htable == NULL)
410                 goto fail;
411
412         for (iter = ec_htable_iter(in[0]->htable);
413              iter != NULL; iter = ec_htable_iter_next(iter)) {
414                 pparse = ec_htable_iter_get_key(iter);
415                 parse = ec_parse_get_first_child(*pparse);
416                 if (parse == NULL)
417                         continue;
418                 if (ec_htable_set(out->htable, &parse, sizeof(parse), NULL,
419                                         NULL) < 0)
420                         goto fail;
421         }
422
423         cond_result_table_free(in, in_len);
424         return out;
425
426 fail:
427         cond_result_free(out);
428         cond_result_table_free(in, in_len);
429         return NULL;
430 }
431
432 static struct cond_result *
433 eval_find(const struct ec_parse *state, struct cond_result **in,
434         size_t in_len)
435 {
436         struct cond_result *out = NULL;
437         struct ec_htable_elt_ref *iter;
438         struct ec_parse * const *pparse;
439         struct ec_parse *parse;
440         const char *id;
441
442         (void)state;
443
444         if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
445                 EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
446                 errno = EINVAL;
447                 goto fail;
448         }
449
450         out = ec_malloc(sizeof(*out));
451         if (out == NULL)
452                 goto fail;
453
454         out->type = NODESET;
455         out->htable = ec_htable();
456         if (out->htable == NULL)
457                 goto fail;
458
459         id = in[1]->str;
460         for (iter = ec_htable_iter(in[0]->htable);
461              iter != NULL; iter = ec_htable_iter_next(iter)) {
462                 pparse = ec_htable_iter_get_key(iter);
463                 parse = ec_parse_find(*pparse, id);
464                 while (parse != NULL) {
465                         if (ec_htable_set(out->htable, &parse,
466                                                 sizeof(parse), NULL,
467                                                 NULL) < 0)
468                                 goto fail;
469                         parse = ec_parse_find_next(*pparse, parse, id, 1);
470                 }
471         }
472
473         cond_result_table_free(in, in_len);
474         return out;
475
476 fail:
477         cond_result_free(out);
478         cond_result_table_free(in, in_len);
479         return NULL;
480 }
481
482 static struct cond_result *
483 eval_func(const char *name, const struct ec_parse *state,
484         struct cond_result **in, size_t in_len)
485 {
486         cond_func_t *f;
487
488         f = ec_dict_get(ec_node_cond_functions, name);
489         if (f == NULL) {
490                 EC_LOG(LOG_ERR, "No such function <%s>\n",
491                         name);
492                 errno = ENOENT;
493                 cond_result_table_free(in, in_len);
494                 return NULL;
495         }
496
497         return f(state, in, in_len);
498 }
499
500
501 static struct cond_result *
502 eval_condition(const struct ec_parse *cond, const struct ec_parse *state)
503 {
504         const struct ec_parse *iter;
505         struct cond_result *res = NULL;
506         struct cond_result **args = NULL;
507         const struct ec_parse *func = NULL, *func_name = NULL, *arg_list = NULL;
508         const struct ec_parse *value = NULL;
509         const char *id;
510         size_t n_arg = 0;
511
512         // XXX fix cast (x3)
513         func = ec_parse_find((void *)cond, "id_function");
514         value = ec_parse_find((void *)cond, "id_value");
515         if (func != NULL) {
516                 EC_PARSE_FOREACH_CHILD(iter, func) {
517                         id = ec_node_id(ec_parse_get_node(iter));
518                         if (!strcmp(id, "id_function_name"))
519                                 func_name = iter;
520                         if (!strcmp(id, "id_arg_list"))
521                                 arg_list = iter;
522                 }
523
524                 iter = ec_parse_find((void *)arg_list, "id_arg");
525                 while (iter != NULL) {
526                         args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
527                         args[n_arg] = eval_condition(iter, state);
528                         if (args[n_arg] == NULL)
529                                 goto fail;
530                         n_arg++;
531                         iter = ec_parse_find_next((void *)arg_list,
532                                                 (void *)iter, "id_arg", 0);
533                 }
534
535                 res = eval_func(ec_strvec_val(ec_parse_strvec(func_name), 0),
536                                 state, args, n_arg);
537                 printf("%s(%p[%zd]) -> %p\n", ec_strvec_val(ec_parse_strvec(func_name), 0),
538                         args, n_arg, res);
539                 args = NULL;
540         } else if (value != NULL) {
541                 printf("%s\n", ec_strvec_val(ec_parse_strvec(value), 0));
542                 res = ec_malloc(sizeof(*res));
543                 if (res == NULL)
544                         goto fail;
545                 res->type = STR;
546                 res->str = ec_strdup(ec_strvec_val(ec_parse_strvec(value), 0));
547                 if (res->str == NULL)
548                         goto fail;
549         } else {
550                 goto fail;
551         }
552
553         return res;
554
555 fail:
556         cond_result_free(res);
557         cond_result_table_free(args, n_arg);
558         return NULL;
559 }
560
561 static int
562 validate_condition(const struct ec_parse *cond, const struct ec_parse *state)
563 {
564         struct cond_result *res;
565         int ret;
566
567         res = eval_condition(cond, state);
568         if (res == NULL)
569                 return -1;
570
571         ret = boolean_value(res);
572         cond_result_free(res);
573
574         return ret;
575 }
576
577 static int
578 ec_node_cond_parse(const struct ec_node *gen_node, struct ec_parse *state,
579                 const struct ec_strvec *strvec)
580 {
581         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
582         int ret;
583
584         ret = validate_condition(node->parsed_cond, state);
585         if (ret < 0)
586                 return ret;
587
588         if (ret == 0)
589                 return EC_PARSE_NOMATCH;
590
591         return ec_node_parse_child(node->child, state, strvec);
592 }
593
594 static int
595 ec_node_cond_complete(const struct ec_node *gen_node,
596                 struct ec_comp *comp,
597                 const struct ec_strvec *strvec)
598 {
599         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
600
601         // XXX eval condition
602         // XXX before or after completing ? configurable ?
603
604         return ec_node_complete_child(node->child, comp, strvec);
605 }
606
607 static void ec_node_cond_free_priv(struct ec_node *gen_node)
608 {
609         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
610
611         ec_free(node->cond_str);
612         node->cond_str = NULL;
613         ec_parse_free(node->parsed_cond);
614         node->parsed_cond = NULL;
615         ec_node_free(node->child);
616 }
617
618 static const struct ec_config_schema ec_node_cond_schema[] = {
619         {
620                 .key = "expr",
621                 .desc = "XXX",
622                 .type = EC_CONFIG_TYPE_STRING,
623         },
624         {
625                 .key = "child",
626                 .desc = "The child node.",
627                 .type = EC_CONFIG_TYPE_NODE,
628         },
629         {
630                 .type = EC_CONFIG_TYPE_NONE,
631         },
632 };
633
634 static int ec_node_cond_set_config(struct ec_node *gen_node,
635                                 const struct ec_config *config)
636 {
637         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
638         const struct ec_config *cond = NULL;
639         struct ec_parse *parsed_cond = NULL;
640         const struct ec_config *child;
641         char *cond_str = NULL;
642
643         cond = ec_config_dict_get(config, "expr");
644         if (cond == NULL) {
645                 errno = EINVAL;
646                 goto fail;
647         }
648
649         cond_str = ec_strdup(cond->string);
650         if (cond_str == NULL)
651                 goto fail;
652
653         child = ec_config_dict_get(config, "child");
654         if (child == NULL)
655                 goto fail;
656
657         /* parse expression to build the cmd child node */
658         parsed_cond = ec_node_cond_build(cond_str);
659         if (parsed_cond == NULL)
660                 goto fail;
661
662         /* ok, store the config */
663         ec_parse_free(node->parsed_cond);
664         node->parsed_cond = parsed_cond;
665         ec_free(node->cond_str);
666         node->cond_str = cond_str;
667         ec_node_free(node->child);
668         node->child = ec_node_clone(child->node);
669
670         return 0;
671
672 fail:
673         ec_parse_free(parsed_cond);
674         ec_free(cond_str);
675         return -1;
676 }
677
678 static size_t
679 ec_node_cond_get_children_count(const struct ec_node *gen_node)
680 {
681         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
682
683         if (node->child == NULL)
684                 return 0;
685         return 1;
686 }
687
688 static int
689 ec_node_cond_get_child(const struct ec_node *gen_node, size_t i,
690                 struct ec_node **child, unsigned int *refs)
691 {
692         struct ec_node_cond *node = (struct ec_node_cond *)gen_node;
693
694         if (i > 0)
695                 return -1;
696
697         *child = node->child;
698         *refs = 1;
699         return 0;
700 }
701
702 static struct ec_node_type ec_node_cond_type = {
703         .name = "cond",
704         .schema = ec_node_cond_schema,
705         .set_config = ec_node_cond_set_config,
706         .parse = ec_node_cond_parse,
707         .complete = ec_node_cond_complete,
708         .size = sizeof(struct ec_node_cond),
709         .free_priv = ec_node_cond_free_priv,
710         .get_children_count = ec_node_cond_get_children_count,
711         .get_child = ec_node_cond_get_child,
712 };
713
714 EC_NODE_TYPE_REGISTER(ec_node_cond_type);
715
716 struct ec_node *ec_node_cond(const char *id, const char *cmd,
717                         struct ec_node *child)
718 {
719         struct ec_config *config = NULL;
720         struct ec_node *gen_node = NULL;
721         int ret;
722
723         if (child == NULL)
724                 return NULL;
725
726         gen_node = ec_node_from_type(&ec_node_cond_type, id);
727         if (gen_node == NULL)
728                 goto fail;
729
730         config = ec_config_dict();
731         if (config == NULL)
732                 goto fail;
733
734         if (ec_config_dict_set(config, "expr", ec_config_string(cmd)) < 0)
735                 goto fail;
736
737         if (ec_config_dict_set(config, "child", ec_config_node(child)) < 0) {
738                 child = NULL; /* freed */
739                 goto fail;
740         }
741         child = NULL;
742
743         ret = ec_node_set_config(gen_node, config);
744         config = NULL; /* freed */
745         if (ret < 0)
746                 goto fail;
747
748         return gen_node;
749
750 fail:
751         ec_node_free(gen_node);
752         ec_node_free(child);
753         ec_config_free(config);
754
755         return NULL;
756 }
757
758 static void ec_node_cond_exit_func(void)
759 {
760         ec_node_free(ec_node_cond_parser);
761         ec_node_cond_parser = NULL;
762         ec_dict_free(ec_node_cond_functions);
763         ec_node_cond_functions = NULL;
764 }
765
766 static int add_func(const char *name, cond_func_t *f)
767 {
768         return ec_dict_set(ec_node_cond_functions, name, f, NULL);
769 }
770
771 static int ec_node_cond_init_func(void)
772 {
773         ec_node_cond_parser = ec_node_cond_build_parser();
774         if (ec_node_cond_parser == NULL)
775                 goto fail;
776
777         ec_node_cond_functions = ec_dict();
778         if (ec_node_cond_functions == NULL)
779                 goto fail;
780
781         if (add_func("root", eval_root) < 0)
782                 goto fail;
783         if (add_func("current", eval_current) < 0)
784                 goto fail;
785         if (add_func("bool", eval_bool) < 0)
786                 goto fail;
787         if (add_func("or", eval_or) < 0)
788                 goto fail;
789         if (add_func("and", eval_and) < 0)
790                 goto fail;
791         if (add_func("first_child", eval_first_child) < 0)
792                 goto fail;
793         if (add_func("find", eval_find) < 0)
794                 goto fail;
795
796         return 0;
797
798 fail:
799         EC_LOG(EC_LOG_ERR, "Failed to initialize condition parser\n");
800         ec_node_cond_exit_func();
801         return -1;
802 }
803
804 static struct ec_init ec_node_cond_init = {
805         .init = ec_node_cond_init_func,
806         .exit = ec_node_cond_exit_func,
807         .priority = 75,
808 };
809
810 EC_INIT_REGISTER(ec_node_cond_init);
811
812 /* LCOV_EXCL_START */
813 static int ec_node_cond_testcase(void)
814 {
815         struct ec_node *node;
816         int testres = 0;
817
818         node =  EC_NODE_SEQ(EC_NO_ID,
819                         EC_NODE_SUBSET(EC_NO_ID,
820                                 ec_node_str("id_node1", "node1"),
821                                 ec_node_str("id_node2", "node2"),
822                                 ec_node_str("id_node3", "node3"),
823                                 ec_node_str("id_node4", "node4")),
824                         ec_node_cond(EC_NO_ID,
825                                 "or(find(root(), id_node1), "
826                                 "  and(find(root(), id_node2),"
827                                 "    find(root(), id_node3)))",
828                                 ec_node_str(EC_NO_ID, "ok")));
829         if (node == NULL) {
830                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
831                 return -1;
832         }
833         testres |= EC_TEST_CHECK_PARSE(node, 2, "node1", "ok");
834         testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node3", "ok");
835         testres |= EC_TEST_CHECK_PARSE(node, 4, "node1", "node2", "node3", "ok");
836         testres |= EC_TEST_CHECK_PARSE(node, 3, "node2", "node1", "ok");
837         testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "node4", "ok");
838         testres |= EC_TEST_CHECK_PARSE(node, -1, "node2", "ok");
839         testres |= EC_TEST_CHECK_PARSE(node, -1, "node3", "ok");
840         testres |= EC_TEST_CHECK_PARSE(node, -1, "node4", "ok");
841         ec_node_free(node);
842
843         // XXX test completion
844
845         return testres;
846 }
847 /* LCOV_EXCL_STOP */
848
849 static struct ec_test ec_node_cond_test = {
850         .name = "node_cond",
851         .test = ec_node_cond_testcase,
852 };
853
854 EC_TEST_REGISTER(ec_node_cond_test);