Skip to content

Commit

Permalink
Fix nested choice (#340)
Browse files Browse the repository at this point in the history
* Add UnitTest for nested Choices

* Fix to support nested Choice statements
  • Loading branch information
JoseIgnacioTamayo authored Feb 27, 2024
1 parent cf4c3bd commit 4e2054b
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 49 deletions.
106 changes: 68 additions & 38 deletions pyangbind/plugin/pybind.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,60 @@ def build_typedefs(ctx, defnd):
class_map[type_name.split(":")[1]] = class_map[type_name]


def get_children_elements(
ctx,
fd,
i_children,
module,
parent,
elements,
imports,
path=str(),
parent_cfg=True,
register_paths=True,
choice=False,
):
# Iterative function that is called to get the elements of
# a list of i_children.
# It treats 'choice' children, which have elements in its cases.
# It extends the list of elements and imports as childs are inspected.

for ch in i_children:
if ch.keyword == "choice":
# These are cases
for sub_choice_ch in ch.i_children:
get_children_elements(
ctx,
fd,
sub_choice_ch.i_children,
module,
parent,
elements,
imports,
path=path,
parent_cfg=parent_cfg,
choice=(ch.arg, sub_choice_ch.arg),
register_paths=register_paths,
)
else:
elements += get_element(
ctx,
fd,
ch,
module,
parent,
path + "/" + ch.arg,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if (hasattr(ch, "i_children") and len(ch.i_children)) or (
ctx.opts.generate_presence and ch.search_one("presence")
):
imports.append(ch.arg)


def get_children(ctx, fd, i_children, module, parent, path=str(), parent_cfg=True, choice=False, register_paths=True):
# Iterative function that is called for all elements that have childen
# data nodes in the tree. This function resolves those nodes into the
Expand Down Expand Up @@ -731,45 +785,21 @@ def get_children(ctx, fd, i_children, module, parent, path=str(), parent_cfg=Tru
# that they are imported. Additionally, we need to find the elements that are
# within a case, and ensure that these are built with the corresponding
# choice specified.
if ctx.opts.split_class_dir:
import_req = []

for ch in i_children:
if ch.keyword == "choice":
for choice_ch in ch.i_children:
# these are case statements
for case_ch in choice_ch.i_children:
elements += get_element(
ctx,
fd,
case_ch,
module,
parent,
path + "/" + case_ch.arg,
parent_cfg=parent_cfg,
choice=(ch.arg, choice_ch.arg),
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if hasattr(case_ch, "i_children") and len(case_ch.i_children):
import_req.append(case_ch.arg)
else:
elements += get_element(
ctx,
fd,
ch,
module,
parent,
path + "/" + ch.arg,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if (hasattr(ch, "i_children") and len(ch.i_children)) or (
ctx.opts.generate_presence and ch.search_one("presence")
):
import_req.append(ch.arg)
import_req = []
get_children_elements(
ctx,
fd,
i_children,
module,
parent,
elements,
import_req,
path=path,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)

# Write out the import statements if needed.
if ctx.opts.split_class_dir:
Expand Down
17 changes: 17 additions & 0 deletions tests/choice/choice.yang
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ module choice {
}
}
}

case case-three {
choice choice-three- {
case case-three-one {
container case-three-one-container {
leaf case-three-one-leaf {
type int8;
}
}
}
case case-three-two {
leaf case-three-two-leaf {
type string;
}
}
}
}
}
}
}
40 changes: 29 additions & 11 deletions tests/choice/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,44 @@ def test_class_has_container(self):
self.assertTrue(hasattr(self.choice_obj, "container"), "Object does not have container")

def test_class_has_choice_containers(self):
for container in ["case_one_container", "case_two_container"]:
for container in ["case_one_container", "case_two_container", "case_three_one_container"]:
with self.subTest(container=container):
self.assertTrue(
hasattr(self.choice_obj.container, container),
"Object does not have choice container %s" % container,
)

def test_class_does_not_have_choices_as_attributes(self):
for choice in ["choice_one", "choice_two", "case_one", "case_two"]:
for choice in [
"choice_one",
"choice_three",
"case_one",
"case_two",
"case_three_one",
"case_three_two",
]:
with self.subTest(choice=choice):
self.assertFalse(
hasattr(self.choice_obj, choice), "Object has an erroneous choice option, %s" % choice
)

def test_case_leaf_default_values(self):
for leaf in ["case_one", "case_two"]:
for leaf in ["case_one", "case_two", "case_three_one"]:
with self.subTest(leaf=leaf):
container = getattr(self.choice_obj.container, "%s_container" % leaf)
value = getattr(container, "%s_leaf" % leaf)
self.assertEqual(value, 0, "Object does not have the correct value for %s_leaf, %s" % (leaf, value))

def test_set_choice_value(self):
self.choice_obj.container.case_one_container.case_one_leaf = 42
self.assertEqual(
self.choice_obj.container.case_one_container.case_one_leaf,
42,
"Object did not specify a value within the choice correctly, %s"
% self.choice_obj.container.case_one_container.case_one_leaf,
)
for leaf in ["case_one", "case_two", "case_three_one"]:
with self.subTest(leaf=leaf):
container = getattr(self.choice_obj.container, "%s_container" % leaf)
value = setattr(container, "%s_leaf" % leaf, 42)
self.assertEqual(
getattr(container, "%s_leaf" % leaf, 0),
42,
"Object did not specify a value within the choice correctly, %s" % container,
)

def test_set_choice_value_doesnt_set_other_choices(self):
self.choice_obj.container.case_one_container.case_one_leaf = 42
Expand All @@ -60,7 +69,7 @@ def test_change_choice_value(self):
self.assertEqual(
self.choice_obj.container.case_two_container.case_two_leaf,
42,
"Object did not allow the other half of the choice field to be specified, %s"
"Object did not allow the other choice field to be specified, %s"
% self.choice_obj.container.case_two_container.case_two_leaf,
)

Expand Down Expand Up @@ -100,6 +109,15 @@ def test_change_choice_list_resets_other_side(self):
"Adding to the second user list did not remove entries from the first",
)

def test_set_nested_choice(self):
for leaf in [
self.choice_obj.container.case_three_one_container.case_three_one_leaf,
self.choice_obj.container.case_three_two_leaf,
]:
with self.subTest(leaf=leaf):
leaf = 99
self.assertEqual(leaf, 99, "Nested choice leaf not set")


if __name__ == "__main__":
unittest.main()

0 comments on commit 4e2054b

Please sign in to comment.