From 92d80d2ae651bc11536c78480560000977aefd3a Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 5 Apr 2016 02:22:38 +0100 Subject: [PATCH] Some more tests... --- gpiozero/input_devices.py | 1 + gpiozero/pins/mock.py | 45 ++++++++++++- tests/test_inputs.py | 132 +++++++++++++++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 3 deletions(-) diff --git a/gpiozero/input_devices.py b/gpiozero/input_devices.py index b19f802..11c325c 100644 --- a/gpiozero/input_devices.py +++ b/gpiozero/input_devices.py @@ -9,6 +9,7 @@ from __future__ import ( import warnings from time import sleep, time +from threading import Event from .exc import InputDeviceError, GPIODeviceError, GPIODeviceClosed from .devices import GPIODevice, CompositeDevice diff --git a/gpiozero/pins/mock.py b/gpiozero/pins/mock.py index 91d4b6a..6cb0a0b 100644 --- a/gpiozero/pins/mock.py +++ b/gpiozero/pins/mock.py @@ -9,13 +9,14 @@ str = type('') from collections import namedtuple from time import time +from threading import Thread, Event try: from math import isclose except ImportError: from ..compat import isclose from . import Pin, PINS_CLEANUP -from ..exc import PinSetInput, PinPWMUnsupported +from ..exc import PinSetInput, PinPWMUnsupported, PinFixedPull PinState = namedtuple('PinState', ('timestamp', 'state')) @@ -162,6 +163,48 @@ class MockPin(Pin): assert isclose(actual.state, expected[1]) +class MockPulledUpPin(MockPin): + """ + This derivative of :class:`MockPin` emulates a pin with a physical pull-up + resistor. + """ + def _set_pull(self, value): + if value != 'up': + raise PinFixedPull('pin has a physical pull-up resistor') + + +class MockChargingPin(MockPin): + """ + This derivative of :class:`MockPin` emulates a pin which, when set to + input, waits a predetermined length of time and then drives itself high + (as if attached to, e.g. a typical circuit using an LDR and a capacitor + to time the charging rate). + """ + def __init__(self, number): + super(MockChargingPin, self).__init__() + self.charge_time = 0.01 + self._charge_stop = Event() + self._charge_thread = None + + def _set_function(self, value): + super(MockChargingPin, self)._set_function(value) + if value == 'input': + if self._charge_thread: + self._charge_stop.set() + self._charge_thread.join() + self._charge_stop.clear() + self._charge_thread = Thread(target=lambda: self._charged) + self._charge_thread.start() + elif value == 'output': + if self.charge_thread: + self._charge_stop.set() + self._charge_thread.join() + + def _charged(self): + if not self._charge_stop.wait(self.charge_time): + self.drive_high() + + class MockPWMPin(MockPin): """ This derivative of :class:`MockPin` adds PWM support. diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 74aebe6..ec01e56 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -8,12 +8,140 @@ str = type('') import pytest +import mock +from threading import Event -from gpiozero.pins.mock import MockPin +from gpiozero.pins.mock import MockPin, MockPulledUpPin, MockChargingPin from gpiozero import * def teardown_function(function): MockPin.clear_pins() -# TODO input_devices tests! +def test_input_initial_values(): + pin = MockPin(2) + device = InputDevice(pin, pull_up=True) + assert pin.function == 'input' + assert pin.pull == 'up' + assert device.pull_up + device.close() + device = InputDevice(pin, pull_up=False) + assert pin.pull == 'down' + assert not device.pull_up + device.close() + +def test_input_is_active(): + pin = MockPin(2) + device = InputDevice(pin, pull_up=True) + pin.drive_high() + assert not device.is_active + pin.drive_low() + assert device.is_active + +def test_input_pulled_up(): + pin = MockPulledUpPin(2) + with pytest.raises(PinFixedPull): + device = InputDevice(pin, pull_up=False) + +def test_input_event_activated(): + event = Event() + pin = MockPin(2) + device = DigitalInputDevice(pin) + device.when_activated = lambda: event.set() + assert not event.wait(0) + pin.drive_high() + assert event.wait(0) + +def test_input_event_deactivated(): + event = Event() + pin = MockPin(2) + device = DigitalInputDevice(pin) + device.when_deactivated = lambda: event.set() + assert not event.wait(0) + pin.drive_high() + assert not event.wait(0) + pin.drive_low() + assert event.wait(0) + +def test_input_wait_active(): + pin = MockPin(2) + device = DigitalInputDevice(pin) + pin.drive_high() + assert device.wait_for_active(1) + assert not device.wait_for_inactive(0) + +def test_input_wait_inactive(): + pin = MockPin(2) + device = DigitalInputDevice(pin) + assert device.wait_for_inactive(1) + assert not device.wait_for_active(0) + +def test_input_smoothed_attrib(): + pin = MockPin(2) + device = SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) + assert device.threshold == 0.5 + assert device.queue_len == 5 + assert not device.partial + device._queue.start() + assert not device.is_active + +def test_input_smoothed_silly(): + pin = MockPin(2) + with pytest.raises(InputDeviceError): + device = SmoothedInputDevice(pin, threshold=-1) + device = SmoothedInputDevice(pin) + del device._queue.stopping + with pytest.raises(AttributeError): + device.close() + +def test_input_smoothed_values(): + pin = MockPin(2) + device = SmoothedInputDevice(pin) + device._queue.start() + assert not device.is_active + pin.drive_high() + assert device.wait_for_active(1) + pin.drive_low() + assert device.wait_for_inactive(1) + +def test_input_button(): + pin = MockPin(2) + button = Button(pin) + assert pin.pull == 'up' + assert not button.is_pressed + pin.drive_low() + assert button.is_pressed + assert button.wait_for_press(1) + pin.drive_high() + assert not button.is_pressed + assert button.wait_for_release(1) + +def test_input_line_sensor(): + pin = MockPin(2) + sensor = LineSensor(pin) + pin.drive_low() # logic is inverted for line sensor + assert sensor.wait_for_line(1) + assert sensor.line_detected + pin.drive_high() + assert sensor.wait_for_no_line(1) + assert not sensor.line_detected + +def test_input_motion_sensor(): + pin = MockPin(2) + sensor = MotionSensor(pin) + pin.drive_high() + assert sensor.wait_for_motion(1) + assert sensor.motion_detected + pin.drive_low() + assert sensor.wait_for_no_motion(1) + assert not sensor.motion_detected + +@pytest.mark.skipif(True, reason='Freezes') +def test_input_light_sensor(): + pin = MockChargingPin(2) + sensor = LightSensor(pin) + pin.charge_time = 1 + assert not sensor.light_detected + pin.charge_time = 0 + assert sensor.light_detected +