Files
kefu/wechat_rpa/test_send_lock.py
T
2026-08-27 14:04:28 +08:00

107 lines
3.7 KiB
Python

# -*- coding: utf-8 -*-
"""发送互斥锁的边界行为测试。
这把锁是双引擎唯一的仲裁点:抢不到就跳过本轮发送。所以它的失败方式必须是
"这一轮没发",绝不能是抛异常打断轮询,更不能把锁吞掉再也不还。
"""
import threading
from unittest import TestCase, main
import send_lock
class FallbackLockTest(TestCase):
"""强制走非 Windows 回退分支(CI / macOS / Linux 开发机走的就是这条)。"""
def setUp(self):
self._saved = (
send_lock._init_done,
send_lock._handle,
send_lock._holder_tid,
)
send_lock._init_done = True
send_lock._handle = None
send_lock._holder_tid = None
while send_lock._FALLBACK_LOCK.locked():
try:
send_lock._FALLBACK_LOCK.release()
except RuntimeError:
break
def tearDown(self):
send_lock._holder_tid = None
while send_lock._FALLBACK_LOCK.locked():
try:
send_lock._FALLBACK_LOCK.release()
except RuntimeError:
break
send_lock._init_done, send_lock._handle, send_lock._holder_tid = self._saved
def test_default_zero_timeout_acquires_instead_of_raising(self):
# threading.Lock 不接受"非阻塞 + 超时"的组合;默认参数正好是这一种
self.assertTrue(send_lock.try_acquire("engine_a", 0))
send_lock.release()
def test_a_second_thread_is_turned_away_without_blocking(self):
self.assertTrue(send_lock.try_acquire("engine_a", 0))
result = {}
def other():
result["acquired"] = send_lock.try_acquire("engine_b", 0)
thread = threading.Thread(target=other)
thread.start()
thread.join(timeout=2.0)
self.assertFalse(thread.is_alive())
self.assertFalse(result["acquired"])
send_lock.release()
def test_release_makes_the_lock_available_again(self):
self.assertTrue(send_lock.try_acquire("engine_a", 0))
send_lock.release()
self.assertTrue(send_lock.try_acquire("engine_b", 0))
send_lock.release()
def test_same_thread_can_never_nest_a_send(self):
self.assertTrue(send_lock.try_acquire("engine_a", 0))
self.assertFalse(send_lock.try_acquire("engine_a", 0))
send_lock.release()
def test_positive_timeout_waits_and_still_succeeds(self):
self.assertTrue(send_lock.try_acquire("engine_a", 0.05))
send_lock.release()
def test_a_foreign_thread_cannot_release_someone_elses_lock(self):
self.assertTrue(send_lock.try_acquire("engine_a", 0))
def other():
send_lock.release()
thread = threading.Thread(target=other)
thread.start()
thread.join(timeout=2.0)
self.assertTrue(send_lock._FALLBACK_LOCK.locked())
send_lock.release()
def test_context_manager_releases_on_exception(self):
with self.assertRaises(RuntimeError):
with send_lock.lock("engine_a", 0) as acquired:
self.assertTrue(acquired)
raise RuntimeError("发送过程中崩了")
self.assertTrue(send_lock.try_acquire("engine_b", 0))
send_lock.release()
class WaitCodeTest(TestCase):
def test_abandoned_wait_is_treated_as_ownership(self):
# 上一个持有者崩溃退出时 Windows 返回 WAIT_ABANDONED:锁已经归本线程,
# 必须当成"抢到了",否则谁也不会去释放它。
self.assertEqual(send_lock._WAIT_ABANDONED, 0x80)
self.assertEqual(send_lock._WAIT_OBJECT_0, 0)
self.assertEqual(send_lock._WAIT_TIMEOUT, 0x102)
if __name__ == "__main__":
main()