diff --git a/.vscode/launch.json b/.vscode/launch.json
index e7d10313..b69af85a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,7 +5,7 @@
"version": "0.2.0",
"configurations": [
{
- "name": "Run techui-builder",
+ "name": "Run techui-builder build",
"type": "debugpy",
"request": "launch",
"justMyCode": true,
@@ -14,5 +14,15 @@
"console": "integratedTerminal",
"args": "build ${workspaceFolder}/example/t01-services/synoptic/techui.yaml"
},
+ {
+ "name": "Run techui-builder status",
+ "type": "debugpy",
+ "request": "launch",
+ "justMyCode": true,
+ "cwd": "${workspaceFolder}",
+ "program": "${workspaceFolder}/src/techui_builder/__main__.py",
+ "console": "integratedTerminal",
+ "args": "status ${workspaceFolder}/example/t01-services/synoptic/techui.yaml"
+ },
]
}
diff --git a/example/t01-services/services/bl01t-di-cam-01/config/ioc.yaml b/example/t01-services/services/bl01t-di-cam-01/config/ioc.yaml
new file mode 100644
index 00000000..0655385c
--- /dev/null
+++ b/example/t01-services/services/bl01t-di-cam-01/config/ioc.yaml
@@ -0,0 +1,171 @@
+# yaml-language-server: $schema=/epics/ibek-defs/ioc.schema.json
+
+ioc_name: "{{ _global.get_env('IOC_NAME') }}"
+
+description: auto-generated by https://github.com/epics-containers/builder2ibek
+
+entities:
+ - type: epics.EpicsEnvSet
+ name: EPICS_TS_MIN_WEST
+ value: "0"
+
+ - type: epics.EpicsCaMaxArrayBytes
+ max_bytes: 100000000
+
+ - type: devIocStats.iocAdminSoft
+ IOC: "{{ ioc_name | upper }}"
+
+ - type: autosave.Autosave
+ P: "{{ ioc_name | upper }}:"
+
+ - type: ADAravis.aravisCamera
+ BUFFERS: 200
+ CLASS: AutoADGenICam
+ ID: 127.0.0.1
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.cam
+ R: ":CAM:"
+
+ - type: ADAravis.aravisSettings
+ camera: DCAM1.cam
+
+ - type: ADCore.NDROI
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.roi
+ QUEUE: 16
+ R: ":ROI:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDStats
+ ADDR: 0
+ HIST_SIZE: 65535
+ NCHANS: 20000
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.stat
+ QUEUE: 16
+ R: ":STAT:"
+ TIMEOUT: 1
+ XSIZE: 1292
+ YSIZE: 964
+
+ - type: ADCore.NDStdArrays
+ ADDR: 0
+ FTVL: UCHAR
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.roi
+ NELEMENTS: 1245488
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.arr
+ QUEUE: 2
+ R: ":ARR:"
+ TIMEOUT: 1
+ TYPE: Int8
+
+ - type: ADCore.NDProcess
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.proc
+ QUEUE: 16
+ R: ":PROC:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDOverlay
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.proc
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.over
+ QUEUE: 16
+ R: ":OVER:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDFileTIFF
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.tiff
+ QUEUE: 16
+ R: ":TIFF:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDFileHDF5
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.hdf
+ QUEUE: 16
+ R: ":HDF5:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDColorConvert
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.cc
+ QUEUE: 4
+ R: ":CC:"
+ TIMEOUT: 1
+
+ - type: ADCore.NDTransform
+ ADDR: 0
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.trans
+ R: ":TRANS:"
+ TIMEOUT: 1
+
+ - type: ffmpegServer.ffmpegStream
+ NDARRAY_ADDR: 0
+ NDARRAY_PORT: DCAM1.cam
+ P: BL01T-DI-CAM-01
+ PORT: DCAM1.mjpg
+ R: ":MJPG:"
+
+ # Initial settings
+
+ - type: epics.StartupCommand
+ command: dbLoadRecords("/epics/ioc/config/devsta.db")
+
+ - type: epics.PostStartupCommand
+ command: callbackSetQueueSize(100000)
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:CAM:GC_GevSCPSPacketSize 1500
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:CAM:GC_StrBytesPerSecond 55000000
+
+ # MJPG
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:MJPG:NDArrayPort DCAM1.cam
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:MJPG:EnableCallbacks Enable
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:MJPG:MAXW 1456
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:MJPG:MAXH 1088
+
+ # CAM
+
+ - type: epics.PostStartupCommand
+ command: dbpf BL01T-DI-CAM-01:CAM:Acquire 1
+
+ # Final setup
+
+ - type: epics.PostStartupCommand
+ command: epicsThreadSleep(1.0)
diff --git a/example/t01-services/services/bl01t-ea-ioc-01/config/fastcs.yaml b/example/t01-services/services/bl01t-ea-temp-01/config/fastcs.yaml
similarity index 92%
rename from example/t01-services/services/bl01t-ea-ioc-01/config/fastcs.yaml
rename to example/t01-services/services/bl01t-ea-temp-01/config/fastcs.yaml
index ad1541d2..790d6aed 100644
--- a/example/t01-services/services/bl01t-ea-ioc-01/config/fastcs.yaml
+++ b/example/t01-services/services/bl01t-ea-temp-01/config/fastcs.yaml
@@ -1,6 +1,6 @@
# yaml-language-server: $schema=schema.json
controllers:
- - name: BL01T-EA-TEST-01
+ - name: BL01T-EA-TEMP-01
type: fastcs.TemperatureController
ip_settings:
ip: "localhost"
diff --git a/example/t01-services/services/bl01t-mo-ioc-01/config/ioc.yaml b/example/t01-services/services/bl01t-mo-motor-01/config/ioc.yaml
similarity index 100%
rename from example/t01-services/services/bl01t-mo-ioc-01/config/ioc.yaml
rename to example/t01-services/services/bl01t-mo-motor-01/config/ioc.yaml
diff --git a/example/t01-services/synoptic/JsonMap.json b/example/t01-services/synoptic/JsonMap.json
new file mode 100644
index 00000000..7b3732ae
--- /dev/null
+++ b/example/t01-services/synoptic/JsonMap.json
@@ -0,0 +1,101 @@
+{
+ "file": "index.bob",
+ "children": [
+ {
+ "file": "dcam1.bob",
+ "children": [
+ {
+ "file": "ProfileCursorGraphs.bob",
+ "macros": {
+ "P": "BL01T-DI-CAM-01",
+ "R": ":CAM:",
+ "label": "CAM",
+ "IOC": "https://t01-opis.diamond.ac.uk/bl01t-di-cam-01"
+ },
+ "displayName": "CAM"
+ },
+ {
+ "file": "techui-support/bob/ADAravis/ADAravis_detail.bob",
+ "macros": {
+ "P": "BL01T-DI-CAM-01",
+ "R": ":CAM:",
+ "label": "CAM"
+ },
+ "displayName": "ADAravis Camera"
+ },
+ {
+ "file": "techui-support/bob/ADAravis/NDPluginROI.pvi.bob",
+ "macros": {
+ "P": "BL01T-DI-CAM-01",
+ "R": ":ROI:",
+ "label": "ROI"
+ },
+ "displayName": "NDPluginROI"
+ },
+ {
+ "file": "techui-support/bob/ADAravis/NDPluginStats.pvi.bob",
+ "macros": {
+ "P": "BL01T-DI-CAM-01",
+ "R": ":STAT:",
+ "label": "STAT"
+ },
+ "displayName": "NDPluginStats"
+ },
+ {
+ "file": "techui-support/bob/ADAravis/NDFileHDF5.pvi.bob",
+ "macros": {
+ "P": "BL01T-DI-CAM-01",
+ "R": ":HDF5:",
+ "label": "HDF5"
+ },
+ "displayName": "NDFileHDF5"
+ }
+ ],
+ "displayName": "Diagnostic Camera 1"
+ },
+ {
+ "file": "test.bob",
+ "exists": false,
+ "displayName": "Diode 1"
+ },
+ {
+ "file": "temp1.bob",
+ "exists": false,
+ "displayName": "Temperaure Controller 1"
+ },
+ {
+ "file": "motor1.bob",
+ "children": [
+ {
+ "file": "$(IOC)/pmacAxis.pvi.bob",
+ "macros": {
+ "M": ":X",
+ "P": "BL01T-MO-MOTOR-01",
+ "label": "X1",
+ "IOC": "https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01"
+ },
+ "displayName": "X1"
+ },
+ {
+ "file": "$(IOC)/pmacAxis.pvi.bob",
+ "macros": {
+ "M": ":A",
+ "P": "BL01T-MO-MOTOR-01",
+ "label": "A",
+ "IOC": "https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01"
+ },
+ "displayName": "A"
+ },
+ {
+ "file": "techui-support/bob/pmac/pmacController.bob",
+ "macros": {
+ "P": "BL01T-MO-BRICK-01"
+ },
+ "displayName": "pmacController"
+ }
+ ],
+ "displayName": "Motor Stage"
+ }
+ ],
+ "displayName": "Display"
+}
diff --git a/example/t01-services/synoptic/config/status.db b/example/t01-services/synoptic/config/status.db
new file mode 100644
index 00000000..b1bf0dae
--- /dev/null
+++ b/example/t01-services/synoptic/config/status.db
@@ -0,0 +1,41 @@
+###################################################
+## THIS FILE HAS BEEN AUTOGENERATED; DO NOT EDIT ##
+###################################################
+
+record(calc, "BL01T-EA-FSHTR-01:STA")
+{
+ field(ACKT, "NO")
+ field(CALC, "(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0")
+ field(INPA, "BL01T-EA-FSHTR-01:FSHTR1.STAT")
+ field(INPB, "")
+ field(INPC, "")
+ field(INPD, "")
+ field(INPE, "")
+ field(INPF, "")
+ field(INPG, "")
+ field(INPH, "")
+ field(INPI, "")
+ field(INPJ, "")
+ field(INPK, "")
+ field(INPL, "")
+ field(SCAN, "1 second")
+}
+
+record(calc, "BL01T-DI-CAM-01:STA")
+{
+ field(ACKT, "NO")
+ field(CALC, "(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0")
+ field(INPA, "BL01T-DI-CAM-01:CAM:DetectorState_RBV")
+ field(INPB, "")
+ field(INPC, "")
+ field(INPD, "")
+ field(INPE, "")
+ field(INPF, "")
+ field(INPG, "")
+ field(INPH, "")
+ field(INPI, "")
+ field(INPJ, "")
+ field(INPK, "")
+ field(INPL, "")
+ field(SCAN, "1 second")
+}
diff --git a/example/t01-services/synoptic/dcam1.bob b/example/t01-services/synoptic/dcam1.bob
new file mode 100644
index 00000000..12797766
--- /dev/null
+++ b/example/t01-services/synoptic/dcam1.bob
@@ -0,0 +1,109 @@
+
+
+ dcam1
+
+ Diagnostic Camera 1
+ 0
+ 0
+ 930
+ 590
+
+ CAM
+ 880
+ 470
+ techui-support/bob/ADAravis/ADAravis_summary.bob
+
+ BL01T-DI-CAM-01
+ :CAM:
+
+ https://t01-opis.diamond.ac.uk/bl01t-di-cam-01
+
+ 0
+ 0
+
+
+ CAM
+ 100
+ 40
+
+ CAM
+
+
+ Open Display
+
+ BL01T-DI-CAM-01
+ :CAM:
+
+
+ techui-support/bob/ADAravis/ADAravis_detail.bob
+ tab
+
+
+ 0
+ 500
+
+
+ ROI
+ 100
+ 40
+
+ ROI
+
+
+ Open Display
+
+ BL01T-DI-CAM-01
+ :ROI:
+
+
+ techui-support/bob/ADAravis/NDPluginROI.pvi.bob
+ tab
+
+
+ 120
+ 500
+
+
+ STAT
+ 100
+ 40
+
+ STAT
+
+
+ Open Display
+
+ BL01T-DI-CAM-01
+ :STAT:
+
+
+ techui-support/bob/ADAravis/NDPluginStats.pvi.bob
+ tab
+
+
+ 240
+ 500
+
+
+ HDF5
+ 100
+ 40
+
+ HDF5
+
+
+ Open Display
+
+ BL01T-DI-CAM-01
+ :HDF5:
+
+
+ techui-support/bob/ADAravis/NDFileHDF5.pvi.bob
+ tab
+
+
+ 360
+ 500
+
+
+
diff --git a/example/t01-services/synoptic/index.bob b/example/t01-services/synoptic/index.bob
index 94b07187..e6cd7aca 100644
--- a/example/t01-services/synoptic/index.bob
+++ b/example/t01-services/synoptic/index.bob
@@ -1,79 +1,158 @@
-
+
+
Display
-
- Detector
- fshtr
-
- techui-support/symbols/shutter 1.svg
-
- 60
- 150
- 50
- 50
-
-
- Detector.bob
- replace
- Open Display
-
-
-
-
- MotorController
- d1
-
- techui-support/symbols/diag-stick.svg
-
- 280
- 150
- 50
- 50
-
-
- MotorController.bob
- replace
- Open Display
-
-
-
-
- TempController
- motor
-
- techui-support/symbols/aperture.svg
-
- 490
- 150
- 50
- 50
-
-
- TempController.bob
- replace
- Open Display
-
-
+
+ dcam1_Group
+ 250
+ 120
+ 100
+ 80
+
+ true
+
+ dcam1
+
+ techui-support/symbols/camera.svg
+
+ 30
+ 30
+ 50
+ 50
+
+
+ Diagnostic Camera 1
+ dcam1.bob
+ replace
+
+
+ $(pv_name)
+$(pv_value)
+$(actions)
+ BL01T-DI-CAM-01:STA
+ true
+
+
+ dcam1_L:abel
+ DCAM1
+ 1
+ $(pv_name)
+$(pv_value)
+$(actions)
+
-
- Label
- FSHTR
+
+ diode1_Group
30
- 130
- 1
-
-
- Label_1
- D1
- 250
120
- 1
+ 100
+ 80
+
+ true
+
+ diode1
+
+ techui-support/symbols/oscilloscope.svg
+
+ 30
+ 30
+ 50
+ 50
+
+
+ Diode 1
+ test.bob
+ replace
+
+
+ $(pv_name)
+$(pv_value)
+$(actions)
+ BL01T-DI-PHDGN-01:STA
+ true
+
+
+ diode1_Label
+ DIODE1
+ 1
+
-
- Label_2
- Motor
+
+ temp1_Group
460
120
- 1
+ 100
+ 80
+
+ true
+
+ temp1
+ BL01T-EA-TEMP-01:STA
+
+ techui-support/symbols/cryocooler.svg
+
+ 30
+ 30
+ 50
+ 50
+
+
+ Temperaure Controller 1
+ temp1.bob
+ replace
+
+
+ $(pv_name)
+$(pv_value)
+$(actions)
+ true
+
+
+ temp1_label
+ temp1
+ 1
+ $(pv_name)
+$(pv_value)
+$(actions)
+
+
+
+ temp1_Group_1
+ 650
+ 120
+ 100
+ 80
+
+ true
+
+ motor1
+ BL01T-MO-MOTOR-01:STA
+
+ techui-support/symbols/translation-stage.svg
+
+ 30
+ 30
+ 50
+ 50
+
+
+ Motor Stage
+ motor1.bob
+ replace
+
+
+ $(pv_name)
+$(pv_value)
+$(actions)
+ true
+
+
+ motor1_label
+ motor1
+ 1
+ $(pv_name)
+$(pv_value)
+$(actions)
+
diff --git a/example/t01-services/synoptic/motor1.bob b/example/t01-services/synoptic/motor1.bob
new file mode 100644
index 00000000..e4abe216
--- /dev/null
+++ b/example/t01-services/synoptic/motor1.bob
@@ -0,0 +1,58 @@
+
+
+ motor1
+
+ Motor Stage
+ 0
+ 0
+ 270
+ 390
+
+ X1
+ 220
+ 120
+ techui-support/bob/pmac/motor_embed.bob
+
+ BL01T-MO-MOTOR-01
+ :X
+
+ https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01
+
+ 0
+ 0
+
+
+ A
+ 220
+ 120
+ techui-support/bob/pmac/motor_embed.bob
+
+ BL01T-MO-MOTOR-01
+ :A
+
+ https://t01-opis.diamond.ac.uk/bl01t-mo-motor-01
+
+ 0
+ 150
+
+
+ pmac.GeoBrick
+ 100
+ 40
+
+ pmac.GeoBrick
+
+
+ Open Display
+
+ BL01T-MO-BRICK-01
+
+ techui-support/bob/pmac/pmacController.bob
+ tab
+
+
+ 0
+ 300
+
+
+
diff --git a/example/t01-services/synoptic/techui.yaml b/example/t01-services/synoptic/techui.yaml
index ffd9f59a..fdc94103 100644
--- a/example/t01-services/synoptic/techui.yaml
+++ b/example/t01-services/synoptic/techui.yaml
@@ -12,12 +12,22 @@ components:
status:
- BL01T-EA-FSHTR-01:FSHTR1.STAT
- d1:
+ diode1:
label: Diode 1
prefix: BL01T-DI-PHDGN-01
file: test.bob
- motor:
+ dcam1:
+ label: Diagnostic Camera 1
+ prefix: BL01T-DI-CAM-01
+ status:
+ - BL01T-DI-CAM-01:CAM:DetectorState_RBV
+
+ temp1:
+ label: Temperaure Controller 1
+ prefix: BL01T-EA-TEMP-01
+
+ motor1:
label: Motor Stage
prefix: BL01T-MO-MOTOR-01
extras:
diff --git a/src/techui_builder/status.py b/src/techui_builder/status.py
index 4847673e..88e6a82a 100644
--- a/src/techui_builder/status.py
+++ b/src/techui_builder/status.py
@@ -28,11 +28,13 @@ class GenerateStatusPvs:
output: Path | None = field(default=None)
def __post_init__(self):
- # Determine the directory to write the json map file to.
+ # Determine the directory to write the status db file to.
# By default, this looks at the location of the techui file, but can
# be overwritten using the --output flag
- self._write_directory: Path = (
- self.output if self.output is not None else self.techui_path.parent
+ self._conf_dir: Path = (
+ self.output
+ if self.output is not None
+ else self.techui_path.parent / "config"
)
try:
@@ -70,13 +72,11 @@ def create_status_pv(self, prefix: str, inputs: list[str]):
self.status_pvs[prefix] = status_pv
def write_status_pvs(self):
- conf_dir = self._write_directory.joinpath("config")
-
# Create the config/ dir if it doesn't exist
- if not conf_dir.exists():
- os.mkdir(conf_dir)
+ if not self._conf_dir.exists():
+ os.mkdir(self._conf_dir)
- with open(conf_dir.joinpath("status.db"), "w") as f:
+ with open(self._conf_dir / "status.db", "w") as f:
# Add a header explaining the file is autogenerated
f.write("#" * 51 + "\n")
f.write(
diff --git a/tests/conftest.py b/tests/conftest.py
index 3fe71d9a..5e22ec61 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,3 +1,4 @@
+import shutil
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch
@@ -16,13 +17,29 @@
@pytest.fixture
-def builder():
- ixx_services = Path(__file__).parent.joinpath(Path("t01-services"))
- techui_path = ixx_services.joinpath("synoptic/techui.yaml")
+def tmp_t01_services(tmp_path) -> Path:
+ # Copy test files to tmp_path so they are visible to the tests
+ shutil.copytree(Path(__file__).parent / "t01-services", tmp_path / "t01-services")
+
+ return tmp_path / "t01-services"
+
+
+@pytest.fixture
+def tmp_test_files(tmp_path) -> Path:
+ # Copy test files to tmp_path so they are visible to the tests
+ shutil.copytree(Path("tests/test_files/"), tmp_path / "test_files")
+
+ return tmp_path / "test_files"
+
+
+@pytest.fixture
+def builder(tmp_t01_services):
+ ixx_services = tmp_t01_services
+ techui_path = ixx_services / "synoptic/techui.yaml"
b = Builder(techui_path)
- b._services_dir = ixx_services.joinpath("services")
- b._write_directory = ixx_services.joinpath("synoptic")
+ b._services_dir = ixx_services / "services"
+ b._write_directory = ixx_services / "synoptic"
return b
@@ -106,8 +123,8 @@ def builder_with_setup(builder: Builder, techui_support):
@pytest.fixture
-def builder_with_test_files(builder: Builder):
- builder._write_directory = Path("tests/test_files/").absolute()
+def builder_with_test_files(builder: Builder, tmp_test_files):
+ builder._write_directory = tmp_test_files
return builder
@@ -118,21 +135,26 @@ def components(builder_with_test_files: Builder):
@pytest.fixture
-def json_map_generator():
+def json_map_generator(tmp_t01_services):
return JsonMapGenerator(
- Path(__file__).parent.joinpath(Path("t01-services/synoptic/index.bob"))
+ tmp_t01_services / "synoptic/index.bob",
+ tmp_t01_services / "synoptic/techui.yaml",
+ output=tmp_t01_services / "synoptic",
)
@pytest.fixture
-def status_gen():
- return GenerateStatusPvs(Path("tests/t01-services/synoptic/techui.yaml").absolute())
+def status_gen(tmp_t01_services):
+ return GenerateStatusPvs(
+ tmp_t01_services / "synoptic/techui.yaml",
+ output=tmp_t01_services / "synoptic/",
+ )
@pytest.fixture
-def test_files():
- screen_path = Path("tests/test_files/test_bob.bob").absolute()
- dest_path = Path("tests/test_files/").absolute()
+def test_files(tmp_test_files):
+ screen_path = tmp_test_files / "test_bob.bob"
+ dest_path = tmp_test_files
return screen_path, dest_path
@@ -145,12 +167,11 @@ def example_json_map_root():
@pytest.fixture
-def json_map_generator_with_test_files():
+def json_map_generator_with_test_files(tmp_t01_services, tmp_test_files):
return JsonMapGenerator(
- bob_path=Path("tests/test_files/test_bob.bob").absolute(),
- techui=Path(__file__).parent.joinpath(
- Path("t01-services/synoptic/techui.yaml")
- ),
+ bob_path=tmp_test_files / "test_bob.bob",
+ techui=tmp_t01_services / "synoptic/techui.yaml",
+ output=tmp_test_files,
)
@@ -216,8 +237,8 @@ def example_display_names_json():
@pytest.fixture
-def generator(techui_support):
- synoptic_dir = Path(__file__).parent.joinpath(Path("t01-services/synoptic"))
+def generator(techui_support, tmp_t01_services):
+ synoptic_dir = tmp_t01_services / "synoptic"
techui_support_path = synoptic_dir.joinpath("techui-support")
g = Generator(synoptic_dir, "test_url", techui_support_path, techui_support)
@@ -226,8 +247,8 @@ def generator(techui_support):
@pytest.fixture
-def autofiller():
- index_bob = Path(__file__).parent.joinpath(Path("t01-services/synoptic/index.bob"))
+def autofiller(tmp_t01_services):
+ index_bob = tmp_t01_services / "synoptic/index.bob"
a = Autofiller(index_bob, {"test_widget": MagicMock(spec=Component)})
@@ -235,15 +256,15 @@ def autofiller():
@pytest.fixture
-def validator():
- test_bobs = [Path("tests/test_files/motor-edited.bob")]
+def validator(tmp_test_files):
+ test_bobs = [tmp_test_files / "motor_edited.bob"]
v = Validator(test_bobs)
return v
@pytest.fixture
-def example_xml_embedded_widget():
+def example_xml_embedded_widget(tmp_test_files):
# You cannot set a text tag of an ObjectifiedElement,
# so we need to make an etree.Element and convert it ...
@@ -257,7 +278,7 @@ def example_xml_embedded_widget():
height_element = SubElement(widget_element, "height")
height_element.text = "120"
file_element = SubElement(widget_element, "file")
- file_element.text = "tests/test-files/motor_embed.bob"
+ file_element.text = str(tmp_test_files / "motor_embed.bob")
macros_element = SubElement(widget_element, "macros")
macro_element_1 = SubElement(macros_element, "macro1")
macro_element_1.text = "test_macro_1"
@@ -269,7 +290,7 @@ def example_xml_embedded_widget():
@pytest.fixture
-def example_xml_related_widget():
+def example_xml_related_widget(tmp_t01_services):
# You cannot set a text tag of an ObjectifiedElement,
# so we need to make an etree.Element and convert it ...
@@ -287,8 +308,8 @@ def example_xml_related_widget():
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"
+ file_element.text = str(
+ tmp_t01_services / "synoptic/techui-support/bob/pmac/motor.bob"
)
desc_element = SubElement(action_element, "description")
desc_element.text = "placeholder description"
@@ -323,7 +344,7 @@ def example_xml_symbol_widget():
@pytest.fixture
-def example_xml_navtabs_widget():
+def example_xml_navtabs_widget(tmp_test_files):
# You cannot set a text tag of an ObjectifiedElement,
# so we need to make an etree.Element and convert it ...
@@ -343,7 +364,7 @@ def example_xml_navtabs_widget():
name_element_1 = SubElement(tab_element_1, "name")
name_element_1.text = "tab1"
file_element_1 = SubElement(tab_element_1, "file")
- file_element_1.text = "tests/test-files/motor_embed.bob"
+ file_element_1.text = str(tmp_test_files / "motor_embed.bob")
macros_element_1 = SubElement(tab_element_1, "macros")
macro_element_1 = SubElement(macros_element_1, "macro1")
macro_element_1.text = "test_macro_1"
@@ -351,7 +372,7 @@ def example_xml_navtabs_widget():
name_element_2 = SubElement(tab_element_2, "name")
name_element_2.text = "tab2"
file_element_2 = SubElement(tab_element_2, "file")
- file_element_2.text = "tests/test-files/motor_embed.bob"
+ file_element_2.text = str(tmp_test_files / "motor_embed.bob")
macros_element_2 = SubElement(tab_element_2, "macros")
macro_element_2 = SubElement(macros_element_2, "macro2")
macro_element_2.text = "test_macro_2"
diff --git a/tests/test_autofiller.py b/tests/test_autofiller.py
index 09da5026..46d62745 100644
--- a/tests/test_autofiller.py
+++ b/tests/test_autofiller.py
@@ -1,6 +1,5 @@
import logging
-from pathlib import Path
-from unittest.mock import Mock, patch
+from unittest.mock import MagicMock, Mock, patch
import pytest
from lxml.etree import ElementTree
@@ -11,7 +10,7 @@
# Imported in to autofill from utils, so that needs to be patched
@patch("techui_builder.autofill.read_bob")
-def test_autofiller_read_bob(mock_read_bob, autofiller):
+def test_autofiller_read_bob(mock_read_bob: MagicMock, autofiller):
mock_read_bob.return_value = (Mock(spec=ElementTree), Mock())
autofiller.read_bob()
@@ -34,14 +33,16 @@ def test_autofiller_autofill_bob(autofiller):
@patch("techui_builder.generate_jsonmap.objectify.deannotate")
@patch("techui_builder.generate_jsonmap.etree.ElementTree")
-def test_autofiller_write_bob(mock_tree, mock_deannotate, autofiller):
+def test_autofiller_write_bob(
+ mock_tree: MagicMock, mock_deannotate: MagicMock, autofiller, tmp_test_files
+):
autofiller.tree = mock_tree
- autofiller.write_bob(Path("tests/test_files/test_autofilled_bob.bob"))
+ autofiller.write_bob(tmp_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"),
+ tmp_test_files / "test_autofilled_bob.bob",
pretty_print=True,
encoding="utf-8",
xml_declaration=True,
@@ -72,7 +73,7 @@ def test_autofiller_write_bob(mock_tree, mock_deannotate, autofiller):
)
@patch("techui_builder.autofill._get_action_group")
def test_autofiller_replace_content(
- mock_get,
+ mock_get: MagicMock,
autofiller,
example_xml_related_widget,
prefix,
@@ -107,7 +108,9 @@ def test_autofiller_replace_content(
@patch("techui_builder.autofill._get_action_group")
-def test_autofiller_replace_content_no_action_group(mock_get, autofiller, caplog):
+def test_autofiller_replace_content_no_action_group(
+ mock_get: MagicMock, autofiller, caplog: pytest.LogCaptureFixture
+):
# Just to only run the code we want to test
autofiller.macros = ["desc"]
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 653bee05..bc9766f1 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -21,10 +21,20 @@ def test_beamline_attributes(builder, attr, expected):
"index, name, label, P, R, attribute, file, extras, child_labels",
[
(0, "fshtr", "Fast Shutter", "BL01T-EA-FSHTR-01", None, None, None, None, None),
- (1, "d1", "Diode 1", "BL01T-DI-PHDGN-01", None, None, "test.bob", None, None),
(
- 2,
- "motor",
+ 1,
+ "diode1",
+ "Diode 1",
+ "BL01T-DI-PHDGN-01",
+ None,
+ None,
+ "test.bob",
+ None,
+ None,
+ ),
+ (
+ 4,
+ "motor1",
"Motor Stage",
"BL01T-MO-MOTOR-01",
None,
@@ -61,7 +71,7 @@ def test_component_attributes(
assert component.extras == extras
-def test_missing_service(builder, caplog):
+def test_missing_service(builder, caplog: pytest.LogCaptureFixture):
builder._extract_entities = Mock(side_effect=OSError())
builder._extract_services()
for log_output in caplog.records:
@@ -104,8 +114,8 @@ def test_gb_extract_entities_ioc_yaml(
prefix = pv.split(":", maxsplit=1)[0]
builder._extract_entities(
- "bl01t-mo-ioc-01",
- builder._services_dir.joinpath("bl01t-mo-ioc-01/config/ioc.yaml"),
+ "bl01t-mo-motor-01",
+ builder._services_dir / "bl01t-mo-motor-01/config/ioc.yaml",
)
entity = builder.entities[prefix][index]
assert entity.type == type
@@ -121,8 +131,8 @@ def test_gb_extract_entities_ioc_yaml(
0,
"fastcs.TemperatureController",
None,
- "BL01T-EA-TEST-01",
- {"name": "BL01T-EA-TEST-01"},
+ "BL01T-EA-TEMP-01",
+ {"name": "BL01T-EA-TEMP-01"},
),
],
)
@@ -136,8 +146,8 @@ def test_gb_extract_entities_fastcs_yaml(
prefix = pv.split(":", maxsplit=1)[0]
builder._extract_entities(
- "bl01t-ea-ioc-01",
- builder._services_dir.joinpath("bl01t-ea-ioc-01/config/fastcs.yaml"),
+ "bl01t-ea-temp-01",
+ builder._services_dir / "bl01t-ea-temp-01/config/fastcs.yaml",
)
entity = builder.entities[prefix][index]
assert entity.type == type
@@ -146,7 +156,9 @@ def test_gb_extract_entities_fastcs_yaml(
assert entity.macros == macros
-def test_gb_extract_services_no_yaml_files(builder, caplog, tmp_path):
+def test_gb_extract_services_no_yaml_files(
+ builder, caplog: pytest.LogCaptureFixture, tmp_path
+):
# We don't want to use builder_with_setup as that calls _extract_services()
# and in turn that calls _extract_entities()
builder._extract_entities = Mock()
@@ -155,8 +167,8 @@ def test_gb_extract_services_no_yaml_files(builder, caplog, tmp_path):
builder.conf.beamline.location = "bl01z"
builder._services_dir = tmp_path
# Temporary files to test against
- (tmp_path / "bl01z-ea-ioc-01").mkdir()
- (tmp_path / "bl01z-ea-ioc-01/config").mkdir()
+ (tmp_path / "bl01z-ea-temp-01").mkdir()
+ (tmp_path / "bl01z-ea-temp-01/config").mkdir()
with pytest.raises(OSError) and caplog.at_level(logging.ERROR):
builder._extract_services()
@@ -165,7 +177,9 @@ def test_gb_extract_services_no_yaml_files(builder, caplog, tmp_path):
assert ("No ioc.yaml or fastcs.yaml found for service:") in log_output.message
-def test_gb_extract_services_both_yaml_files(builder, caplog, tmp_path):
+def test_gb_extract_services_both_yaml_files(
+ builder, caplog: pytest.LogCaptureFixture, tmp_path
+):
# We don't want to use builder_with_setup as that calls _extract_services()
# and in turn that calls _extract_entities()
builder._extract_entities = Mock()
@@ -174,10 +188,10 @@ def test_gb_extract_services_both_yaml_files(builder, caplog, tmp_path):
builder.conf.beamline.location = "bl01z"
builder._services_dir = tmp_path
# Temporary files to test against
- (tmp_path / "bl01z-ea-ioc-01").mkdir()
- (tmp_path / "bl01z-ea-ioc-01/config").mkdir()
- (tmp_path / "bl01z-ea-ioc-01/config/ioc.yaml").write_text("name: test")
- (tmp_path / "bl01z-ea-ioc-01/config/fastcs.yaml").write_text("name: other")
+ (tmp_path / "bl01z-ea-temp-01").mkdir()
+ (tmp_path / "bl01z-ea-temp-01/config").mkdir()
+ (tmp_path / "bl01z-ea-temp-01/config/ioc.yaml").write_text("name: test")
+ (tmp_path / "bl01z-ea-temp-01/config/fastcs.yaml").write_text("name: other")
with caplog.at_level(logging.CRITICAL):
with pytest.raises(SystemExit):
@@ -222,7 +236,7 @@ def test_create_screens(builder_with_setup):
# builder_with_setup._validate_screen.assert_called()
-def test_create_screens_no_entities(builder, caplog):
+def test_create_screens_no_entities(builder, caplog: pytest.LogCaptureFixture):
builder.entities = []
# We only wan't to capture CRITICAL output in this test
@@ -237,7 +251,9 @@ def test_create_screens_no_entities(builder, caplog):
) in log_output.message
-def test_create_screens_extra_p_does_not_exist(builder_with_setup, caplog):
+def test_create_screens_extra_p_does_not_exist(
+ builder_with_setup, caplog: pytest.LogCaptureFixture
+):
# We don't want to actually generate a screen
builder_with_setup._generate_screen = Mock(side_effect=None)
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 387263ea..199749eb 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,5 +1,4 @@
import logging
-import os
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch
@@ -27,6 +26,7 @@
def test_app():
result = runner.invoke(app)
help_result = runner.invoke(app, ["--help"])
+
assert result.exit_code == 2
assert help_result.exit_code == 0
# for some reason passing '--help' outputs an extra newline at the end
@@ -37,12 +37,14 @@ def test_app():
def test_app_version():
result = runner.invoke(app, ["--version"])
+
assert result.exit_code == 0
assert "techui-builder version:" in result.output
def test_app_schema():
result = runner.invoke(schema_app)
+
assert result.exit_code == 0
assert (
"\u2705 Wrote schemas/techui.schema.json\n\u2705 "
@@ -51,33 +53,36 @@ def test_app_schema():
@patch("techui_builder.status.status_run")
-def test_status_run(mock_status_run, caplog):
+def test_status_run(mock_status_run: MagicMock, tmp_t01_services):
ClearRecords()
mock_status_run.return_value = Mock()
- result = runner.invoke(status_app, ["example/t01-services/synoptic/techui.yaml"])
+
+ result = runner.invoke(status_app, [str(tmp_t01_services / "synoptic/techui.yaml")])
+
assert result.exit_code == 0
-def test_status_run_output_directory():
+def test_status_run_output_directory(tmp_t01_services):
ClearRecords()
- output_dir = Path("example/t01-services/")
+
+ output_dir = tmp_t01_services
+
result = runner.invoke(
- status_app, ["-o", str(output_dir), "example/t01-services/synoptic/techui.yaml"]
+ status_app,
+ [str(tmp_t01_services / "synoptic/techui.yaml"), "-o", str(output_dir)],
)
+
assert result.exit_code == 0
- assert Path.exists(output_dir.joinpath("config/status.db"))
- if Path.exists(output_dir.joinpath("config/status.db")):
- os.remove(output_dir.joinpath("config/status.db"))
- os.removedirs(output_dir.joinpath("config"))
+ assert Path.exists(output_dir / "synoptic/config/status.db")
@patch("techui_builder._logger.Logger")
-def test_log_level(mock_logger):
+def test_log_level(mock_logger: MagicMock):
log_level("INFO")
mock_logger.assert_called_once()
-def test_find_dirs(caplog):
+def test_find_dirs():
mock_services = MagicMock(spec=Path)
mock_services.relative_to.return_value = Path("mock_rel_path")
mock_parent = MagicMock(spec=Path)
@@ -94,7 +99,7 @@ def test_find_dirs(caplog):
assert services == Path("mock_rel_path")
-def test_find_dirs_no_ixx_services_dir(caplog):
+def test_find_dirs_no_ixx_services_dir(caplog: pytest.LogCaptureFixture):
test_file = MagicMock(spec=Path)
test_file.parents = []
@@ -108,7 +113,7 @@ def test_find_dirs_no_ixx_services_dir(caplog):
assert exc_info.value.code is None
-def test_find_dirs_jxx_services(caplog):
+def test_find_dirs_jxx_services(caplog: pytest.LogCaptureFixture):
mock_services = MagicMock(spec=Path)
mock_services.relative_to.return_value = Path("mock_rel_path")
mock_parent = MagicMock(spec=Path)
@@ -127,7 +132,7 @@ def test_find_dirs_jxx_services(caplog):
assert "ixx-services relative path:" in caplog.text
-def test_find_bob(caplog):
+def test_find_bob(caplog: pytest.LogCaptureFixture):
bob_file = Mock(spec=Path)
bob_file.exists = MagicMock(return_value=True)
@@ -138,8 +143,9 @@ def test_find_bob(caplog):
assert bob_file == file
-def test_find_bob_bob_file_does_not_exist(caplog):
+def test_find_bob_bob_file_does_not_exist(caplog: pytest.LogCaptureFixture):
bad_bob_file = Path("bad_bob_file")
+
with caplog.at_level(logging.CRITICAL) and pytest.raises(SystemExit) as exc_info:
find_bob(bad_bob_file, Mock(spec=Path))
@@ -150,7 +156,7 @@ def test_find_bob_bob_file_does_not_exist(caplog):
assert exc_info.value.code is None
-def test_find_bob_no_bob_file_finds_default_bob_file(caplog):
+def test_find_bob_no_bob_file_finds_default_bob_file(caplog: pytest.LogCaptureFixture):
mock_bob_file = Path("mock_bob_file")
mock_synoptic_dir = MagicMock(spec=Path)
mock_synoptic_dir.glob.return_value = iter([mock_bob_file])
@@ -162,7 +168,7 @@ def test_find_bob_no_bob_file_finds_default_bob_file(caplog):
assert f"bob file: {mock_bob_file}" in log_output.message
-def test_find_bob_no_bob_file_found(caplog):
+def test_find_bob_no_bob_file_found(caplog: pytest.LogCaptureFixture):
mock_synoptic_dir = MagicMock(spec=Path)
mock_synoptic_dir.glob.return_value = iter([])
@@ -183,7 +189,12 @@ def test_find_bob_no_bob_file_found(caplog):
@patch("techui_builder.main_app.find_dirs")
@patch("techui_builder.main_app.Autofiller")
@patch("techui_builder.main_app.Builder")
-def test_main(mock_builder, mock_autofiller, mock_find_dirs, mock_find_bob):
+def test_main(
+ mock_builder: MagicMock,
+ mock_autofiller: MagicMock,
+ mock_find_dirs: MagicMock,
+ mock_find_bob: MagicMock,
+):
mock_find_dirs.return_value = Mock(), Mock()
mock_path = Mock(spec=Path)
main(mock_path)
@@ -192,50 +203,54 @@ def test_main(mock_builder, mock_autofiller, mock_find_dirs, mock_find_bob):
mock_find_bob.assert_called_once()
-def test_main_json_map_no_bob_generation(caplog):
+def test_main_json_map_no_bob_generation(caplog: pytest.LogCaptureFixture):
runner.invoke(app, ["--generate-jsonmap"])
+
for log_output in caplog.records:
assert (
" Option '--generate-jsonmap' requires an argument." in log_output.message
)
-def test_main_json_map_wrong_file(caplog):
+def test_main_json_map_wrong_file(caplog: pytest.LogCaptureFixture):
result = runner.invoke(generate_jsonmap_app, ["map.json"])
+
assert result.exit_code == 1
for log_output in caplog.records:
assert "No such file or directory" in log_output.message
-def test_main_json_map_generation(caplog):
+def test_main_json_map_generation(caplog: pytest.LogCaptureFixture, tmp_t01_services):
runner.invoke(
generate_jsonmap_app,
[
- "tests/t01-services/synoptic/index.bob",
+ str(tmp_t01_services / "synoptic/index.bob"),
],
)
- if Path.exists(Path("tests/t01-services/synoptic/JsonMap.json")):
- os.remove("tests/t01-services/synoptic/JsonMap.json")
+
for log_output in caplog.records:
assert "Json map generated for (from" in log_output.message
-def test_main_json_map_generation_output_directory(caplog):
- output_dir = Path("tests/t01-services/")
+def test_main_json_map_generation_output_directory(
+ caplog: pytest.LogCaptureFixture, tmp_t01_services
+):
+ output_dir = tmp_t01_services
+
runner.invoke(
generate_jsonmap_app,
[
+ str(tmp_t01_services / "synoptic/index.bob"),
"-o",
str(output_dir),
- "tests/t01-services/synoptic/index.bob",
],
)
- assert Path.exists(output_dir.joinpath("JsonMap.json"))
- os.remove(output_dir.joinpath("JsonMap.json"))
+
+ assert Path.exists(output_dir / "JsonMap.json")
for log_output in caplog.records:
assert "Json map generated for (from" in log_output.message
-def test_main_without_techui_yaml(caplog):
+def test_main_json_map_generation_without_techui_yaml(caplog: pytest.LogCaptureFixture):
result = runner.invoke(generate_jsonmap_app)
assert "Missing argument 'BOB_PATH'." in result.output
diff --git a/tests/test_files/motor-edited.bob b/tests/test_files/motor_edited.bob
similarity index 91%
rename from tests/test_files/motor-edited.bob
rename to tests/test_files/motor_edited.bob
index 06900ea4..e5c37b75 100644
--- a/tests/test_files/motor-edited.bob
+++ b/tests/test_files/motor_edited.bob
@@ -12,7 +12,7 @@
X
205
120
- tests/test-files/motor_embed.bob
+ motor_embed.bob
BL01T-MO-MOTOR-01
X
diff --git a/tests/test_files/test_bob_embedded.bob b/tests/test_files/test_bob_embedded.bob
index f7349327..cf67cc2d 100644
--- a/tests/test_files/test_bob_embedded.bob
+++ b/tests/test_files/test_bob_embedded.bob
@@ -21,11 +21,16 @@ $(pv_value)
$(actions)
- Embedded Display
+ Z
+ 220
+ 120
motor_embed.bob
+
+ BL01T-MO-MOTOR-01
+ :EMBED
+
+
150
120
- 160
- 80
diff --git a/tests/test_generate.py b/tests/test_generate.py
index af4197f2..6b497b9a 100644
--- a/tests/test_generate.py
+++ b/tests/test_generate.py
@@ -24,15 +24,15 @@ def y(self, val: int):
self._y = val
-def test_generator_get_screen_dimensions_good(generator):
- test_embedded_screen = "tests/test_files/motor_embed.bob"
+def test_generator_get_screen_dimensions_good(generator, tmp_test_files):
+ test_embedded_screen = tmp_test_files / "motor_embed.bob"
x, y = generator._get_screen_dimensions(test_embedded_screen)
assert x == 120
assert y == 205
-def test_generator_get_screen_dimensions_default(generator):
- test_embedded_screen = "tests/test_files/motor_bad.bob"
+def test_generator_get_screen_dimensions_default(generator, tmp_test_files):
+ test_embedded_screen = tmp_test_files / "motor_bad.bob"
x, y = generator._get_screen_dimensions(test_embedded_screen)
assert x == 100
assert y == 100
@@ -46,8 +46,8 @@ def test_generator_get_widget_dimensions_good(generator):
assert width == 205
-def test_generator_get_widget_dimensions_default(generator):
- widget_bad = Path("tests/test_files/widget_bad.xml")
+def test_generator_get_widget_dimensions_default(generator, tmp_test_files):
+ widget_bad = tmp_test_files / "widget_bad.xml"
with open(widget_bad) as f:
xml_content_bad = f.read()
@@ -57,8 +57,10 @@ def test_generator_get_widget_dimensions_default(generator):
assert width == 100
-def test_generator_get_widget_dimensions_default_attribute_error(generator):
- widget_bad = Path("tests/test_files/widget_bad_2.xml")
+def test_generator_get_widget_dimensions_default_attribute_error(
+ generator, tmp_test_files
+):
+ widget_bad = tmp_test_files / "widget_bad_2.xml"
with open(widget_bad) as f:
xml_content_bad = f.read()
@@ -76,8 +78,8 @@ def test_generator_get_widget_position(generator):
assert y == 0
-def test_generator_get_widget_position_default(generator):
- widget_bad = Path("tests/test_files/widget_bad.xml")
+def test_generator_get_widget_position_default(generator, tmp_test_files):
+ widget_bad = tmp_test_files / "widget_bad.xml"
with open(widget_bad) as f:
xml_content_bad = f.read()
@@ -87,8 +89,10 @@ def test_generator_get_widget_position_default(generator):
assert y == 100
-def test_generator_get_widget_position_default_attribute_error(generator):
- widget_bad = Path("tests/test_files/widget_bad_2.xml")
+def test_generator_get_widget_position_default_attribute_error(
+ generator, tmp_test_files
+):
+ widget_bad = tmp_test_files / "widget_bad_2.xml"
with open(widget_bad) as f:
xml_content_bad = f.read()
@@ -106,7 +110,7 @@ def test_generator_get_group_dimensions(generator):
assert width == 300
-def test_generator_create_widgets_keyerror(generator, caplog):
+def test_generator_create_widgets_keyerror(generator, caplog: pytest.LogCaptureFixture):
generator._get_screen_dimensions = Mock(return_value=(800, 1280))
screen_name = "test"
component = Entity(
@@ -126,7 +130,9 @@ def test_generator_create_widgets_keyerror(generator, caplog):
)
-def test_generator_create_widgets_embedded(generator, example_pgen_embedded_widget):
+def test_generator_create_widgets_embedded(
+ generator, example_pgen_embedded_widget, tmp_test_files
+):
generator._allocate_widget = Mock(return_value=example_pgen_embedded_widget)
screen_name = "test"
@@ -142,7 +148,7 @@ def test_generator_create_widgets_embedded(generator, example_pgen_embedded_widg
name=screen_name,
component=component,
)
- control_widget = Path("tests/test_files/widget.xml")
+ control_widget = tmp_test_files / "widget.xml"
with open(control_widget) as f:
xml_content = f.read()
@@ -207,7 +213,7 @@ def test_generator_update_macros_suffix_with_child_labels(generator):
assert updated_macros["label"] == child_label
-def test_generator_allocate_widget(generator):
+def test_generator_allocate_widget(generator, tmp_test_files):
generator._update_macros = Mock(
return_value=("CAM", {"P": "BL01T-DI-IOC-01", "R": ":CAM:", "label": "CAM"})
)
@@ -226,7 +232,7 @@ def test_generator_allocate_widget(generator):
macros={"P": "BL01T-DI-IOC-01", "R": ":CAM:"},
)
widget = generator._allocate_widget(scrn_mapping, component)
- control_widget = Path("tests/test_files/widget.xml")
+ control_widget = tmp_test_files / "widget.xml"
with open(control_widget) as f:
xml_content = f.read()
@@ -234,7 +240,7 @@ def test_generator_allocate_widget(generator):
assert str(widget) == xml_content
-def test_generator_allocate_widget_with_remote_screens(generator):
+def test_generator_allocate_widget_with_remote_screens(generator, tmp_test_files):
generator._update_macros = Mock(
return_value=("CAM", {"P": "BL01T-DI-IOC-01", "R": ":CAM:", "label": "CAM"})
)
@@ -250,7 +256,7 @@ def test_generator_allocate_widget_with_remote_screens(generator):
macros={"P": "BL01T-DI-IOC-01", "R": ":CAM:"},
)
widget = generator._allocate_widget(scrn_mapping, component)
- control_widget = Path("tests/test_files/widget_url_screen.xml")
+ control_widget = tmp_test_files / "widget_url_screen.xml"
with open(control_widget) as f:
xml_content = f.read()
@@ -258,7 +264,7 @@ def test_generator_allocate_widget_with_remote_screens(generator):
assert str(widget) == xml_content
-def test_generator_allocate_widget_with_custom_suffix(generator):
+def test_generator_allocate_widget_with_custom_suffix(generator, tmp_test_files):
generator._update_macros = Mock(return_value=("CAM", {"P": "BL01T-DI-IOC-01"}))
generator._get_screen_dimensions = Mock(return_value=(40, 100))
@@ -275,7 +281,7 @@ def test_generator_allocate_widget_with_custom_suffix(generator):
macros={"P": "BL01T-DI-IOC-01"},
)
widget = generator._allocate_widget(scrn_mapping, component)
- control_widget = Path("tests/test_files/widget_custom_suffix.xml")
+ control_widget = tmp_test_files / "widget_custom_suffix.xml"
with open(control_widget) as f:
xml_content = f.read()
@@ -283,7 +289,9 @@ def test_generator_allocate_widget_with_custom_suffix(generator):
assert str(widget) == xml_content
-def test_generator_create_widgets_related(generator, example_pgen_related_widget):
+def test_generator_create_widgets_related(
+ generator, example_pgen_related_widget, tmp_test_files
+):
generator._allocate_widget = Mock(return_value=example_pgen_related_widget)
generator._get_screen_dimensions = Mock(return_value=(800, 1280))
@@ -300,14 +308,15 @@ def test_generator_create_widgets_related(generator, example_pgen_related_widget
component=component,
)
- control_widget = Path("tests/test_files/widget_related.xml")
+ control_widget = tmp_test_files / "widget_related.xml"
with open(control_widget) as f:
xml_content = f.read()
+
assert str(widgets[0]) == xml_content
# def test_generator_create_widgets_related_no_suffix(
-# generator, example_pgen_related_widget
+# generator, example_pgen_related_widget, tmp_test_files
# ):
# generator._allocate_widget = Mock(return_value=example_pgen_related_widget)
# generator._get_screen_dimensions = Mock(return_value=(800, 1280))
@@ -325,7 +334,7 @@ def test_generator_create_widgets_related(generator, example_pgen_related_widget
# component=component,
# )
-# control_widget = Path("tests/test_files/widget_related_no_suffix.xml")
+# control_widget = tmp_test_files / "widget_related_no_suffix.xml"
# with open(control_widget) as f:
# xml_content = f.read()
@@ -392,7 +401,7 @@ def test_generator_build_screen(generator, components):
def test_build_groups_with_label(generator, components):
- screen_name = "motor"
+ screen_name = "motor1"
generator.widgets = [Mock(), Mock(), Mock()]
generator._create_widgets = Mock(return_value=Mock())
generator.layout_widgets = Mock(
@@ -429,18 +438,20 @@ def test_build_groups(generator, components):
assert xml.xpath("//name")[0] == "test"
-def test_generator_write_screen(generator):
+def test_generator_write_screen(generator, tmp_test_files):
screen_name = "test"
generator.screen_ = pscreen.Screen("test")
generator.widgets = [Mock(), Mock()]
- generator.write_screen(screen_name, Path("tests/test_files/"))
- assert Path("tests/test_files/test.bob").exists()
- Path("tests/test_files/test.bob").unlink()
+ generator.write_screen(screen_name, tmp_test_files)
+
+ assert Path.exists(tmp_test_files / "test.bob")
-def test_generator_write_screen_no_widgets(generator, caplog):
+def test_generator_write_screen_no_widgets(
+ generator, caplog: pytest.LogCaptureFixture, tmp_test_files
+):
screen_name = "test"
generator.screen_ = pscreen.Screen("test")
generator.widgets = []
- generator.write_screen(screen_name, Path("tests/test_files/"))
+ generator.write_screen(screen_name, tmp_test_files)
assert "Could not write screen: test as no widgets were available" in caplog.text
diff --git a/tests/test_generate_jsonmap.py b/tests/test_generate_jsonmap.py
index 31099c34..923d0aad 100644
--- a/tests/test_generate_jsonmap.py
+++ b/tests/test_generate_jsonmap.py
@@ -1,5 +1,4 @@
import logging
-import os
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch
@@ -20,7 +19,7 @@
@patch("techui_builder.generate_jsonmap.Logger")
-def test_log_level(mock_logger):
+def test_log_level(mock_logger: MagicMock):
log_level("INFO")
mock_logger.assert_called_once()
@@ -31,33 +30,36 @@ def test_write_json_map_no_synoptic(json_map_generator):
json_map_generator.write_json_map()
-def test_app():
- result = runner.invoke(app, ["tests/t01-services/synoptic/techui.yaml"])
- if Path.exists(Path("tests/t01-services/synoptic/JsonMap.json")):
- os.remove("tests/t01-services/synoptic/JsonMap.json")
+def test_app(tmp_t01_services):
+ result = runner.invoke(
+ app,
+ [
+ str(tmp_t01_services / "synoptic/index.bob"),
+ "--output",
+ str(tmp_t01_services / "synoptic"),
+ ],
+ )
+
assert result.exit_code == 0
@patch("techui_builder.generate_jsonmap.yaml.safe_load")
-def test_json_map_generator_techui_exception(mock_safe_load, json_map_generator):
+def test_json_map_generator_techui_exception(
+ mock_safe_load: MagicMock, json_map_generator, tmp_test_files
+):
mock_safe_load.side_effect = Exception("YAML load error")
with pytest.raises(Exception) as excinfo:
- json_map_generator.__init__(bob_path=Path("tests/test_files/test_bob.bob"))
+ json_map_generator.__init__(bob_path=tmp_test_files / "test_bob.bob")
+
assert "No such file or directory" in str(excinfo.value)
-def test_write_json_map(json_map_generator):
- test_map = JsonMap(
- str(Path(__file__).parent.joinpath("test_files/test_bob.bob")), None
- )
+def test_write_json_map(json_map_generator, tmp_test_files):
+ test_map = JsonMap(str(tmp_test_files / "test_bob.bob"), None)
# We don't want cover _generate_json_map in this test
json_map_generator.generate_json_map = Mock(return_value=test_map)
- # Make sure opis/ dir exists
- if not Path.exists(json_map_generator._write_directory):
- os.mkdir(json_map_generator._write_directory)
-
# We don't want to access the _serialise_json_map function in this test
with patch(
"techui_builder.generate_jsonmap._serialise_json_map"
@@ -66,24 +68,18 @@ def test_write_json_map(json_map_generator):
json_map_generator.write_json_map()
- dest_path = json_map_generator._write_directory.joinpath("JsonMap.json")
- assert Path.exists(dest_path)
+ dest_path = json_map_generator._write_directory / "JsonMap.json"
- if Path.exists(dest_path):
- os.remove(dest_path)
+ assert Path.exists(dest_path)
# We don't want to access the _get_action_group function in this test
@patch("techui_builder.generate_jsonmap._get_action_group")
def test_generate_json_map(
- mock_get_action_group,
+ mock_get_action_group: MagicMock,
json_map_generator_with_test_files,
example_json_map,
):
- json_map_generator_with_test_files.bob_path = Path(
- "tests/test_files/test_bob.bob"
- ).absolute()
-
mock_xml = objectify.Element("action")
mock_xml["file"] = "test_child_bob.bob"
mock_get_action_group.return_value = mock_xml
@@ -103,7 +99,7 @@ def test_generate_json_map(
def test_generate_json_map_embedded_screen(
- json_map_generator_with_test_files, example_json_map
+ json_map_generator_with_test_files, example_json_map, tmp_test_files
):
list_names = [
"Display",
@@ -119,9 +115,9 @@ def test_generate_json_map_embedded_screen(
side_effect=list_names
)
- json_map_generator_with_test_files.bob_path = Path(
- "tests/test_files/test_bob_embedded.bob"
- ).absolute()
+ json_map_generator_with_test_files.bob_path = (
+ tmp_test_files / "test_bob_embedded.bob"
+ )
example_json_map.file = "test_bob_embedded.bob"
example_json_map.children.append(
@@ -129,7 +125,7 @@ def test_generate_json_map_embedded_screen(
"$(IOC)/pmacAxis.pvi.bob",
display_name="Embedded Display",
exists=False,
- macros={"M": "$(M)", "P": "$(P)"},
+ macros={"M": ":EMBED", "P": "BL01T-MO-MOTOR-01", "label": "EMBED"},
)
)
@@ -141,7 +137,7 @@ def test_generate_json_map_embedded_screen(
def test_generate_json_map_nav_tabs(
- json_map_generator_with_test_files, example_json_map_root
+ json_map_generator_with_test_files, example_json_map_root, tmp_test_files
):
json_map_generator_with_test_files._parse_display_name = Mock(
side_effect=["Display", "Tab1", "Tab2"]
@@ -150,9 +146,9 @@ def test_generate_json_map_nav_tabs(
side_effect=["Display", "Tab1", "Tab2"]
)
- json_map_generator_with_test_files.bob_path = Path(
- "tests/test_files/test_bob_navtabs.bob"
- ).absolute()
+ json_map_generator_with_test_files.bob_path = (
+ tmp_test_files / "test_bob_navtabs.bob"
+ )
example_json_map_root.file = "test_bob_navtabs.bob"
example_json_map_root.children.extend(
@@ -193,12 +189,12 @@ def test_parse_display_name_returns_none(json_map_generator):
assert display_name is None
-def test_fix_names_json_map_recursive(json_map_generator, example_display_names_json):
+def test_fix_names_json_map_recursive(
+ json_map_generator, example_display_names_json, tmp_test_files
+):
"""Test duplicate names are enumerated correctly for all children"""
- test_display_names_json = JsonMap(
- str(Path(__file__).parent.joinpath("test_files/test_bob.bob")), None
- )
+ test_display_names_json = JsonMap(str(tmp_test_files / "test_bob.bob"), None)
test_display_names_json_det1 = JsonMap(
"test_child_bob.bob", "Detector", macros={"P": "PV-DET-01"}, exists=False
@@ -235,7 +231,7 @@ def test_fix_names_json_map_recursive(json_map_generator, example_display_names_
# We don't want to access the _get_action_group function in this test
@patch("techui_builder.generate_jsonmap._get_action_group")
def test_generate_json_map_get_macros(
- mock_get_action_group,
+ mock_get_action_group: MagicMock,
json_map_generator_with_test_files,
example_json_map,
):
@@ -263,11 +259,9 @@ def test_generate_json_map_get_macros(
def test_generate_json_map_xml_parse_error(
- json_map_generator_with_test_files,
+ json_map_generator_with_test_files, tmp_test_files
):
- json_map_generator_with_test_files.bob_path = Path(
- "tests/test_files/test_bob_bad.bob"
- ).absolute()
+ json_map_generator_with_test_files.bob_path = tmp_test_files / "test_bob_bad.bob"
test_json_map = json_map_generator_with_test_files.generate_json_map(
json_map_generator_with_test_files.bob_path,
@@ -279,7 +273,7 @@ def test_generate_json_map_xml_parse_error(
@patch("techui_builder.generate_jsonmap._get_action_group")
def test_generate_json_map_other_exception(
- mock_get_action_group,
+ mock_get_action_group: MagicMock,
json_map_generator_with_test_files,
):
mock_get_action_group.side_effect = Exception("Some exception")
@@ -310,8 +304,8 @@ def test_serialise_json_map(example_json_map):
}
-def test_get_action_group():
- test_bob = objectify.parse("tests/test_files/test_bob.bob")
+def test_get_action_group(tmp_test_files):
+ test_bob = objectify.parse(tmp_test_files / "test_bob.bob")
widget = test_bob.find(".//widget")
assert widget is not None
@@ -320,8 +314,8 @@ def test_get_action_group():
assert action_group is not None
-def test_get_action_group_no_action_elements():
- test_bob = objectify.parse("tests/test_files/test_bob.bob")
+def test_get_action_group_no_action_elements(tmp_test_files):
+ test_bob = objectify.parse(tmp_test_files / "test_bob.bob")
widget = test_bob.find(".//widget")
assert widget is not None
@@ -333,7 +327,7 @@ def test_get_action_group_no_action_elements():
assert action_group is None
-def test_get_action_group_no_actions_group(caplog):
+def test_get_action_group_no_actions_group(caplog: pytest.LogCaptureFixture):
# Use a blank xml element
widget = objectify.ObjectifiedElement()
# TODO: Do widgets always have a name attr, or _can_ it be empty??
@@ -348,7 +342,7 @@ def test_get_action_group_no_actions_group(caplog):
def test_get_component_label(json_map_generator_with_test_files):
display_name = json_map_generator_with_test_files._get_component_label(
- "motor",
+ "motor1",
None,
None,
)
@@ -358,7 +352,7 @@ def test_get_component_label(json_map_generator_with_test_files):
def test_get_component_label_child_labels(json_map_generator_with_test_files):
display_name = json_map_generator_with_test_files._get_component_label(
"X",
- current_component_name="motor",
+ current_component_name="motor1",
display_name="X",
)
assert display_name == "X1"
@@ -369,7 +363,7 @@ def test_get_component_label_child_labels_with_name_already_pregenerated(
):
display_name = json_map_generator_with_test_files._get_component_label(
"X1",
- current_component_name="motor",
+ current_component_name="motor1",
display_name="X",
)
assert display_name == "X1"
@@ -403,7 +397,7 @@ def test_get_nav_tabs(example_xml_navtabs_widget):
assert isinstance(tabs_widget, list)
-def test_get_nav_tabs_no_tabs_group(caplog):
+def test_get_nav_tabs_no_tabs_group(caplog: pytest.LogCaptureFixture):
mock_navtabs = MagicMock(spec=objectify.ObjectifiedElement)
mock_navtabs.name = "no_tabs"
diff --git a/tests/test_status.py b/tests/test_status.py
index 678c0596..eea38536 100644
--- a/tests/test_status.py
+++ b/tests/test_status.py
@@ -1,6 +1,5 @@
from io import StringIO
-from pathlib import Path
-from unittest.mock import Mock, mock_open, patch
+from unittest.mock import MagicMock, Mock, mock_open, patch
import pytest
from softioc.builder import ClearRecords, records
@@ -10,17 +9,20 @@
@patch("techui_builder.status.GenerateStatusPvs.write_status_pvs")
@patch("techui_builder.status.GenerateStatusPvs.create_status_pv")
-def test_status_run(mock_create, mock_write):
+def test_status_run(mock_create: MagicMock, mock_write: MagicMock, tmp_t01_services):
mock_create.return_value = Mock()
mock_write.return_value = Mock()
- status_run(Path("tests/t01-services/synoptic/techui.yaml").absolute())
- mock_create.assert_called_once()
- mock_write.assert_called_once()
+ status_run(tmp_t01_services / "synoptic/techui.yaml")
-def test_status_run_invalid_yaml(caplog):
+ mock_create.assert_called()
+ mock_write.assert_called()
+
+
+def test_status_run_invalid_yaml(caplog: pytest.LogCaptureFixture, tmp_test_files):
with pytest.raises(Exception): # noqa: B017
- status_run(Path("tests/invalid_techui.yaml").absolute())
+ status_run(tmp_test_files / "invalid_techui.yaml")
+
assert "Error loading techui.yaml" in caplog.text
@@ -87,7 +89,7 @@ def test_status_write_status_pvs(status_gen):
# Check open() was called with the correct args
m.assert_called_once_with(
- Path(status_gen._write_directory.joinpath("config/status.db")),
+ status_gen._conf_dir / "status.db",
"w",
)
mock_print.assert_called_once()
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 13a675e1..fb2565c9 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,5 +1,4 @@
-from pathlib import Path
-from unittest.mock import Mock, patch
+from unittest.mock import MagicMock, patch
from lxml.etree import _ElementTree
from lxml.objectify import Element, ObjectifiedElement
@@ -8,10 +7,10 @@
@patch("techui_builder.utils.get_widgets")
-def test_read_bob(mock_get_widgets):
- mock_get_widgets.return_value = {"test_widget": Mock(spec=ObjectifiedElement)}
+def test_read_bob(mock_get_widgets: MagicMock, tmp_test_files):
+ mock_get_widgets.return_value = {"test_widget": MagicMock(spec=ObjectifiedElement)}
- tree, widgets = read_bob(Path("tests/test_files/index.bob"))
+ tree, widgets = read_bob(tmp_test_files / "index.bob")
assert isinstance(tree, _ElementTree)
assert isinstance(widgets["test_widget"], ObjectifiedElement)
diff --git a/tests/test_validator.py b/tests/test_validator.py
index 86278379..de60c188 100644
--- a/tests/test_validator.py
+++ b/tests/test_validator.py
@@ -1,5 +1,4 @@
-from pathlib import Path
-from unittest.mock import Mock, patch
+from unittest.mock import MagicMock, Mock, patch
from lxml.etree import Element, _ElementTree, tostring
from lxml.objectify import fromstring
@@ -18,11 +17,11 @@ 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"
+ assert list(validator.validate.keys())[0] == "motor_edited"
@patch("techui_builder.validator.read_bob")
-def test_validator_read_bob(mock_read_bob, validator):
+def test_validator_read_bob(mock_read_bob: MagicMock, validator):
# 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())
@@ -31,7 +30,7 @@ def test_validator_read_bob(mock_read_bob, validator):
# TODO: Clean up this test... (make fixture for mock xml?)
-def test_validator_validate_bob(validator, example_xml_embedded_widget):
+def test_validator_validate_bob(validator, example_xml_embedded_widget, tmp_test_files):
# 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")
@@ -46,10 +45,10 @@ def test_validator_validate_bob(validator, example_xml_embedded_widget):
{"motor": (mock_element)},
)
)
- validator.validate = {"motor-edited": Path("tests/test_files/motor-edited.bob")}
+ validator.validate = {"motor_edited": tmp_test_files / "motor_edited.bob"}
test_pwidget = EmbeddedDisplay(
"motor",
- "tests/test-files/motor_embed.bob",
+ tmp_test_files / "motor_embed.bob",
0,
0,
205,
@@ -57,4 +56,4 @@ def test_validator_validate_bob(validator, example_xml_embedded_widget):
)
test_pwidget.macro("macro1", "test_macro_1")
- validator.validate_bob("motor-edited", "motor", [test_pwidget])
+ validator.validate_bob("motor_edited", "motor", [test_pwidget])