From 1131c4b85d4b5b407324b8771b4b0e3d5d73fb8d Mon Sep 17 00:00:00 2001 From: huyenngn Date: Thu, 7 Mar 2024 16:41:20 +0100 Subject: [PATCH] feat(decl): Implement recursive sync --- capellambse/decl.py | 11 ++++++++--- tests/test_decl.py | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/capellambse/decl.py b/capellambse/decl.py index 2b0fa9e31..1247b2d05 100644 --- a/capellambse/decl.py +++ b/capellambse/decl.py @@ -249,7 +249,7 @@ def _operate_sync( for obj in v: try: - find_args = obj.pop("find") + find_args = obj["find"] except KeyError: raise ValueError( "Expected `find` key in sync object" @@ -274,6 +274,8 @@ def _operate_sync( if candidates: candidate = candidates[0] + if sync := obj.pop("sync", None): + yield from _operate_sync(promises, candidate, sync) if mods := obj.pop("set", None): yield from _operate_modify(promises, candidate, mods) if ext := obj.pop("add", None): @@ -284,12 +286,15 @@ def _operate_sync( promise = Promise(promise) yield (promise, candidate) else: - newobj_props = find_args | obj.get("set", {}) + newobj_props = ( + find_args | obj.pop("set", {}) | obj.pop("add", {}) + ) if "promise_id" in obj: - newobj_props["promise_id"] = obj["promise_id"] + newobj_props["promise_id"] = obj.pop("promise_id") yield from _create_complex_objects( promises, parent, attr, [newobj_props] ) + yield from _operate_sync(promises, parent, modifications) yield from () diff --git a/tests/test_decl.py b/tests/test_decl.py index 683432c28..0052587c7 100644 --- a/tests/test_decl.py +++ b/tests/test_decl.py @@ -610,7 +610,7 @@ def test_sync_operation_finds_and_modifies_existing_objects_in_the_list( assert root_function.functions[0].description == new_description @staticmethod - def test_sync_operation_finds_and_modifies_existing_objects_in_the_list_recurse( + def test_sync_operation_overwrites_iterables( model: capellambse.MelodyModel, ) -> None: root_function = model.by_uuid(ROOT_FUNCTION) @@ -648,7 +648,7 @@ def test_sync_operation_finds_and_modifies_existing_objects_in_the_list_recurse( ) @staticmethod - def test_sync_operation_finds_and_extends_existing_objects_in_the_list_recurse( + def test_sync_operation_extends_iterables( model: capellambse.MelodyModel, ) -> None: root_function = model.by_uuid(ROOT_FUNCTION) @@ -685,6 +685,30 @@ def test_sync_operation_finds_and_extends_existing_objects_in_the_list_recurse( and new_input in subfunc.inputs.by_name ) + @staticmethod + def test_sync_operation_recursive(model: capellambse.MelodyModel) -> None: + root_package = model.la.data_package + package_name = "The new package" + subpackage_name = "The new subpackage" + assert package_name not in root_package.packages.by_name + yml = f"""\ + - parent: !uuid {root_package.uuid} + sync: + packages: + - find: + name: "{package_name}" + sync: + packages: + - find: + name: {subpackage_name} + + """ + + decl.apply(model, io.StringIO(yml)) + assert package_name in root_package.packages.by_name + package = root_package.packages.by_name(package_name) + assert subpackage_name in package.packages.by_name + @staticmethod def test_sync_operation_creates_a_new_object_if_it_didnt_find_a_match( model: capellambse.MelodyModel, @@ -693,6 +717,7 @@ def test_sync_operation_creates_a_new_object_if_it_didnt_find_a_match( new_name = "The new function" new_description = "This is a new function." assert new_name not in root_function.functions.by_name + input = "Power port" yml = f"""\ - parent: !uuid {ROOT_FUNCTION} sync: @@ -701,6 +726,9 @@ def test_sync_operation_creates_a_new_object_if_it_didnt_find_a_match( name: {new_name} set: description: {new_description!r} + add: + inputs: + - name: {input} """ decl.apply(model, io.StringIO(yml)) @@ -708,6 +736,7 @@ def test_sync_operation_creates_a_new_object_if_it_didnt_find_a_match( assert new_name in root_function.functions.by_name func = root_function.functions.by_name(new_name, single=True) assert func.description == new_description + assert input in func.inputs.by_name @staticmethod def test_sync_operation_resolves_promises_with_newly_created_objects(