diff --git a/tests/conftest.py b/tests/conftest.py index 92e088b..fe0a905 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,10 @@ from unittest.mock import MagicMock, patch import pytest +from lxml.etree import Element, SubElement, tostring +from lxml.objectify import fromstring +from techui_builder.autofill import Autofiller from techui_builder.builder import Builder, JsonMap from techui_builder.generate import Generator from techui_builder.validator import Validator @@ -62,9 +65,94 @@ def generator(): return g +@pytest.fixture +def autofiller(): + index_bob = Path(__file__).parent.joinpath(Path("t01-services/synoptic/index.bob")) + + a = Autofiller(index_bob) + + return a + + @pytest.fixture def validator(): test_bobs = [Path("tests/test_files/motor-edited.bob")] v = Validator(test_bobs) return v + + +@pytest.fixture +def example_embedded_widget(): + # You cannot set a text tag of an ObjectifiedElement, + # so we need to make an etree.Element and convert it ... + + widget_element = Element("widget") + widget_element.set("type", "embedded") + widget_element.set("version", "2.0.0") + name_element = SubElement(widget_element, "name") + name_element.text = "motor" + width_element = SubElement(widget_element, "width") + width_element.text = "205" + height_element = SubElement(widget_element, "height") + height_element.text = "120" + file_element = SubElement(widget_element, "file") + file_element.text = ( + "example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob" + ) + + # ... which requires this horror + widget_element = fromstring(tostring(widget_element)) + + return widget_element + + +@pytest.fixture +def example_related_widget(): + # You cannot set a text tag of an ObjectifiedElement, + # so we need to make an etree.Element and convert it ... + + widget_element = Element("widget") + widget_element.set("type", "action_button") + widget_element.set("version", "2.0.0") + name_element = SubElement(widget_element, "name") + name_element.text = "motor" + width_element = SubElement(widget_element, "width") + width_element.text = "205" + height_element = SubElement(widget_element, "height") + height_element.text = "120" + + actions_element = SubElement(widget_element, "actions") + action_element = SubElement(actions_element, "action") + action_element.set("type", "open_display") + file_element = SubElement(action_element, "file") + file_element.text = ( + "example/t01-services/synoptic/techui-support/bob/pmac/motor.bob" + ) + desc_element = SubElement(action_element, "description") + desc_element.text = "placeholder description" + + # ... which requires this horror + widget_element = fromstring(tostring(widget_element)) + + return widget_element + + +@pytest.fixture +def example_symbol_widget(): + # You cannot set a text tag of an ObjectifiedElement, + # so we need to make an etree.Element and convert it ... + widget_element = Element("widget") + widget_element.set("type", "symbol") + widget_element.set("version", "2.0.0") + name_element = SubElement(widget_element, "name") + name_element.text = "motor" + width_element = SubElement(widget_element, "width") + width_element.text = "205" + height_element = SubElement(widget_element, "height") + height_element.text = "120" + + # ... which requires this horror + widget_element = fromstring(tostring(widget_element)) + + return widget_element diff --git a/tests/test_autofiller.py b/tests/test_autofiller.py new file mode 100644 index 0000000..564ee94 --- /dev/null +++ b/tests/test_autofiller.py @@ -0,0 +1,123 @@ +import logging +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest +from lxml.etree import ElementTree +from lxml.objectify import Element + +from techui_builder.models import Component + + +def test_autofiller_read_bob(autofiller): + # Imported in to autofill from utils, so that needs to be patched + with patch("techui_builder.autofill.read_bob") as mock_read_bob: + mock_read_bob.return_value = (Mock(spec=ElementTree), Mock()) + + autofiller.read_bob() + + mock_read_bob.assert_called() + + +def test_autofiller_autofill_bob(autofiller): + autofiller.replace_content = Mock() + # This mess of a Mock represents a basic Builder object with a components dict + mock_builder = Mock(conf=Mock(components={"test_widget": Mock(spec=Component)})) + + mock_widget = Element("widget") + + autofiller.widgets = {"test_widget": mock_widget} + + autofiller.autofill_bob(mock_builder) + + autofiller.replace_content.assert_called() + assert mock_widget.find("run_actions_on_mouse_click") == "true" + + +def test_autofiller_write_bob(autofiller): + with ( + patch("techui_builder.builder.etree.ElementTree") as mock_tree, + patch("techui_builder.builder.objectify.deannotate") as mock_deannotate, + ): + autofiller.tree = mock_tree + + autofiller.write_bob(Path("tests/test_files/test_autofilled_bob.bob")) + + mock_deannotate.assert_called_once() + mock_tree.write.assert_called_once_with( + Path("tests/test_files/test_autofilled_bob.bob"), + pretty_print=True, + encoding="utf-8", + xml_declaration=True, + ) + + +@pytest.mark.parametrize( + "prefix, description, filename, expected_desc, expected_file", + [ + ("TEST_1", None, None, "test_component", "test_component.bob"), + ("TEST_2", "test_desc", "test_file.bob", "test_desc", "test_file.bob"), + ], +) +def test_autofiller_replace_content( + autofiller, + example_related_widget, + prefix, + description, + filename, + expected_desc, + expected_file, +): + with patch("techui_builder.autofill._get_action_group") as mock_get: + mock_get.return_value = example_related_widget.actions.action + + mock_component = Mock( + spec=Component, + prefix=prefix, + desc=description, + file=filename, + ) + + autofiller.replace_content( + example_related_widget, + "test_component", + mock_component, + ) + + assert example_related_widget.pv_name == f"{prefix}:DEVSTA" + assert example_related_widget.actions.action.description.text == expected_desc + assert example_related_widget.actions.action.file.text == expected_file + + +def test_autofiller_replace_content_no_action_group(autofiller, caplog): + # Just to only run the code we want to test + autofiller.macros = ["desc"] + + with patch("techui_builder.autofill._get_action_group") as mock_get: + # Simulate no action group found + mock_get.return_value = None + + mock_component = Mock( + spec=Component, + desc="description", + ) + + with caplog.at_level(logging.DEBUG): + autofiller.replace_content(None, "", mock_component) + + for log_output in caplog.records: + assert "Skipping replace_content for" in log_output.message + + +def test_autofiller_replace_content_unsupported_macro(autofiller): + autofiller.macros = ["bad_macro"] + + mock_component = Mock( + spec=Component, + bad_macro="bad_macro", + ) + + with pytest.raises(ValueError) as e: + autofiller.replace_content(None, "", mock_component) + + assert e == "The provided macro type is not supported." diff --git a/tests/test_builder.py b/tests/test_builder.py index 6c6374b..9002f1c 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -5,6 +5,7 @@ import pytest from lxml import objectify +from phoebusgen.widget import ActionButton, Group from techui_builder.builder import ( JsonMap, @@ -108,6 +109,30 @@ def test_gb_extract_entities(builder, index, type, desc, P, M, R): # noqa: N803 assert entity.R == R +def test_builder_generate_screen(builder_with_setup): + # with ( + # patch("techui_builder.builder.Generator.build_screen") as mock_build_screen, + # patch("techui_builder.builder.Generator.write_screen") as mock_write_screen, + # ): + builder_with_setup.generator.build_screen = Mock() + builder_with_setup.generator.write_screen = Mock() + + builder_with_setup._generate_screen("TEST") + + builder_with_setup.generator.build_screen.assert_called_once() + builder_with_setup.generator.write_screen.assert_called_once() + + +def test_builder_validate_screen(builder_with_setup): + builder_with_setup.validator.validate_bob = Mock() + builder_with_setup.generator.widgets = [Mock(spec=ActionButton)] + builder_with_setup.generator.group = Mock(spec=Group, name="TEST") + + builder_with_setup._validate_screen("TEST") + + builder_with_setup.validator.validate_bob.assert_called_once() + + def test_create_screens(builder_with_setup): # We don't want to access Generator in this test builder_with_setup._generate_screen = Mock() diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..4fe352c --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,27 @@ +from pathlib import Path +from unittest.mock import Mock, patch + +from lxml.etree import _ElementTree +from lxml.objectify import Element, ObjectifiedElement + +from techui_builder.utils import get_widgets, read_bob + + +def test_read_bob(): + with patch("techui_builder.utils.get_widgets") as mock_get_widgets: + mock_get_widgets.return_value = {"test_widget": Mock(spec=ObjectifiedElement)} + + tree, widgets = read_bob(Path("tests/test_files/index.bob")) + + assert isinstance(tree, _ElementTree) + assert isinstance(widgets["test_widget"], ObjectifiedElement) + mock_get_widgets.assert_called_once() + + +def test_get_widgets(example_symbol_widget): + test_root = Element("root") + test_root.append(example_symbol_widget) + + widgets = get_widgets(test_root) + + assert "motor" in widgets.keys() diff --git a/tests/test_validator.py b/tests/test_validator.py index 0ee4da1..3936c43 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -1,7 +1,7 @@ from pathlib import Path from unittest.mock import Mock, patch -from lxml.etree import Element, SubElement, _ElementTree, tostring +from lxml.etree import Element, _ElementTree, tostring from lxml.objectify import fromstring from phoebusgen.widget import EmbeddedDisplay @@ -31,21 +31,11 @@ def test_validator_read_bob(validator): # TODO: Clean up this test... (make fixture for mock xml?) -def test_validator_validate_bob(validator): +def test_validator_validate_bob(validator, example_embedded_widget): # You cannot set a text tag of an ObjectifiedElement, # so we need to make an etree.Element and convert it ... mock_root_element = Element("root") - mock_widget_element = SubElement(mock_root_element, "widget") - mock_name_element = SubElement(mock_widget_element, "name") - mock_name_element.text = "motor" - mock_width_element = SubElement(mock_widget_element, "width") - mock_width_element.text = "205" - mock_height_element = SubElement(mock_widget_element, "height") - mock_height_element.text = "120" - mock_file_element = SubElement(mock_widget_element, "file") - mock_file_element.text = ( - "example/t01-services/synoptic/techui_supportbob/pmac/motor_embed.bob" - ) + mock_root_element.append(example_embedded_widget) # ... which requires this horror mock_element = fromstring(tostring(mock_root_element)) # mock_element = ObjectifiedElement(mock_widget_element) @@ -59,7 +49,7 @@ def test_validator_validate_bob(validator): validator.validate = {"motor-edited": Path("tests/test_files/motor-edited.bob")} test_pwidget = EmbeddedDisplay( "motor", - "example/t01-services/synoptic/techui_supportbob/pmac/motor_embed.bob", + "example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob", 0, 0, 205,