diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f5163f5..91697a20 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -57,6 +57,8 @@
* Added libgen.h for baseline()
### Corrected Bugs
+* Bugfix: [Nodes from more than one of the choice's branches exist at the same time](https://github.com/clicon/clixon/issues/81)
+ * Note it may still be possible to load a file with multiple choice elements via netconf, but it will not pass validate.
* Bugfix: Default NACM policies applied even if NACM is disabled
* [Identityref inside augment statement](https://github.com/clicon/clixon/issues/77)
* Yang-stmt enhanced with "shortcut" to original module
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 408cfac8..09985887 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -487,6 +487,50 @@ xml_yang_validate_rpc(cxobj *xrpc,
goto done;
}
+/*! Check if an xml node is a part of a choice and have >1 siblings
+ * @param[in] xt XML node to be validated
+ * @param[in] yt xt:s yang statement
+ * @param[out] cbret Error buffer (set w netconf error if retval == 0)
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed (cbret set)
+ * @retval -1 Error
+ */
+static int
+check_choice(cxobj *xt,
+ yang_stmt *yt,
+ cbuf *cbret)
+{
+ int retval = -1;
+ yang_node *yc;
+ yang_stmt *y;
+ yang_node *yp;
+ cxobj *x;
+ cxobj *xp;
+
+ if ((yc = yang_choice(yt)) == NULL)
+ goto ok;
+ if ((xp = xml_parent(xt)) == NULL)
+ goto ok;
+ x = NULL; /* Find a child with same yang spec */
+ while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL) {
+ if ((x != xt) &&
+ (y = xml_spec(x)) != NULL &&
+ (yp = yang_choice(y)) != NULL &&
+ yp == (yang_node*)yc){
+ if (netconf_bad_element(cbret, "application", xml_name(x), "Element in choice statement already exists") < 0)
+ goto done;
+ goto fail;
+ }
+ }
+ ok:
+ retval = 1;
+ done:
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
/*! Check if an xml node lacks mandatory children
* @param[in] xt XML node to be validated
* @param[in] yt xt:s yang statement
@@ -623,6 +667,10 @@ xml_yang_validate_add(cxobj *xt,
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
+ if ((ret = check_choice(xt, yt, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
if ((ret = check_mandatory(xt, yt, cbret)) < 0)
goto done;
if (ret == 0)
diff --git a/test/test_choice.sh b/test/test_choice.sh
index a281cf54..f1d5192f 100755
--- a/test/test_choice.sh
+++ b/test/test_choice.sh
@@ -115,6 +115,15 @@ sleep $RCWAIT
new "netconf validate empty"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$"
+new "netconf set protocol both udp and tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$"
+
+new "netconf validate both udp and tcp fail"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationbad-elementudperrorElement in choice statement already exists]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$"
+
new "netconf set empty protocol"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$"
@@ -139,6 +148,14 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$"
+#-- restconf
+
+new "restconf DELETE whole datastore"
+expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
+
+new "restconf set protocol tcp+udp fail"
+expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": null, "udp": null}}')" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "bad-element","error-info": {"bad-element": "udp"},"error-severity": "error","error-message": "Element in choice statement already exists"}}}
'
+
new "restconf set protocol tcp"
expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d {\"system:protocol\":{\"tcp\":null}})" 0 ""
@@ -146,12 +163,21 @@ new "restconf get protocol tcp"
expecteq "$(curl -s -X GET http://localhost/restconf/data/system:system)" 0 '{"system:system": {"protocol": {"tcp": null}}}
'
+new "restconf set protocol tcp+udp fail"
+expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": null, "udp": null}}')" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "bad-element","error-info": {"bad-element": "udp"},"error-severity": "error","error-message": "Element in choice statement already exists"}}}
'
+
new "cli set protocol udp"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol udp" 0 "^$"
new "cli get protocol udp"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol udp$"
+new "cli change protocol to tcp"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol tcp" 0 "^$"
+
+new "cli get protocol tcp"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol tcp$"
+
new "cli delete all"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o delete all" 0 "^$"
@@ -159,6 +185,15 @@ new "commit"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o commit" 0 "^$"
# Second shorthand (no case)
+new "netconf set protocol both udp and tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$"
+
+new "netconf validate both udp and tcp fail"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationbad-elementudperrorElement in choice statement already exists]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$"
+
new "netconf set shorthand tcp"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$"