新增功能
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from wechat_bot import WeChatBot
|
||||
|
||||
|
||||
class SessionFingerprintTest(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _bot():
|
||||
bot = WeChatBot.__new__(WeChatBot)
|
||||
bot.scale = 1.0
|
||||
bot.session_item_h = 64
|
||||
bot._known_fps = set()
|
||||
return bot
|
||||
|
||||
@staticmethod
|
||||
def _row(name_variant: int) -> np.ndarray:
|
||||
img = np.full((64, 230, 3), 238, dtype=np.uint8)
|
||||
|
||||
# 两个客户使用完全相同的高对比头像。
|
||||
for y in range(20, 44):
|
||||
for x in range(12, 42):
|
||||
tone = 45 if ((x // 5) + (y // 4)) % 2 else 205
|
||||
img[y, x, :] = tone
|
||||
|
||||
# 在昵称首行画两组分布明显不同的合成字形。
|
||||
if name_variant == 1:
|
||||
for x in (62, 68, 76, 84, 96, 108, 122):
|
||||
img[12:28, x:x + 2, :] = 35
|
||||
img[13:15, 62:126, :] = 35
|
||||
img[21:23, 68:110, :] = 35
|
||||
else:
|
||||
for y in (12, 16, 21, 26):
|
||||
img[y:y + 2, 62:132, :] = 35
|
||||
for offset in range(16):
|
||||
img[11 + offset, 136 + offset:138 + offset, :] = 35
|
||||
return img
|
||||
|
||||
def test_same_avatar_different_name_patterns_are_isolated(self):
|
||||
bot = self._bot()
|
||||
first = bot._session_fingerprint(self._row(1), 32)
|
||||
second = bot._session_fingerprint(self._row(2), 32)
|
||||
|
||||
self.assertEqual(16, len(first))
|
||||
self.assertEqual(16, len(second))
|
||||
self.assertEqual(first[:8], second[:8], "测试前提:头像哈希必须相同")
|
||||
self.assertNotEqual(first, second)
|
||||
|
||||
def test_small_render_noise_and_label_inversion_are_normalized(self):
|
||||
bot = self._bot()
|
||||
original = self._row(1)
|
||||
canonical = bot._session_fingerprint(original, 32)
|
||||
|
||||
rng = np.random.default_rng(20260723)
|
||||
noise = rng.integers(-2, 3, size=original.shape, dtype=np.int16)
|
||||
variant = np.clip(original.astype(np.int16) + noise, 0, 255).astype(np.uint8)
|
||||
# 模拟选中态:文字区前景/背景同时反相,绝对边缘强度仍应稳定。
|
||||
variant[9:30, 58:190, :] = 255 - variant[9:30, 58:190, :]
|
||||
|
||||
self.assertEqual(canonical, bot._session_fingerprint(variant, 32))
|
||||
|
||||
def test_new_composite_fingerprint_never_merges_into_legacy_avatar_key(self):
|
||||
bot = self._bot()
|
||||
composite = bot._session_fingerprint(self._row(1), 32)
|
||||
legacy = composite[:8]
|
||||
|
||||
bot._known_fps = {legacy}
|
||||
self.assertEqual(composite, bot._canonical_fp(composite))
|
||||
self.assertIn(composite, bot._known_fps)
|
||||
|
||||
def test_identical_visible_rows_use_isolated_fallbacks(self):
|
||||
bot = self._bot()
|
||||
first_row = self._row(1)
|
||||
second_row = first_row.copy()
|
||||
# 消息预览属于可见行兜底摘要,但不参与稳定昵称签名。
|
||||
first_row[38:45, 70:100, :] = 60
|
||||
second_row[38:45, 120:160, :] = 60
|
||||
image = np.vstack((first_row, second_row))
|
||||
|
||||
first = bot._session_fingerprint(image, 32)
|
||||
second = bot._session_fingerprint(image, 96)
|
||||
self.assertEqual(16, len(first))
|
||||
self.assertEqual(16, len(second))
|
||||
self.assertNotEqual(first, second)
|
||||
|
||||
def test_out_of_bounds_fallback_is_16_bytes_and_image_specific(self):
|
||||
bot = self._bot()
|
||||
first_img = np.zeros((8, 10, 3), dtype=np.uint8)
|
||||
second_img = first_img.copy()
|
||||
second_img[0, 0, 0] = 1
|
||||
|
||||
first = bot._session_fingerprint(first_img, 0)
|
||||
second = bot._session_fingerprint(second_img, 0)
|
||||
self.assertEqual(16, len(first))
|
||||
self.assertEqual(16, len(second))
|
||||
self.assertNotEqual(first, second)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user