Files
2026-07-13 13:05:14 +08:00

208 lines
7.8 KiB
Python

from __future__ import annotations
import pytest
from rerun._tracing_session import (
_generate_session_id,
_is_valid_session_id,
tracing_session,
)
def test_generated_id_is_valid() -> None:
for _ in range(8):
sid = _generate_session_id()
assert _is_valid_session_id(sid), f"generated invalid id: {sid!r}"
def test_validation_rejects_malformed_ids() -> None:
bad_ids = [
"",
"rs_",
"rs_cafebab", # 7 hex chars
"rs_cafebabe1", # 9 hex chars
"rs_CAFEBABE", # uppercase
"rs_cafebabz", # non-hex
"xx_cafebabe", # wrong prefix
"cafebabe", # missing prefix
]
for sid in bad_ids:
assert not _is_valid_session_id(sid), f"unexpectedly accepted: {sid!r}"
def test_logs_session_id_at_scope_entry(monkeypatch: pytest.MonkeyPatch) -> None:
"""The session id must be forwarded to the Rust `tracing` stack on scope entry."""
import rerun_bindings # noqa: TID251
captured: list[str] = []
def fake_log(sid: str) -> None:
captured.append(sid)
# The context manager imports its bindings lazily from `rerun_bindings`, so
# patching the symbols on that module is what gets picked up at scope entry.
# `_is_telemetry_active` is forced to `True` so the test exercises the
# active-telemetry branch regardless of whether `TELEMETRY_ENABLED=true`
# was set for the running process (CI does not set it).
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", fake_log)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_finished", lambda *_args: None)
with tracing_session() as sid:
pass
assert captured == [sid], f"expected Rust logger to be called once with {sid!r}, got: {captured!r}"
def test_logs_metrics_at_scope_exit(monkeypatch: pytest.MonkeyPatch) -> None:
"""A normal exit must invoke `_log_tracing_session_finished` once with the active session id."""
import rerun_bindings # noqa: TID251
finished_calls: list[tuple[object, ...]] = []
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", lambda _sid: None)
monkeypatch.setattr(
rerun_bindings,
"_log_tracing_session_finished",
lambda *args: finished_calls.append(args),
)
with tracing_session() as sid:
pass
assert len(finished_calls) == 1, f"expected one finished call, got: {finished_calls!r}"
args = finished_calls[0]
# Signature: (sid, elapsed_s, cpu_user_s, cpu_system_s, cpu_iowait_s, net_rx_mb)
assert args[0] == sid
assert isinstance(args[1], float) and args[1] >= 0.0
# Remaining four fields are float|None depending on psutil/platform availability.
for field in args[2:]:
assert field is None or isinstance(field, float)
def test_skips_metrics_log_when_block_raises(monkeypatch: pytest.MonkeyPatch) -> None:
"""If the `with` body raises, the finished-log must not fire (simpler control flow)."""
import rerun_bindings # noqa: TID251
finished_calls: list[tuple[object, ...]] = []
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", lambda _sid: None)
monkeypatch.setattr(
rerun_bindings,
"_log_tracing_session_finished",
lambda *args: finished_calls.append(args),
)
class _Boom(Exception):
pass
with pytest.raises(_Boom):
with tracing_session():
raise _Boom
assert finished_calls == [], f"expected no finished call on early exit, got: {finished_calls!r}"
def test_psutil_failure_does_not_propagate(monkeypatch: pytest.MonkeyPatch) -> None:
"""A psutil failure during snapshot or delta must never break the `with` block."""
import rerun._tracing_session as ts
import rerun_bindings # noqa: TID251
finished_calls: list[tuple[object, ...]] = []
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", lambda _sid: None)
monkeypatch.setattr(
rerun_bindings,
"_log_tracing_session_finished",
lambda *args: finished_calls.append(args),
)
class _BrokenPsutil:
@staticmethod
def Process() -> None:
raise OSError("simulated AccessDenied")
@staticmethod
def net_io_counters() -> None:
raise OSError("simulated permission failure")
monkeypatch.setattr(ts, "psutil", _BrokenPsutil)
body_ran = False
with tracing_session() as sid:
body_ran = True
assert body_ran, "with-block body must execute even when psutil snapshots fail"
assert len(finished_calls) == 1, f"expected one finished call, got: {finished_calls!r}"
args = finished_calls[0]
assert args[0] == sid
assert isinstance(args[1], float)
# All four metric fields must be None when psutil fails.
assert args[2:] == (None, None, None, None)
def test_finished_log_failure_does_not_propagate(monkeypatch: pytest.MonkeyPatch) -> None:
"""If `_log_tracing_session_finished` itself raises, the `with` block must still complete cleanly."""
import rerun_bindings # noqa: TID251
def boom(*_args: object) -> None:
raise RuntimeError("simulated tracing failure")
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", lambda _sid: None)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_finished", boom)
# No exception should escape the context manager.
with tracing_session() as sid:
assert sid.startswith("rs_")
def test_warns_and_no_ops_when_telemetry_inactive(
caplog: pytest.LogCaptureFixture,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Warn and yield an empty id when `TELEMETRY_ENABLED` is not truthy."""
import rerun_bindings # noqa: TID251
# Force the inactive-telemetry branch regardless of process-wide env so
# this case exercises in CI even when telemetry happens to be active.
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: False)
with caplog.at_level("WARNING", logger="rerun"):
with tracing_session() as sid:
assert sid == ""
assert any("TELEMETRY_ENABLED=true" in r.getMessage() for r in caplog.records), (
f"expected a WARNING about TELEMETRY_ENABLED, got: {[r.getMessage() for r in caplog.records]}"
)
def test_nested_sessions_shadow_and_restore(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Nested `with tracing_session()` blocks shadow the outer id while open and restore it on exit.
Standard `ContextVar` token-reset semantics.
"""
import rerun_bindings # noqa: TID251
monkeypatch.setattr(rerun_bindings, "_is_telemetry_active", lambda: True)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_started", lambda _sid: None)
monkeypatch.setattr(rerun_bindings, "_log_tracing_session_finished", lambda *_args: None)
var = rerun_bindings._get_tracing_session_var()
assert var.get(None) is None, "expected no active session before any scope"
with tracing_session() as t1:
assert var.get(None) == t1, f"outer scope should see its own id, got {var.get(None)!r}"
with tracing_session() as t2:
assert t2 != t1, "nested scope should generate a distinct id"
assert var.get(None) == t2, f"inner scope should shadow outer, got {var.get(None)!r}"
assert var.get(None) == t1, f"outer id should be restored after inner exits, got {var.get(None)!r}"
assert var.get(None) is None, f"session should be cleared after outermost exits, got {var.get(None)!r}"