Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Better error reporting on bad ioc.yaml #276

Merged
merged 7 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/ibek/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ def render(self, context: Any, template_text: Any) -> str:
ioc_yaml_file_name=self.file_name,
ioc_name=self.ioc_name,
)
except Exception:
print(f"ERROR RENDERING TEMPLATE:\n{template_text}")
raise
except Exception as e:
raise ValueError(
f"Error rendering template:\n{template_text}\nError:{e}"
) from e

def render_map(self, context: Any, map: Mapping[str, str | None]) -> dict[str, str]:
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/samples/iocs/DLS8515.ibek.ioc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ entities:

- type: ipac.Hy8002
name: BLX4I-VA-IOC-01-Slot4
# interrupt_vector: Vec1
interrupt_vector: Vec1
slot: 4

- type: DLS8515.DLS8515
Expand Down
238 changes: 238 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,247 @@
"""
System tests that run the CLI commands and compare the output to expected
results.
"""

import json
import subprocess
import sys
from pathlib import Path

import pytest
from pytest_mock import MockerFixture

import ibek.utils as utils
from ibek import __version__
from ibek.globals import (
GLOBALS,
PVI_YAML_PATTERN,
SUPPORT_YAML_PATTERN,
)
from ibek.runtime_cmds.commands import generate
from ibek.support_cmds.commands import generate_links
from tests.conftest import run_cli


def test_cli_version():
cmd = [sys.executable, "-m", "ibek", "--version"]
assert subprocess.check_output(cmd).decode().strip() == __version__


def test_ibek_schema(tmp_path: Path, samples: Path):
"""generate the global ibek schema"""
schema_path = tmp_path / "schema.json"
run_cli("support", "generate-schema", "--output", schema_path)
expected = json.loads(
(samples / "schemas" / "ibek.support.schema.json").read_text()
)

actual = json.loads((schema_path).read_text())
assert expected == actual


def test_single_schema(tmp_path: Path, samples: Path):
"""generate schema from a support module definition yaml"""

schema_path = tmp_path / "single.ibek.support.schema.json"
yaml_path = samples / "support" / "motorSim.ibek.support.yaml"
run_cli(
"ioc", "generate-schema", "--no-ibek-defs", yaml_path, "--output", schema_path
)

expected = json.loads(
(samples / "schemas" / "single.ibek.ioc.schema.json").read_text()
)

actual = json.loads((schema_path).read_text())
assert expected == actual


def test_motor_sim_schema(tmp_path: Path, samples: Path):
"""generate schema for a container with two support modules"""

schema_combined = tmp_path / "motorSim.ibek.ioc.schema.json"
yaml_path1 = samples / "support" / "asyn.ibek.support.yaml"
yaml_path2 = samples / "support" / "motorSim.ibek.support.yaml"
run_cli(
"ioc",
"generate-schema",
"--no-ibek-defs",
yaml_path1,
yaml_path2,
"--output",
schema_combined,
)

expected = json.loads(
(samples / "schemas" / "motorSim.ibek.ioc.schema.json").read_text()
)

actual = json.loads((schema_combined).read_text())
assert expected == actual


def test_build_runtime_motorSim(tmp_epics_root: Path, samples: Path):
"""
build an ioc runtime script from an IOC instance entity file
and multiple support module definition files

Also verifies database subst file generation for multiple
entity instantiations.
"""

generic_generate(
tmp_epics_root,
samples,
"motorSim",
["motorSim", "asyn"],
)


def test_build_utils_features(tmp_epics_root: Path, samples: Path):
"""
build an ioc runtime script to verify utils features
"""
generic_generate(
tmp_epics_root,
samples,
"utils",
["utils"],
)


def test_generate_links_ibek(samples: Path, mocker: MockerFixture):
symlink_mock = mocker.patch("ibek.support_cmds.commands.symlink_files")

generate_links(samples / "support")

symlink_mock.assert_any_call(
samples / "support", PVI_YAML_PATTERN, GLOBALS.PVI_DEFS
)
symlink_mock.assert_any_call(
samples / "support", SUPPORT_YAML_PATTERN, GLOBALS.IBEK_DEFS
)


def test_ipac(tmp_epics_root: Path, samples: Path):
"""
Tests that an id argument can include another argument in its default value
"""

# reset the InterruptVector counter to its initial state (if already used)
if "InterruptVector" in utils.UTILS.variables:
utils.UTILS.variables["InterruptVector"] = 191

generic_generate(
tmp_epics_root,
samples,
"ipac-test",
["ipac", "epics"],
)


def test_gauges(tmp_epics_root: Path, samples: Path):
"""
Tests that an id argument can include another argument in its default value
"""
generic_generate(
tmp_epics_root,
samples,
"gauges",
["asyn", "gauges"],
)


def test_quadem(tmp_epics_root: Path, samples: Path):
"""
Tests the use of CollectionDefinitions in an IOC instance
this example uses the tetramm beam position monitor module
"""
generic_generate(
tmp_epics_root,
samples,
"quadem",
["ADCore", "quadem"],
)


def generic_generate(
epics_root: Path, samples: Path, ioc_name: str, support_names: list[str]
):
ioc_yaml = samples / "iocs" / f"{ioc_name}.ibek.ioc.yaml"
support_yamls = [
samples / "support" / f"{name}.ibek.support.yaml" for name in support_names
]
expected_outputs = samples / "outputs" / ioc_name

generate(ioc_yaml, support_yamls)

for output in expected_outputs.glob("*"):
actual = epics_root / "runtime" / output.name
if not actual.exists():
actual = epics_root / "opi" / output.name
assert actual.exists(), "Missing output file"
assert output.read_text().strip() == actual.read_text().strip()


def test_andreas_motors(tmp_epics_root: Path, samples: Path):
"""
Motor and axis example
"""
generic_generate(
tmp_epics_root,
samples,
"technosoft",
["asyn", "technosoft"],
)


def test_list(tmp_epics_root: Path, samples: Path):
"""
Motor and axis example
"""
generic_generate(
tmp_epics_root,
samples,
"listarg",
["listarg"],
)


def test_fast_vacuum(tmp_epics_root: Path, samples: Path):
"""
Cut down copy of dlsPLC containing fast vacuum master and channel
"""
generic_generate(
tmp_epics_root,
samples,
"fastVacuum",
["fastVacuum"],
)


def test_dls_plc(tmp_epics_root: Path, samples: Path):
"""
Cut down copy of dlsPLC containing fast vacuum master and channel
"""
generic_generate(
tmp_epics_root,
samples,
"dlsPLC",
["dlsPLC", "asyn"],
)


def test_dls8515(tmp_epics_root: Path, samples: Path):
"""
Cut down copy of dlsPLC containing fast vacuum master and channel
"""
with pytest.raises(ValueError) as ctx:
generic_generate(
tmp_epics_root,
samples,
"DLS8515",
["DLS8515", "ipac", "epics"],
)

assert "`ipac.Hy8002`.interrupt_vector" in str(ctx.value)
5 changes: 2 additions & 3 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import shutil
from pathlib import Path

import jinja2
import pytest

from ibek.commands import semver_compare
Expand Down Expand Up @@ -87,11 +86,11 @@ def test_strict():
assert text == "giles of age 59"

my_template = "{{ person.name ~ ' of age ' ~ height }}"
with pytest.raises(jinja2.exceptions.UndefinedError):
with pytest.raises(ValueError):
text = UTILS.render({"person": p}, my_template)

my_template = "{{ person.name ~ ' of age ' ~ person.height }}"
with pytest.raises(jinja2.exceptions.UndefinedError):
with pytest.raises(ValueError):
text = UTILS.render({"person": p}, my_template)


Expand Down