11import logging
22import os
3+ from io import StringIO
34from pathlib import Path
4- from unittest .mock import Mock , patch
5+ from unittest .mock import Mock , mock_open , patch
56
67import pytest
78from lxml import objectify
89from phoebusgen .widget import ActionButton , Group
10+ from softioc .builder import ClearRecords , records
911
1012from techui_builder .builder import (
1113 JsonMap ,
@@ -67,6 +69,80 @@ def test_component_attributes(
6769 assert component .extras == extras
6870
6971
72+ def test_builder_create_devsta_pv (builder ):
73+ p = "BL01T-MO-MOTOR-01"
74+ inpa = "BL01T-MO-MOTOR-01:MOTOR1.MOVN"
75+ builder ._create_devsta_pv (prefix = p , inputs = [inpa ])
76+
77+ devsta_pv = """
78+ record(calc, "BL01T-MO-MOTOR-01:DEVSTA")
79+ {
80+ field(ACKT, "NO")
81+ field(CALC, "(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0")
82+ field(INPA, "BL01T-MO-MOTOR-01:MOTOR1.MOVN")
83+ field(INPB, "")
84+ field(INPC, "")
85+ field(INPD, "")
86+ field(INPE, "")
87+ field(INPF, "")
88+ field(INPG, "")
89+ field(INPH, "")
90+ field(INPI, "")
91+ field(INPJ, "")
92+ field(INPK, "")
93+ field(INPL, "")
94+ field(SCAN, "1 second")
95+ }
96+ """
97+
98+ assert builder .devsta_pvs != {}
99+
100+ # Fake file-like object to "print" the record to
101+ auto_devsta_pv = StringIO ()
102+ # Get the string representation of the record
103+ builder .devsta_pvs [p ].Print (auto_devsta_pv )
104+
105+ assert auto_devsta_pv .getvalue () == devsta_pv
106+
107+ # Make sure the record is deleted
108+ ClearRecords ()
109+
110+
111+ def test_builder_write_devsta_pvs (builder ):
112+ # To mock the open() function used in _write_devsta_pvs
113+ m = mock_open ()
114+
115+ p = "BL01T-MO-MOTOR-01"
116+ inpa = "BL01T-MO-MOTOR-01:MOTOR1.MOVN"
117+ devsta_pv = records .calc ( # pyright: ignore[reportAttributeAccessIssue]
118+ f"{ p } :DEVSTA" ,
119+ CALC = "(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0" ,
120+ SCAN = "1 second" ,
121+ ACKT = "NO" ,
122+ INPA = inpa ,
123+ )
124+ builder .devsta_pvs [p ] = devsta_pv
125+
126+ # Mock the Print() function so we don't actually write a file
127+ with (
128+ patch ("builtins.open" , m ),
129+ patch ("techui_builder.builder.Record.Print" ) as mock_print ,
130+ ):
131+ builder .write_devsta_pvs ()
132+
133+ # Check open() was called with the correct args
134+ m .assert_called_once_with (
135+ Path (
136+ "/workspaces/techui-builder/tests/t01-services/synoptic/config/devsta.db"
137+ ),
138+ "w" ,
139+ )
140+ mock_print .assert_called_once ()
141+
142+ # Make sure the record is deleted
143+ ClearRecords ()
144+
145+
70146def test_missing_service (builder , caplog ):
71147 builder ._extract_entities = Mock (side_effect = OSError ())
72148 builder ._extract_services ()
@@ -134,16 +210,22 @@ def test_builder_validate_screen(builder_with_setup):
134210
135211
136212def test_create_screens (builder_with_setup ):
213+ # We don't want to make a devsta PV in this test
214+ builder_with_setup ._create_devsta_pv = Mock ()
137215 # We don't want to access Generator in this test
138216 builder_with_setup ._generate_screen = Mock ()
139217 builder_with_setup ._validate_screen = Mock ()
218+
140219 builder_with_setup .create_screens ()
141220
142221 builder_with_setup ._generate_screen .assert_called ()
143222 # builder_with_setup._validate_screen.assert_called()
144223
145224
146225def test_create_screens_no_entities (builder , caplog ):
226+ # We don't want to make a devsta PV in this test
227+ builder ._create_devsta_pv = Mock ()
228+
147229 builder .entities = []
148230
149231 # We only wan't to capture CRITICAL output in this test
@@ -156,6 +238,8 @@ def test_create_screens_no_entities(builder, caplog):
156238
157239
158240def test_create_screens_extra_p_does_not_exist (builder_with_setup , caplog ):
241+ # We don't want to make a devsta PV in this test
242+ builder_with_setup ._create_devsta_pv = Mock ()
159243 # We don't want to actually generate a screen
160244 builder_with_setup ._generate_screen = Mock (side_effect = None )
161245
0 commit comments