diff --git a/engine/gen/Storyboard/GenericAction/TrafficSinkAction.h b/engine/gen/Storyboard/GenericAction/TrafficSinkAction.h index db18e47132e267c0bb1103ae9f62f1c1bd4345a1..58f3823aceaa839da229eab8f8364f9bea180dd2 100644 --- a/engine/gen/Storyboard/GenericAction/TrafficSinkAction.h +++ b/engine/gen/Storyboard/GenericAction/TrafficSinkAction.h @@ -54,7 +54,8 @@ private: [=]() { return ConvertScenarioPosition(environment, trafficSinkAction_->GetPosition()); }, trafficSinkAction_->IsSetTrafficDefinition() ? std::make_optional(ConvertScenarioTrafficDefinition(trafficSinkAction_->GetTrafficDefinition())) - : std::nullopt}, + : std::nullopt + }, OpenScenarioEngine::v1_2::TrafficSinkAction::Interfaces{ environment}); } diff --git a/generator/open_scenario_tree.py b/generator/open_scenario_tree.py index 7ad40fcc9f4774c11229dd5917deeb8c20c10e1d..6e90e654bb0182f41266372d6af66f032139a3c4 100644 --- a/generator/open_scenario_tree.py +++ b/generator/open_scenario_tree.py @@ -21,15 +21,15 @@ class Entity: @dataclass class PrimitiveType: - name: str + name: str -@dataclass +@dataclass(frozen=True) class OscProperty: - name: str - type: Union[str, PrimitiveType] - optional: bool - list: bool - choice: bool + name: str + type: Union[str, PrimitiveType] + optional: bool + list: bool + choice: bool class OscNode: PROPAGATED_PROPERTIES = { diff --git a/generator/property_view.py b/generator/property_view.py new file mode 100644 index 0000000000000000000000000000000000000000..ae50f06b433060fa8ff5d2cbe5f192b34507b300 --- /dev/null +++ b/generator/property_view.py @@ -0,0 +1,42 @@ +from typing import List +from open_scenario_tree import OscProperty +from converter import Converter + +def feature_decorator(condition, prefix, suffix = ""): + def decorator(func): + def wrapper(self, *args, **kwargs): + prefix_, suffix_ = ("", "") + if condition(self): + prefix_ = prefix if isinstance(prefix, str) else prefix(self) + suffix_ = suffix if isinstance(suffix, str) else suffix(self) + return f'{prefix_}{func(self, *args, **kwargs)}{suffix_}' + return wrapper + return decorator + +class PropertyView: + CONVERTER_PREFIX = "ConvertScenario" + + def __init__(self, node_name, converter: Converter, properties: List[OscProperty]): + self.node_name = node_name + self.converter = converter + self.properties = properties + + @staticmethod + def capitalize_first(s): + return s[0].upper() + s[1:] if s else s + + def get_property(self, prop): + prefix = f"{self.node_name}->IsSet{self.capitalize_first(prop.name)}() ? std::make_optional(" if prop.optional else "" + suffix = ") : std::nullopt" if prop.optional else "" + return f"{prefix}{self.node_name}->Get{self.capitalize_first(prop.name)}(){suffix}" + + @feature_decorator(condition = lambda self: self.converter and self.converter.runtime, + prefix="[=](){ return ", + suffix="; }") + @feature_decorator(condition = lambda self: self.converter, + prefix=lambda self: f"{self.CONVERTER_PREFIX}{self.capitalize_first(self.converter.name)}(", + suffix=")") + @feature_decorator(condition = lambda self: self.converter and self.converter.dependencies, + prefix=lambda self: f'{", ".join([d.name for d in self.converter.dependencies])}, ') + def __str__(self): + return ", ".join(self.get_property(prop) for prop in self.properties) diff --git a/generator/tests/test_nodeview.py b/generator/tests/test_nodeview.py index 3c7e81cb020819eccd9161f0faa46389ed699edc..589710a19bc31d47ec66f5083b6d7437a92fd570 100644 --- a/generator/tests/test_nodeview.py +++ b/generator/tests/test_nodeview.py @@ -3,11 +3,8 @@ from open_scenario_tree import OscNode, parse_properties from converter import Converter, Dependency from typing import List import pytest -import dataclasses -#from test_property_view import PropertyView -class PropertyView: - pass +from property_view import PropertyView def capitalize_first(s): return s[0].upper() + s[1:] if s else s @@ -21,54 +18,28 @@ class NodeView: def __init__(self, node: OscNode, converter: List[Converter]): self.node = node + self.member_name = f'{lowercase_first(self.node.name)}{self.MEMBER_SUFFIX}' self.converter = converter - self.properties_view = self._generate_properties_view() + self._properties = self._generate_properties_view() self.dependencies = self._generate_dependencies() self.includes = self._generate_converter_includes() - @property - def name(self): - return f'{lowercase_first(self.node.name)}{self.MEMBER_SUFFIX}' - def _generate_properties_view(self): - properties_view = [] + properties = [] + unconverted_properties = set(self.node.properties) for converter in self.converter: if set(converter.properties).issubset({prop.name for prop in self.node.properties}): - property_view = PropertyView(converter, ) - property_view.name=f"{self.CONVERTER_PREFIX}{capitalize_first(converter.name)}" - property_view.members=[f"{self.name}->Get{capitalize_first(prop)}()" for prop in converter.properties] - if converter.keep: - properties_view.extend([ - PropertyView( - name=f"{self.name}->Get{capitalize_first(prop)}") for prop in converter.keep - if all(f"{self.name}->Get{capitalize_first(prop)}" != prop_view.name for prop_view in properties_view)]) - if converter.dependencies: - property_view.dependencies=[dep.name for dep in converter.dependencies] - if converter.runtime: - property_view.opening="[=](){ return " - property_view.closing="; }" - properties_view.extend(property_view) - - - optional_props = [prop for prop in self.node.properties if prop.optional] - properties_view.extend([ - PropertyView( - name=f"{self.name}->Get{capitalize_first(prop.name)}", - opening=f"{self.name}->IsSet{capitalize_first(prop.name)}() ? std::make_optional(", - closing=") : std::nullopt") for prop in optional_props for prop_view in properties_view]) - - # Add remaining properties that are not consumed by the converters - consumed_props = [prop for converter in self.converter for prop in converter.properties] - remaining_props = [prop for prop in self.node.properties if prop.name not in consumed_props and not prop.optional] - properties_view.extend([ - PropertyView( - name=f"{self.name}->Get{capitalize_first(prop.name)}") for prop in remaining_props]) - - return properties_view + consumed_properties = [prop for prop in self.node.properties if prop.name in converter.properties] + properties.append(PropertyView(self.member_name, converter, consumed_properties)) + unconverted_properties.difference_update(consumed_properties) + unconverted_properties.update([prop for prop in self.node.properties if prop.name in converter.keep]) + for prop in unconverted_properties: + properties.append(PropertyView(self.member_name, None, [prop])) + return properties @property - def new_properties(self): - return sorted([str(prop) for prop in self.properties_view]) + def properties(self): + return sorted([str(prop) for prop in self._properties]) def _generate_converter_includes(self): includes = [] @@ -97,12 +68,12 @@ def node_with_3_props(): def test__given_node_and_no_converters__relays_name(node_with_3_props): node_view = NodeView(node_with_3_props, []) - assert node_view.name == "nodeUnderTest_" + assert node_view.member_name == "nodeUnderTest_" def test__given_node_and_no_converters__generates_property_views_without_converters(node_with_3_props): node_view = NodeView(node_with_3_props, []) - assert node_view.new_properties == [ + assert node_view.properties == [ "nodeUnderTest_->GetPropName1()", "nodeUnderTest_->GetPropName2()", "nodeUnderTest_->GetPropName3()" ] @@ -114,7 +85,7 @@ def test__given_node_and_no_converters__generates_no_conversion_includes(node_wi def test__given_node_and_consuming_converter__combines_props_and_consumes_them(node_with_3_props): node_view = NodeView(node_with_3_props, [Converter([], "customPropName", {'Properties': ['propName1', 'propName3']})]) - assert node_view.new_properties == [ + assert node_view.properties == [ "ConvertScenarioCustomPropName(nodeUnderTest_->GetPropName1(), nodeUnderTest_->GetPropName3())", "nodeUnderTest_->GetPropName2()"] @@ -129,7 +100,7 @@ def test__given_node_and_keeping_converter__combines_props_and_keeps_prop(node_w 'Keep': ['propName3'] })]) - assert node_view.new_properties == [ + assert node_view.properties == [ "ConvertScenarioCustomPropName(nodeUnderTest_->GetPropName1(), nodeUnderTest_->GetPropName3())", "nodeUnderTest_->GetPropName2()", "nodeUnderTest_->GetPropName3()"] @@ -143,7 +114,7 @@ def test__given_node_and_two_keeping_converters__combines_props_and_keeps_prop_o 'Keep': ['propName3'] })]) - assert node_view.new_properties == [ + assert node_view.properties == [ "ConvertScenarioCustomPropName1(nodeUnderTest_->GetPropName1(), nodeUnderTest_->GetPropName3())", "ConvertScenarioCustomPropName2(nodeUnderTest_->GetPropName2(), nodeUnderTest_->GetPropName3())", "nodeUnderTest_->GetPropName3()"] @@ -155,21 +126,21 @@ def test__given_node_and_converter_with_deps_and_props__integrates_deps_and_cons 'Dependencies': ['Environment']}) node_view = NodeView(node_with_3_props, [converter]) - assert "ConvertScenarioCustomType(environment, nodeUnderTest_->GetPropName1())" in node_view.new_properties + assert "ConvertScenarioCustomType(environment, nodeUnderTest_->GetPropName1())" in node_view.properties def test__given_node_and_converter_with_deps_and_no_props__integrates_deps_and_consumes_default_prop(node_with_3_props): fake_deps = {"Environment": Dependency("environment", "std::shared_ptr<mantle_api::IEnvironment>", "<mantle_api/i_environment.h>")} converter = Converter(fake_deps, "propName1", {'Dependencies': ['Environment']}) node_view = NodeView(node_with_3_props, [converter]) - assert "ConvertScenarioPropName1(environment, nodeUnderTest_->GetPropName1())" in node_view.new_properties + assert "ConvertScenarioPropName1(environment, nodeUnderTest_->GetPropName1())" in node_view.properties def test__given_node_and_non_matching_converter__ignores_converter(node_with_3_props): fake_deps = {"Environment": Dependency("environment", "std::shared_ptr<mantle_api::IEnvironment>", "<mantle_api/i_environment.h>")} unused_converter = Converter(fake_deps, "someArbitraryProperty", {'Dependencies': ['Environment']}) node_view = NodeView(node_with_3_props, [unused_converter]) - assert node_view.new_properties == [ + assert node_view.properties == [ "nodeUnderTest_->GetPropName1()", "nodeUnderTest_->GetPropName2()", "nodeUnderTest_->GetPropName3()"] @@ -198,7 +169,7 @@ def test__given_node_and_runtime_converter__creates_lambda_wrapper_for_converter 'Type': 'runtime' })]) - assert "[=](){ return ConvertScenarioPropName1(nodeUnderTest_->GetPropName1()); }" in node_view.new_properties + assert "[=](){ return ConvertScenarioPropName1(nodeUnderTest_->GetPropName1()); }" in node_view.properties def test__given_node_and_runtime_converter_with_deps__creates_lambda_wrapper_for_converter_stage(node_with_3_props): fake_deps = {"Environment": Dependency("environment", "std::shared_ptr<mantle_api::IEnvironment>", "<mantle_api/i_environment.h>")} @@ -206,7 +177,7 @@ def test__given_node_and_runtime_converter_with_deps__creates_lambda_wrapper_for 'Type': 'runtime' })]) - assert "[=](){ return ConvertScenarioPropName1(environment, nodeUnderTest_->GetPropName1()); }" in node_view.new_properties + assert "[=](){ return ConvertScenarioPropName1(environment, nodeUnderTest_->GetPropName1()); }" in node_view.properties # assert str(PropertyView( # opening="[=](){ return ", @@ -225,7 +196,7 @@ def test__given_node_with_optional_property__generates_ternary_operator(): })) node_view = NodeView(node_under_test, []) - assert node_view.new_properties == [ + assert node_view.properties == [ "nodeUnderTest_->IsSetPropName1() ? std::make_optional(nodeUnderTest_->GetPropName1()) : std::nullopt"] def test__given_node_with_optional_property_and_converter__generates_ternary_operator(): @@ -234,8 +205,8 @@ def test__given_node_with_optional_property_and_converter__generates_ternary_ope })) node_view = NodeView(node_under_test, [Converter([], "customPropName", {'Properties': ['propName1']})]) - assert node_view.new_properties == [ - "nodeUnderTest_->IsSetPropName1() ? std::make_optional(ConvertScenarioCustomPropName(nodeUnderTest_->GetPropName1())) : std::nullopt"] + assert node_view.properties == [ + "ConvertScenarioCustomPropName(nodeUnderTest_->IsSetPropName1() ? std::make_optional(nodeUnderTest_->GetPropName1()) : std::nullopt)"] def test__given_node_with_optional_property_and_runtime__generates_ternary_operator(): node_under_test = OscNode("NodeUnderTest", parse_properties({ @@ -243,5 +214,5 @@ def test__given_node_with_optional_property_and_runtime__generates_ternary_opera })) node_view = NodeView(node_under_test, [Converter([], "customPropName", {'Properties': ['propName1'], 'Type': 'runtime'})]) - assert node_view.new_properties == [ - "[=](){ return nodeUnderTest_->IsSetPropName1() ? std::make_optional(ConvertScenarioCustomPropName(nodeUnderTest_->GetPropName1())) : std::nullopt }"] + assert node_view.properties == [ + "[=](){ return ConvertScenarioCustomPropName(nodeUnderTest_->IsSetPropName1() ? std::make_optional(nodeUnderTest_->GetPropName1()) : std::nullopt); }"] diff --git a/generator/tests/test_property_view.py b/generator/tests/test_property_view.py index 6b1be24a192b09383d4bf88756c08e216342e15f..4b2dc223f7332baca58ed2ff0140d0eda709e382 100644 --- a/generator/tests/test_property_view.py +++ b/generator/tests/test_property_view.py @@ -1,91 +1,32 @@ -from typing import List - import pytest -from open_scenario_tree import OscProperty -from converter import Converter, Dependency - - -# def test_my_decorator_used(): -# obj = PropertyView("node", "Method", True, False) -# assert str(obj) == "node_->IsSetMethod ? std::make_optional(node_->GetMethod()) : std::nullopt" - - -# def test_my_decorator_unused(): -# obj = PropertyView("node", "Method", False, False) -# assert str(obj) == "node_->GetMethod()" - -# def test_my_decorator_used_2(): -# obj = PropertyView("node", "Method", False, True) -# assert str(obj) == "[=](){ return node_->GetMethod(); }" - -# def test_my_decorator_used_3(): -# obj = PropertyView("node", "Method", True, True) -# assert str(obj) == "[=](){ return node_->IsSetMethod ? std::make_optional(node_->GetMethod()) : std::nullopt; }" - -def feature_decorator(condition, prefix, suffix = ""): - def decorator(func): - def wrapper(self, *args, **kwargs): - prefix_, suffix_ = ("", "") - if condition(self): - prefix_ = prefix if isinstance(prefix, str) else prefix(self) - suffix_ = suffix if isinstance(suffix, str) else suffix(self) - return f'{prefix_}{func(self, *args, **kwargs)}{suffix_}' - return wrapper - return decorator - -class PropertyView: - CONVERTER_PREFIX = "ConvertScenario" - MEMBER_SUFFIX = "_" - - def __init__(self, node, converter: Converter, properties: List[OscProperty]): - self.node = f'{node}{self.MEMBER_SUFFIX}' - self.converter = converter - self.properties = properties - - @staticmethod - def capitalize_first(s): - return s[0].upper() + s[1:] if s else s - - def get_property(self, prop): - prefix = f"{self.node}->IsSet{self.capitalize_first(prop.name)} ? std::make_optional(" if prop.optional else "" - suffix = ") : std::nullopt" if prop.optional else "" - return f"{prefix}{self.node}->Get{self.capitalize_first(prop.name)}(){suffix}" - - @feature_decorator(condition = lambda self: self.converter and self.converter.runtime, - prefix="[=](){ return ", - suffix="; }") - @feature_decorator(condition = lambda self: self.converter, - prefix=lambda self: f"{self.CONVERTER_PREFIX}{self.capitalize_first(self.converter.name)}(", - suffix=")") - @feature_decorator(condition = lambda self: self.converter and self.converter.dependencies, - prefix=lambda self: f'{", ".join([d.name for d in self.converter.dependencies])}, ') - def __str__(self): - return ", ".join(self.get_property(prop) for prop in self.properties) +from property_view import PropertyView +from converter import Converter, Dependency +from open_scenario_tree import OscProperty @pytest.mark.parametrize("node, converter, oscProperty, expected_result", [ - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", False, False, False)], "ConvertScenarioPropName(node_->GetPropName())"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", True, False, False)], "ConvertScenarioPropName(node_->IsSetPropName ? std::make_optional(node_->GetPropName()) : std::nullopt)"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", False, True, False)], "ConvertScenarioPropName(node_->GetPropName())"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", False, False, True)], "ConvertScenarioPropName(node_->GetPropName())"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", True, True, False)], "ConvertScenarioPropName(node_->IsSetPropName ? std::make_optional(node_->GetPropName()) : std::nullopt)"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", True, False, True)], "ConvertScenarioPropName(node_->IsSetPropName ? std::make_optional(node_->GetPropName()) : std::nullopt)"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", False, True, True)], "ConvertScenarioPropName(node_->GetPropName())"), - ("node", Converter([], "propName", {}), [OscProperty("propName", "type", True, True, True)], "ConvertScenarioPropName(node_->IsSetPropName ? std::make_optional(node_->GetPropName()) : std::nullopt)") + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", False, False, False)], "ConvertScenarioPropName(node_->GetPropName())"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", True, False, False)], "ConvertScenarioPropName(node_->IsSetPropName() ? std::make_optional(node_->GetPropName()) : std::nullopt)"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", False, True, False)], "ConvertScenarioPropName(node_->GetPropName())"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", False, False, True)], "ConvertScenarioPropName(node_->GetPropName())"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", True, True, False)], "ConvertScenarioPropName(node_->IsSetPropName() ? std::make_optional(node_->GetPropName()) : std::nullopt)"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", True, False, True)], "ConvertScenarioPropName(node_->IsSetPropName() ? std::make_optional(node_->GetPropName()) : std::nullopt)"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", False, True, True)], "ConvertScenarioPropName(node_->GetPropName())"), + ("node_", Converter([], "propName", {}), [OscProperty("propName", "type", True, True, True)], "ConvertScenarioPropName(node_->IsSetPropName() ? std::make_optional(node_->GetPropName()) : std::nullopt)") ]) def test__given_property_with_converter__generates_property_views_with_converters(node, converter, oscProperty, expected_result): obj = PropertyView(node, converter, oscProperty) assert str(obj) == expected_result def test__given_property_and_optional_property_with_converter__generates_property_views_with_ternary_operator(): - obj = PropertyView("node", Converter([], "MyConv", {}), [OscProperty("propName1", "type", False, False, False), OscProperty("propName2", "type", True, False, False)]) - assert str(obj) == "ConvertScenarioMyConv(node_->GetPropName1(), node_->IsSetPropName2 ? std::make_optional(node_->GetPropName2()) : std::nullopt)" + obj = PropertyView("node_", Converter([], "MyConv", {}), [OscProperty("propName1", "type", False, False, False), OscProperty("propName2", "type", True, False, False)]) + assert str(obj) == "ConvertScenarioMyConv(node_->GetPropName1(), node_->IsSetPropName2() ? std::make_optional(node_->GetPropName2()) : std::nullopt)" def test__given_property_with_no_converters__generates_property_views_without_converters(): - obj = PropertyView("node", None, [OscProperty("propName1", "type", False, False, False)]) + obj = PropertyView("node_", None, [OscProperty("propName1", "type", False, False, False)]) assert str(obj) == "node_->GetPropName1()" def test__given_property_and_optional_property_with_converter__generates_property_views_with_ternary_operator213(): dep = {"Environment": Dependency("environment", "", "")} - obj = PropertyView("node", Converter(dep, "MyConv", {'Dependencies': ['Environment']}), [OscProperty("propName1", "type", False, False, False)]) + obj = PropertyView("node_", Converter(dep, "MyConv", {'Dependencies': ['Environment']}), [OscProperty("propName1", "type", False, False, False)]) assert str(obj) == "ConvertScenarioMyConv(environment, node_->GetPropName1())" \ No newline at end of file