Skip to content

Comments

test: new test suite (pytest-based) by Ken Lauer#59

Merged
ralphlange merged 9 commits intoepics-extensions:masterfrom
ralphlange:switch_to_pytest
Feb 18, 2026
Merged

test: new test suite (pytest-based) by Ken Lauer#59
ralphlange merged 9 commits intoepics-extensions:masterfrom
ralphlange:switch_to_pytest

Conversation

@ralphlange
Copy link
Contributor

@ralphlange ralphlange force-pushed the switch_to_pytest branch 2 times, most recently from c2e3a01 to 0559840 Compare October 15, 2025 17:45
@ralphlange ralphlange changed the title test: new test suite (pvtest-based) by Ken Lauer test: new test suite (pytest-based) by Ken Lauer Oct 15, 2025
@ralphlange ralphlange force-pushed the switch_to_pytest branch 3 times, most recently from 07f369f to e4a03a3 Compare December 2, 2025 14:19
@ralphlange ralphlange force-pushed the switch_to_pytest branch 2 times, most recently from 749f723 to 833dd02 Compare December 2, 2025 19:02
- Update to Ubuntu 24.04 as Access Security tests
  fail on Ubuntu 22.04 for unknown reasons
- Add caPutLog to 3.14 and 3.15 setup
- Use caPutLog "master" for 3.15 and 7, R4.0 for 3.14
- let the system choose an arbitrary port
@ralphlange
Copy link
Contributor Author

Ken Lauer's farewell gift to the CA Gateway.

Please have a look - the level of Python is way over my head...

Copy link

@mdavidsaver mdavidsaver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have read as far as testTop/pyTestsApp/gateway_tests/test_cs_studio.py.

@ralphlange I have tried to note some of the less familiar bits of python. Specifically the use of pytest fixtures. While I can not argue with their expediency, I find this feature to be quite non-obvious.

textwrap.indent(str(stdout), " "),
)

threading.Thread(daemon=True, target=read_stdout).start()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This thread is never joined. Since the function waits on the child, misbehavior on interpreter exit is unlikely. Still, this could instead be:

slirp = threading.Thread(target=read_stdout)
slirp.start()

... yield proc
... proc.wait()

slirp.join()

ioc, mask=dbr.DBE_PROPERTY, use_ctrl=True, callback=on_change_ioc
)

time.sleep(0.1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if these sleeps are actually necessary, so maybe not a big deal. Still, fixed sleeps with no explanation have a certain code smell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree for this instance.

In general (based on experience with the old Gateway tests), I found such naps necessary because it's a three-body setup (IOC, Gateway, test) and the test can't easily see the connection between Gateway and IOC.

Comment on lines +397 to +398
@pytest.fixture(scope="function")
def standard_env() -> Generator[EnvironmentInfo, None, None]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I note usage of pytest fixtures as I found these super confusing when I first saw eg.

def test_cs_studio_value_and_prop_monitor(standard_env: conftest.EnvironmentInfo):

without understanding where this magical standard_env argument comes from.

Comment on lines 96 to 102
ca.put(ioc_hihi, 123.0, wait=True)
time.sleep(0.1)

ca.put(ioc_value, 11.0, wait=True)
time.sleep(0.1)

assert events_received_ioc == events_received_gw, (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this sort of sequence will be prone to timing related erroneous failures. These sleep(0.1) are effectively settling times. 100ms is probably fine for local testing, but not so for shared GHA runners.

One way to explicitly synchronize would be to use a threading.Condition to wait for an expected end state. Although even then there could be problems if the expected end state is not the actual end state.

Something like:

done = threading.Condition()
def on_change_ioc(pvname=None, **kws):
    with done:
        ... update ioc_struct
        done.notify_all()
def on_change_gw(pvname=None, **kws):
    with done:
        ... update gw_start
        done.notify_all()
... # setup subscriptions and put

with done:
    while gw_struct.get('value')!=11 or ioc_struct.get('value')!=11: # I'm guessing at the key names
        assert done.wait(timeout=10.0)
    assert not done.wait(timeout=1.0) # ensure no unexpected event

... # full test of result

@mdavidsaver
Copy link

I have a branch which updates test_structures.py as a demonstration

https://github.com/mdavidsaver/ca-gateway/tree/refs/heads/pr-59

@ralphlange
Copy link
Contributor Author

Like this, I assume?

Disclaimer: I had Jules (Google's agentic AI) apply the pattern from your example to all tests.

@ralphlange
Copy link
Contributor Author

Fails on Base 3.14 - need to investigate.

Some runners on GitHub Actions seem to be heavily overbooked.
As suggested by @mdavidsaver in his review of PR 59,
replace fixed time.sleep() calls with threading.Condition synchronization.
This makes the tests more reliable and potentially faster
by waiting exactly as long as needed for asynchronous events.

- Systematic use of Condition and while loops for synchronization.
- Generous 10s timeouts for stability.
- Retained or added small fixed sleeps (0.1s - 0.3s) for clean shutdown,
  especially to prevent race conditions during gateway termination.

Co-authored-by: google-labs-jules[bot]
- Replace immediate `return` and `exit(0)` calls with a loop control flag
  in `gateServer::mainLoop` to allow graceful resource cleanup
- Explicitly clean up `gateRateStatsTimer` and `epicsTimerQueueActive`
  at the end of the server loop
- Add a 1-second quiescence period in tests for EPICS 3.14
  before terminating to prevent race condition crashes during shutdown
- Update `test_cs_studio_value_and_prop_monitor` to skip property update checks
  if `DBE_PROPERTY` is not supported (e.g., on EPICS 3.14).

Co-authored-by: google-labs-jules[bot]
@ralphlange
Copy link
Contributor Author

ralphlange commented Feb 16, 2026

I think this is in good shape now.
(After Jules seems to have found the issue on EPICS 3.14.)

Any last words?!

@ralphlange ralphlange merged commit b8a23e3 into epics-extensions:master Feb 18, 2026
10 checks passed
ralphlange added a commit that referenced this pull request Feb 18, 2026
@ralphlange ralphlange deleted the switch_to_pytest branch February 18, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants