Fix #279 once and for all (ha! ;)

This implements the proposal discussed in the re-opened #279 to add a
pin_factory argument at the device level and remove the ability to
specify a pin instance to device constructors (they now only accept a
pin specification).

Note: there's still a couple of bits to tidy up (tests on "real" Pis,
and pin_factory.release_all needs refinement) but the test suite is now
at least capable of passing on a PC.
This commit is contained in:
Dave Jones
2017-07-04 00:26:41 +01:00
parent 448feaf68f
commit c820636fcb
14 changed files with 396 additions and 403 deletions

View File

@@ -11,7 +11,7 @@ import os
import atexit
import weakref
import warnings
from collections import namedtuple, defaultdict
from collections import namedtuple
from itertools import chain
from types import FunctionType
from threading import Lock
@@ -194,77 +194,21 @@ class Device(ValuesMixin, GPIOBase):
property, the :attr:`value` property, and the :meth:`close` method).
"""
pin_factory = None # instance of a Factory sub-class
_reservations = defaultdict(list) # maps pin addresses to lists of devices
_res_lock = Lock()
def __init__(self, **kwargs):
# Force pin_factory to be keyword-only, even in Python 2
self.pin_factory = kwargs.pop('pin_factory', Device.pin_factory)
if kwargs:
raise TypeError("Device.__init__() got unexpected keyword "
"argument '%s'" % kwargs.popitem()[0])
super(Device, self).__init__()
def __repr__(self):
return "<gpiozero.%s object>" % (self.__class__.__name__)
def _reserve_pins(self, *pins_or_addresses):
"""
Called to indicate that the device reserves the right to use the
specified *pins_or_addresses*. This should be done during device
construction. If pins are reserved, you must ensure that the
reservation is released by eventually called :meth:`_release_pins`.
The *pins_or_addresses* can be actual :class:`Pin` instances or the
addresses of pin instances (each address is a tuple of strings). The
latter form is permitted to ensure that devices do not have to
construct :class:`Pin` objects to reserve pins. This is important as
constructing a pin often configures it (e.g. as an input) which
conflicts with alternate pin functions like SPI.
"""
addresses = (
p.address if isinstance(p, Pin) else p
for p in pins_or_addresses
)
with Device._res_lock:
for address in addresses:
for device_ref in Device._reservations[address]:
device = device_ref()
if device is not None and self._conflicts_with(device):
raise GPIOPinInUse(
'pin %s is already in use by %r' % (
'/'.join(address), device)
)
Device._reservations[address].append(weakref.ref(self))
def _release_pins(self, *pins_or_addresses):
"""
Releases the reservation of this device against *pins_or_addresses*.
This is typically called during :meth:`close` to clean up reservations
taken during construction. Releasing a reservation that is not
currently held will be silently ignored (to permit clean-up after
failed / partial construction).
"""
addresses = (
p.address if isinstance(p, Pin) else p
for p in pins_or_addresses
)
with Device._res_lock:
for address in addresses:
Device._reservations[address] = [
ref for ref in Device._reservations[address]
if ref() not in (self, None) # may as well clean up dead refs
]
def _release_all(self):
"""
Releases all pin reservations taken out by this device. See
:meth:`_release_pins` for further information).
"""
with Device._res_lock:
Device._reservations = defaultdict(list, {
address: [
ref for ref in conflictors
if ref() not in (self, None)
]
for address, conflictors in Device._reservations.items()
})
def _conflicts_with(self, other):
"""
Called by :meth:`_reserve_pin` to test whether the *other*
Called by :meth:`Factory.reserve_pins` to test whether the *other*
:class:`Device` using a common pin conflicts with this device's intent
to use it. The default is ``True`` indicating that all devices conflict
with common pins. Sub-classes may override this to permit more nuanced
@@ -315,6 +259,7 @@ class CompositeDevice(Device):
self._named = frozendict({})
self._namedtuple = None
self._order = kwargs.pop('_order', None)
pin_factory = kwargs.pop('pin_factory', None)
try:
if self._order is None:
self._order = sorted(kwargs.keys())
@@ -336,7 +281,8 @@ class CompositeDevice(Device):
dev.close()
raise
self._all = args + tuple(kwargs[v] for v in self._order)
super(CompositeDevice, self).__init__()
kwargs = {'pin_factory': pin_factory} if pin_factory is not None else {}
super(CompositeDevice, self).__init__(**kwargs)
def __getattr__(self, name):
# if _named doesn't exist yet, pretend it's an empty dict
@@ -412,20 +358,17 @@ class GPIODevice(Device):
this is ``None``, :exc:`GPIOPinMissing` will be raised. If the pin is
already in use by another device, :exc:`GPIOPinInUse` will be raised.
"""
def __init__(self, pin=None):
super(GPIODevice, self).__init__()
def __init__(self, pin=None, **kwargs):
super(GPIODevice, self).__init__(**kwargs)
# self._pin must be set before any possible exceptions can be raised
# because it's accessed in __del__. However, it mustn't be given the
# value of pin until we've verified that it isn't already allocated
self._pin = None
if pin is None:
raise GPIOPinMissing('No pin given')
if isinstance(pin, Pin):
self._reserve_pins(pin)
else:
# Check you can reserve *before* constructing the pin
self._reserve_pins(Device.pin_factory.pin_address(pin))
pin = Device.pin_factory.pin(pin)
# Check you can reserve *before* constructing the pin
self.pin_factory.reserve_pins(self, pin)
pin = self.pin_factory.pin(pin)
self._pin = pin
self._active_state = True
self._inactive_state = False
@@ -443,7 +386,7 @@ class GPIODevice(Device):
def close(self):
super(GPIODevice, self).close()
if self._pin is not None:
self._release_pins(self._pin)
self.pin_factory.release_pins(self, self._pin.number)
self._pin.close()
self._pin = None
@@ -512,10 +455,10 @@ def _default_pin_factory(name=os.getenv('GPIOZERO_PIN_FACTORY', None)):
def _devices_shutdown():
if Device.pin_factory:
with Device._res_lock:
with Device.pin_factory._res_lock:
reserved_devices = {
dev
for ref_list in Device._reservations.values()
for ref_list in Device.pin_factory._reservations.values()
for ref in ref_list
for dev in (ref(),)
if dev is not None

View File

@@ -8,6 +8,10 @@ from __future__ import (
)
str = type('')
from weakref import ref
from collections import defaultdict
from threading import Lock
from ..exc import (
PinInvalidFunction,
PinSetInput,
@@ -20,6 +24,7 @@ from ..exc import (
SPIFixedBitOrder,
SPIFixedSelect,
SPIFixedWordSize,
GPIOPinInUse,
)
@@ -36,10 +41,61 @@ class Factory(object):
applicable:
* :meth:`close`
* :meth:`reserve_pins`
* :meth:`release_pins`
* :meth:`release_all`
* :meth:`pin`
* :meth:`spi`
* :meth:`_get_pi_info`
"""
def __init__(self):
self._reservations = defaultdict(list)
self._res_lock = Lock()
def reserve_pins(self, requester, *pins):
"""
Called to indicate that the device reserves the right to use the
specified *pins*. This should be done during device construction. If
pins are reserved, you must ensure that the reservation is released by
eventually called :meth:`release_pins`.
"""
with self._res_lock:
for pin in pins:
for reserver_ref in self._reservations[pin]:
reserver = reserver_ref()
if reserver is not None and requester._conflicts_with(reserver):
raise GPIOPinInUse('pin %s is already in use by %r' %
(pin, reserver))
self._reservations[pin].append(ref(requester))
def release_pins(self, reserver, *pins):
"""
Releases the reservation of *reserver* against *pins*. This is
typically called during :meth:`Device.close` to clean up reservations
taken during construction. Releasing a reservation that is not currently
held will be silently ignored (to permit clean-up after failed / partial
construction).
"""
with self._res_lock:
for pin in pins:
self._reservations[pin] = [
ref for ref in self._reservations[pin]
if ref() not in (reserver, None) # may as well clean up dead refs
]
def release_all(self, reserver):
"""
Releases all pin reservations taken out by *reserver*. See
:meth:`release_pins` for further information).
"""
with self._res_lock:
self._reservations = defaultdict(list, {
pin: [
ref for ref in conflictors
if ref() not in (reserver, None)
]
for pin, conflictors in self._reservations.items()
})
def close(self):
"""
@@ -63,19 +119,6 @@ class Factory(object):
"""
raise PinUnsupported("Individual pins are not supported by this pin factory")
def pin_address(self, spec):
"""
Returns the address that a pin *would* have if constructed from the
given *spec*.
This unusual method is used by the pin reservation system to check
for conflicts *prior* to pin construction; with most implementations,
pin construction implicitly alters the state of the pin (e.g. setting
it to an input). This allows pin reservation to take place without
affecting the state of other components.
"""
raise NotImplementedError
def spi(self, **spi_args):
"""
Returns an instance of an :class:`SPI` interface, for the specified SPI
@@ -89,21 +132,6 @@ class Factory(object):
def _get_address(self):
raise NotImplementedError
address = property(
lambda self: self._get_address(),
doc="""\
Returns a tuple of strings representing the address of the factory.
For the Pi itself this is a tuple of one string representing the Pi's
address (e.g. "localhost"). Expander chips can return a tuple appending
whatever string they require to uniquely identify the expander chip
amongst all factories in the system.
.. note::
This property *must* return an immutable object capable of being
used as a dictionary key.
""")
def _get_pi_info(self):
return None
@@ -128,7 +156,6 @@ class Pin(object):
represent the capabilities of pins. Descendents *must* override the
following methods:
* :meth:`_get_address`
* :meth:`_get_function`
* :meth:`_set_function`
* :meth:`_get_state`
@@ -153,7 +180,7 @@ class Pin(object):
"""
def __repr__(self):
return self.address[-1]
return "<Pin>"
def close(self):
"""
@@ -195,18 +222,6 @@ class Pin(object):
self.function = 'input'
self.pull = pull
def _get_address(self):
raise NotImplementedError
address = property(
lambda self: self._get_address(),
doc="""\
The address of the pin. This property is a tuple of strings constructed
from the owning factory's address with the unique address of the pin
appended to it. The tuple as a whole uniquely identifies the pin
amongst all pins attached to the system.
""")
def _get_function(self):
return "input"

View File

@@ -8,6 +8,8 @@ str = type('')
import io
import warnings
from collections import defaultdict
from threading import Lock
try:
from spidev import SpiDev
@@ -31,6 +33,8 @@ class LocalPiFactory(PiFactory):
:class:`~gpiozero.pins.native.NativePin`).
"""
pins = {}
_reservations = defaultdict(list)
_res_lock = Lock()
def __init__(self):
super(LocalPiFactory, self).__init__()
@@ -40,14 +44,13 @@ class LocalPiFactory(PiFactory):
('software', 'exclusive'): LocalPiSoftwareSPI,
('software', 'shared'): LocalPiSoftwareSPIShared,
}
# Override the pins dict to be this class' pins dict. This is a bit of
# a dirty hack, but ensures that anyone evil enough to mix pin
# implementations doesn't try and control the same pin with different
# backends
# Override the reservations and pins dict to be this class' attributes.
# This is a bit of a dirty hack, but ensures that anyone evil enough to
# mix pin implementations doesn't try and control the same pin with
# different backends
self.pins = LocalPiFactory.pins
def _get_address(self):
return ('localhost',)
self._reservations = LocalPiFactory._reservations
self._res_lock = LocalPiFactory._res_lock
def _get_revision(self):
# Cache the result as we can reasonably assume it won't change during
@@ -74,19 +77,19 @@ class LocalPiPin(PiPin):
class LocalPiHardwareSPI(SPI, Device):
def __init__(self, factory, port, device):
if SpiDev is None:
raise ImportError('failed to import spidev')
self._port = port
self._device = device
self._interface = None
self._address = factory.address + ('SPI(port=%d, device=%d)' % (port, device),)
if SpiDev is None:
raise ImportError('failed to import spidev')
super(LocalPiHardwareSPI, self).__init__()
pins = SPI_HARDWARE_PINS[port]
self._reserve_pins(
factory.pin_address(pins['clock']),
factory.pin_address(pins['mosi']),
factory.pin_address(pins['miso']),
factory.pin_address(pins['select'][device])
self.pin_factory.reserve_pins(
self,
pins['clock'],
pins['mosi'],
pins['miso'],
pins['select'][device]
)
self._interface = SpiDev()
self._interface.open(port, device)
@@ -98,7 +101,7 @@ class LocalPiHardwareSPI(SPI, Device):
self._interface.close()
finally:
self._interface = None
self._release_all()
self.pin_factory.release_all(self)
super(LocalPiHardwareSPI, self).close()
@property
@@ -148,10 +151,6 @@ class LocalPiHardwareSPI(SPI, Device):
class LocalPiSoftwareSPI(SPI, OutputDevice):
def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin):
self._bus = None
self._address = factory.address + (
'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
clock_pin, mosi_pin, miso_pin, select_pin),
)
super(LocalPiSoftwareSPI, self).__init__(select_pin, active_high=False)
try:
self._clock_phase = False
@@ -163,6 +162,7 @@ class LocalPiSoftwareSPI(SPI, OutputDevice):
raise
def _conflicts_with(self, other):
# XXX Need to refine this
return not (
isinstance(other, LocalPiSoftwareSPI) and
(self.pin.number != other.pin.number)

View File

@@ -40,7 +40,7 @@ class MockPin(PiPin):
def __init__(self, factory, number):
super(MockPin, self).__init__(factory, number)
self._function = 'input'
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pull = 'up' if factory.pi_info.pulled_up(repr(self)) else 'floating'
self._state = self._pull == 'up'
self._bounce = None
self._edges = 'both'
@@ -94,7 +94,7 @@ class MockPin(PiPin):
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]):
if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self)
if value not in ('floating', 'up', 'down'):
raise PinInvalidPull('pull must be floating, up, or down')
@@ -423,14 +423,12 @@ class MockFactory(LocalPiFactory):
pin_class = pkg_resources.load_entry_point(dist, group, pin_class.lower())
self.pin_class = pin_class
def _get_address(self):
return ('mock',)
def _get_revision(self):
return self._revision
def reset(self):
self.pins.clear()
self._reservations.clear()
def pin(self, spec, pin_class=None, **kwargs):
if pin_class is None:

View File

@@ -228,7 +228,7 @@ class NativePin(LocalPiPin):
self._change_thread = None
self._change_event = Event()
self.function = 'input'
self.pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
self.bounce = None
self.edges = 'both'
@@ -236,7 +236,7 @@ class NativePin(LocalPiPin):
self.frequency = None
self.when_changed = None
self.function = 'input'
self.pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
def _get_function(self):
return self.GPIO_FUNCTION_NAMES[(self.factory.mem[self._func_offset] >> self._func_shift) & 7]
@@ -269,7 +269,7 @@ class NativePin(LocalPiPin):
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]):
if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self)
try:
value = self.GPIO_PULL_UPS[value]

View File

@@ -7,8 +7,9 @@ from __future__ import (
str = type('')
import io
from threading import RLock
from threading import RLock, Lock
from types import MethodType
from collections import defaultdict
try:
from weakref import ref, WeakMethod
except ImportError:
@@ -48,6 +49,7 @@ class PiFactory(Factory):
forms the base of :class:`~gpiozero.pins.local.LocalPiFactory`.
"""
def __init__(self):
super(PiFactory, self).__init__()
self._info = None
self.pins = {}
self.pin_class = None
@@ -72,10 +74,6 @@ class PiFactory(Factory):
self.pins[n] = pin
return pin
def pin_address(self, spec):
n = self._to_gpio(spec)
return self.address + ('GPIO%d' % n,)
def _to_gpio(self, spec):
"""
Converts the pin *spec* to a GPIO port number.
@@ -240,23 +238,23 @@ class PiPin(Pin):
self._when_changed = None
self._number = number
try:
factory.pi_info.physical_pin(self.address[-1])
factory.pi_info.physical_pin('GPIO%d' % self.number)
except PinNoPins:
warnings.warn(
PinNonPhysical(
'no physical pins exist for %s' % self.address[-1]))
'no physical pins exist for GPIO%d' % self.number))
@property
def number(self):
return self._number
def __repr__(self):
return 'GPIO%d' % self._number
@property
def factory(self):
return self._factory
def _get_address(self):
return self.factory.address + ('GPIO%d' % self.number,)
def _call_when_changed(self):
"""
Called to fire the :attr:`when_changed` event handler; override this

View File

@@ -7,7 +7,6 @@ from __future__ import (
str = type('')
import os
from weakref import proxy
import pigpio
@@ -126,9 +125,6 @@ class PiGPIOFactory(PiFactory):
def _get_revision(self):
return self.connection.get_hardware_revision()
def _get_address(self):
return ("%s:%d" % (self.host, self.port),)
def spi(self, **spi_args):
intf = super(PiGPIOFactory, self).spi(**spi_args)
self._spis.append(intf)
@@ -166,7 +162,7 @@ class PiGPIOPin(PiPin):
def __init__(self, factory, number):
super(PiGPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pull = 'up' if factory.pi_info.pulled_up(repr(self)) else 'floating'
self._pwm = False
self._bounce = None
self._callback = None
@@ -183,7 +179,7 @@ class PiGPIOPin(PiPin):
self.frequency = None
self.when_changed = None
self.function = 'input'
self.pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self.pull = 'up' if self.factory.pi_info.pulled_up(repr(self)) else 'floating'
def _get_function(self):
return self.GPIO_FUNCTION_NAMES[self.factory.connection.get_mode(self.number)]
@@ -225,7 +221,7 @@ class PiGPIOPin(PiPin):
def _set_pull(self, value):
if self.function != 'input':
raise PinFixedPull('cannot set pull on non-input pin %r' % self)
if value != 'up' and self.factory.pi_info.pulled_up(self.address[-1]):
if value != 'up' and self.factory.pi_info.pulled_up(repr(self)):
raise PinFixedPull('%r has a physical pull-up resistor' % self)
try:
self.factory.connection.set_pull_up_down(self.number, self.GPIO_PULL_UPS[value])
@@ -296,19 +292,17 @@ class PiGPIOHardwareSPI(SPI, Device):
def __init__(self, factory, port, device):
self._port = port
self._device = device
self._factory = proxy(factory)
self._factory = factory
self._handle = None
super(PiGPIOHardwareSPI, self).__init__()
pins = SPI_HARDWARE_PINS[port]
self._reserve_pins(*(
factory.address + ('GPIO%d' % pin,)
for pin in (
pins['clock'],
pins['mosi'],
pins['miso'],
pins['select'][device]
)
))
self._factory.reserve_pins(
self,
pins['clock'],
pins['mosi'],
pins['miso'],
pins['select'][device]
)
self._spi_flags = 8 << 16
self._baud = 500000
self._handle = self._factory.connection.spi_open(
@@ -330,7 +324,7 @@ class PiGPIOHardwareSPI(SPI, Device):
if not self.closed:
self._factory.connection.spi_close(self._handle)
self._handle = None
self._release_all()
self._factory.release_all(self)
super(PiGPIOHardwareSPI, self).close()
@property
@@ -397,13 +391,14 @@ class PiGPIOSoftwareSPI(SPI, Device):
self._clock_pin = clock_pin
self._mosi_pin = mosi_pin
self._miso_pin = miso_pin
self._factory = proxy(factory)
self._factory = factory
super(PiGPIOSoftwareSPI, self).__init__()
self._reserve_pins(
factory.pin_address(clock_pin),
factory.pin_address(mosi_pin),
factory.pin_address(miso_pin),
factory.pin_address(select_pin),
self._factory.reserve_pins(
self,
clock_pin,
mosi_pin,
miso_pin,
select_pin,
)
self._spi_flags = 0
self._baud = 100000
@@ -434,7 +429,7 @@ class PiGPIOSoftwareSPI(SPI, Device):
if not self.closed:
self._closed = True
self._factory.connection.bb_spi_close(self._select_pin)
self._release_all()
self.factory.release_all(self)
super(PiGPIOSoftwareSPI, self).close()
@property

View File

@@ -85,7 +85,7 @@ class RPiGPIOPin(LocalPiPin):
def __init__(self, factory, number):
super(RPiGPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pwm = None
self._frequency = None
self._duty_cycle = None

View File

@@ -80,7 +80,7 @@ class RPIOPin(LocalPiPin):
def __init__(self, factory, number):
super(RPIOPin, self).__init__(factory, number)
self._pull = 'up' if factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pull = 'up' if self.factory.pi_info.pulled_up(self.address[-1]) else 'floating'
self._pwm = False
self._duty_cycle = None
self._bounce = None

View File

@@ -36,7 +36,6 @@ def setup_function(function):
def teardown_function(function):
Device.pin_factory.reset()
Device._reservations.clear()
def teardown_module(module):
# make sure we reset the default
@@ -47,7 +46,7 @@ def test_composite_output_on_off():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
device.on()
assert all((pin1.state, pin2.state, pin3.state))
device.off()
@@ -57,7 +56,7 @@ def test_composite_output_toggle():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
device.toggle()
assert all((pin1.state, pin2.state, pin3.state))
device[0].off()
@@ -70,7 +69,7 @@ def test_composite_output_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device:
with CompositeOutputDevice(OutputDevice(2), OutputDevice(3), foo=OutputDevice(4)) as device:
assert device.value == (0, 0, 0)
device.toggle()
assert device.value == (1, 1, 1)
@@ -83,7 +82,7 @@ def test_led_board_on_off():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3) as board:
with LEDBoard(2, 3, foo=4) as board:
assert isinstance(board[0], LED)
assert isinstance(board[1], LED)
assert isinstance(board[2], LED)
@@ -140,7 +139,7 @@ def test_led_board_active_low():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, active_high=False) as board:
with LEDBoard(2, 3, foo=4, active_high=False) as board:
assert not board.active_high
assert not board[0].active_high
assert not board[1].active_high
@@ -164,7 +163,7 @@ def test_led_board_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3) as board:
with LEDBoard(2, 3, foo=4) as board:
assert board.value == (0, 0, 0)
board.value = (0, 1, 0)
assert board.value == (0, 1, 0)
@@ -175,7 +174,7 @@ def test_led_board_pwm_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
with LEDBoard(2, 3, foo=4, pwm=True) as board:
assert board.value == (0, 0, 0)
board.value = (0, 1, 0)
assert board.value == (0, 1, 0)
@@ -186,7 +185,7 @@ def test_led_board_pwm_bad_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True) as board:
with LEDBoard(2, 3, foo=4, pwm=True) as board:
with pytest.raises(ValueError):
board.value = (-1, 0, 0)
with pytest.raises(ValueError):
@@ -196,20 +195,20 @@ def test_led_board_initial_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, initial_value=0) as board:
with LEDBoard(2, 3, foo=4, initial_value=0) as board:
assert board.value == (0, 0, 0)
with LEDBoard(pin1, pin2, foo=pin3, initial_value=1) as board:
with LEDBoard(2, 3, foo=4, initial_value=1) as board:
assert board.value == (1, 1, 1)
def test_led_board_pwm_initial_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0) as board:
with LEDBoard(2, 3, foo=4, pwm=True, initial_value=0) as board:
assert board.value == (0, 0, 0)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=1) as board:
with LEDBoard(2, 3, foo=4, pwm=True, initial_value=1) as board:
assert board.value == (1, 1, 1)
with LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=0.5) as board:
with LEDBoard(2, 3, foo=4, pwm=True, initial_value=0.5) as board:
assert board.value == (0.5, 0.5, 0.5)
def test_led_board_pwm_bad_initial_value():
@@ -217,15 +216,15 @@ def test_led_board_pwm_bad_initial_value():
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with pytest.raises(ValueError):
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=-1)
LEDBoard(2, 3, foo=4, pwm=True, initial_value=-1)
with pytest.raises(ValueError):
LEDBoard(pin1, pin2, foo=pin3, pwm=True, initial_value=2)
LEDBoard(2, 3, foo=4, pwm=True, initial_value=2)
def test_led_board_nested():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(2, LEDBoard(3, 4)) as board:
assert list(led.pin for led in board.leds) == [pin1, pin2, pin3]
assert board.value == (0, (0, 0))
board.value = (1, (0, 1))
@@ -237,7 +236,7 @@ def test_led_board_bad_blink():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(2, LEDBoard(3, 4)) as board:
with pytest.raises(ValueError):
board.blink(fade_in_time=1, fade_out_time=1)
with pytest.raises(ValueError):
@@ -251,7 +250,7 @@ def test_led_board_blink_background():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
# Instantiation takes a long enough time that it throws off our timing
# here!
pin1.clear_states()
@@ -276,7 +275,7 @@ def test_led_board_blink_foreground():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -298,7 +297,7 @@ def test_led_board_blink_control():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -326,7 +325,7 @@ def test_led_board_blink_take_over():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -351,7 +350,7 @@ def test_led_board_blink_control_all():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -376,7 +375,7 @@ def test_led_board_blink_interrupt_on():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
board.blink(1, 0.1)
sleep(0.2)
board.off() # should interrupt while on
@@ -388,7 +387,7 @@ def test_led_board_blink_interrupt_off():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board:
with LEDBoard(4, LEDBoard(5, 6)) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -405,7 +404,7 @@ def test_led_board_fade_background():
pin1 = Device.pin_factory.pin(4)
pin2 = Device.pin_factory.pin(5)
pin3 = Device.pin_factory.pin(6)
with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board:
with LEDBoard(4, LEDBoard(5, 6, pwm=True), pwm=True) as board:
pin1.clear_states()
pin2.clear_states()
pin3.clear_states()
@@ -442,7 +441,7 @@ def test_led_bar_graph_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3) as graph:
with LEDBarGraph(2, 3, 4) as graph:
assert isinstance(graph[0], LED)
assert isinstance(graph[1], LED)
assert isinstance(graph[2], LED)
@@ -475,7 +474,7 @@ def test_led_bar_graph_active_low():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, active_high=False) as graph:
with LEDBarGraph(2, 3, 4, active_high=False) as graph:
assert not graph.active_high
assert not graph[0].active_high
assert not graph[1].active_high
@@ -497,7 +496,7 @@ def test_led_bar_graph_pwm_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph:
with LEDBarGraph(2, 3, 4, pwm=True) as graph:
assert isinstance(graph[0], PWMLED)
assert isinstance(graph[1], PWMLED)
assert isinstance(graph[2], PWMLED)
@@ -524,7 +523,7 @@ def test_led_bar_graph_bad_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3) as graph:
with LEDBarGraph(2, 3, 4) as graph:
with pytest.raises(ValueError):
graph.value = -2
with pytest.raises(ValueError):
@@ -535,20 +534,20 @@ def test_led_bar_graph_bad_init():
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with pytest.raises(TypeError):
LEDBarGraph(pin1, pin2, foo=pin3)
LEDBarGraph(2, 3, foo=4)
with pytest.raises(ValueError):
LEDBarGraph(pin1, pin2, pin3, initial_value=-2)
LEDBarGraph(2, 3, 4, initial_value=-2)
with pytest.raises(ValueError):
LEDBarGraph(pin1, pin2, pin3, initial_value=2)
LEDBarGraph(2, 3, 4, initial_value=2)
def test_led_bar_graph_initial_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph:
with LEDBarGraph(2, 3, 4, initial_value=1/3) as graph:
assert graph.value == 1/3
assert pin1.state and not (pin2.state or pin3.state)
with LEDBarGraph(pin1, pin2, pin3, initial_value=-1/3) as graph:
with LEDBarGraph(2, 3, 4, initial_value=-1/3) as graph:
assert graph.value == -1/3
assert pin3.state and not (pin1.state or pin2.state)
@@ -556,10 +555,10 @@ def test_led_bar_graph_pwm_initial_value():
pin1 = Device.pin_factory.pin(2)
pin2 = Device.pin_factory.pin(3)
pin3 = Device.pin_factory.pin(4)
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph:
with LEDBarGraph(2, 3, 4, pwm=True, initial_value=0.5) as graph:
assert graph.value == 0.5
assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0)
with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=-0.5) as graph:
with LEDBarGraph(2, 3, 4, pwm=True, initial_value=-0.5) as graph:
assert graph.value == -0.5
assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1)
@@ -585,7 +584,7 @@ def test_traffic_lights():
red_pin = Device.pin_factory.pin(2)
amber_pin = Device.pin_factory.pin(3)
green_pin = Device.pin_factory.pin(4)
with TrafficLights(red_pin, amber_pin, green_pin) as board:
with TrafficLights(2, 3, 4) as board:
board.red.on()
assert board.red.value
assert not board.amber.value
@@ -598,7 +597,7 @@ def test_traffic_lights():
assert amber_pin.state
board.yellow.off()
assert not amber_pin.state
with TrafficLights(red=red_pin, yellow=amber_pin, green=green_pin) as board:
with TrafficLights(red=2, yellow=3, green=4) as board:
board.yellow.on()
assert not board.red.value
assert board.amber.value
@@ -618,7 +617,7 @@ def test_traffic_lights_bad_init():
green_pin = Device.pin_factory.pin(4)
yellow_pin = Device.pin_factory.pin(5)
with pytest.raises(ValueError):
TrafficLights(red=red_pin, amber=amber_pin, yellow=yellow_pin, green=green_pin)
TrafficLights(red=2, amber=3, yellow=5, green=4)
def test_pi_traffic():
pins = [Device.pin_factory.pin(n) for n in (9, 10, 11)]
@@ -677,9 +676,9 @@ def test_traffic_lights_buzzer():
buzzer_pin = Device.pin_factory.pin(5)
button_pin = Device.pin_factory.pin(6)
with TrafficLightsBuzzer(
TrafficLights(red_pin, amber_pin, green_pin),
Buzzer(buzzer_pin),
Button(button_pin)) as board:
TrafficLights(2, 3, 4),
Buzzer(5),
Button(6)) as board:
board.lights.red.on()
board.buzzer.on()
assert red_pin.state

View File

@@ -33,7 +33,7 @@ def test_device_non_physical():
def test_device_init():
pin = Device.pin_factory.pin(2)
with GPIODevice(pin) as device:
with GPIODevice(2) as device:
assert not device.closed
assert device.pin == pin
@@ -56,9 +56,9 @@ def test_device_close():
def test_device_reopen_same_pin():
pin = Device.pin_factory.pin(2)
with GPIODevice(pin) as device:
with GPIODevice(2) as device:
pass
with GPIODevice(pin) as device2:
with GPIODevice(2) as device2:
assert not device2.closed
assert device2.pin is pin
assert device.closed

View File

@@ -18,22 +18,21 @@ from gpiozero import *
def teardown_function(function):
Device.pin_factory.reset()
Device._reservations.clear()
def test_input_initial_values():
pin = Device.pin_factory.pin(4)
with InputDevice(pin, pull_up=True) as device:
with InputDevice(4, pull_up=True) as device:
assert pin.function == 'input'
assert pin.pull == 'up'
assert device.pull_up
with InputDevice(pin, pull_up=False) as device:
with InputDevice(4, pull_up=False) as device:
assert pin.pull == 'down'
assert not device.pull_up
def test_input_is_active_low():
pin = Device.pin_factory.pin(2)
with InputDevice(pin, pull_up=True) as device:
with InputDevice(2, pull_up=True) as device:
pin.drive_high()
assert not device.is_active
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO2, pull_up=True, is_active=False>'
@@ -43,7 +42,7 @@ def test_input_is_active_low():
def test_input_is_active_high():
pin = Device.pin_factory.pin(4)
with InputDevice(pin, pull_up=False) as device:
with InputDevice(4, pull_up=False) as device:
pin.drive_high()
assert device.is_active
assert repr(device) == '<gpiozero.InputDevice object on pin GPIO4, pull_up=False, is_active=True>'
@@ -54,12 +53,12 @@ def test_input_is_active_high():
def test_input_pulled_up():
pin = Device.pin_factory.pin(2)
with pytest.raises(PinFixedPull):
InputDevice(pin, pull_up=False)
InputDevice(2, pull_up=False)
def test_input_event_activated():
event = Event()
pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device:
with DigitalInputDevice(4) as device:
device.when_activated = lambda: event.set()
assert not event.is_set()
pin.drive_high()
@@ -68,7 +67,7 @@ def test_input_event_activated():
def test_input_event_deactivated():
event = Event()
pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device:
with DigitalInputDevice(4) as device:
device.when_deactivated = lambda: event.set()
assert not event.is_set()
pin.drive_high()
@@ -84,7 +83,7 @@ def test_input_partial_callback():
return a + b
bar = partial(foo, 1)
baz = partial(bar, 2)
with DigitalInputDevice(pin) as device:
with DigitalInputDevice(4) as device:
device.when_activated = baz
assert not event.is_set()
pin.drive_high()
@@ -92,20 +91,20 @@ def test_input_partial_callback():
def test_input_wait_active():
pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device:
with DigitalInputDevice(4) as device:
pin.drive_high()
assert device.wait_for_active(1)
assert not device.wait_for_inactive(0)
def test_input_wait_inactive():
pin = Device.pin_factory.pin(4)
with DigitalInputDevice(pin) as device:
with DigitalInputDevice(4) as device:
assert device.wait_for_inactive(1)
assert not device.wait_for_active(0)
def test_input_smoothed_attrib():
pin = Device.pin_factory.pin(4)
with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device:
with SmoothedInputDevice(4, threshold=0.5, queue_len=5, partial=False) as device:
assert repr(device) == '<gpiozero.SmoothedInputDevice object on pin GPIO4, pull_up=False>'
assert device.threshold == 0.5
assert device.queue_len == 5
@@ -117,7 +116,7 @@ def test_input_smoothed_attrib():
def test_input_smoothed_values():
pin = Device.pin_factory.pin(4)
with SmoothedInputDevice(pin) as device:
with SmoothedInputDevice(4) as device:
device._queue.start()
assert not device.is_active
pin.drive_high()
@@ -127,7 +126,7 @@ def test_input_smoothed_values():
def test_input_button():
pin = Device.pin_factory.pin(2)
with Button(pin) as button:
with Button(2) as button:
assert pin.pull == 'up'
assert not button.is_pressed
pin.drive_low()
@@ -139,7 +138,7 @@ def test_input_button():
def test_input_line_sensor():
pin = Device.pin_factory.pin(4)
with LineSensor(pin) as sensor:
with LineSensor(4) as sensor:
pin.drive_low() # logic is inverted for line sensor
assert sensor.wait_for_line(1)
assert sensor.line_detected
@@ -149,7 +148,7 @@ def test_input_line_sensor():
def test_input_motion_sensor():
pin = Device.pin_factory.pin(4)
with MotionSensor(pin) as sensor:
with MotionSensor(4) as sensor:
pin.drive_high()
assert sensor.wait_for_motion(1)
assert sensor.motion_detected
@@ -161,7 +160,7 @@ def test_input_motion_sensor():
reason='timing is too random on pypy')
def test_input_light_sensor():
pin = Device.pin_factory.pin(4, pin_class=MockChargingPin)
with LightSensor(pin) as sensor:
with LightSensor(4) as sensor:
pin.charge_time = 0.1
assert sensor.wait_for_dark(1)
pin.charge_time = 0.0
@@ -173,10 +172,10 @@ def test_input_distance_sensor():
echo_pin = Device.pin_factory.pin(4)
trig_pin = Device.pin_factory.pin(5, pin_class=MockTriggerPin, echo_pin=echo_pin, echo_time=0.02)
with pytest.raises(ValueError):
DistanceSensor(echo_pin, trig_pin, max_distance=-1)
DistanceSensor(4, 5, max_distance=-1)
# normal queue len is large (because the sensor is *really* jittery) but
# we want quick tests and we've got precisely controlled pins :)
with DistanceSensor(echo_pin, trig_pin, queue_len=5, max_distance=1) as sensor:
with DistanceSensor(4, 5, queue_len=5, max_distance=1) as sensor:
assert sensor.max_distance == 1
assert sensor.trigger is trig_pin
assert sensor.echo is echo_pin

View File

@@ -16,29 +16,73 @@ except ImportError:
import pytest
from gpiozero.pins.mock import MockPWMPin
from gpiozero.pins.mock import MockPin, MockPWMPin
from gpiozero import *
def setup_function(function):
# dirty, but it does the job
Device.pin_factory.pin_class = MockPWMPin if function.__name__ in (
'test_output_pwm_states',
'test_output_pwm_read',
'test_output_pwm_write',
'test_output_pwm_toggle',
'test_output_pwm_active_high_read',
'test_output_pwm_bad_value',
'test_output_pwm_write_closed',
'test_output_pwm_write_silly',
'test_output_pwm_blink_background',
'test_output_pwm_blink_foreground',
'test_output_pwm_fade_background',
'test_output_pwm_fade_foreground',
'test_output_pwm_pulse_background',
'test_output_pwm_pulse_foreground',
'test_output_pwm_blink_interrupt',
'test_rgbled_initial_value',
'test_rgbled_initial_bad_value',
'test_rgbled_value',
'test_rgbled_bad_value',
'test_rgbled_toggle',
'test_rgbled_blink_background',
'test_rgbled_blink_foreground',
'test_rgbled_fade_background',
'test_rgbled_fade_foreground',
'test_rgbled_pulse_background',
'test_rgbled_pulse_foreground',
'test_rgbled_blink_interrupt',
'test_rgbled_close',
'test_motor_pins',
'test_motor_close',
'test_motor_value',
'test_motor_bad_value',
'test_motor_reverse',
'test_servo_pins',
'test_servo_bad_value',
'test_servo_close',
'test_servo_pulse_width',
'test_servo_values',
'test_angular_servo_range',
'test_angular_servo_angles',
) else MockPin
def teardown_function(function):
Device.pin_factory.reset()
Device._reservations.clear()
def test_output_initial_values():
pin = Device.pin_factory.pin(2)
with OutputDevice(pin, initial_value=False) as device:
with OutputDevice(2, initial_value=False) as device:
assert pin.function == 'output'
assert not pin.state
with OutputDevice(pin, initial_value=True) as device:
with OutputDevice(2, initial_value=True) as device:
assert pin.state
state = pin.state
with OutputDevice(pin, initial_value=None) as device:
with OutputDevice(2, initial_value=None) as device:
assert state == pin.state
def test_output_write_active_high():
pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device:
with OutputDevice(2) as device:
device.on()
assert pin.state
device.off()
@@ -46,14 +90,14 @@ def test_output_write_active_high():
def test_output_write_active_low():
pin = Device.pin_factory.pin(2)
with OutputDevice(pin, active_high=False) as device:
with OutputDevice(2, active_high=False) as device:
device.on()
assert not pin.state
device.off()
assert pin.state
def test_output_write_closed():
with OutputDevice(Device.pin_factory.pin(2)) as device:
with OutputDevice(2) as device:
device.close()
assert device.closed
device.close()
@@ -63,14 +107,14 @@ def test_output_write_closed():
def test_output_write_silly():
pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device:
with OutputDevice(2) as device:
pin.function = 'input'
with pytest.raises(AttributeError):
device.on()
def test_output_value():
pin = Device.pin_factory.pin(2)
with OutputDevice(pin) as device:
with OutputDevice(2) as device:
assert not device.value
assert not pin.state
device.on()
@@ -82,7 +126,7 @@ def test_output_value():
def test_output_digital_toggle():
pin = Device.pin_factory.pin(2)
with DigitalOutputDevice(pin) as device:
with DigitalOutputDevice(2) as device:
assert not device.value
assert not pin.state
device.toggle()
@@ -96,7 +140,7 @@ def test_output_digital_toggle():
reason='timing is too random on pypy')
def test_output_blink_background():
pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device:
with DigitalOutputDevice(4) as device:
start = time()
device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -114,7 +158,7 @@ def test_output_blink_background():
reason='timing is too random on pypy')
def test_output_blink_foreground():
pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device:
with DigitalOutputDevice(4) as device:
start = time()
device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -128,7 +172,7 @@ def test_output_blink_foreground():
def test_output_blink_interrupt_on():
pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device:
with DigitalOutputDevice(4) as device:
device.blink(1, 0.1)
sleep(0.2)
device.off() # should interrupt while on
@@ -136,7 +180,7 @@ def test_output_blink_interrupt_on():
def test_output_blink_interrupt_off():
pin = Device.pin_factory.pin(4)
with DigitalOutputDevice(pin) as device:
with DigitalOutputDevice(4) as device:
device.blink(0.1, 1)
sleep(0.2)
device.off() # should interrupt while off
@@ -144,23 +188,23 @@ def test_output_blink_interrupt_off():
def test_output_pwm_bad_initial_value():
with pytest.raises(ValueError):
PWMOutputDevice(Device.pin_factory.pin(2), initial_value=2)
PWMOutputDevice(2, initial_value=2)
def test_output_pwm_not_supported():
with pytest.raises(AttributeError):
PWMOutputDevice(Device.pin_factory.pin(2))
PWMOutputDevice(2)
def test_output_pwm_states():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
device.value = 0.1
device.value = 0.2
device.value = 0.0
pin.assert_states([0.0, 0.1, 0.2, 0.0])
def test_output_pwm_read():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with PWMOutputDevice(pin, frequency=100) as device:
pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2, frequency=100) as device:
assert device.frequency == 100
device.value = 0.1
assert isclose(device.value, 0.1)
@@ -172,15 +216,15 @@ def test_output_pwm_read():
assert device.frequency is None
def test_output_pwm_write():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
device.on()
device.off()
pin.assert_states([False, True, False])
def test_output_pwm_toggle():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
device.toggle()
device.value = 0.5
device.value = 0.1
@@ -189,8 +233,8 @@ def test_output_pwm_toggle():
pin.assert_states([False, True, 0.5, 0.1, 0.9, False])
def test_output_pwm_active_high_read():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with PWMOutputDevice(pin, active_high=False) as device:
pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2, active_high=False) as device:
device.value = 0.1
assert isclose(device.value, 0.1)
assert isclose(pin.state, 0.9)
@@ -198,19 +242,21 @@ def test_output_pwm_active_high_read():
assert device.value
def test_output_pwm_bad_value():
with PWMOutputDevice(Device.pin_factory.pin(2, pin_class=MockPWMPin)) as device:
pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2) as device:
with pytest.raises(ValueError):
device.value = 2
def test_output_pwm_write_closed():
with PWMOutputDevice(Device.pin_factory.pin(2, pin_class=MockPWMPin)) as device:
pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2) as device:
device.close()
with pytest.raises(GPIODeviceClosed):
device.on()
def test_output_pwm_write_silly():
pin = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(2)
with PWMOutputDevice(2) as device:
pin.function = 'input'
with pytest.raises(AttributeError):
device.off()
@@ -218,8 +264,8 @@ def test_output_pwm_write_silly():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_blink_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -236,8 +282,8 @@ def test_output_pwm_blink_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_blink_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -252,8 +298,8 @@ def test_output_pwm_blink_foreground():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_fade_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.blink(0, 0, 0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -286,8 +332,8 @@ def test_output_pwm_fade_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_fade_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -318,8 +364,8 @@ def test_output_pwm_fade_foreground():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_pulse_background():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.pulse(0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -352,8 +398,8 @@ def test_output_pwm_pulse_background():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_output_pwm_pulse_foreground():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
start = time()
device.pulse(0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -382,8 +428,8 @@ def test_output_pwm_pulse_foreground():
])
def test_output_pwm_blink_interrupt():
pin = Device.pin_factory.pin(4, pin_class=MockPWMPin)
with PWMOutputDevice(pin) as device:
pin = Device.pin_factory.pin(4)
with PWMOutputDevice(4) as device:
device.blink(1, 0.1)
sleep(0.2)
device.off() # should interrupt while on
@@ -394,8 +440,8 @@ def test_rgbled_missing_pins():
RGBLED()
def test_rgbled_initial_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(1, 2, 3, initial_value=(0.1, 0.2, 0)) as device:
assert r.frequency
assert g.frequency
assert b.frequency
@@ -405,24 +451,24 @@ def test_rgbled_initial_value():
def test_rgbled_initial_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False, initial_value=(0, 1, 1)) as device:
with RGBLED(1, 2, 3, pwm=False, initial_value=(0, 1, 1)) as device:
assert r.state == 0
assert g.state == 1
assert b.state == 1
def test_rgbled_initial_bad_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with pytest.raises(ValueError):
RGBLED(r, g, b, initial_value=(0.1, 0.2, 1.2))
RGBLED(1, 2, 3, initial_value=(0.1, 0.2, 1.2))
def test_rgbled_initial_bad_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with pytest.raises(ValueError):
RGBLED(r, g, b, pwm=False, initial_value=(0.1, 0.2, 0))
RGBLED(1, 2, 3, pwm=False, initial_value=(0.1, 0.2, 0))
def test_rgbled_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(1, 2, 3) as device:
assert isinstance(device._leds[0], PWMLED)
assert isinstance(device._leds[1], PWMLED)
assert isinstance(device._leds[2], PWMLED)
@@ -440,7 +486,7 @@ def test_rgbled_value():
def test_rgbled_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
assert isinstance(device._leds[0], LED)
assert isinstance(device._leds[1], LED)
assert isinstance(device._leds[2], LED)
@@ -454,35 +500,35 @@ def test_rgbled_value_nonpwm():
assert device.value == (0, 0, 0)
def test_rgbled_bad_value():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(1, 2, 3) as device:
with pytest.raises(ValueError):
device.value = (2, 0, 0)
with RGBLED(r, g, b) as device:
with RGBLED(1, 2, 3) as device:
with pytest.raises(ValueError):
device.value = (0, -1, 0)
def test_rgbled_bad_value_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (2, 0, 0)
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, -1, 0)
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0.5, 0, 0)
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, 0.5, 0)
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.value = (0, 0, 0.5)
def test_rgbled_toggle():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(1, 2, 3) as device:
assert not device.is_active
assert device.value == (0, 0, 0)
device.toggle()
@@ -494,7 +540,7 @@ def test_rgbled_toggle():
def test_rgbled_toggle_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
assert not device.is_active
assert device.value == (0, 0, 0)
device.toggle()
@@ -506,7 +552,7 @@ def test_rgbled_toggle_nonpwm():
def test_rgbled_blink_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.blink(fade_in_time=1)
with pytest.raises(ValueError):
@@ -515,8 +561,8 @@ def test_rgbled_blink_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -537,7 +583,7 @@ def test_rgbled_blink_background():
reason='timing is too random on pypy')
def test_rgbled_blink_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
start = time()
device.blink(0.1, 0.1, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -557,8 +603,8 @@ def test_rgbled_blink_background_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_blink_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -577,7 +623,7 @@ def test_rgbled_blink_foreground():
reason='timing is too random on pypy')
def test_rgbled_blink_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
start = time()
device.blink(0.1, 0.1, n=2, background=False)
assert isclose(time() - start, 0.4, abs_tol=0.05)
@@ -595,8 +641,8 @@ def test_rgbled_blink_foreground_nonpwm():
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_fade_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.blink(0, 0, 0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -631,15 +677,15 @@ def test_rgbled_fade_background():
def test_rgbled_fade_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_fade_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -672,15 +718,15 @@ def test_rgbled_fade_foreground():
def test_rgbled_fade_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.blink(0, 0, 0.2, 0.2, n=2, background=False)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_pulse_background():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.pulse(0.2, 0.2, n=2)
assert isclose(time() - start, 0, abs_tol=0.05)
@@ -715,15 +761,15 @@ def test_rgbled_pulse_background():
def test_rgbled_pulse_background_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2)
@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'),
reason='timing is too random on pypy')
def test_rgbled_pulse_foreground():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
start = time()
device.pulse(0.2, 0.2, n=2, background=False)
assert isclose(time() - start, 0.8, abs_tol=0.05)
@@ -756,13 +802,13 @@ def test_rgbled_pulse_foreground():
def test_rgbled_pulse_foreground_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
with pytest.raises(ValueError):
device.pulse(0.2, 0.2, n=2, background=False)
def test_rgbled_blink_interrupt():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (4, 5, 6))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(1, 2, 3) as device:
device.blink(1, 0.1)
sleep(0.2)
device.off() # should interrupt while on
@@ -772,7 +818,7 @@ def test_rgbled_blink_interrupt():
def test_rgbled_blink_interrupt_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (4, 5, 6))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
device.blink(1, 0.1)
sleep(0.2)
device.off() # should interrupt while on
@@ -781,8 +827,8 @@ def test_rgbled_blink_interrupt_nonpwm():
b.assert_states([0, 1, 0])
def test_rgbled_close():
r, g, b = (Device.pin_factory.pin(i, pin_class=MockPWMPin) for i in (1, 2, 3))
with RGBLED(r, g, b) as device:
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(1, 2, 3) as device:
assert not device.closed
device.close()
assert device.closed
@@ -791,7 +837,7 @@ def test_rgbled_close():
def test_rgbled_close_nonpwm():
r, g, b = (Device.pin_factory.pin(i) for i in (1, 2, 3))
with RGBLED(r, g, b, pwm=False) as device:
with RGBLED(1, 2, 3, pwm=False) as device:
assert not device.closed
device.close()
assert device.closed
@@ -803,9 +849,9 @@ def test_motor_missing_pins():
Motor()
def test_motor_pins():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Motor(f, b) as device:
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(1, 2) as device:
assert device.forward_device.pin is f
assert isinstance(device.forward_device, PWMOutputDevice)
assert device.backward_device.pin is b
@@ -814,16 +860,16 @@ def test_motor_pins():
def test_motor_pins_nonpwm():
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device:
with Motor(1, 2, pwm=False) as device:
assert device.forward_device.pin is f
assert isinstance(device.forward_device, DigitalOutputDevice)
assert device.backward_device.pin is b
assert isinstance(device.backward_device, DigitalOutputDevice)
def test_motor_close():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Motor(f, b) as device:
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(1, 2) as device:
device.close()
assert device.closed
assert device.forward_device.pin is None
@@ -834,16 +880,16 @@ def test_motor_close():
def test_motor_close_nonpwm():
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device:
with Motor(1, 2, pwm=False) as device:
device.close()
assert device.closed
assert device.forward_device.pin is None
assert device.backward_device.pin is None
def test_motor_value():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Motor(f, b) as device:
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(1, 2) as device:
device.value = -1
assert device.is_active
assert device.value == -1
@@ -868,7 +914,7 @@ def test_motor_value():
def test_motor_value_nonpwm():
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device:
with Motor(1, 2, pwm=False) as device:
device.value = -1
assert device.is_active
assert device.value == -1
@@ -883,9 +929,9 @@ def test_motor_value_nonpwm():
assert b.state == 0 and f.state == 0
def test_motor_bad_value():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Motor(f, b) as device:
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(1, 2) as device:
with pytest.raises(ValueError):
device.value = -2
with pytest.raises(ValueError):
@@ -898,7 +944,7 @@ def test_motor_bad_value():
def test_motor_bad_value_nonpwm():
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device:
with Motor(1, 2, pwm=False) as device:
with pytest.raises(ValueError):
device.value = -2
with pytest.raises(ValueError):
@@ -909,9 +955,9 @@ def test_motor_bad_value_nonpwm():
device.value = -0.5
def test_motor_reverse():
f = Device.pin_factory.pin(1, pin_class=MockPWMPin)
b = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Motor(f, b) as device:
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(1, 2) as device:
device.forward()
assert device.value == 1
assert b.state == 0 and f.state == 1
@@ -928,7 +974,7 @@ def test_motor_reverse():
def test_motor_reverse_nonpwm():
f = Device.pin_factory.pin(1)
b = Device.pin_factory.pin(2)
with Motor(f, b, pwm=False) as device:
with Motor(1, 2, pwm=False) as device:
device.forward()
assert device.value == 1
assert b.state == 0 and f.state == 1
@@ -937,28 +983,28 @@ def test_motor_reverse_nonpwm():
assert b.state == 1 and f.state == 0
def test_servo_pins():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin)
with Servo(p) as device:
p = Device.pin_factory.pin(1)
with Servo(1) as device:
assert device.pwm_device.pin is p
assert isinstance(device.pwm_device, PWMOutputDevice)
def test_servo_bad_value():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin)
p = Device.pin_factory.pin(1)
with pytest.raises(ValueError):
Servo(p, initial_value=2)
Servo(1, initial_value=2)
with pytest.raises(ValueError):
Servo(p, min_pulse_width=30/1000)
Servo(1, min_pulse_width=30/1000)
with pytest.raises(ValueError):
Servo(p, max_pulse_width=30/1000)
Servo(1, max_pulse_width=30/1000)
def test_servo_pins_nonpwm():
p = Device.pin_factory.pin(2)
with pytest.raises(PinPWMUnsupported):
Servo(p)
Servo(1)
def test_servo_close():
p = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Servo(p) as device:
p = Device.pin_factory.pin(2)
with Servo(1) as device:
device.close()
assert device.closed
assert device.pwm_device.pin is None
@@ -966,8 +1012,8 @@ def test_servo_close():
assert device.closed
def test_servo_pulse_width():
p = Device.pin_factory.pin(2, pin_class=MockPWMPin)
with Servo(p, min_pulse_width=5/10000, max_pulse_width=25/10000) as device:
p = Device.pin_factory.pin(2)
with Servo(1, min_pulse_width=5/10000, max_pulse_width=25/10000) as device:
assert isclose(device.min_pulse_width, 5/10000)
assert isclose(device.max_pulse_width, 25/10000)
assert isclose(device.frame_width, 20/1000)
@@ -980,8 +1026,8 @@ def test_servo_pulse_width():
assert device.pulse_width is None
def test_servo_values():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin)
with Servo(p) as device:
p = Device.pin_factory.pin(1)
with Servo(1) as device:
device.min()
assert device.is_active
assert device.value == -1
@@ -1007,14 +1053,14 @@ def test_servo_values():
assert device.value is None
def test_angular_servo_range():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin)
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device:
p = Device.pin_factory.pin(1)
with AngularServo(1, initial_angle=15, min_angle=0, max_angle=90) as device:
assert device.min_angle == 0
assert device.max_angle == 90
def test_angular_servo_angles():
p = Device.pin_factory.pin(1, pin_class=MockPWMPin)
with AngularServo(p) as device:
p = Device.pin_factory.pin(1)
with AngularServo(1) as device:
device.angle = 0
assert device.angle == 0
assert isclose(device.value, 0)
@@ -1026,7 +1072,7 @@ def test_angular_servo_angles():
assert isclose(device.value, -1)
device.detach()
assert device.angle is None
with AngularServo(p, initial_angle=15, min_angle=0, max_angle=90) as device:
with AngularServo(1, initial_angle=15, min_angle=0, max_angle=90) as device:
assert device.angle == 15
assert isclose(device.value, -2/3)
device.angle = 0
@@ -1037,7 +1083,7 @@ def test_angular_servo_angles():
assert isclose(device.value, 1)
device.angle = None
assert device.angle is None
with AngularServo(p, min_angle=45, max_angle=-45) as device:
with AngularServo(1, min_angle=45, max_angle=-45) as device:
assert device.angle == 0
assert isclose(device.value, 0)
device.angle = -45

View File

@@ -33,50 +33,50 @@ def test_spi_hardware_params():
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
io_open.return_value.__enter__.return_value = ['Revision: a21042']
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \
patch('gpiozero.pins.local.SpiDev'):
with Device.pin_factory.spi() as device:
factory = NativeFactory()
with patch('gpiozero.pins.local.SpiDev'):
with factory.spi() as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(port=0, device=0) as device:
with factory.spi(port=0, device=0) as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(port=0, device=1) as device:
with factory.spi(port=0, device=1) as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11) as device:
with factory.spi(clock_pin=11) as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=8) as device:
with factory.spi(clock_pin=11, mosi_pin=10, select_pin=8) as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=10, select_pin=7) as device:
with factory.spi(clock_pin=11, mosi_pin=10, select_pin=7) as device:
assert isinstance(device, LocalPiHardwareSPI)
with Device.pin_factory.spi(shared=True) as device:
with factory.spi(shared=True) as device:
assert isinstance(device, LocalPiHardwareSPIShared)
with pytest.raises(ValueError):
Device.pin_factory.spi(port=1)
factory.spi(port=1)
with pytest.raises(ValueError):
Device.pin_factory.spi(device=2)
factory.spi(device=2)
with pytest.raises(ValueError):
Device.pin_factory.spi(port=0, clock_pin=12)
factory.spi(port=0, clock_pin=12)
with pytest.raises(ValueError):
Device.pin_factory.spi(foo='bar')
factory.spi(foo='bar')
def test_spi_software_params():
with patch('os.open'), patch('mmap.mmap') as mmap_mmap, patch('io.open') as io_open:
mmap_mmap.return_value = array(nstr('B'), (0,) * 4096)
io_open.return_value.__enter__.return_value = ['Revision: a21042']
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \
patch('gpiozero.pins.local.SpiDev'):
with Device.pin_factory.spi(select_pin=6) as device:
factory = NativeFactory()
with patch('gpiozero.pins.local.SpiDev'):
with factory.spi(select_pin=6) as device:
assert isinstance(device, LocalPiSoftwareSPI)
with Device.pin_factory.spi(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
with factory.spi(clock_pin=11, mosi_pin=9, miso_pin=10) as device:
assert isinstance(device, LocalPiSoftwareSPI)
with Device.pin_factory.spi(select_pin=6, shared=True) as device:
with factory.spi(select_pin=6, shared=True) as device:
assert isinstance(device, LocalPiSoftwareSPIShared)
with patch('gpiozero.devices.Device.pin_factory', NativeFactory()), \
patch('gpiozero.pins.local.SpiDev', None):
# Clear out the old factory's pins cache (this is only necessary
# because we're being very naughty switching out pin factories)
Device.pin_factory.pins.clear()
with patch('gpiozero.pins.local.SpiDev', None):
# Clear out the old factory's caches (this is only necessary because
# we're being naughty switching out patches)
factory.pins.clear()
factory._reservations.clear()
# Ensure software fallback works when SpiDev isn't present
with Device.pin_factory.spi() as device:
with factory.spi() as device:
assert isinstance(device, LocalPiSoftwareSPI)
def test_spi_hardware_conflict():