fix/stop lamni tests from leaking a fake bec global across the suite
test_lamni_account_change_reset.py and test_lamni_tomo_angles.py set
builtins.__dict__["bec"] directly with no cleanup, so a leaked fake
account ("e22222") from an earlier test file was still in place when
test_x_ray_eye_align.py later constructed a real LamNI(), tripping
_maybe_reset_params_on_account_change()'s interactive yesno() prompt
and failing under pytest's captured stdin. Use monkeypatch.setitem so
pytest reverts the global after each test, matching the pattern already
used in test_lamni_tomo_alignment_scan.py.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #266.
This commit is contained in:
@@ -60,9 +60,9 @@ def make_lamni(answer=True):
|
||||
return obj
|
||||
|
||||
|
||||
def test_skips_silently_when_no_active_account():
|
||||
def test_skips_silently_when_no_active_account(monkeypatch):
|
||||
lamni = make_lamni()
|
||||
builtins.__dict__["bec"] = type("Bec", (), {"active_account": ""})()
|
||||
monkeypatch.setitem(builtins.__dict__, "bec", type("Bec", (), {"active_account": ""})())
|
||||
|
||||
lamni._maybe_reset_params_on_account_change()
|
||||
|
||||
@@ -70,10 +70,10 @@ def test_skips_silently_when_no_active_account():
|
||||
assert lamni.OMNYTools.prompts == []
|
||||
|
||||
|
||||
def test_skips_silently_on_same_account():
|
||||
def test_skips_silently_on_same_account(monkeypatch):
|
||||
lamni = make_lamni()
|
||||
lamni.client.set_global_var("defaults_applied_for_account", "e12345")
|
||||
builtins.__dict__["bec"] = type("Bec", (), {"active_account": "e12345"})()
|
||||
monkeypatch.setitem(builtins.__dict__, "bec", type("Bec", (), {"active_account": "e12345"})())
|
||||
lamni.tomo_shellstep = 99.0
|
||||
|
||||
lamni._maybe_reset_params_on_account_change()
|
||||
@@ -82,12 +82,12 @@ def test_skips_silently_on_same_account():
|
||||
assert lamni.tomo_shellstep == 99.0
|
||||
|
||||
|
||||
def test_prompts_and_resets_on_new_account_when_confirmed():
|
||||
def test_prompts_and_resets_on_new_account_when_confirmed(monkeypatch):
|
||||
lamni = make_lamni(answer=True)
|
||||
lamni.client.set_global_var("defaults_applied_for_account", "e11111")
|
||||
lamni.tomo_shellstep = 99.0
|
||||
lamni.tomo_circfov = 123.0
|
||||
builtins.__dict__["bec"] = type("Bec", (), {"active_account": "e22222"})()
|
||||
monkeypatch.setitem(builtins.__dict__, "bec", type("Bec", (), {"active_account": "e22222"})())
|
||||
|
||||
lamni._maybe_reset_params_on_account_change()
|
||||
|
||||
@@ -99,11 +99,11 @@ def test_prompts_and_resets_on_new_account_when_confirmed():
|
||||
assert lamni.client.get_global_var("defaults_applied_for_account") == "e22222"
|
||||
|
||||
|
||||
def test_records_account_without_resetting_when_declined():
|
||||
def test_records_account_without_resetting_when_declined(monkeypatch):
|
||||
lamni = make_lamni(answer=False)
|
||||
lamni.client.set_global_var("defaults_applied_for_account", "e11111")
|
||||
lamni.tomo_shellstep = 99.0
|
||||
builtins.__dict__["bec"] = type("Bec", (), {"active_account": "e22222"})()
|
||||
monkeypatch.setitem(builtins.__dict__, "bec", type("Bec", (), {"active_account": "e22222"})())
|
||||
|
||||
lamni._maybe_reset_params_on_account_change()
|
||||
|
||||
|
||||
@@ -152,7 +152,9 @@ class _FakeScans:
|
||||
dataset_id_on_hold = _FakeContextManager()
|
||||
|
||||
|
||||
def make_lamni_for_tomo_scan(tomo_angle_stepsize: float, active_account: str) -> LamNI:
|
||||
def make_lamni_for_tomo_scan(
|
||||
monkeypatch, tomo_angle_stepsize: float, active_account: str
|
||||
) -> LamNI:
|
||||
"""Bare LamNI instance with tomo_scan()'s "new scan" branch reachable,
|
||||
everything downstream of it stubbed out (sample database, PDF report,
|
||||
per-sub-tomogram scanning) so only the account-handling logic under
|
||||
@@ -167,22 +169,26 @@ def make_lamni_for_tomo_scan(tomo_angle_stepsize: float, active_account: str) ->
|
||||
obj.lamnigui_show_progress = lambda: None
|
||||
obj.at_each_angle_hook = None
|
||||
obj.OMNYTools = types.SimpleNamespace(printgreenbold=lambda msg: None)
|
||||
builtins.__dict__["bec"] = types.SimpleNamespace(
|
||||
active_account=active_account,
|
||||
queue=types.SimpleNamespace(next_scan_number=1),
|
||||
builtin_actors=types.SimpleNamespace(
|
||||
scan_interlock=types.SimpleNamespace(trigger_setting=None, enabled=False)
|
||||
monkeypatch.setitem(
|
||||
builtins.__dict__,
|
||||
"bec",
|
||||
types.SimpleNamespace(
|
||||
active_account=active_account,
|
||||
queue=types.SimpleNamespace(next_scan_number=1),
|
||||
builtin_actors=types.SimpleNamespace(
|
||||
scan_interlock=types.SimpleNamespace(trigger_setting=None, enabled=False)
|
||||
),
|
||||
),
|
||||
)
|
||||
builtins.__dict__["scans"] = _FakeScans()
|
||||
monkeypatch.setitem(builtins.__dict__, "scans", _FakeScans())
|
||||
return obj
|
||||
|
||||
|
||||
def test_tomo_scan_skips_sample_database_when_no_active_account():
|
||||
def test_tomo_scan_skips_sample_database_when_no_active_account(monkeypatch):
|
||||
"""Empty active_account (e.g. a dev/sim session) must not crash and must
|
||||
not try to register a sample -- tomo_id falls back to 0, mirroring
|
||||
Flomni.tomo_scan()'s identical guard."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: (_ for _ in ()).throw(
|
||||
AssertionError("add_sample_database must not be called with no active account")
|
||||
)
|
||||
@@ -192,10 +198,10 @@ def test_tomo_scan_skips_sample_database_when_no_active_account():
|
||||
assert lamni.tomo_id == 0
|
||||
|
||||
|
||||
def test_tomo_scan_registers_sample_with_plain_string_account():
|
||||
def test_tomo_scan_registers_sample_with_plain_string_account(monkeypatch):
|
||||
"""A real active_account must be passed through as-is -- calling
|
||||
.decode() on it (a plain str, not bytes) raises AttributeError."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="e12345")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="e12345")
|
||||
recorded = {}
|
||||
|
||||
def _fake_add_sample_database(samplename, date, eaccount, scan_number, setup, info, user):
|
||||
@@ -210,11 +216,11 @@ def test_tomo_scan_registers_sample_with_plain_string_account():
|
||||
assert lamni.tomo_id == 42
|
||||
|
||||
|
||||
def test_tomo_scan_shows_progress_gui():
|
||||
def test_tomo_scan_shows_progress_gui(monkeypatch):
|
||||
"""tomo_scan() must open/show the progress GUI unconditionally at the
|
||||
start of every scan, mirroring Flomni.tomo_scan()'s
|
||||
self.flomnigui_show_progress() call -- lamni.py never had this at all."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: 0
|
||||
calls = []
|
||||
lamni.lamnigui_show_progress = lambda: calls.append("shown")
|
||||
@@ -224,7 +230,7 @@ def test_tomo_scan_shows_progress_gui():
|
||||
assert calls == ["shown"]
|
||||
|
||||
|
||||
def test_tomo_scan_clears_heartbeat_on_normal_completion():
|
||||
def test_tomo_scan_clears_heartbeat_on_normal_completion(monkeypatch):
|
||||
"""The busy-detector heartbeat must be cleared as soon as the scan
|
||||
finishes, not left for the next poll to time out (120s) before the GUI
|
||||
stops showing "beamline busy" for an already-finished scan -- mirrors
|
||||
@@ -236,7 +242,7 @@ def test_tomo_scan_clears_heartbeat_on_normal_completion():
|
||||
since tomo_scan()'s own "new scan" branch already resets the heartbeat
|
||||
to None *before* the scan body runs.
|
||||
"""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: 0
|
||||
|
||||
def _fake_sub_tomo_scan(subtomo_number, start_angle=None):
|
||||
@@ -249,12 +255,12 @@ def test_tomo_scan_clears_heartbeat_on_normal_completion():
|
||||
assert lamni.progress["heartbeat"] is None
|
||||
|
||||
|
||||
def test_tomo_scan_clears_heartbeat_even_if_scan_raises():
|
||||
def test_tomo_scan_clears_heartbeat_even_if_scan_raises(monkeypatch):
|
||||
"""The heartbeat must be cleared on every exit path, including a
|
||||
mid-scan exception -- otherwise a crashed scan looks permanently "busy"
|
||||
to the GUI until the 120s staleness timeout, or forever if polled more
|
||||
often than that."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: 0
|
||||
|
||||
def _boom(subtomo_number, start_angle=None):
|
||||
@@ -282,10 +288,10 @@ def test_subtomo_starts_near_zero(subtomo_number, expected):
|
||||
assert lamni._subtomo_starts_near_zero(subtomo_number) is expected
|
||||
|
||||
|
||||
def test_zero_deg_reference_disabled_by_default():
|
||||
def test_zero_deg_reference_disabled_by_default(monkeypatch):
|
||||
"""zero_deg_reference_at_each_subtomo defaults to False -- a fresh
|
||||
tomo_scan() must not fire any extra 0-deg shots."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: 0
|
||||
recorded = []
|
||||
lamni._tomo_scan_at_angle = lambda angle, subtomo_number: recorded.append(
|
||||
@@ -298,11 +304,11 @@ def test_zero_deg_reference_disabled_by_default():
|
||||
assert recorded == []
|
||||
|
||||
|
||||
def test_zero_deg_reference_fires_for_odd_subtomos_and_final_shot():
|
||||
def test_zero_deg_reference_fires_for_odd_subtomos_and_final_shot(monkeypatch):
|
||||
"""With the flag on, a fresh tomo_scan() must fire an extra angle-0 shot
|
||||
before each odd sub-tomogram (1, 3, 5, 7) plus one final shot after
|
||||
sub-tomogram 8 completes -- mirrors Flomni.tomo_scan()'s equivalent."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.add_sample_database = lambda *a, **k: 0
|
||||
lamni.zero_deg_reference_at_each_subtomo = True
|
||||
recorded = []
|
||||
@@ -316,12 +322,12 @@ def test_zero_deg_reference_fires_for_odd_subtomos_and_final_shot():
|
||||
assert recorded == [(0, 1), (0, 3), (0, 5), (0, 7), (0, 8)]
|
||||
|
||||
|
||||
def test_zero_deg_reference_skipped_when_resuming_mid_subtomo():
|
||||
def test_zero_deg_reference_skipped_when_resuming_mid_subtomo(monkeypatch):
|
||||
"""A resume (start_angle given explicitly for the first sub-tomogram of
|
||||
this call) must skip the extra shot for that first sub-tomogram --
|
||||
we're not actually passing through 0 deg at that moment -- but must
|
||||
still fire normally for later sub-tomograms in the same call."""
|
||||
lamni = make_lamni_for_tomo_scan(45.0, active_account="")
|
||||
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
|
||||
lamni.zero_deg_reference_at_each_subtomo = True
|
||||
recorded = []
|
||||
lamni._tomo_scan_at_angle = lambda angle, subtomo_number: recorded.append(
|
||||
|
||||
Reference in New Issue
Block a user