Files
wehub-resource-sync 09e9f3545f
CodeQL / Analyze (push) Waiting to run
dependency-audit / pip-audit (push) Waiting to run
Test / Code Quality (push) Has been cancelled
Test / Test (macos-latest, Python 3.10) (push) Has been cancelled
Test / Test (macos-latest, Python 3.11) (push) Has been cancelled
Test / Test (macos-latest, Python 3.12) (push) Has been cancelled
Test / Test (macos-latest, Python 3.13) (push) Has been cancelled
Test / Test (macos-latest, Python 3.14) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.10) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.11) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.12) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.13) (push) Has been cancelled
Test / Test (ubuntu-latest, Python 3.14) (push) Has been cancelled
Test / Test (windows-latest, Python 3.10) (push) Has been cancelled
Test / Test (windows-latest, Python 3.11) (push) Has been cancelled
Test / Test (windows-latest, Python 3.12) (push) Has been cancelled
Test / Test (windows-latest, Python 3.13) (push) Has been cancelled
Test / Test (windows-latest, Python 3.14) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:30:13 +08:00

113 lines
4.0 KiB
Python

"""Unit tests for the :class:`LoopBoundPrimitive` template-method base.
The mixin factors the one axis on which the six loop-bound collaborators'
``set_bound_loop`` bodies historically diverged: the trivial owners only stored
the binding, while the clear-on-rebind owners additionally discarded cached
loop-bound state *when the loop actually changed*. The template method always
stores and fires :meth:`_on_loop_rebind` only on a real change, before the
store. These tests pin that contract directly so a future edit to the base can't
silently break either family.
"""
from __future__ import annotations
import asyncio
from notebooklm._loop_bound import LoopBoundPrimitive
class _Recorder(LoopBoundPrimitive):
"""Owner that records every ``_on_loop_rebind`` call and the loop seen."""
def __init__(self) -> None:
self.rebinds: list[tuple[object, object]] = []
# Captures ``self._bound_loop`` *as observed inside the hook* so the
# tests can assert the hook runs BEFORE the store.
self.bound_loop_during_hook: list[object] = []
def _on_loop_rebind(self, old: object, new: object) -> None:
self.rebinds.append((old, new))
self.bound_loop_during_hook.append(self._bound_loop)
def test_default_hook_is_a_noop() -> None:
"""The base ``_on_loop_rebind`` does nothing (the trivial-owner default)."""
base = LoopBoundPrimitive()
# No exception, no state — the default body is a pass.
base._on_loop_rebind(None, object())
def test_default_bound_loop_is_none() -> None:
"""A fresh instance starts unbound so the affinity guard is a no-op."""
assert LoopBoundPrimitive()._bound_loop is None
def test_set_bound_loop_stores_the_binding() -> None:
"""``set_bound_loop`` always records the new loop (matches trivial owners)."""
rec = _Recorder()
loop = asyncio.new_event_loop()
try:
rec.set_bound_loop(loop)
assert rec._bound_loop is loop
finally:
loop.close()
def test_hook_fires_only_on_a_real_change() -> None:
"""Re-binding the *same* loop must not fire the hook."""
rec = _Recorder()
loop = asyncio.new_event_loop()
try:
rec.set_bound_loop(loop)
assert rec.rebinds == [(None, loop)] # None -> loop is a change
rec.set_bound_loop(loop) # same loop: no hook
assert rec.rebinds == [(None, loop)]
finally:
loop.close()
def test_hook_fires_on_each_distinct_rebind_with_old_and_new() -> None:
"""The hook sees the correct ``(old, new)`` pair on every real change."""
rec = _Recorder()
loop_a = asyncio.new_event_loop()
loop_b = asyncio.new_event_loop()
try:
rec.set_bound_loop(loop_a)
rec.set_bound_loop(loop_b)
rec.set_bound_loop(None)
assert rec.rebinds == [(None, loop_a), (loop_a, loop_b), (loop_b, None)]
finally:
loop_a.close()
loop_b.close()
def test_hook_runs_before_the_store() -> None:
"""``_on_loop_rebind`` must observe the OLD binding (hook before store).
Clear-on-rebind owners rely on this ordering to discard state captured
under the old loop before ``_bound_loop`` advances to the new one.
"""
rec = _Recorder()
loop_a = asyncio.new_event_loop()
loop_b = asyncio.new_event_loop()
try:
rec.set_bound_loop(loop_a)
# During the None->A rebind the hook saw the pre-store value (None).
assert rec.bound_loop_during_hook == [None]
rec.set_bound_loop(loop_b)
# During the A->B rebind the hook saw the still-old value (loop_a).
assert rec.bound_loop_during_hook == [None, loop_a]
# And the final state is the new loop.
assert rec._bound_loop is loop_b
finally:
loop_a.close()
loop_b.close()
def test_set_bound_loop_none_on_a_fresh_instance_is_a_noop() -> None:
"""``set_bound_loop(None)`` when already ``None`` is not a change."""
rec = _Recorder()
rec.set_bound_loop(None)
assert rec.rebinds == []
assert rec._bound_loop is None