Skip to content
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
26 changes: 26 additions & 0 deletions client/python/tests/e2e/cases/test_demo_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ def _driven_handles(anim: dict) -> list[int]:
return target.get("handles", []) if target.get("kind") == "Stimuli" else []


# Animations that move their target every frame, so its position is a runtime
# value rather than something the tutorial script sets.
_MOVING_ANIMATIONS = frozenset(
{"MoveAlongPath2D", "MoveAlongSegments2D", "ExternalPosition2D"}
)


def _moves_its_target(anim: dict) -> bool:
"""True when the animation writes its target's position on every frame."""
body = anim.get("animation")
if isinstance(body, dict):
return bool(_MOVING_ANIMATIONS & body.keys())
return body in _MOVING_ANIMATIONS


def _canonical(cfg: dict) -> dict:
"""Reduce a config to the parts a tutorial is responsible for reproducing."""
scene = cfg["scene"]
Expand All @@ -77,6 +92,12 @@ def _canonical(cfg: dict) -> dict:
by_handle = sorted(entries, key=int)
name_of = {h: entries[h]["name"] for h in by_handle}
driven = {str(h) for a in animations.values() for h in _driven_handles(a)}
moved = {
str(h)
for a in animations.values()
if _moves_its_target(a)
for h in _driven_handles(a)
}

stimuli = []
for handle in by_handle:
Expand All @@ -86,6 +107,11 @@ def _canonical(cfg: dict) -> dict:
# either state at the moment we look.
stim["common"]["flags"].pop("enabled", None)
body = stim["body"]
if handle in moved:
# A sweep owns this stimulus's position: by the time the config is
# retrieved the animation has already moved it some way along its
# path, and where exactly depends on how many frames have passed.
body.get("transform", {}).pop("pos_px", None)
if body["type"] == "Grating" and body["params"]["drift_speed_hz"] != 0.0:
body["params"].pop("phase_cycles", None) # advances every frame
stimuli.append([entries[handle]["name"], _round(stim)])
Expand Down
13 changes: 10 additions & 3 deletions client/python/tests/e2e/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import sys
import tempfile
import time
import warnings

import pytest

Expand Down Expand Up @@ -68,9 +69,15 @@ def server_process(server_address: str):
proc.wait(timeout=20)
except subprocess.TimeoutExpired:
# A fullscreen Vulkan renderer can take longer than a polite terminate
# allows to hand the display back. The suite is over either way, so the
# teardown kills rather than failing the last test that ran.
# allows to hand the display back, so escalate rather than fail the last
# test that ran.
proc.kill()
proc.wait(timeout=5)
try:
proc.wait(timeout=30)
except subprocess.TimeoutExpired:
# SIGKILL is not negotiable, so a process still here is stuck in the
# kernel giving the display back. Say so and let the suite finish:
# how long cleanup took is not a result about the code under test.
warnings.warn(f"vstimd (pid {proc.pid}) has not exited after SIGKILL")
log_file.close()
print(f"\nServer log: {log_path}")
13 changes: 10 additions & 3 deletions client/python/tests/e2e/test_psychopy_visual.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import sys
import tempfile
import time
import warnings

import pytest

Expand Down Expand Up @@ -75,10 +76,16 @@ def server_process(server_address: str):
proc.wait(timeout=20)
except subprocess.TimeoutExpired:
# A fullscreen Vulkan renderer can take longer than a polite terminate
# allows to hand the display back. The suite is over either way, so the
# teardown kills rather than failing the last test that ran.
# allows to hand the display back, so escalate rather than fail the last
# test that ran.
proc.kill()
proc.wait(timeout=5)
try:
proc.wait(timeout=30)
except subprocess.TimeoutExpired:
# SIGKILL is not negotiable, so a process still here is stuck in the
# kernel giving the display back. Say so and let the suite finish:
# how long cleanup took is not a result about the code under test.
warnings.warn(f"vstimd (pid {proc.pid}) has not exited after SIGKILL")
log_file.close()
print(f"\nServer log: {log_path}")

Expand Down
Loading