mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Permit replacement of pin_factory without closing old factory. However, continue closing devices associated with extant pin factory at script termination.
182 lines
5.4 KiB
Python
182 lines
5.4 KiB
Python
from __future__ import (
|
|
unicode_literals,
|
|
absolute_import,
|
|
print_function,
|
|
division,
|
|
)
|
|
str = type('')
|
|
try:
|
|
range = xrange
|
|
except NameError:
|
|
pass
|
|
|
|
import io
|
|
import os
|
|
from time import sleep
|
|
|
|
import pytest
|
|
import pkg_resources
|
|
|
|
from gpiozero import (
|
|
PinFixedPull,
|
|
PinInvalidPull,
|
|
PinInvalidFunction,
|
|
PinPWMUnsupported,
|
|
Device,
|
|
)
|
|
from gpiozero.pins.mock import MockConnectedPin, MockFactory
|
|
try:
|
|
from math import isclose
|
|
except ImportError:
|
|
from gpiozero.compat import isclose
|
|
|
|
|
|
# This module assumes you've wired the following GPIO pins together. The pins
|
|
# can be re-configured via the listed environment variables (useful for when
|
|
# your testing rig requires different pins because the defaults interfere with
|
|
# attached hardware).
|
|
TEST_PIN = int(os.getenv('GPIOZERO_TEST_PIN', '22'))
|
|
INPUT_PIN = int(os.getenv('GPIOZERO_TEST_INPUT_PIN', '27'))
|
|
|
|
|
|
@pytest.fixture(
|
|
scope='module',
|
|
params=[
|
|
name
|
|
for name in pkg_resources.get_distribution('gpiozero').get_entry_map('gpiozero_pin_factories').keys()
|
|
if not name.endswith('Pin') # leave out compatibility names
|
|
])
|
|
def pin_factory(request):
|
|
try:
|
|
factory = pkg_resources.load_entry_point('gpiozero', 'gpiozero_pin_factories', request.param)()
|
|
except Exception as e:
|
|
pytest.skip("skipped factory %s: %s" % (request.param, str(e)))
|
|
else:
|
|
Device.pin_factory = factory
|
|
def fin():
|
|
Device.pin_factory = MockFactory()
|
|
request.addfinalizer(fin)
|
|
return factory
|
|
|
|
|
|
@pytest.fixture(scope='function')
|
|
def pins(request, pin_factory):
|
|
# Why return both pins in a single fixture? If we defined one fixture for
|
|
# each pin then pytest will (correctly) test RPiGPIOPin(22) against
|
|
# NativePin(27) and so on. This isn't supported, so we don't test it
|
|
input_pin = pin_factory.pin(INPUT_PIN)
|
|
input_pin.function = 'input'
|
|
input_pin.pull = 'down'
|
|
if pin_factory.__class__.__name__ == 'MockFactory':
|
|
test_pin = pin_factory.pin(TEST_PIN, pin_class=MockConnectedPin, input_pin=input_pin)
|
|
else:
|
|
test_pin = pin_factory.pin(TEST_PIN)
|
|
def fin():
|
|
test_pin.close()
|
|
input_pin.close()
|
|
request.addfinalizer(fin)
|
|
return test_pin, input_pin
|
|
|
|
|
|
def test_pin_numbers(pins):
|
|
test_pin, input_pin = pins
|
|
assert test_pin.number == TEST_PIN
|
|
assert input_pin.number == INPUT_PIN
|
|
|
|
def test_function_bad(pins):
|
|
test_pin, input_pin = pins
|
|
with pytest.raises(PinInvalidFunction):
|
|
test_pin.function = 'foo'
|
|
|
|
def test_output(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.function = 'output'
|
|
test_pin.state = 0
|
|
assert input_pin.state == 0
|
|
test_pin.state = 1
|
|
assert input_pin.state == 1
|
|
|
|
def test_output_with_state(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.output_with_state(0)
|
|
assert input_pin.state == 0
|
|
test_pin.output_with_state(1)
|
|
assert input_pin.state == 1
|
|
|
|
def test_pull(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.function = 'input'
|
|
test_pin.pull = 'up'
|
|
assert input_pin.state == 1
|
|
test_pin.pull = 'down'
|
|
test_pin, input_pin = pins
|
|
assert input_pin.state == 0
|
|
|
|
def test_pull_bad(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.function = 'input'
|
|
with pytest.raises(PinInvalidPull):
|
|
test_pin.pull = 'foo'
|
|
with pytest.raises(PinInvalidPull):
|
|
test_pin.input_with_pull('foo')
|
|
|
|
def test_pull_down_warning(pin_factory):
|
|
if pin_factory.pi_info.pulled_up('GPIO2'):
|
|
pin = pin_factory.pin(2)
|
|
try:
|
|
with pytest.raises(PinFixedPull):
|
|
pin.pull = 'down'
|
|
with pytest.raises(PinFixedPull):
|
|
pin.input_with_pull('down')
|
|
finally:
|
|
pin.close()
|
|
else:
|
|
pytest.skip("GPIO2 isn't pulled up on this pi")
|
|
|
|
def test_input_with_pull(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.input_with_pull('up')
|
|
assert input_pin.state == 1
|
|
test_pin.input_with_pull('down')
|
|
assert input_pin.state == 0
|
|
|
|
def test_bad_duty_cycle(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.function = 'output'
|
|
try:
|
|
# NOTE: There's some race in RPi.GPIO that causes a segfault if we
|
|
# don't pause before starting PWM; only seems to happen when stopping
|
|
# and restarting PWM very rapidly (i.e. between test cases).
|
|
if Device.pin_factory.__class__.__name__ == 'RPiGPIOFactory':
|
|
sleep(0.1)
|
|
test_pin.frequency = 100
|
|
except PinPWMUnsupported:
|
|
pytest.skip("%r doesn't support PWM" % test_pin.factory)
|
|
else:
|
|
try:
|
|
with pytest.raises(ValueError):
|
|
test_pin.state = 1.1
|
|
finally:
|
|
test_pin.frequency = None
|
|
|
|
def test_duty_cycles(pins):
|
|
test_pin, input_pin = pins
|
|
test_pin.function = 'output'
|
|
try:
|
|
# NOTE: see above
|
|
if Device.pin_factory.__class__.__name__ == 'RPiGPIOFactory':
|
|
sleep(0.1)
|
|
test_pin.frequency = 100
|
|
except PinPWMUnsupported:
|
|
pytest.skip("%r doesn't support PWM" % test_pin.factory)
|
|
else:
|
|
try:
|
|
for duty_cycle in (0.0, 0.1, 0.5, 1.0):
|
|
test_pin.state = duty_cycle
|
|
assert test_pin.state == duty_cycle
|
|
total = sum(input_pin.state for i in range(20000))
|
|
assert isclose(total / 20000, duty_cycle, rel_tol=0.1, abs_tol=0.1)
|
|
finally:
|
|
test_pin.frequency = None
|
|
|