From d503f4982b8b7c4b5caff2d67f32ca7a72d8d3d5 Mon Sep 17 00:00:00 2001 From: Ollie Copping Date: Mon, 17 Nov 2025 15:44:41 +0000 Subject: [PATCH 01/13] Add validator script and object skeleton --- src/techui_builder/validator.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/techui_builder/validator.py diff --git a/src/techui_builder/validator.py b/src/techui_builder/validator.py new file mode 100644 index 0000000..9ec98e0 --- /dev/null +++ b/src/techui_builder/validator.py @@ -0,0 +1,10 @@ +import logging +from dataclasses import dataclass +from pathlib import Path + +LOGGER = logging.getLogger(__name__) + + +@dataclass +class Validator: + path: Path From 0bc043ff01b76dafa9cff0d3c0033b6b9c228a81 Mon Sep 17 00:00:00 2001 From: Ollie Copping Date: Wed, 19 Nov 2025 10:40:49 +0000 Subject: [PATCH 02/13] Add basic validate check function --- src/techui_builder/validator.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/techui_builder/validator.py b/src/techui_builder/validator.py index 9ec98e0..8200998 100644 --- a/src/techui_builder/validator.py +++ b/src/techui_builder/validator.py @@ -1,10 +1,23 @@ import logging -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path +from lxml import etree + LOGGER = logging.getLogger(__name__) @dataclass class Validator: path: Path + validate: bool = field(default=False, init=False, repr=False) + + def check_bob(self): + # etree has to used as objectify ignore comments + xml = etree.parse(self.path) + # fetch all the comments at the base of the tree + comments = list(xml.getroot().itersiblings(tag=etree.Comment, preceding=True)) + if len(comments) > 0: + # Check if any comments found are the manually saved tag + if any(str(comment).startswith(" + + motor + + motor + 0 + 0 + 255 + 470 + + X + 205 + 120 + example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob + +

BL01T-MO-MOTOR-01

+ X +
+ 0 + 0 +
+
+
diff --git a/tests/test_files/motor.bob b/tests/test_files/motor.bob index 529674f..70d68b7 100644 --- a/tests/test_files/motor.bob +++ b/tests/test_files/motor.bob @@ -11,7 +11,7 @@ X 205 120 - ../../../../src/techui-support/bob/pmac/motor_embed.bob + example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob

BL01T-MO-MOTOR-01

X @@ -23,7 +23,7 @@ A 205 120 - ../../../../src/techui-support/bob/pmac/motor_embed.bob + example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob

BL01T-MO-MOTOR-01

A @@ -43,7 +43,7 @@

BL01T-MO-BRICK-01

- ../../../../src/techui-support/bob/pmac/pmacController.bob + example/t01-services/synoptic/techui-support/bob/pmac/pmacController.bob tab diff --git a/tests/test_files/motor_bad.bob b/tests/test_files/motor_bad.bob index 34d32fc..145fc6d 100644 --- a/tests/test_files/motor_bad.bob +++ b/tests/test_files/motor_bad.bob @@ -9,7 +9,7 @@ X 205 120 - ../../../../src/techui-support/bob/pmac/motor_embed.bob + example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob

BL01T-MO-MOTOR-01

X @@ -21,7 +21,7 @@ A 205 120 - ../../../../src/techui-support/bob/pmac/motor_embed.bob + example/t01-services/synoptic/techui-support/bob/pmac/motor_embed.bob

BL01T-MO-MOTOR-01

A @@ -41,7 +41,7 @@

BL01T-MO-BRICK-01

- ../../../../src/techui-support/bob/pmac/pmacController.bob + example/t01-services/synoptic/techui-support/bob/pmac/pmacController.bob tab diff --git a/tests/test_validator.py b/tests/test_validator.py new file mode 100644 index 0000000..0ee4da1 --- /dev/null +++ b/tests/test_validator.py @@ -0,0 +1,69 @@ +from pathlib import Path +from unittest.mock import Mock, patch + +from lxml.etree import Element, SubElement, _ElementTree, tostring +from lxml.objectify import fromstring +from phoebusgen.widget import EmbeddedDisplay + + +def test_validator_check_bobs(validator): + validator._check_bob = Mock() + + validator.check_bobs() + + validator._check_bob.assert_called() + + +def test_validator_check_bob(validator): + validator._check_bob(validator.bobs[0]) + + assert len(validator.validate.keys()) > 0 + assert list(validator.validate.keys())[0] == "motor-edited" + + +def test_validator_read_bob(validator): + with patch("techui_builder.validator.read_bob") as mock_read_bob: + # We need to set the spec of the first Mock so it knows + # it has a getroot() function + mock_read_bob.return_value = (Mock(spec=_ElementTree), Mock()) + + validator._read_bob(validator.bobs[0]) + + +# TODO: Clean up this test... (make fixture for mock xml?) +def test_validator_validate_bob(validator): + # 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" + ) + # ... which requires this horror + mock_element = fromstring(tostring(mock_root_element)) + # mock_element = ObjectifiedElement(mock_widget_element) + # mock_name_element.text = "motor" + validator._read_bob = Mock( + return_value=( + Mock(), + {"motor": (mock_element)}, + ) + ) + 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", + 0, + 0, + 205, + 120, + ) + + validator.validate_bob("motor-edited", "motor", [test_pwidget])