Skip to content

Commit 6887f19

Browse files
committed
usb1.testUSB1: Do not allocate libusb transfers in tests.
From 1.0.25, calling libusb_free_transfer without having assigned it to a device segfaults. This is not a supported call sequence, so hook into {,de}allocation functions during affected test methods, and use this to check their consistency.
1 parent d9fb777 commit 6887f19

1 file changed

Lines changed: 52 additions & 1 deletion

File tree

usb1/testUSB1.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
# pylint: disable=invalid-name, missing-docstring, too-many-public-methods
1818

19-
from ctypes import pointer
19+
from ctypes import pointer, sizeof
20+
import functools
21+
import gc
2022
import itertools
2123
import sys
2224
import unittest
25+
import weakref
2326
import usb1
2427
from . import libusb1
2528

@@ -38,7 +41,50 @@ def open(self):
3841
'usb1.USBContext() fails - no USB bus on system ?'
3942
)
4043

44+
def checkTransferAllocCount(func):
45+
@functools.wraps(func)
46+
def wrapper(self, *args, **kw):
47+
before = self.transfer_alloc_count
48+
libusb_free_transfer = libusb1.libusb_free_transfer
49+
libusb_alloc_transfer = libusb1.libusb_alloc_transfer
50+
try:
51+
libusb1.libusb_free_transfer = self._fakeFreeTransfer
52+
libusb1.libusb_alloc_transfer = self._fakeAllocTransfer
53+
print('running')
54+
result = func(self, *args, **kw)
55+
print('done')
56+
finally:
57+
libusb1.libusb_free_transfer = libusb_free_transfer
58+
libusb1.libusb_alloc_transfer = libusb_alloc_transfer
59+
gc.collect()
60+
self.assertEqual(self.transfer_alloc_count, before)
61+
return result
62+
return wrapper
63+
4164
class USBTransferTests(unittest.TestCase):
65+
def __init__(self, *args, **kw):
66+
super().__init__(*args, **kw)
67+
usb1.loadLibrary()
68+
self.transfer_alloc_count = 0
69+
70+
def _fakeFreeTransfer(self, _):
71+
self.transfer_alloc_count -= 1
72+
73+
def _fakeAllocTransfer(self, isochronous_count):
74+
self.transfer_alloc_count += 1
75+
buffer = bytearray(
76+
sizeof(
77+
libusb1.libusb_transfer,
78+
) + sizeof(
79+
libusb1.libusb_iso_packet_descriptor,
80+
) * max(0, isochronous_count - 1),
81+
)
82+
transfer = libusb1.libusb_transfer.from_buffer(buffer)
83+
# Keep a reference (in the finalizer itself) to the buffer for as long
84+
# as transfer is alive.
85+
weakref.finalize(transfer, lambda _: None, buffer)
86+
return pointer(transfer)
87+
4288
@staticmethod
4389
def getTransfer(iso_packets=0):
4490
# Dummy handle
@@ -66,6 +112,7 @@ def testHasCapability():
66112
"""
67113
usb1.hasCapability(usb1.CAP_HAS_CAPABILITY)
68114

115+
@checkTransferAllocCount
69116
def testSetControl(self):
70117
"""
71118
Simplest test: feed some data, must not raise.
@@ -129,20 +176,23 @@ def callback(transfer):
129176
# No callback
130177
setter(endpoint, buff)
131178

179+
@checkTransferAllocCount
132180
def testSetBulk(self):
133181
"""
134182
Simplest test: feed some data, must not raise.
135183
Also, test setBuffer/getBuffer.
136184
"""
137185
self._testTransferSetter(self.getTransfer(), 'setBulk')
138186

187+
@checkTransferAllocCount
139188
def testSetInterrupt(self):
140189
"""
141190
Simplest test: feed some data, must not raise.
142191
Also, test setBuffer/getBuffer.
143192
"""
144193
self._testTransferSetter(self.getTransfer(), 'setInterrupt')
145194

195+
@checkTransferAllocCount
146196
def testSetIsochronous(self):
147197
"""
148198
Simplest test: feed some data, must not raise.
@@ -174,6 +224,7 @@ def testSetIsochronous(self):
174224
buff,
175225
)
176226

227+
@checkTransferAllocCount
177228
def testSetGetCallback(self):
178229
transfer = self.getTransfer()
179230
def callback(transfer):

0 commit comments

Comments
 (0)