v3.32.4: rebuild the link on card insertion (the first attempt)
release / release (push) Has been cancelled
release / release (push) Has been cancelled
v3.32.2 fixed the endless re-arm, but on Windows the first attempt still failed on the old link (seen in the confirmation log: the "self._con.connect()" traceback) and only the retry recovered - a failed init, a 2-4 s delay and a traceback in the log. The monitor's insert path now marks the transport stale under _CARD_LOCK, so the first equip attempt rebuilds the PC/SC link. It also covers a link created at a cardless startup (never connected). The rebuild still happens in the equip worker, never in the pyscard monitor thread, and _ensure_transport releases the old link first. Docs: the READMEs' troubleshooting bullet. Tests: an insertion marks the transport for the rebuild. 1014 Python + 953 frontend green; simple-v462.
This commit is contained in:
@@ -35,8 +35,11 @@ class TestCardPresenceObserver(unittest.TestCase):
|
||||
self.trigger_mock = self.trigger.start()
|
||||
self.saved_auto = S._AUTO_EQUIP
|
||||
S._AUTO_EQUIP = True
|
||||
self.saved_stale = S._TRANSPORT_STALE
|
||||
S._TRANSPORT_STALE = False
|
||||
|
||||
def tearDown(self):
|
||||
S._TRANSPORT_STALE = self.saved_stale
|
||||
S._AUTO_EQUIP = self.saved_auto
|
||||
self.trigger.stop()
|
||||
self.patcher.stop()
|
||||
@@ -61,6 +64,16 @@ class TestCardPresenceObserver(unittest.TestCase):
|
||||
self.assertEqual(self.disconnects, [])
|
||||
self.trigger_mock.assert_called_once()
|
||||
|
||||
def test_insertion_marks_the_transport_for_a_rebuild(self):
|
||||
# The first equip attempt must already use a fresh link: after a swap
|
||||
# the old one cannot connect on Windows, and a cardless-startup link
|
||||
# never connected (v3.32.4).
|
||||
S._TRANSPORT_STALE = False
|
||||
self.server.card_present = False
|
||||
self.observer.update(None, ([FakeCard('Test Reader 00 00')], []))
|
||||
self.assertTrue(S._TRANSPORT_STALE)
|
||||
self.trigger_mock.assert_called_once()
|
||||
|
||||
def test_insertion_does_not_trigger_when_disabled(self):
|
||||
S._AUTO_EQUIP = False
|
||||
self.server.card_present = False
|
||||
|
||||
@@ -444,6 +444,28 @@ class AutoEquipTests(unittest.TestCase):
|
||||
self.assertIn('AUTO-EQUIP: init output:', out)
|
||||
self.assertIn('ProtocolError: Connection failed', out)
|
||||
|
||||
def test_a_long_traceback_keeps_its_exception_line(self):
|
||||
# cmd2 renders a rich-box traceback; a fixed head slice cut the
|
||||
# exception itself off (seen live on Windows 2026-10-21), so the tail
|
||||
# is always included.
|
||||
srv = self.make_server(None)
|
||||
|
||||
def onecmd(cmd):
|
||||
srv.app.card = SimpleNamespace(_scc='scc')
|
||||
sys.stderr.write('Traceback (most recent call last):\n')
|
||||
for i in range(30):
|
||||
sys.stderr.write(' File "f%d", line %d, in step%d\n' % (i, i, i))
|
||||
sys.stderr.write('CardConnectionException: No card is inserted\n')
|
||||
|
||||
srv.app.onecmd_plus_hooks = onecmd
|
||||
with mock.patch.object(server, '_ensure_transport', lambda s: True), \
|
||||
mock.patch.object(server, '_apply_equipped_card', lambda s: None), \
|
||||
mock.patch('sys.stderr', new=StringIO()) as err:
|
||||
self.assertFalse(server._auto_equip_attempt(srv))
|
||||
out = err.getvalue()
|
||||
self.assertIn('AUTO-EQUIP: init output:', out)
|
||||
self.assertIn('CardConnectionException: No card is inserted', out)
|
||||
|
||||
def test_failed_attempt_unequips_the_half_initialized_shell(self):
|
||||
calls = []
|
||||
srv = self.make_server(None)
|
||||
|
||||
Reference in New Issue
Block a user