mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Add "real" pins tests
This is just a quicky for people to start playing with - it's not complete in any way, shape, or form. This is how I envisage the "real" pin tests being done; part of the test suite with a `skipif` to ensure they don't get run on non-Pi platforms, with a fixture to loop over whatever pin implementations are found (we can't always assume all of them: for example, RPIO doesn't work on a Pi 2), and a relatively simple wiring for the test. In this case I've assumed GPIOs 22 and 27 are wired together. They're next to each other, so a jumper is sufficient to run the test. PRs extending the coverage are very welcome (I've already discovered and fixed several silly bugs in NativePin!). I've left all the interesting hard stuff for people to play with (PWM testing: statistical sampling? debounce compensation testing: timing?). When I've got a second, I'll looking into hooking up my Pi Zero as a Travis-esque test-bed for this, triggered by GitHub webhooks (not sure how I'll deal with reporting yet).
This commit is contained in:
@@ -245,6 +245,7 @@ class NativePin(Pin):
|
||||
def close(self):
|
||||
self.when_changed = None
|
||||
self.function = 'input'
|
||||
self.pull = 'up' if self.number in (2, 3) else 'floating'
|
||||
|
||||
def _get_function(self):
|
||||
return self.GPIO_FUNCTION_NAMES[(self._MEM[self._func_offset] >> self._func_shift) & 7]
|
||||
@@ -253,7 +254,7 @@ class NativePin(Pin):
|
||||
try:
|
||||
value = self.GPIO_FUNCTIONS[value]
|
||||
except KeyError:
|
||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % self)
|
||||
raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
|
||||
self._MEM[self._func_offset] = (
|
||||
self._MEM[self._func_offset]
|
||||
& ~(7 << self._func_shift)
|
||||
@@ -282,7 +283,7 @@ class NativePin(Pin):
|
||||
try:
|
||||
value = self.GPIO_PULL_UPS[value]
|
||||
except KeyError:
|
||||
raise PinInvalidPull('invalid pull direction "%s" for pin %r' % self)
|
||||
raise PinInvalidPull('invalid pull direction "%s" for pin %r' % (value, self))
|
||||
self._MEM[self._MEM.GPPUD_OFFSET] = value
|
||||
sleep(0.000000214)
|
||||
self._MEM[self._pull_offset] = 1 << self._pull_shift
|
||||
|
||||
@@ -13,6 +13,7 @@ from ..exc import (
|
||||
PinInvalidFunction,
|
||||
PinSetInput,
|
||||
PinFixedPull,
|
||||
PinInvalidPull,
|
||||
)
|
||||
|
||||
|
||||
@@ -144,7 +145,7 @@ class PiGPIOPin(Pin):
|
||||
self.frequency = None
|
||||
self.when_changed = None
|
||||
self.function = 'input'
|
||||
self.pull = 'floating'
|
||||
self.pull = 'up' if self.number in (2, 3) else 'floating'
|
||||
|
||||
def _get_function(self):
|
||||
return self.GPIO_FUNCTION_NAMES[self._connection.get_mode(self._number)]
|
||||
|
||||
143
tests/test_real_pins.py
Normal file
143
tests/test_real_pins.py
Normal file
@@ -0,0 +1,143 @@
|
||||
from __future__ import (
|
||||
unicode_literals,
|
||||
absolute_import,
|
||||
print_function,
|
||||
division,
|
||||
)
|
||||
str = type('')
|
||||
|
||||
|
||||
import io
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from gpiozero import PinFixedPull, PinInvalidPull, PinInvalidFunction
|
||||
|
||||
|
||||
# This module assumes you've wired the following GPIO pins together
|
||||
TEST_PIN = 22
|
||||
INPUT_PIN = 27
|
||||
|
||||
|
||||
# Skip the entire module if we're not on a Pi
|
||||
def is_a_pi():
|
||||
with io.open('/proc/cpuinfo', 'r') as cpuinfo:
|
||||
for line in cpuinfo:
|
||||
if line.startswith('Hardware'):
|
||||
hardware, colon, soc = line.strip().split(None, 2)
|
||||
return soc in ('BCM2708', 'BCM2709', 'BCM2835', 'BCM2836')
|
||||
else:
|
||||
return False
|
||||
pytestmark = pytest.mark.skipif(not is_a_pi(), reason='tests cannot run on non-Pi platforms')
|
||||
del is_a_pi
|
||||
|
||||
|
||||
# Try and import as many pin libraries as possible
|
||||
PIN_CLASSES = []
|
||||
try:
|
||||
from gpiozero.pins.rpigpio import RPiGPIOPin
|
||||
PIN_CLASSES.append(RPiGPIOPin)
|
||||
except ImportError:
|
||||
RPiGPIOPin = None
|
||||
try:
|
||||
from gpiozero.pins.rpio import RPIOPin
|
||||
PIN_CLASSES.append(RPIOPin)
|
||||
except ImportError:
|
||||
RPIOPin = None
|
||||
try:
|
||||
from gpiozero.pins.pigpiod import PiGPIOPin
|
||||
PIN_CLASSES.append(PiGPIOPin)
|
||||
except ImportError:
|
||||
PiGPIOPin = None
|
||||
try:
|
||||
from gpiozero.pins.native import NativePin
|
||||
PIN_CLASSES.append(NativePin)
|
||||
except ImportError:
|
||||
NativePin = None
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', params=PIN_CLASSES)
|
||||
def pin_class(request):
|
||||
# pigpiod needs to be running for PiGPIOPin
|
||||
if request.param.__name__ == 'PiGPIOPin':
|
||||
subprocess.check_call(['sudo', 'pigpiod'])
|
||||
# Unfortunately, pigpiod provides no option for running in the
|
||||
# foreground, so we have to use the sledgehammer of killall to get shot
|
||||
# of it
|
||||
def kill_pigpiod():
|
||||
subprocess.check_call(['sudo', 'killall', 'pigpiod'])
|
||||
request.addfinalizer(kill_pigpiod)
|
||||
return request.param
|
||||
|
||||
@pytest.fixture
|
||||
def pins(request, pin_class):
|
||||
# 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
|
||||
test_pin = pin_class(22)
|
||||
input_pin = pin_class(27)
|
||||
input_pin.function = 'input'
|
||||
input_pin.pull = 'down'
|
||||
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 == 22
|
||||
assert input_pin.number == 27
|
||||
|
||||
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'
|
||||
|
||||
def test_pull_down_warning(pin_class):
|
||||
pin = pin_class(2)
|
||||
try:
|
||||
with pytest.raises(PinFixedPull):
|
||||
pin.pull = 'down'
|
||||
finally:
|
||||
pin.close()
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user