refactor: 复用持久设备工作线程 (#110)
This commit is contained in:
@@ -3,7 +3,13 @@
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
from src.pdd_device_service import PddDeviceError, PddDeviceService
|
||||
from src.pdd_device_service import (
|
||||
PddDeviceError,
|
||||
PddDeviceService,
|
||||
PersistentPddDeviceService,
|
||||
bind_thread_device_service,
|
||||
current_thread_device_service,
|
||||
)
|
||||
from src.performance_timing import TaskPerformanceTrace
|
||||
|
||||
|
||||
@@ -89,5 +95,86 @@ class PddDeviceServiceTest(unittest.TestCase):
|
||||
self.assertEqual(errors, ["DEVICE_THREAD_VIOLATION"])
|
||||
|
||||
|
||||
class PersistentPddDeviceServiceTest(unittest.TestCase):
|
||||
def test_same_serial_within_ttl_connects_once_but_checks_each_task(self):
|
||||
connected = []
|
||||
device = FakeDevice()
|
||||
service = PersistentPddDeviceService(
|
||||
lambda serial: connected.append(serial) or device,
|
||||
ttl_seconds=90,
|
||||
)
|
||||
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
|
||||
self.assertEqual(connected, ["USB-001"])
|
||||
self.assertEqual(device.current_calls, 2)
|
||||
|
||||
def test_expired_or_changed_serial_discards_cached_connection(self):
|
||||
now = [10.0]
|
||||
connected = []
|
||||
service = PersistentPddDeviceService(
|
||||
lambda serial: connected.append(serial) or FakeDevice(),
|
||||
ttl_seconds=5,
|
||||
monotonic=lambda: now[0],
|
||||
)
|
||||
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
now[0] = 16.0
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
with service.connect("USB-002"):
|
||||
pass
|
||||
|
||||
self.assertEqual(connected, ["USB-001", "USB-001", "USB-002"])
|
||||
|
||||
def test_failed_cached_health_check_reconnects_only_once(self):
|
||||
class UnhealthyDevice(FakeDevice):
|
||||
def app_current(self):
|
||||
self.current_calls += 1
|
||||
if self.current_calls >= 2:
|
||||
raise OSError("offline")
|
||||
return {"package": "com.xunmeng.pinduoduo"}
|
||||
|
||||
devices = [UnhealthyDevice(), FakeDevice()]
|
||||
connected = []
|
||||
service = PersistentPddDeviceService(
|
||||
lambda serial: connected.append(serial) or devices.pop(0)
|
||||
)
|
||||
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
with service.connect("USB-001") as current:
|
||||
self.assertIsInstance(current, FakeDevice)
|
||||
|
||||
self.assertEqual(connected, ["USB-001", "USB-001"])
|
||||
|
||||
def test_release_and_context_binding_stay_on_owner_thread(self):
|
||||
service = PersistentPddDeviceService(lambda _serial: FakeDevice())
|
||||
with service.connect("USB-001"):
|
||||
pass
|
||||
with bind_thread_device_service(service):
|
||||
self.assertIs(current_thread_device_service(), service)
|
||||
self.assertIsNone(current_thread_device_service())
|
||||
|
||||
errors = []
|
||||
|
||||
def release_from_other_thread():
|
||||
try:
|
||||
service.release_cached()
|
||||
except PddDeviceError as exc:
|
||||
errors.append(exc.code)
|
||||
|
||||
worker = threading.Thread(target=release_from_other_thread)
|
||||
worker.start()
|
||||
worker.join()
|
||||
self.assertEqual(errors, ["DEVICE_THREAD_VIOLATION"])
|
||||
service.release_cached()
|
||||
self.assertFalse(service.has_cached_device)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1153,6 +1153,36 @@ class PDDTaskPageEventTest(unittest.TestCase):
|
||||
events.shutdown()
|
||||
page.deleteLater()
|
||||
|
||||
def test_auto_fetch_reuses_one_fixed_worker_thread_across_cycles(self):
|
||||
gateway = RecordingClaimGateway()
|
||||
page = PDDTaskPage()
|
||||
events = PDDTaskPageEvent(
|
||||
page,
|
||||
self.repository,
|
||||
claim_gateway=gateway,
|
||||
settings_repository=self._saved_settings(),
|
||||
collect_service_factory=fake_collect_factory,
|
||||
no_task_delay_ms=10,
|
||||
)
|
||||
|
||||
page.autoFetchRequested.emit()
|
||||
self.assertTrue(
|
||||
wait_until(self.app, lambda: len(gateway.thread_ids) >= 2),
|
||||
"自动获取没有完成两轮串行调度",
|
||||
)
|
||||
executor_thread = events._claim_thread
|
||||
page.autoFetchRequested.emit()
|
||||
self.assertTrue(wait_until(self.app, lambda: not events._claim_busy))
|
||||
|
||||
self.assertIs(events._claim_thread, executor_thread)
|
||||
self.assertTrue(
|
||||
executor_thread is not None and executor_thread.isRunning()
|
||||
)
|
||||
self.assertEqual(len(set(gateway.thread_ids)), 1)
|
||||
events.shutdown()
|
||||
self.assertFalse(executor_thread.isRunning())
|
||||
page.deleteLater()
|
||||
|
||||
def test_new_claim_refreshes_table_before_collection_finishes(self):
|
||||
gateway = RecordingClaimGateway(collect_admin_task("COL-EARLY"))
|
||||
collect_started = threading.Event()
|
||||
|
||||
@@ -513,6 +513,10 @@ class SettingsPageEventTest(unittest.TestCase):
|
||||
)
|
||||
page.deviceTableModel.set_checked_serial("USB-001")
|
||||
self._wait_until(lambda: page.eventBinder._pdd_check_thread is None)
|
||||
changed_serials = []
|
||||
page.eventBinder.androidDeviceConfigurationChanged.connect(
|
||||
changed_serials.append
|
||||
)
|
||||
|
||||
page.saveButton.click()
|
||||
self.assertFalse(page.saveButton.isEnabled())
|
||||
@@ -530,6 +534,7 @@ class SettingsPageEventTest(unittest.TestCase):
|
||||
"当前使用设备:USB-001(已保存)",
|
||||
)
|
||||
self.assertTrue(page.deleteButton.isEnabled())
|
||||
self.assertEqual(changed_serials, ["USB-001"])
|
||||
page.eventBinder.shutdown()
|
||||
page.deleteLater()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user