diff --git a/docs/api_boards.rst b/docs/api_boards.rst index f1fdb32..f50b21a 100644 --- a/docs/api_boards.rst +++ b/docs/api_boards.rst @@ -18,7 +18,7 @@ individually. LED Board ========= -.. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False) +.. autoclass:: LEDBoard(\*pins, pwm=False, active_high=True, initial_value=False, \*\*named_pins) :inherited-members: :members: diff --git a/docs/api_generic.rst b/docs/api_generic.rst index 1f6ac25..621154f 100644 --- a/docs/api_generic.rst +++ b/docs/api_generic.rst @@ -81,10 +81,10 @@ Base Classes .. autoclass:: SPIDevice :members: -.. autoclass:: InternalDevice +.. autoclass:: InternalDevice() :members: -.. autoclass:: CompositeDevice +.. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs) :members: Input Devices diff --git a/docs/api_source_tools.rst b/docs/api_tools.rst similarity index 54% rename from docs/api_source_tools.rst rename to docs/api_tools.rst index f0cb189..adb1737 100644 --- a/docs/api_source_tools.rst +++ b/docs/api_tools.rst @@ -2,13 +2,20 @@ Source Tools ============ -.. currentmodule:: gpiozero +.. currentmodule:: gpiozero.tools GPIO Zero includes several utility routines which are intended to be used with -the :attr:`~SourceMixin.source` and :attr:`~ValuesMixin.values` attributes -common to most devices in the library. Given that ``source`` and ``values`` -deal with infinite iterators, another excellent source of utilities is the -:mod:`itertools` module in the standard library. +the :attr:`~gpiozero.SourceMixin.source` and +:attr:`~gpiozero.ValuesMixin.values` attributes common to most devices in the +library. These utility routines are in the ``tools`` module of GPIO Zero and +are typically imported as follows:: + + from gpiozero.tools import scaled, negated, conjunction + +Given that :attr:`~gpiozero.SourceMixin.source` and +:attr:`~gpiozero.ValuesMixin.values` deal with infinite iterators, another +excellent source of utilities is the :mod:`itertools` module in the standard +library. Single source conversions ========================= diff --git a/docs/changelog.rst b/docs/changelog.rst index 99b3c12..d926ba9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -12,6 +12,8 @@ Release 1.2.0 (2016-04-??) * Added :class:`LineSensor` class for single line-sensors (`#109`_) * Added :class:`DistanceSensor` class for HC-SR04 ultra-sonic sensors (`#114`_) * Added :class:`SnowPi` class for the Ryanteck Snow-pi board (`#130`_) +* Added :attr:`~Button.when_held` (and related properties) to :class:`Button` + (`#115`_) * Fixed issues with installing GPIO Zero for python 3 on Raspbian Wheezy releases (`#140`_) * Added support for lots of ADC chips (MCP3xxx family) (`#162`_) - many thanks @@ -35,6 +37,7 @@ reports in this version - keep 'em coming! .. _#69: https://github.com/RPi-Distro/python-gpiozero/issues/69 .. _#109: https://github.com/RPi-Distro/python-gpiozero/issues/109 .. _#114: https://github.com/RPi-Distro/python-gpiozero/issues/114 +.. _#115: https://github.com/RPi-Distro/python-gpiozero/issues/115 .. _#130: https://github.com/RPi-Distro/python-gpiozero/issues/130 .. _#140: https://github.com/RPi-Distro/python-gpiozero/issues/140 .. _#162: https://github.com/RPi-Distro/python-gpiozero/issues/162 diff --git a/docs/images/composed_devices.pdf b/docs/images/composed_devices.pdf index 40d4ed7..10d7699 100644 Binary files a/docs/images/composed_devices.pdf and b/docs/images/composed_devices.pdf differ diff --git a/docs/images/composed_devices.png b/docs/images/composed_devices.png index e4ee206..07965ef 100644 Binary files a/docs/images/composed_devices.png and b/docs/images/composed_devices.png differ diff --git a/docs/images/composed_devices.svg b/docs/images/composed_devices.svg index c4fa63a..fed5a09 100644 --- a/docs/images/composed_devices.svg +++ b/docs/images/composed_devices.svg @@ -4,11 +4,11 @@ - + classes - + RGBLED @@ -16,83 +16,98 @@ PWMLED - -PWMLED + +PWMLED RGBLED->PWMLED - - + + LEDBoard - -LEDBoard + +LEDBoard LEDBoard->PWMLED - - + + LED - -LED + +LED LEDBoard->LED - - + + + + +LEDBarGraph + +LEDBarGraph + + +LEDBarGraph->PWMLED + + + + +LEDBarGraph->LED + + -TrafficLightsBuzzer - -TrafficLightsBuzzer +TrafficLightsBuzzer + +TrafficLightsBuzzer -TrafficLights - -TrafficLights +TrafficLights + +TrafficLights -TrafficLightsBuzzer->TrafficLights - - +TrafficLightsBuzzer->TrafficLights + + -Buzzer - -Buzzer +Buzzer + +Buzzer -TrafficLightsBuzzer->Buzzer - - +TrafficLightsBuzzer->Buzzer + + -Button - -Button +Button + +Button -TrafficLightsBuzzer->Button - - +TrafficLightsBuzzer->Button + + -Robot - -Robot +Robot + +Robot -Motor - -Motor +Motor + +Motor -Robot->Motor - - +Robot->Motor + + diff --git a/docs/images/composite_device_hierarchy.dot b/docs/images/composite_device_hierarchy.dot index ec8c0b3..ce932bc 100644 --- a/docs/images/composite_device_hierarchy.dot +++ b/docs/images/composite_device_hierarchy.dot @@ -32,6 +32,5 @@ digraph classes { RyanteckRobot->Robot; CamJamKitRobot->Robot; - RGBLED->CompositeDevice; Motor->CompositeDevice; } diff --git a/docs/images/composite_device_hierarchy.pdf b/docs/images/composite_device_hierarchy.pdf index 32f2d27..5c01bed 100644 Binary files a/docs/images/composite_device_hierarchy.pdf and b/docs/images/composite_device_hierarchy.pdf differ diff --git a/docs/images/composite_device_hierarchy.png b/docs/images/composite_device_hierarchy.png index da5096f..9a7bebb 100644 Binary files a/docs/images/composite_device_hierarchy.png and b/docs/images/composite_device_hierarchy.png differ diff --git a/docs/images/composite_device_hierarchy.svg b/docs/images/composite_device_hierarchy.svg index 01adcce..ca6cde2 100644 --- a/docs/images/composite_device_hierarchy.svg +++ b/docs/images/composite_device_hierarchy.svg @@ -4,25 +4,25 @@ - + classes - + Device - -Device + +Device CompositeDevice - -CompositeDevice + +CompositeDevice CompositeDevice->Device - - + + CompositeOutputDevice @@ -31,8 +31,8 @@ CompositeOutputDevice->CompositeDevice - - + + LEDCollection @@ -141,8 +141,8 @@ Robot->CompositeDevice - - + + RyanteckRobot @@ -164,25 +164,15 @@ - -RGBLED - -RGBLED - - -RGBLED->CompositeDevice - - - -Motor - -Motor +Motor + +Motor -Motor->CompositeDevice - - +Motor->CompositeDevice + + diff --git a/docs/images/gpio_device_hierarchy.dot b/docs/images/gpio_device_hierarchy.dot index 7ff0bed..3054865 100644 --- a/docs/images/gpio_device_hierarchy.dot +++ b/docs/images/gpio_device_hierarchy.dot @@ -30,5 +30,6 @@ digraph classes { Buzzer->DigitalOutputDevice; PWMOutputDevice->OutputDevice; PWMLED->PWMOutputDevice; + RGBLED->Device; } diff --git a/docs/images/gpio_device_hierarchy.pdf b/docs/images/gpio_device_hierarchy.pdf index 9dc2a3e..495dfa1 100644 Binary files a/docs/images/gpio_device_hierarchy.pdf and b/docs/images/gpio_device_hierarchy.pdf differ diff --git a/docs/images/gpio_device_hierarchy.png b/docs/images/gpio_device_hierarchy.png index ff0de16..83f0c8c 100644 Binary files a/docs/images/gpio_device_hierarchy.png and b/docs/images/gpio_device_hierarchy.png differ diff --git a/docs/images/gpio_device_hierarchy.svg b/docs/images/gpio_device_hierarchy.svg index 1b35ddb..3a7d460 100644 --- a/docs/images/gpio_device_hierarchy.svg +++ b/docs/images/gpio_device_hierarchy.svg @@ -11,8 +11,8 @@ Device - -Device + +Device GPIODevice @@ -21,8 +21,8 @@ GPIODevice->Device - - + + SmoothedInputDevice @@ -164,5 +164,15 @@ + +RGBLED + +RGBLED + + +RGBLED->Device + + + diff --git a/docs/index.rst b/docs/index.rst index 14357b4..cb32ed0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,7 +14,7 @@ Table of Contents api_boards api_other api_generic - api_source_tools + api_tools api_pins api_exc changelog diff --git a/gpiozero/__init__.py b/gpiozero/__init__.py index 9c7f0c7..0ef0810 100644 --- a/gpiozero/__init__.py +++ b/gpiozero/__init__.py @@ -121,19 +121,3 @@ from .other_devices import ( PingServer, TimeOfDay, ) -from .source_tools import ( - averaged, - clamped, - conjunction, - cos_values, - disjunction, - inverted, - negated, - post_delayed, - pre_delayed, - quantized, - queued, - random_values, - scaled, - sin_values, -) diff --git a/gpiozero/boards.py b/gpiozero/boards.py index f79f56e..689492a 100644 --- a/gpiozero/boards.py +++ b/gpiozero/boards.py @@ -258,7 +258,7 @@ class LEDBoard(LEDCollection): on_time, off_time, fade_in_time, fade_out_time, n, background ) - def _blink_device(self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): + def _blink_device(self, on_time, off_time, fade_in_time, fade_out_time, n, fps=25): sequence = [] if fade_in_time > 0: sequence += [ diff --git a/gpiozero/devices.py b/gpiozero/devices.py index 4bf127b..c89ae17 100644 --- a/gpiozero/devices.py +++ b/gpiozero/devices.py @@ -107,7 +107,13 @@ class GPIOMeta(type): try: old_close() finally: - del cls._INSTANCES[key] + try: + del cls._INSTANCES[key] + except KeyError: + # If the _refs go negative (too many closes) + # just ignore the resulting KeyError here - + # it's already gone + pass self.close = close cls._INSTANCES[key] = weakref.proxy(self) else: diff --git a/gpiozero/mixins.py b/gpiozero/mixins.py index 31ce8b3..d1d4d90 100644 --- a/gpiozero/mixins.py +++ b/gpiozero/mixins.py @@ -374,7 +374,7 @@ class HoldMixin(EventsMixin): @hold_time.setter def hold_time(self, value): if value < 0: - raise BadWaitTime('source_delay must be 0 or greater') + raise BadWaitTime('hold_time must be 0 or greater') self._hold_time = float(value) @property @@ -403,8 +403,8 @@ class HoldMixin(EventsMixin): The length of time (in seconds) that the device has been held for. This is counted from the first execution of the :attr:`when_held` event rather than when the device activated, in contrast to - :attr:`active_time`. If the device is not currently held, this is - ``None``. + :attr:`~EventsMixin.active_time`. If the device is not currently held, + this is ``None``. """ if self._held_from is not None: return time() - self._held_from diff --git a/gpiozero/output_devices.py b/gpiozero/output_devices.py index 8964bdf..037f749 100644 --- a/gpiozero/output_devices.py +++ b/gpiozero/output_devices.py @@ -19,8 +19,8 @@ class OutputDevice(SourceMixin, GPIODevice): Represents a generic GPIO output device. This class extends :class:`GPIODevice` to add facilities common to GPIO - output devices: an :meth:`on` method to switch the device on, and a - corresponding :meth:`off` method. + output devices: an :meth:`on` method to switch the device on, a + corresponding :meth:`off` method, and a :meth:`toggle` method. :param int pin: The GPIO pin (in BCM numbering) that the device is connected to. If @@ -123,10 +123,9 @@ class DigitalOutputDevice(OutputDevice): """ Represents a generic output device with typical on/off behaviour. - This class extends :class:`OutputDevice` with a :meth:`toggle` method to - switch the device between its on and off states, and a :meth:`blink` method - which uses an optional background thread to handle toggling the device - state without further interaction. + This class extends :class:`OutputDevice` with a :meth:`blink` method which + uses an optional background thread to handle toggling the device state + without further interaction. """ def __init__(self, pin=None, active_high=True, initial_value=False): self._blink_thread = None @@ -458,7 +457,7 @@ class PWMOutputDevice(OutputDevice): self._blink_thread = None def _blink_device( - self, on_time, off_time, fade_in_time, fade_out_time, n, fps=50): + self, on_time, off_time, fade_in_time, fade_out_time, n, fps=25): sequence = [] if fade_in_time > 0: sequence += [ @@ -686,7 +685,7 @@ class RGBLED(SourceMixin, Device): def _blink_device( self, on_time, off_time, fade_in_time, fade_out_time, on_color, - off_color, n, fps=50): + off_color, n, fps=25): # Define some simple lambdas to perform linear interpolation between # off_color and on_color lerp = lambda t, fade_in: tuple( diff --git a/gpiozero/pins/mock.py b/gpiozero/pins/mock.py index e98180a..101ae7d 100644 --- a/gpiozero/pins/mock.py +++ b/gpiozero/pins/mock.py @@ -159,7 +159,7 @@ class MockPin(Pin): # that's about all we can reasonably expect in a non-realtime # environment on a Pi 1) for actual, expected in zip(self.states, expected_states): - assert isclose(actual.timestamp, expected[0], rel_tol=0.01, abs_tol=0.01) + assert isclose(actual.timestamp, expected[0], rel_tol=0.05, abs_tol=0.05) assert isclose(actual.state, expected[1]) diff --git a/gpiozero/source_tools.py b/gpiozero/tools.py similarity index 100% rename from gpiozero/source_tools.py rename to gpiozero/tools.py diff --git a/tests/test_boards.py b/tests/test_boards.py index c32052f..304e128 100644 --- a/tests/test_boards.py +++ b/tests/test_boards.py @@ -31,84 +31,84 @@ def test_composite_output_on_off(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) - device.on() - assert all((pin1.state, pin2.state, pin3.state)) - device.off() - assert not any((pin1.state, pin2.state, pin3.state)) + with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: + device.on() + assert all((pin1.state, pin2.state, pin3.state)) + device.off() + assert not any((pin1.state, pin2.state, pin3.state)) def test_composite_output_toggle(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) - device.toggle() - assert all((pin1.state, pin2.state, pin3.state)) - device[0].off() - device.toggle() - assert pin1.state - assert not pin2.state - assert not pin3.state + with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: + device.toggle() + assert all((pin1.state, pin2.state, pin3.state)) + device[0].off() + device.toggle() + assert pin1.state + assert not pin2.state + assert not pin3.state def test_composite_output_value(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - device = CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) - assert device.value == (0, 0, 0) - device.toggle() - assert device.value == (1, 1, 1) - device.value = (1, 0, 1) - assert device[0].is_active - assert not device[1].is_active - assert device[2].is_active + with CompositeOutputDevice(OutputDevice(pin1), OutputDevice(pin2), foo=OutputDevice(pin3)) as device: + assert device.value == (0, 0, 0) + device.toggle() + assert device.value == (1, 1, 1) + device.value = (1, 0, 1) + assert device[0].is_active + assert not device[1].is_active + assert device[2].is_active def test_led_board_on_off(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, pin2, foo=pin3) - assert isinstance(board[0], LED) - assert isinstance(board[1], LED) - assert isinstance(board[2], LED) - board.on() - assert all((pin1.state, pin2.state, pin3.state)) - board.off() - assert not any((pin1.state, pin2.state, pin3.state)) - board[0].on() - assert board.value == (1, 0, 0) - assert pin1.state - assert not pin2.state - assert not pin3.state - board.toggle() - assert board.value == (0, 1, 1) - assert not pin1.state - assert pin2.state - assert pin3.state + with LEDBoard(pin1, pin2, foo=pin3) as board: + assert isinstance(board[0], LED) + assert isinstance(board[1], LED) + assert isinstance(board[2], LED) + board.on() + assert all((pin1.state, pin2.state, pin3.state)) + board.off() + assert not any((pin1.state, pin2.state, pin3.state)) + board[0].on() + assert board.value == (1, 0, 0) + assert pin1.state + assert not pin2.state + assert not pin3.state + board.toggle() + assert board.value == (0, 1, 1) + assert not pin1.state + assert pin2.state + assert pin3.state def test_led_board_nested(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - assert list(led.pin for led in board.leds) == [pin1, pin2, pin3] - assert board.value == (0, (0, 0)) - board.value = (1, (0, 1)) - assert pin1.state - assert not pin2.state - assert pin3.state + with LEDBoard(pin1, LEDBoard(pin2, pin3)) 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)) + assert pin1.state + assert not pin2.state + assert pin3.state def test_led_board_bad_blink(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - with pytest.raises(ValueError): - board.blink(fade_in_time=1, fade_out_time=1) - with pytest.raises(ValueError): - board.blink(fade_out_time=1) - with pytest.raises(ValueError): - board.pulse() + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + with pytest.raises(ValueError): + board.blink(fade_in_time=1, fade_out_time=1) + with pytest.raises(ValueError): + board.blink(fade_out_time=1) + with pytest.raises(ValueError): + board.pulse() @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -116,19 +116,19 @@ def test_led_board_blink_background(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(0.1, 0.1, n=2) - board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test - test = [ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ] - pin1.assert_states_and_times(test) - pin2.assert_states_and_times(test) - pin3.assert_states_and_times(test) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(0.1, 0.1, n=2) + board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test + test = [ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ] + pin1.assert_states_and_times(test) + pin2.assert_states_and_times(test) + pin3.assert_states_and_times(test) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -136,18 +136,18 @@ def test_led_board_blink_foreground(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(0.1, 0.1, n=2, background=False) - test = [ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ] - pin1.assert_states_and_times(test) - pin2.assert_states_and_times(test) - pin3.assert_states_and_times(test) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(0.1, 0.1, n=2, background=False) + test = [ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ] + pin1.assert_states_and_times(test) + pin2.assert_states_and_times(test) + pin3.assert_states_and_times(test) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -155,24 +155,24 @@ def test_led_board_blink_control(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(0.1, 0.1, n=2) - # make sure the blink thread's started - while not board._blink_leds: - sleep(0.00001) - board[1][0].off() # immediately take over the second LED - board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test - test = [ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ] - pin1.assert_states_and_times(test) - pin3.assert_states_and_times(test) - print(pin2.states) - pin2.assert_states_and_times([(0.0, False), (0.0, True), (0.0, False)]) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(0.1, 0.1, n=2) + # make sure the blink thread's started + while not board._blink_leds: + sleep(0.00001) + board[1][0].off() # immediately take over the second LED + board._blink_thread.join() # naughty, but ensures no arbitrary waits in the test + test = [ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ] + pin1.assert_states_and_times(test) + pin3.assert_states_and_times(test) + print(pin2.states) + pin2.assert_states_and_times([(0.0, False), (0.0, True), (0.0, False)]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -180,21 +180,21 @@ def test_led_board_blink_take_over(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board[1].blink(0.1, 0.1, n=2) - board.blink(0.1, 0.1, n=2) # immediately take over blinking - board[1]._blink_thread.join() - board._blink_thread.join() - test = [ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ] - pin1.assert_states_and_times(test) - pin2.assert_states_and_times(test) - pin3.assert_states_and_times(test) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board[1].blink(0.1, 0.1, n=2) + board.blink(0.1, 0.1, n=2) # immediately take over blinking + board[1]._blink_thread.join() + board._blink_thread.join() + test = [ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ] + pin1.assert_states_and_times(test) + pin2.assert_states_and_times(test) + pin3.assert_states_and_times(test) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -202,47 +202,47 @@ def test_led_board_blink_control_all(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(0.1, 0.1, n=2) - # make sure the blink thread's started - while not board._blink_leds: - sleep(0.00001) - board[0].off() # immediately take over all LEDs - board[1][0].off() - board[1][1].off() - board._blink_thread.join() # blink should terminate here anyway - test = [ - (0.0, False), - (0.0, True), - (0.0, False), - ] - pin1.assert_states_and_times(test) - pin2.assert_states_and_times(test) - pin3.assert_states_and_times(test) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(0.1, 0.1, n=2) + # make sure the blink thread's started + while not board._blink_leds: + sleep(0.00001) + board[0].off() # immediately take over all LEDs + board[1][0].off() + board[1][1].off() + board._blink_thread.join() # blink should terminate here anyway + test = [ + (0.0, False), + (0.0, True), + (0.0, False), + ] + pin1.assert_states_and_times(test) + pin2.assert_states_and_times(test) + pin3.assert_states_and_times(test) def test_led_board_blink_interrupt_on(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(1, 0.1) - sleep(0.2) - board.off() # should interrupt while on - pin1.assert_states([False, True, False]) - pin2.assert_states([False, True, False]) - pin3.assert_states([False, True, False]) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(1, 0.1) + sleep(0.2) + board.off() # should interrupt while on + pin1.assert_states([False, True, False]) + pin2.assert_states([False, True, False]) + pin3.assert_states([False, True, False]) def test_led_board_blink_interrupt_off(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3)) - board.blink(0.1, 1) - sleep(0.2) - board.off() # should interrupt while off - pin1.assert_states([False, True, False]) - pin2.assert_states([False, True, False]) - pin3.assert_states([False, True, False]) + with LEDBoard(pin1, LEDBoard(pin2, pin3)) as board: + board.blink(0.1, 1) + sleep(0.2) + board.off() # should interrupt while off + pin1.assert_states([False, True, False]) + pin2.assert_states([False, True, False]) + pin3.assert_states([False, True, False]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -250,76 +250,76 @@ def test_led_board_fade_background(): pin1 = MockPWMPin(2) pin2 = MockPWMPin(3) pin3 = MockPWMPin(4) - board = LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) - board.blink(0, 0, 0.1, 0.1, n=2) - board._blink_thread.join() - test = [ - (0.0, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - ] - pin1.assert_states_and_times(test) - pin2.assert_states_and_times(test) - pin3.assert_states_and_times(test) + with LEDBoard(pin1, LEDBoard(pin2, pin3, pwm=True), pwm=True) as board: + board.blink(0, 0, 0.2, 0.2, n=2) + board._blink_thread.join() + test = [ + (0.0, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + ] + pin1.assert_states_and_times(test) + pin2.assert_states_and_times(test) + pin3.assert_states_and_times(test) def test_led_bar_graph_value(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) - graph = LEDBarGraph(pin1, pin2, pin3) - graph.value = 0 - assert not any((pin1.state, pin2.state, pin3.state)) - graph.value = 1 - assert all((pin1.state, pin2.state, pin3.state)) - graph.value = 1/3 - assert pin1.state and not (pin2.state or pin3.state) - graph.value = -1/3 - assert pin3.state and not (pin1.state or pin2.state) - pin1.state = True - pin2.state = True - assert graph.value == 1 - pin3.state = False - assert graph.value == 2/3 - pin3.state = True - pin1.state = False - assert graph.value == -2/3 + with LEDBarGraph(pin1, pin2, pin3) as graph: + graph.value = 0 + assert not any((pin1.state, pin2.state, pin3.state)) + graph.value = 1 + assert all((pin1.state, pin2.state, pin3.state)) + graph.value = 1/3 + assert pin1.state and not (pin2.state or pin3.state) + graph.value = -1/3 + assert pin3.state and not (pin1.state or pin2.state) + pin1.state = True + pin2.state = True + assert graph.value == 1 + pin3.state = False + assert graph.value == 2/3 + pin3.state = True + pin1.state = False + assert graph.value == -2/3 def test_led_bar_graph_pwm_value(): pin1 = MockPWMPin(2) pin2 = MockPWMPin(3) pin3 = MockPWMPin(4) - graph = LEDBarGraph(pin1, pin2, pin3, pwm=True) - graph.value = 0 - assert not any((pin1.state, pin2.state, pin3.state)) - graph.value = 1 - assert all((pin1.state, pin2.state, pin3.state)) - graph.value = 1/3 - assert pin1.state and not (pin2.state or pin3.state) - graph.value = -1/3 - assert pin3.state and not (pin1.state or pin2.state) - graph.value = 1/2 - assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) - pin1.state = 0 - pin3.state = 1 - assert graph.value == -1/2 + with LEDBarGraph(pin1, pin2, pin3, pwm=True) as graph: + graph.value = 0 + assert not any((pin1.state, pin2.state, pin3.state)) + graph.value = 1 + assert all((pin1.state, pin2.state, pin3.state)) + graph.value = 1/3 + assert pin1.state and not (pin2.state or pin3.state) + graph.value = -1/3 + assert pin3.state and not (pin1.state or pin2.state) + graph.value = 1/2 + assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) + pin1.state = 0 + pin3.state = 1 + assert graph.value == -1/2 def test_led_bar_graph_bad_init(): pin1 = MockPin(2) @@ -330,26 +330,26 @@ def test_led_bar_graph_bad_init(): def test_pi_liter(): pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)] - board = PiLiter() - assert [device.pin for device in board] == pins + with PiLiter() as board: + assert [device.pin for device in board] == pins def test_pi_liter_graph(): pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)] - board = PiLiterBarGraph() - board.value = 0.5 - assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0] - pins[4].state = 1 - assert board.value == 5/8 + with PiLiterBarGraph() as board: + board.value = 0.5 + assert [pin.state for pin in pins] == [1, 1, 1, 1, 0, 0, 0, 0] + pins[4].state = 1 + assert board.value == 5/8 def test_traffic_lights(): red_pin = MockPin(2) amber_pin = MockPin(3) green_pin = MockPin(4) - board = TrafficLights(red_pin, amber_pin, green_pin) - board.red.on() - assert red_pin.state - assert not amber_pin.state - assert not green_pin.state + with TrafficLights(red_pin, amber_pin, green_pin) as board: + board.red.on() + assert red_pin.state + assert not amber_pin.state + assert not green_pin.state def test_traffic_lights_bad_init(): with pytest.raises(ValueError): @@ -357,13 +357,13 @@ def test_traffic_lights_bad_init(): def test_pi_traffic(): pins = [MockPin(n) for n in (9, 10, 11)] - board = PiTraffic() - assert [device.pin for device in board] == pins + with PiTraffic() as board: + assert [device.pin for device in board] == pins def test_snow_pi(): pins = [MockPin(n) for n in (23, 24, 25, 17, 18, 22, 7, 8, 9)] - board = SnowPi() - assert [device.pin for device in board.leds] == pins + with SnowPi() as board: + assert [device.pin for device in board.leds] == pins def test_traffic_lights_buzzer(): red_pin = MockPin(2) @@ -371,59 +371,59 @@ def test_traffic_lights_buzzer(): green_pin = MockPin(4) buzzer_pin = MockPin(5) button_pin = MockPin(6) - board = TrafficLightsBuzzer( + with TrafficLightsBuzzer( TrafficLights(red_pin, amber_pin, green_pin), Buzzer(buzzer_pin), - Button(button_pin)) - board.lights.red.on() - board.buzzer.on() - assert red_pin.state - assert not amber_pin.state - assert not green_pin.state - assert buzzer_pin.state - button_pin.drive_low() - assert board.button.is_active + Button(button_pin)) as board: + board.lights.red.on() + board.buzzer.on() + assert red_pin.state + assert not amber_pin.state + assert not green_pin.state + assert buzzer_pin.state + button_pin.drive_low() + assert board.button.is_active def test_fish_dish(): pins = [MockPin(n) for n in (9, 22, 4, 8, 7)] - board = FishDish() - assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins + with FishDish() as board: + assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins def test_traffic_hat(): pins = [MockPin(n) for n in (24, 23, 22, 5, 25)] - board = TrafficHat() - assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins + with TrafficHat() as board: + assert [led.pin for led in board.lights] + [board.buzzer.pin, board.button.pin] == pins def test_robot(): pins = [MockPWMPin(n) for n in (2, 3, 4, 5)] - robot = Robot((2, 3), (4, 5)) - assert ( - [device.pin for device in robot.left_motor] + - [device.pin for device in robot.right_motor]) == pins - robot.forward() - assert [pin.state for pin in pins] == [1, 0, 1, 0] - robot.backward() - assert [pin.state for pin in pins] == [0, 1, 0, 1] - robot.forward(0.5) - assert [pin.state for pin in pins] == [0.5, 0, 0.5, 0] - robot.left() - assert [pin.state for pin in pins] == [0, 1, 1, 0] - robot.right() - assert [pin.state for pin in pins] == [1, 0, 0, 1] - robot.reverse() - assert [pin.state for pin in pins] == [0, 1, 1, 0] - robot.stop() - assert [pin.state for pin in pins] == [0, 0, 0, 0] + with Robot((2, 3), (4, 5)) as robot: + assert ( + [device.pin for device in robot.left_motor] + + [device.pin for device in robot.right_motor]) == pins + robot.forward() + assert [pin.state for pin in pins] == [1, 0, 1, 0] + robot.backward() + assert [pin.state for pin in pins] == [0, 1, 0, 1] + robot.forward(0.5) + assert [pin.state for pin in pins] == [0.5, 0, 0.5, 0] + robot.left() + assert [pin.state for pin in pins] == [0, 1, 1, 0] + robot.right() + assert [pin.state for pin in pins] == [1, 0, 0, 1] + robot.reverse() + assert [pin.state for pin in pins] == [0, 1, 1, 0] + robot.stop() + assert [pin.state for pin in pins] == [0, 0, 0, 0] def test_ryanteck_robot(): pins = [MockPWMPin(n) for n in (17, 18, 22, 23)] - board = RyanteckRobot() - assert [device.pin for motor in board for device in motor] == pins + with RyanteckRobot() as board: + assert [device.pin for motor in board for device in motor] == pins def test_camjam_kit_robot(): pins = [MockPWMPin(n) for n in (9, 10, 7, 8)] - board = CamJamKitRobot() - assert [device.pin for motor in board for device in motor] == pins + with CamJamKitRobot() as board: + assert [device.pin for motor in board for device in motor] == pins def test_energenie_bad_init(): with pytest.raises(ValueError): @@ -433,26 +433,26 @@ def test_energenie_bad_init(): def test_energenie(): pins = [MockPin(n) for n in (17, 22, 23, 27, 24, 25)] - device1 = Energenie(1, initial_value=True) - device2 = Energenie(2, initial_value=False) - assert device1.value - assert not device2.value - [pin.clear_states() for pin in pins] - device1.on() - assert device1.value - pins[0].assert_states_and_times([(0.0, False), (0.0, True)]) - pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) - pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) - pins[3].assert_states_and_times([(0.0, False), (0.0, True)]) - pins[4].assert_states_and_times([(0.0, False)]) - pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) - [pin.clear_states() for pin in pins] - device2.on() - assert device2.value - pins[0].assert_states_and_times([(0.0, True), (0.0, False)]) - pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) - pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) - pins[3].assert_states_and_times([(0.0, True), (0.0, True)]) - pins[4].assert_states_and_times([(0.0, False)]) - pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) + with Energenie(1, initial_value=True) as device1, \ + Energenie(2, initial_value=False) as device2: + assert device1.value + assert not device2.value + [pin.clear_states() for pin in pins] + device1.on() + assert device1.value + pins[0].assert_states_and_times([(0.0, False), (0.0, True)]) + pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) + pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) + pins[3].assert_states_and_times([(0.0, False), (0.0, True)]) + pins[4].assert_states_and_times([(0.0, False)]) + pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) + [pin.clear_states() for pin in pins] + device2.on() + assert device2.value + pins[0].assert_states_and_times([(0.0, True), (0.0, False)]) + pins[1].assert_states_and_times([(0.0, True), (0.0, True)]) + pins[2].assert_states_and_times([(0.0, True), (0.0, True)]) + pins[3].assert_states_and_times([(0.0, True), (0.0, True)]) + pins[4].assert_states_and_times([(0.0, False)]) + pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) diff --git a/tests/test_devices.py b/tests/test_devices.py index 1db7fab..79ae98a 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -24,21 +24,22 @@ def test_device_no_pin(): def test_device_init(): pin = MockPin(2) - device = GPIODevice(pin) - assert not device.closed - assert device.pin == pin + with GPIODevice(pin) as device: + assert not device.closed + assert device.pin == pin def test_device_init_twice_same_pin(): pin = MockPin(2) - device = GPIODevice(pin) - with pytest.raises(GPIOPinInUse): - device2 = GPIODevice(pin) + with GPIODevice(pin) as device: + with pytest.raises(GPIOPinInUse): + device2 = GPIODevice(pin) def test_device_init_twice_different_pin(): pin = MockPin(2) - device = GPIODevice(pin) pin2 = MockPin(3) - device2 = GPIODevice(pin2) + with GPIODevice(pin) as device: + with GPIODevice(pin2) as device2: + pass def test_device_close(): pin = MockPin(2) @@ -56,11 +57,12 @@ def test_device_reopen_same_pin(): assert device2.pin == pin assert device.closed assert device.pin is None + device2.close() def test_device_repr(): pin = MockPin(2) - device = GPIODevice(pin) - assert repr(device) == '' % pin + with GPIODevice(pin) as device: + assert repr(device) == '' % pin def test_device_repr_after_close(): pin = MockPin(2) @@ -70,9 +72,9 @@ def test_device_repr_after_close(): def test_device_unknown_attr(): pin = MockPin(2) - device = GPIODevice(pin) - with pytest.raises(AttributeError): - device.foo = 1 + with GPIODevice(pin) as device: + with pytest.raises(AttributeError): + device.foo = 1 def test_device_context_manager(): pin = MockPin(2) @@ -81,35 +83,35 @@ def test_device_context_manager(): assert device.closed def test_composite_device_sequence(): - device = CompositeDevice( - InputDevice(MockPin(2)), - InputDevice(MockPin(3)) - ) - assert len(device) == 2 - assert device[0].pin.number == 2 - assert device[1].pin.number == 3 - assert device.tuple._fields == ('_0', '_1') + with CompositeDevice( + InputDevice(MockPin(2)), + InputDevice(MockPin(3)) + ) as device: + assert len(device) == 2 + assert device[0].pin.number == 2 + assert device[1].pin.number == 3 + assert device.tuple._fields == ('_0', '_1') def test_composite_device_values(): - device = CompositeDevice( - InputDevice(MockPin(2)), - InputDevice(MockPin(3)) - ) - assert device.value == (0, 0) - assert not device.is_active - device[0].pin.drive_high() - assert device.value == (1, 0) - assert device.is_active + with CompositeDevice( + InputDevice(MockPin(2)), + InputDevice(MockPin(3)) + ) as device: + assert device.value == (0, 0) + assert not device.is_active + device[0].pin.drive_high() + assert device.value == (1, 0) + assert device.is_active def test_composite_device_named(): - device = CompositeDevice( - foo=InputDevice(MockPin(2)), - bar=InputDevice(MockPin(3)), - _order=('foo', 'bar') - ) - assert device.tuple._fields == ('foo', 'bar') - assert device.value == (0, 0) - assert not device.is_active + with CompositeDevice( + foo=InputDevice(MockPin(2)), + bar=InputDevice(MockPin(3)), + _order=('foo', 'bar') + ) as device: + assert device.tuple._fields == ('foo', 'bar') + assert device.value == (0, 0) + assert not device.is_active def test_composite_device_bad_init(): with pytest.raises(ValueError): diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 1e92879..ee1d62a 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -25,131 +25,121 @@ def teardown_function(function): 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() + with InputDevice(pin, pull_up=True) as device: + 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 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 + with InputDevice(pin, pull_up=True) as device: + 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) + 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) + with DigitalInputDevice(pin) as device: + 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) + with DigitalInputDevice(pin) as device: + 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) + with DigitalInputDevice(pin) as device: + 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) + with DigitalInputDevice(pin) as device: + 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() + with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device: + 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_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) + with SmoothedInputDevice(pin) as device: + 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) + with Button(pin) as button: + 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 + with LineSensor(pin) as sensor: + 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 + with MotionSensor(pin) as sensor: + 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(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_input_light_sensor(): pin = MockChargingPin(2) - sensor = LightSensor(pin) - pin.charge_time = 0.1 - assert sensor.wait_for_dark(1) - pin.charge_time = 0.0 - assert sensor.wait_for_light(1) + with LightSensor(pin) as sensor: + pin.charge_time = 0.1 + assert sensor.wait_for_dark(1) + pin.charge_time = 0.0 + assert sensor.wait_for_light(1) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') @@ -162,22 +152,22 @@ def test_input_distance_sensor(): DistanceSensor(echo_pin, trig_pin, 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 :) - sensor = DistanceSensor(echo_pin, trig_pin, queue_len=5, max_distance=1) - assert sensor.max_distance == 1 - assert sensor.trigger is trig_pin - assert sensor.echo is echo_pin - assert sensor.wait_for_out_of_range(1) - assert not sensor.in_range - assert sensor.distance == 1.0 # should be waay before max-distance so this should work - trig_pin.echo_time = 0.0 - assert sensor.wait_for_in_range(1) - assert sensor.in_range - assert sensor.distance < sensor.threshold_distance # depending on speed of machine, may not reach 0 here - sensor.threshold_distance = 0.1 - assert sensor.threshold_distance == 0.1 - with pytest.raises(ValueError): - sensor.max_distance = -1 - sensor.max_distance = 20 - assert sensor.max_distance == 20 - assert sensor.threshold_distance == 0.1 + with DistanceSensor(echo_pin, trig_pin, 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 + assert sensor.wait_for_out_of_range(1) + assert not sensor.in_range + assert sensor.distance == 1.0 # should be waay before max-distance so this should work + trig_pin.echo_time = 0.0 + assert sensor.wait_for_in_range(1) + assert sensor.in_range + assert sensor.distance < sensor.threshold_distance # depending on speed of machine, may not reach 0 here + sensor.threshold_distance = 0.1 + assert sensor.threshold_distance == 0.1 + with pytest.raises(ValueError): + sensor.max_distance = -1 + sensor.max_distance = 20 + assert sensor.max_distance == 20 + assert sensor.threshold_distance == 0.1 diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 0d120bf..27bc97d 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -25,114 +25,112 @@ def teardown_function(function): def test_output_initial_values(): pin = MockPin(2) - device = OutputDevice(pin, initial_value=False) - assert pin.function == 'output' - assert not pin.state - device.close() - device = OutputDevice(pin, initial_value=True) - assert pin.state - device.close() - state = pin.state - device = OutputDevice(pin, initial_value=None) - assert state == pin.state + with OutputDevice(pin, initial_value=False) as device: + assert pin.function == 'output' + assert not pin.state + with OutputDevice(pin, initial_value=True) as device: + assert pin.state + state = pin.state + with OutputDevice(pin, initial_value=None) as device: + assert state == pin.state def test_output_write_active_high(): pin = MockPin(2) - device = OutputDevice(pin) - device.on() - assert pin.state - device.off() - assert not pin.state + with OutputDevice(pin) as device: + device.on() + assert pin.state + device.off() + assert not pin.state def test_output_write_active_low(): pin = MockPin(2) - device = OutputDevice(pin, active_high=False) - device.on() - assert not pin.state - device.off() - assert pin.state + with OutputDevice(pin, active_high=False) as device: + device.on() + assert not pin.state + device.off() + assert pin.state def test_output_write_closed(): - device = OutputDevice(MockPin(2)) - device.close() - with pytest.raises(GPIODeviceClosed): - device.on() + with OutputDevice(MockPin(2)) as device: + device.close() + with pytest.raises(GPIODeviceClosed): + device.on() def test_output_write_silly(): pin = MockPin(2) - device = OutputDevice(pin) - pin.function = 'input' - with pytest.raises(AttributeError): - device.on() + with OutputDevice(pin) as device: + pin.function = 'input' + with pytest.raises(AttributeError): + device.on() def test_output_value(): pin = MockPin(2) - device = OutputDevice(pin) - assert not device.value - assert not pin.state - device.on() - assert device.value - assert pin.state - device.value = False - assert not device.value - assert not pin.state + with OutputDevice(pin) as device: + assert not device.value + assert not pin.state + device.on() + assert device.value + assert pin.state + device.value = False + assert not device.value + assert not pin.state def test_output_digital_toggle(): pin = MockPin(2) - device = DigitalOutputDevice(pin) - assert not device.value - assert not pin.state - device.toggle() - assert device.value - assert pin.state - device.toggle() - assert not device.value - assert not pin.state + with DigitalOutputDevice(pin) as device: + assert not device.value + assert not pin.state + device.toggle() + assert device.value + assert pin.state + device.toggle() + assert not device.value + assert not pin.state @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_blink_background(): pin = MockPin(2) - device = DigitalOutputDevice(pin) - device.blink(0.1, 0.1, n=2) - device._blink_thread.join() # naughty, but ensures no arbitrary waits in the test - pin.assert_states_and_times([ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ]) + with DigitalOutputDevice(pin) as device: + device.blink(0.1, 0.1, n=2) + device._blink_thread.join() # naughty, but ensures no arbitrary waits in the test + pin.assert_states_and_times([ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_blink_foreground(): pin = MockPin(2) - device = DigitalOutputDevice(pin) - device.blink(0.1, 0.1, n=2, background=False) - pin.assert_states_and_times([ - (0.0, False), - (0.0, True), - (0.1, False), - (0.1, True), - (0.1, False) - ]) + with DigitalOutputDevice(pin) as device: + device.blink(0.1, 0.1, n=2, background=False) + pin.assert_states_and_times([ + (0.0, False), + (0.0, True), + (0.1, False), + (0.1, True), + (0.1, False) + ]) def test_output_blink_interrupt_on(): pin = MockPin(2) - device = DigitalOutputDevice(pin) - device.blink(1, 0.1) - sleep(0.2) - device.off() # should interrupt while on - pin.assert_states([False, True, False]) + with DigitalOutputDevice(pin) as device: + device.blink(1, 0.1) + sleep(0.2) + device.off() # should interrupt while on + pin.assert_states([False, True, False]) def test_output_blink_interrupt_off(): pin = MockPin(2) - device = DigitalOutputDevice(pin) - device.blink(0.1, 1) - sleep(0.2) - device.off() # should interrupt while off - pin.assert_states([False, True, False]) + with DigitalOutputDevice(pin) as device: + device.blink(0.1, 1) + sleep(0.2) + device.off() # should interrupt while off + pin.assert_states([False, True, False]) def test_output_pwm_bad_initial_value(): with pytest.raises(ValueError): @@ -144,50 +142,50 @@ def test_output_pwm_not_supported(): def test_output_pwm_states(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.value = 0.1 - device.value = 0.2 - device.value = 0.0 - pin.assert_states([0.0, 0.1, 0.2, 0.0]) + with PWMOutputDevice(pin) 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 = MockPWMPin(2) - device = PWMOutputDevice(pin, frequency=100) - assert device.frequency == 100 - device.value = 0.1 - assert isclose(device.value, 0.1) - assert isclose(pin.state, 0.1) - assert device.is_active - device.frequency = None - assert not device.value - assert not device.is_active - assert device.frequency is None + with PWMOutputDevice(pin, frequency=100) as device: + assert device.frequency == 100 + device.value = 0.1 + assert isclose(device.value, 0.1) + assert isclose(pin.state, 0.1) + assert device.is_active + device.frequency = None + assert not device.value + assert not device.is_active + assert device.frequency is None def test_output_pwm_write(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.on() - device.off() - pin.assert_states([False, True, False]) + with PWMOutputDevice(pin) as device: + device.on() + device.off() + pin.assert_states([False, True, False]) def test_output_pwm_toggle(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.toggle() - device.value = 0.5 - device.value = 0.1 - device.toggle() - device.off() - pin.assert_states([False, True, 0.5, 0.1, 0.9, False]) + with PWMOutputDevice(pin) as device: + device.toggle() + device.value = 0.5 + device.value = 0.1 + device.toggle() + device.off() + pin.assert_states([False, True, 0.5, 0.1, 0.9, False]) def test_output_pwm_active_high_read(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin, active_high=False) - device.value = 0.1 - assert isclose(device.value, 0.1) - assert isclose(pin.state, 0.9) - device.frequency = None - assert device.value + with PWMOutputDevice(pin, active_high=False) as device: + device.value = 0.1 + assert isclose(device.value, 0.1) + assert isclose(pin.state, 0.9) + device.frequency = None + assert device.value def test_output_pwm_bad_value(): with pytest.raises(ValueError): @@ -201,108 +199,108 @@ def test_output_pwm_write_closed(): def test_output_pwm_write_silly(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - pin.function = 'input' - with pytest.raises(AttributeError): - device.off() + with PWMOutputDevice(pin) as device: + pin.function = 'input' + with pytest.raises(AttributeError): + device.off() @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_pwm_blink_background(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.blink(0.1, 0.1, n=2) - device._blink_thread.join() - pin.assert_states_and_times([ - (0.0, 0), - (0.0, 1), - (0.1, 0), - (0.1, 1), - (0.1, 0) - ]) + with PWMOutputDevice(pin) as device: + device.blink(0.1, 0.1, n=2) + device._blink_thread.join() + pin.assert_states_and_times([ + (0.0, 0), + (0.0, 1), + (0.1, 0), + (0.1, 1), + (0.1, 0) + ]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_pwm_blink_foreground(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.blink(0.1, 0.1, n=2, background=False) - pin.assert_states_and_times([ - (0.0, 0), - (0.0, 1), - (0.1, 0), - (0.1, 1), - (0.1, 0) - ]) + with PWMOutputDevice(pin) as device: + device.blink(0.1, 0.1, n=2, background=False) + pin.assert_states_and_times([ + (0.0, 0), + (0.0, 1), + (0.1, 0), + (0.1, 1), + (0.1, 0) + ]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_pwm_fade_background(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.blink(0, 0, 0.1, 0.1, n=2) - device._blink_thread.join() - pin.assert_states_and_times([ - (0.0, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - ]) + with PWMOutputDevice(pin) as device: + device.blink(0, 0, 0.2, 0.2, n=2) + device._blink_thread.join() + pin.assert_states_and_times([ + (0.0, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + ]) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_output_pwm_fade_foreground(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.blink(0, 0, 0.1, 0.1, n=2, background=False) - pin.assert_states_and_times([ - (0.0, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - ]) + with PWMOutputDevice(pin) as device: + device.blink(0, 0, 0.2, 0.2, n=2, background=False) + pin.assert_states_and_times([ + (0.0, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + ]) def test_output_pwm_blink_interrupt(): pin = MockPWMPin(2) - device = PWMOutputDevice(pin) - device.blink(1, 0.1) - sleep(0.2) - device.off() # should interrupt while on - pin.assert_states([0, 1, 0]) + with PWMOutputDevice(pin) as device: + device.blink(1, 0.1) + sleep(0.2) + device.off() # should interrupt while on + pin.assert_states([0, 1, 0]) def test_rgbled_missing_pins(): with pytest.raises(ValueError): @@ -310,116 +308,116 @@ def test_rgbled_missing_pins(): def test_rgbled_initial_value(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) - assert r.frequency - assert g.frequency - assert b.frequency - assert isclose(r.state, 0.1) - assert isclose(g.state, 0.2) - assert isclose(b.state, 0.0) + with RGBLED(r, g, b, initial_value=(0.1, 0.2, 0)) as device: + assert r.frequency + assert g.frequency + assert b.frequency + assert isclose(r.state, 0.1) + assert isclose(g.state, 0.2) + assert isclose(b.state, 0.0) def test_rgbled_value(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - assert not device.is_active - assert device.value == (0, 0, 0) - device.on() - assert device.is_active - assert device.value == (1, 1, 1) - device.off() - assert not device.is_active - assert device.value == (0, 0, 0) + with RGBLED(r, g, b) as device: + assert not device.is_active + assert device.value == (0, 0, 0) + device.on() + assert device.is_active + assert device.value == (1, 1, 1) + device.off() + assert not device.is_active + assert device.value == (0, 0, 0) def test_rgbled_toggle(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - assert not device.is_active - assert device.value == (0, 0, 0) - device.toggle() - assert device.is_active - assert device.value == (1, 1, 1) - device.toggle() - assert not device.is_active - assert device.value == (0, 0, 0) + with RGBLED(r, g, b) as device: + assert not device.is_active + assert device.value == (0, 0, 0) + device.toggle() + assert device.is_active + assert device.value == (1, 1, 1) + device.toggle() + assert not device.is_active + assert device.value == (0, 0, 0) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_rgbled_blink_background(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - device.blink(0.1, 0.1, n=2) - device._blink_thread.join() - expected = [ - (0.0, 0), - (0.0, 1), - (0.1, 0), - (0.1, 1), - (0.1, 0) - ] - r.assert_states_and_times(expected) - g.assert_states_and_times(expected) - b.assert_states_and_times(expected) + with RGBLED(r, g, b) as device: + device.blink(0.1, 0.1, n=2) + device._blink_thread.join() + expected = [ + (0.0, 0), + (0.0, 1), + (0.1, 0), + (0.1, 1), + (0.1, 0) + ] + r.assert_states_and_times(expected) + g.assert_states_and_times(expected) + b.assert_states_and_times(expected) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_rgbled_blink_foreground(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - device.blink(0.1, 0.1, n=2, background=False) - expected = [ - (0.0, 0), - (0.0, 1), - (0.1, 0), - (0.1, 1), - (0.1, 0) - ] - r.assert_states_and_times(expected) - g.assert_states_and_times(expected) - b.assert_states_and_times(expected) + with RGBLED(r, g, b) as device: + device.blink(0.1, 0.1, n=2, background=False) + expected = [ + (0.0, 0), + (0.0, 1), + (0.1, 0), + (0.1, 1), + (0.1, 0) + ] + r.assert_states_and_times(expected) + g.assert_states_and_times(expected) + b.assert_states_and_times(expected) @pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), reason='timing is too random on pypy') def test_rgbled_fade_background(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - device.blink(0, 0, 0.1, 0.1, n=2) - device._blink_thread.join() - expected = [ - (0.0, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - (0.02, 0.2), - (0.02, 0.4), - (0.02, 0.6), - (0.02, 0.8), - (0.02, 1), - (0.02, 0.8), - (0.02, 0.6), - (0.02, 0.4), - (0.02, 0.2), - (0.02, 0), - ] - r.assert_states_and_times(expected) - g.assert_states_and_times(expected) - b.assert_states_and_times(expected) + with RGBLED(r, g, b) as device: + device.blink(0, 0, 0.2, 0.2, n=2) + device._blink_thread.join() + expected = [ + (0.0, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + (0.04, 0.2), + (0.04, 0.4), + (0.04, 0.6), + (0.04, 0.8), + (0.04, 1), + (0.04, 0.8), + (0.04, 0.6), + (0.04, 0.4), + (0.04, 0.2), + (0.04, 0), + ] + r.assert_states_and_times(expected) + g.assert_states_and_times(expected) + b.assert_states_and_times(expected) def test_output_rgbled_blink_interrupt(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) - device = RGBLED(r, g, b) - device.blink(1, 0.1) - sleep(0.2) - device.off() # should interrupt while on - r.assert_states([0, 1, 0]) - g.assert_states([0, 1, 0]) - b.assert_states([0, 1, 0]) + with RGBLED(r, g, b) as device: + device.blink(1, 0.1) + sleep(0.2) + device.off() # should interrupt while on + r.assert_states([0, 1, 0]) + g.assert_states([0, 1, 0]) + b.assert_states([0, 1, 0]) def test_motor_missing_pins(): with pytest.raises(ValueError): @@ -428,55 +426,55 @@ def test_motor_missing_pins(): def test_motor_pins(): f = MockPWMPin(1) b = MockPWMPin(2) - device = Motor(f, b) - assert device.forward_device.pin is f - assert device.backward_device.pin is b + with Motor(f, b) as device: + assert device.forward_device.pin is f + assert device.backward_device.pin is b def test_motor_close(): f = MockPWMPin(1) b = MockPWMPin(2) - device = Motor(f, b) - device.close() - assert device.closed - assert device.forward_device.pin is None - assert device.backward_device.pin is None + with Motor(f, b) 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 = MockPWMPin(1) b = MockPWMPin(2) - device = Motor(f, b) - device.value = -1 - assert device.is_active - assert device.value == -1 - assert b.state == 1 and f.state == 0 - device.value = 1 - assert device.is_active - assert device.value == 1 - assert b.state == 0 and f.state == 1 - device.value = 0.5 - assert device.is_active - assert device.value == 0.5 - assert b.state == 0 and f.state == 0.5 - device.value = 0 - assert not device.is_active - assert not device.value - assert b.state == 0 and f.state == 0 + with Motor(f, b) as device: + device.value = -1 + assert device.is_active + assert device.value == -1 + assert b.state == 1 and f.state == 0 + device.value = 1 + assert device.is_active + assert device.value == 1 + assert b.state == 0 and f.state == 1 + device.value = 0.5 + assert device.is_active + assert device.value == 0.5 + assert b.state == 0 and f.state == 0.5 + device.value = 0 + assert not device.is_active + assert not device.value + assert b.state == 0 and f.state == 0 def test_motor_bad_value(): f = MockPWMPin(1) b = MockPWMPin(2) - device = Motor(f, b) - with pytest.raises(ValueError): - device.value = 2 + with Motor(f, b) as device: + with pytest.raises(ValueError): + device.value = 2 def test_motor_reverse(): f = MockPWMPin(1) b = MockPWMPin(2) - device = Motor(f, b) - device.forward() - assert device.value == 1 - assert b.state == 0 and f.state == 1 - device.reverse() - assert device.value == -1 - assert b.state == 1 and f.state == 0 + with Motor(f, b) as device: + device.forward() + assert device.value == 1 + assert b.state == 0 and f.state == 1 + device.reverse() + assert device.value == -1 + assert b.state == 1 and f.state == 0 diff --git a/tests/test_source_tools.py b/tests/test_tools.py similarity index 98% rename from tests/test_source_tools.py rename to tests/test_tools.py index 9ba32cb..d95378c 100644 --- a/tests/test_source_tools.py +++ b/tests/test_tools.py @@ -11,7 +11,7 @@ import pytest from math import sin, cos, radians from time import time -from gpiozero import * +from gpiozero.tools import * try: from math import isclose except ImportError: