mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	
							
								
								
									
										11
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| language: python | ||||
| python: | ||||
|     - "3.5" | ||||
|     - "3.4" | ||||
|     - "3.3" | ||||
|     - "3.2" | ||||
|     - "2.7" | ||||
|     - "pypy" | ||||
|     - "pypy3" | ||||
| install: "pip install -e .[test]" | ||||
| script: make test | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -98,7 +98,7 @@ develop: tags | ||||
| 	@# These have to be done separately to avoid a cockup... | ||||
| 	$(PIP) install -U setuptools | ||||
| 	$(PIP) install -U pip | ||||
| 	$(PIP) install -e . | ||||
| 	$(PIP) install -e .[doc,test] | ||||
|  | ||||
| test: | ||||
| 	$(COVERAGE) run -m $(PYTEST) tests -v | ||||
|   | ||||
							
								
								
									
										20
									
								
								coverage.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								coverage.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| [run] | ||||
| branch = True | ||||
| include = gpiozero/* | ||||
| ;omit = */bar.py,*/baz.py | ||||
|  | ||||
| [report] | ||||
| ignore_errors = True | ||||
| show_missing = True | ||||
| exclude_lines = | ||||
|     pragma: no cover | ||||
|     def __repr__ | ||||
|     if self\.debug | ||||
|     raise AssertionError | ||||
|     raise NotImplementedError | ||||
|     if 0: | ||||
|     if __name__ == .__main__.: | ||||
|  | ||||
| [html] | ||||
| directory = coverage | ||||
|  | ||||
							
								
								
									
										80
									
								
								docs/api_exc.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								docs/api_exc.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| ========== | ||||
| Exceptions | ||||
| ========== | ||||
|  | ||||
| .. currentmodule:: gpiozero | ||||
|  | ||||
| The following exceptions are defined by GPIO Zero. Please note that multiple | ||||
| inheritance is heavily used in the exception hierarchy to make testing for | ||||
| exceptions easier. For example, to capture any exception generated by GPIO | ||||
| Zero's code:: | ||||
|  | ||||
|     from gpiozero import * | ||||
|  | ||||
|     led = PWMLED(17) | ||||
|     try: | ||||
|         led.value = 2 | ||||
|     except GPIOZeroError: | ||||
|         print('A GPIO Zero error occurred') | ||||
|  | ||||
| Since all GPIO Zero's exceptions descend from :exc:`GPIOZeroError`, this will | ||||
| work. However, certain specific errors have multiple parents. For example, in | ||||
| the case that an out of range value is passed to :attr:`OutputDevice.value` you | ||||
| would expect a :exc:`ValueError` to be raised. In fact, a | ||||
| :exc:`OutputDeviceBadValue` error will be raised. However, note that this | ||||
| descends from both :exc:`GPIOZeroError` (indirectly) and from :exc:`ValueError` | ||||
| so you can still do:: | ||||
|  | ||||
|     from gpiozero import * | ||||
|  | ||||
|     led = PWMLED(17) | ||||
|     try: | ||||
|         led.value = 2 | ||||
|     except ValueError: | ||||
|         print('Bad value specified') | ||||
|  | ||||
|  | ||||
| .. autoexception:: GPIOZeroError | ||||
|  | ||||
| .. autoexception:: CompositeDeviceError | ||||
|  | ||||
| .. autoexception:: GPIODeviceError | ||||
|  | ||||
| .. autoexception:: GPIODeviceClosed | ||||
|  | ||||
| .. autoexception:: GPIOPinInUse | ||||
|  | ||||
| .. autoexception:: GPIOPinMissing | ||||
|  | ||||
| .. autoexception:: GPIOBadQueueLen | ||||
|  | ||||
| .. autoexception:: InputDeviceError | ||||
|  | ||||
| .. autoexception:: OutputDeviceError | ||||
|  | ||||
| .. autoexception:: OutputDeviceBadValue | ||||
|  | ||||
| .. autoexception:: PinError | ||||
|  | ||||
| .. autoexception:: PinFixedFunction | ||||
|  | ||||
| .. autoexception:: PinInvalidFunction | ||||
|  | ||||
| .. autoexception:: PinInvalidState | ||||
|  | ||||
| .. autoexception:: PinInvalidPull | ||||
|  | ||||
| .. autoexception:: PinInvalidEdges | ||||
|  | ||||
| .. autoexception:: PinSetInput | ||||
|  | ||||
| .. autoexception:: PinFixedPull | ||||
|  | ||||
| .. autoexception:: PinEdgeDetectUnsupported | ||||
|  | ||||
| .. autoexception:: PinPWMError | ||||
|  | ||||
| .. autoexception:: PinPWMUnsupported | ||||
|  | ||||
| .. autoexception:: PinPWMFixedValue | ||||
|  | ||||
| @@ -13,5 +13,6 @@ Table of Contents | ||||
|     api_boards | ||||
|     api_generic | ||||
|     api_pins | ||||
|     api_exc | ||||
|     changelog | ||||
|     license | ||||
|   | ||||
| @@ -5,7 +5,20 @@ from __future__ import ( | ||||
|     division, | ||||
| ) | ||||
|  | ||||
| from .pins.exc import ( | ||||
| from .pins import ( | ||||
|     Pin, | ||||
| ) | ||||
| from .exc import ( | ||||
|     GPIOZeroError, | ||||
|     CompositeDeviceError, | ||||
|     GPIODeviceError, | ||||
|     GPIODeviceClosed, | ||||
|     GPIOPinInUse, | ||||
|     GPIOPinMissing, | ||||
|     GPIOBadQueueLen, | ||||
|     InputDeviceError, | ||||
|     OutputDeviceError, | ||||
|     OutputDeviceBadValue, | ||||
|     PinError, | ||||
|     PinFixedFunction, | ||||
|     PinInvalidFunction, | ||||
| @@ -19,15 +32,6 @@ from .pins.exc import ( | ||||
|     PinPWMUnsupported, | ||||
|     PinPWMFixedValue, | ||||
| ) | ||||
| from .pins import ( | ||||
|     Pin, | ||||
| ) | ||||
| from .exc import ( | ||||
|     GPIODeviceClosed, | ||||
|     GPIODeviceError, | ||||
|     InputDeviceError, | ||||
|     OutputDeviceError, | ||||
| ) | ||||
| from .devices import ( | ||||
|     GPIODevice, | ||||
|     CompositeDevice, | ||||
|   | ||||
							
								
								
									
										27
									
								
								gpiozero/compat.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								gpiozero/compat.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
| import cmath | ||||
|  | ||||
|  | ||||
| # Back-ported from python 3.5; see | ||||
| # github.com/PythonCHB/close_pep/blob/master/is_close.py for original | ||||
| # implementation | ||||
| def isclose(a, b, rel_tol=1e-9, abs_tol=0.0): | ||||
|     if rel_tol < 0.0 or abs_tol < 0.0: | ||||
|         raise ValueError('error tolerances must be non-negative') | ||||
|     if a == b: # fast-path for exact equality | ||||
|         return True | ||||
|     if cmath.isinf(a) or cmath.isinf(b): | ||||
|         return False | ||||
|     diff = abs(b - a) | ||||
|     return ( | ||||
|         (diff <= abs(rel_tol * b)) or | ||||
|         (diff <= abs(rel_tol * a)) or | ||||
|         (diff <= abs_tol) | ||||
|         ) | ||||
| @@ -13,7 +13,12 @@ from threading import Thread, Event, RLock | ||||
| from collections import deque | ||||
| from types import FunctionType | ||||
|  | ||||
| from .exc import GPIODeviceError, GPIODeviceClosed, InputDeviceError | ||||
| from .exc import ( | ||||
|     GPIOPinMissing, | ||||
|     GPIOPinInUse, | ||||
|     GPIODeviceClosed, | ||||
|     GPIOBadQueueLen, | ||||
|     ) | ||||
|  | ||||
| # Get a pin implementation to use as the default; we prefer RPi.GPIO's here | ||||
| # as it supports PWM, and all Pi revisions. If no third-party libraries are | ||||
| @@ -203,7 +208,8 @@ class GPIODevice(ValuesMixin, GPIOBase): | ||||
|  | ||||
|     :param int pin: | ||||
|         The GPIO pin (in BCM numbering) that the device is connected to. If | ||||
|         this is ``None`` a :exc:`GPIODeviceError` will be raised. | ||||
|         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__() | ||||
| @@ -212,12 +218,12 @@ class GPIODevice(ValuesMixin, GPIOBase): | ||||
|         # value of pin until we've verified that it isn't already allocated | ||||
|         self._pin = None | ||||
|         if pin is None: | ||||
|             raise GPIODeviceError('No pin given') | ||||
|             raise GPIOPinMissing('No pin given') | ||||
|         if isinstance(pin, int): | ||||
|             pin = DefaultPin(pin) | ||||
|         with _PINS_LOCK: | ||||
|             if pin in _PINS: | ||||
|                 raise GPIODeviceError( | ||||
|                 raise GPIOPinInUse( | ||||
|                     'pin %r is already in use by another gpiozero object' % pin | ||||
|                 ) | ||||
|             _PINS.add(pin) | ||||
| @@ -342,7 +348,7 @@ class GPIOQueue(GPIOThread): | ||||
|         assert isinstance(parent, GPIODevice) | ||||
|         super(GPIOQueue, self).__init__(target=self.fill) | ||||
|         if queue_len < 1: | ||||
|             raise InputDeviceError('queue_len must be at least one') | ||||
|             raise GPIOBadQueueLen('queue_len must be at least one') | ||||
|         self.queue = deque(maxlen=queue_len) | ||||
|         self.partial = partial | ||||
|         self.sample_wait = sample_wait | ||||
|   | ||||
| @@ -4,16 +4,72 @@ from __future__ import ( | ||||
|     absolute_import, | ||||
|     division, | ||||
| ) | ||||
| str = type('') | ||||
|  | ||||
| class GPIODeviceError(Exception): | ||||
|     pass | ||||
|  | ||||
| class GPIOZeroError(Exception): | ||||
|     "Base class for all exceptions in GPIO Zero" | ||||
|  | ||||
| class CompositeDeviceError(GPIOZeroError): | ||||
|     "Base class for errors specific to the CompositeDevice hierarchy" | ||||
|  | ||||
| class GPIODeviceError(GPIOZeroError): | ||||
|     "Base class for errors specific to the GPIODevice hierarchy" | ||||
|  | ||||
| class GPIODeviceClosed(GPIODeviceError): | ||||
|     pass | ||||
|     "Error raised when an operation is attempted on a closed device" | ||||
|  | ||||
| class GPIOPinInUse(GPIODeviceError): | ||||
|     "Error raised when attempting to use a pin already in use by another device" | ||||
|  | ||||
| class GPIOPinMissing(GPIODeviceError, ValueError): | ||||
|     "Error raised when a pin number is not specified" | ||||
|  | ||||
| class GPIOBadQueueLen(GPIODeviceError, ValueError): | ||||
|     "Error raised when non-positive queue length is specified" | ||||
|  | ||||
| class InputDeviceError(GPIODeviceError): | ||||
|     pass | ||||
|     "Base class for errors specific to the InputDevice hierarchy" | ||||
|  | ||||
| class OutputDeviceError(GPIODeviceError): | ||||
|     pass | ||||
|     "Base class for errors specified to the OutputDevice hierarchy" | ||||
|  | ||||
| class OutputDeviceBadValue(OutputDeviceError, ValueError): | ||||
|     "Error raised when ``value`` is set to an invalid value" | ||||
|  | ||||
| class PinError(GPIOZeroError): | ||||
|     "Base class for errors related to pin implementations" | ||||
|  | ||||
| class PinFixedFunction(PinError, AttributeError): | ||||
|     "Error raised when attempting to change the function of a fixed type pin" | ||||
|  | ||||
| class PinInvalidFunction(PinError, ValueError): | ||||
|     "Error raised when attempting to change the function of a pin to an invalid value" | ||||
|  | ||||
| class PinInvalidState(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid state to a pin" | ||||
|  | ||||
| class PinInvalidPull(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid pull-up to a pin" | ||||
|  | ||||
| class PinInvalidEdges(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid edge detection to a pin" | ||||
|  | ||||
| class PinSetInput(PinError, AttributeError): | ||||
|     "Error raised when attempting to set a read-only pin" | ||||
|  | ||||
| class PinFixedPull(PinError, AttributeError): | ||||
|     "Error raised when attempting to set the pull of a pin with fixed pull-up" | ||||
|  | ||||
| class PinEdgeDetectUnsupported(PinError, AttributeError): | ||||
|     "Error raised when attempting to use edge detection on unsupported pins" | ||||
|  | ||||
| class PinPWMError(PinError): | ||||
|     "Base class for errors related to PWM implementations" | ||||
|  | ||||
| class PinPWMUnsupported(PinPWMError, AttributeError): | ||||
|     "Error raised when attempting to activate PWM on unsupported pins" | ||||
|  | ||||
| class PinPWMFixedValue(PinPWMError, AttributeError): | ||||
|     "Error raised when attempting to initialize PWM on an input pin" | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ from time import sleep | ||||
| from threading import Lock | ||||
| from itertools import repeat, cycle, chain | ||||
|  | ||||
| from .exc import OutputDeviceError, GPIODeviceError, GPIODeviceClosed | ||||
| from .exc import OutputDeviceBadValue, GPIOPinMissing, GPIODeviceClosed | ||||
| from .devices import GPIODevice, GPIOThread, CompositeDevice, SourceMixin | ||||
|  | ||||
|  | ||||
| @@ -24,7 +24,7 @@ class OutputDevice(SourceMixin, GPIODevice): | ||||
|  | ||||
|     :param int pin: | ||||
|         The GPIO pin (in BCM numbering) that the device is connected to. If | ||||
|         this is ``None`` a :exc:`GPIODeviceError` will be raised. | ||||
|         this is ``None`` a :exc:`GPIOPinMissing` will be raised. | ||||
|  | ||||
|     :param bool active_high: | ||||
|         If ``True`` (the default), the :meth:`on` method will set the GPIO to | ||||
| @@ -54,7 +54,7 @@ class OutputDevice(SourceMixin, GPIODevice): | ||||
|             value = not value | ||||
|         try: | ||||
|             self.pin.state = bool(value) | ||||
|         except ValueError: | ||||
|         except AttributeError: | ||||
|             self._check_open() | ||||
|             raise | ||||
|  | ||||
| @@ -269,7 +269,7 @@ class PWMOutputDevice(OutputDevice): | ||||
|     def __init__(self, pin=None, active_high=True, initial_value=0, frequency=100): | ||||
|         self._blink_thread = None | ||||
|         if not 0 <= initial_value <= 1: | ||||
|             raise OutputDeviceError("initial_value must be between 0 and 1") | ||||
|             raise OutputDeviceBadValue("initial_value must be between 0 and 1") | ||||
|         super(PWMOutputDevice, self).__init__(pin, active_high) | ||||
|         try: | ||||
|             # XXX need a way of setting these together | ||||
| @@ -299,7 +299,7 @@ class PWMOutputDevice(OutputDevice): | ||||
|         if not self.active_high: | ||||
|             value = 1 - value | ||||
|         if not 0 <= value <= 1: | ||||
|             raise OutputDeviceError("PWM value must be between 0 and 1") | ||||
|             raise OutputDeviceBadValue("PWM value must be between 0 and 1") | ||||
|         try: | ||||
|             self.pin.state = value | ||||
|         except AttributeError: | ||||
| @@ -505,7 +505,7 @@ class RGBLED(SourceMixin, CompositeDevice): | ||||
|         self._leds = () | ||||
|         self._blink_thread = None | ||||
|         if not all([red, green, blue]): | ||||
|             raise OutputDeviceError('red, green, and blue pins must be provided') | ||||
|             raise GPIOPinMissing('red, green, and blue pins must be provided') | ||||
|         super(RGBLED, self).__init__() | ||||
|         self._leds = tuple(PWMLED(pin, active_high) for pin in (red, green, blue)) | ||||
|         self.value = initial_value | ||||
| @@ -680,7 +680,7 @@ class Motor(SourceMixin, CompositeDevice): | ||||
|     """ | ||||
|     def __init__(self, forward=None, backward=None): | ||||
|         if not all([forward, backward]): | ||||
|             raise OutputDeviceError( | ||||
|             raise GPIOPinMissing( | ||||
|                 'forward and backward pins must be provided' | ||||
|             ) | ||||
|         super(Motor, self).__init__() | ||||
| @@ -722,7 +722,7 @@ class Motor(SourceMixin, CompositeDevice): | ||||
|     @value.setter | ||||
|     def value(self, value): | ||||
|         if not -1 <= value <= 1: | ||||
|             raise OutputDeviceError("Motor value must be between -1 and 1") | ||||
|             raise OutputDeviceBadValue("Motor value must be between -1 and 1") | ||||
|         if value > 0: | ||||
|             self.forward(value) | ||||
|         elif value < 0: | ||||
|   | ||||
| @@ -6,7 +6,7 @@ from __future__ import ( | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
| from .exc import ( | ||||
| from ..exc import ( | ||||
|     PinFixedFunction, | ||||
|     PinSetInput, | ||||
|     PinFixedPull, | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| class PinError(Exception): | ||||
|     "Base class for errors related to pin implementations" | ||||
|  | ||||
| class PinFixedFunction(PinError, AttributeError): | ||||
|     "Error raised when attempting to change the function of a fixed type pin" | ||||
|  | ||||
| class PinInvalidFunction(PinError, ValueError): | ||||
|     "Error raised when attempting to change the function of a pin to an invalid value" | ||||
|  | ||||
| class PinInvalidState(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid state to a pin" | ||||
|  | ||||
| class PinInvalidPull(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid pull-up to a pin" | ||||
|  | ||||
| class PinInvalidEdges(PinError, ValueError): | ||||
|     "Error raised when attempting to assign an invalid edge detection to a pin" | ||||
|  | ||||
| class PinSetInput(PinError, AttributeError): | ||||
|     "Error raised when attempting to set a read-only pin" | ||||
|  | ||||
| class PinFixedPull(PinError, AttributeError): | ||||
|     "Error raised when attempting to set the pull of a pin with fixed pull-up" | ||||
|  | ||||
| class PinEdgeDetectUnsupported(PinError, AttributeError): | ||||
|     "Error raised when attempting to use edge detection on unsupported pins" | ||||
|  | ||||
| class PinPWMError(PinError): | ||||
|     "Base class for errors related to PWM implementations" | ||||
|  | ||||
| class PinPWMUnsupported(PinPWMError, AttributeError): | ||||
|     "Error raised when attempting to activate PWM on unsupported pins" | ||||
|  | ||||
| class PinPWMFixedValue(PinPWMError, AttributeError): | ||||
|     "Error raised when attempting to initialize PWM on an input pin" | ||||
|  | ||||
							
								
								
									
										171
									
								
								gpiozero/pins/mock.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								gpiozero/pins/mock.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| from collections import namedtuple | ||||
| from time import time | ||||
| try: | ||||
|     from math import isclose | ||||
| except ImportError: | ||||
|     from ..compat import isclose | ||||
|  | ||||
| from . import Pin, PINS_CLEANUP | ||||
| from ..exc import PinSetInput, PinPWMUnsupported | ||||
|  | ||||
|  | ||||
| PinState = namedtuple('PinState', ('timestamp', 'state')) | ||||
|  | ||||
| class MockPin(Pin): | ||||
|     """ | ||||
|     A mock pin used primarily for testing. This class does *not* support PWM. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, number): | ||||
|         if not (0 <= number < 54): | ||||
|             raise ValueError('invalid pin %d specified (must be 0..53)' % number) | ||||
|         self._number = number | ||||
|         self._function = 'input' | ||||
|         self._state = False | ||||
|         self._pull = 'floating' | ||||
|         self._bounce = None | ||||
|         self._edges = 'both' | ||||
|         self._when_changed = None | ||||
|         self._last_change = time() | ||||
|         self.states = [PinState(0.0, False)] | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'MOCK%d' % self._number | ||||
|  | ||||
|     @property | ||||
|     def number(self): | ||||
|         return self._number | ||||
|  | ||||
|     def close(self): | ||||
|         self.when_changed = None | ||||
|         self.function = 'input' | ||||
|  | ||||
|     def _get_function(self): | ||||
|         return self._function | ||||
|  | ||||
|     def _set_function(self, value): | ||||
|         assert value in ('input', 'output') | ||||
|         self._function = value | ||||
|         if value == 'input': | ||||
|             # Drive the input to the pull | ||||
|             self._set_pull(self._get_pull()) | ||||
|  | ||||
|     def _get_state(self): | ||||
|         return self._state | ||||
|  | ||||
|     def _set_state(self, value): | ||||
|         if self._function == 'input': | ||||
|             raise PinSetInput() | ||||
|         assert self._function == 'output' | ||||
|         assert 0 <= value <= 1 | ||||
|         if self._state != value: | ||||
|             t = time() | ||||
|             self._state = value | ||||
|             self.states.append(PinState(t - self._last_change, value)) | ||||
|             self._last_change = t | ||||
|  | ||||
|     def _get_frequency(self): | ||||
|         return None | ||||
|  | ||||
|     def _set_frequency(self, value): | ||||
|         raise PinPWMUnsupported() | ||||
|  | ||||
|     def _get_pull(self): | ||||
|         return self._pull | ||||
|  | ||||
|     def _set_pull(self, value): | ||||
|         assert self._function == 'input' | ||||
|         assert value in ('floating', 'up', 'down') | ||||
|         self._pull = value | ||||
|         if value == 'up': | ||||
|             self.drive_high() | ||||
|         elif value == 'down': | ||||
|             self.drive_low() | ||||
|  | ||||
|     def _get_bounce(self): | ||||
|         return self._bounce | ||||
|  | ||||
|     def _set_bounce(self, value): | ||||
|         # XXX Need to implement this | ||||
|         self._bounce = value | ||||
|  | ||||
|     def _get_edges(self): | ||||
|         return self._edges | ||||
|  | ||||
|     def _set_edges(self, value): | ||||
|         assert value in ('none', 'falling', 'rising', 'both') | ||||
|         self._edges = value | ||||
|  | ||||
|     def _get_when_changed(self): | ||||
|         return self._when_changed | ||||
|  | ||||
|     def _set_when_changed(self, value): | ||||
|         self._when_changed = value | ||||
|  | ||||
|     def drive_high(self): | ||||
|         assert self._function == 'input' | ||||
|         if not self._state: | ||||
|             t = time() | ||||
|             self._state = True | ||||
|             self.states.append(PinState(t - self._last_change, True)) | ||||
|             self._last_change = t | ||||
|             if self._edges in ('both', 'rising') and self._when_changed is not None: | ||||
|                 self._when_changed() | ||||
|  | ||||
|     def drive_low(self): | ||||
|         assert self._function == 'input' | ||||
|         if self._state: | ||||
|             t = time() | ||||
|             self._state = False | ||||
|             self.states.append(PinState(t - self._last_change, False)) | ||||
|             self._last_change = t | ||||
|             if self._edges in ('both', 'falling') and self._when_changed is not None: | ||||
|                 self._when_changed() | ||||
|  | ||||
|     def clear_states(self): | ||||
|         self._last_change = time() | ||||
|         self.states = [PinState(0.0, self.state)] | ||||
|  | ||||
|     def assert_states(self, expected_states): | ||||
|         # Tests that the pin went through the expected states (a list of values) | ||||
|         for actual, expected in zip(self.states, expected_states): | ||||
|             assert actual.state == expected | ||||
|  | ||||
|     def assert_states_and_times(self, expected_states): | ||||
|         # Tests that the pin went through the expected states at the expected | ||||
|         # times (times are compared with a tolerance of tens-of-milliseconds as | ||||
|         # 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.state, expected[1]) | ||||
|  | ||||
|  | ||||
| class MockPWMPin(MockPin): | ||||
|     """ | ||||
|     This derivative of :class:`MockPin` adds PWM support. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, number): | ||||
|         super(MockPWMPin, self).__init__(number) | ||||
|         self._frequency = None | ||||
|  | ||||
|     def _get_frequency(self): | ||||
|         return self._frequency | ||||
|  | ||||
|     def _set_frequency(self, value): | ||||
|         if value is not None: | ||||
|             assert self._function == 'output' | ||||
|         self._frequency = value | ||||
|         if value is None: | ||||
|             self.state = False | ||||
|  | ||||
| @@ -17,7 +17,7 @@ from threading import Thread, Event, Lock | ||||
| from collections import Counter | ||||
|  | ||||
| from . import Pin, PINS_CLEANUP | ||||
| from .exc import ( | ||||
| from ..exc import ( | ||||
|     PinInvalidPull, | ||||
|     PinInvalidEdges, | ||||
|     PinInvalidFunction, | ||||
|   | ||||
| @@ -9,7 +9,7 @@ str = type('') | ||||
| from RPi import GPIO | ||||
|  | ||||
| from . import Pin | ||||
| from .exc import ( | ||||
| from ..exc import ( | ||||
|     PinInvalidFunction, | ||||
|     PinSetInput, | ||||
|     PinFixedPull, | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import RPIO | ||||
| import RPIO.PWM | ||||
|  | ||||
| from . import Pin, PINS_CLEANUP | ||||
| from .exc import ( | ||||
| from ..exc import ( | ||||
|     PinInvalidFunction, | ||||
|     PinSetInput, | ||||
|     PinFixedPull, | ||||
|   | ||||
							
								
								
									
										10
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								setup.py
									
									
									
									
									
								
							| @@ -49,8 +49,18 @@ __requires__ = [ | ||||
| ] | ||||
|  | ||||
| __extra_requires__ = { | ||||
|     'doc':   ['sphinx'], | ||||
|     'test':  ['pytest', 'coverage', 'mock'], | ||||
| } | ||||
|  | ||||
| if sys.version_info[:2] == (3, 2): | ||||
|     # Particular versions are required for Python 3.2 compatibility | ||||
|     __extra_requires__['doc'].extend([ | ||||
|         'Jinja2<2.7', | ||||
|         'MarkupSafe<0.16', | ||||
|         ]) | ||||
|     __extra_requires__['test'][1] = 'coverage<4.0dev' | ||||
|  | ||||
| __entry_points__ = { | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										17
									
								
								tests/test_boards.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/test_boards.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from gpiozero.pins.mock import MockPin | ||||
| from gpiozero import * | ||||
|  | ||||
|  | ||||
| # TODO boards tests! | ||||
|  | ||||
							
								
								
									
										17
									
								
								tests/test_devices.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/test_devices.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from gpiozero.pins.mock import MockPin | ||||
| from gpiozero import * | ||||
|  | ||||
|  | ||||
| # TODO devices tests! | ||||
|  | ||||
							
								
								
									
										16
									
								
								tests/test_inputs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/test_inputs.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from gpiozero.pins.mock import MockPin | ||||
| from gpiozero import * | ||||
|  | ||||
|  | ||||
| # TODO input_devices tests! | ||||
							
								
								
									
										74
									
								
								tests/test_mock_pin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								tests/test_mock_pin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| from threading import Event | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from gpiozero.pins.mock import MockPin, MockPWMPin | ||||
| from gpiozero import * | ||||
|  | ||||
|  | ||||
| # Some rough tests to make sure our MockPin is up to snuff. This is just | ||||
| # enough to get reasonable coverage but it's by no means comprehensive... | ||||
|  | ||||
| def test_mock_pin_init(): | ||||
|     with pytest.raises(ValueError): | ||||
|         MockPin(60) | ||||
|     assert MockPin(2).number == 2 | ||||
|  | ||||
| def test_mock_pin_frequency_unsupported(): | ||||
|     with pytest.raises(AttributeError): | ||||
|         pin = MockPin(3) | ||||
|         pin.frequency = 100 | ||||
|  | ||||
| def test_mock_pin_frequency_supported(): | ||||
|     pin = MockPWMPin(3) | ||||
|     pin.function = 'output' | ||||
|     assert pin.frequency is None | ||||
|     pin.frequency = 100 | ||||
|     pin.state = 0.5 | ||||
|     pin.frequency = None | ||||
|     assert not pin.state | ||||
|  | ||||
| def test_mock_pin_pull(): | ||||
|     pin = MockPin(4) | ||||
|     pin.function = 'input' | ||||
|     assert pin.pull == 'floating' | ||||
|     pin.pull = 'up' | ||||
|     assert pin.state | ||||
|     pin.pull = 'down' | ||||
|     assert not pin.state | ||||
|  | ||||
| def test_mock_pin_edges(): | ||||
|     pin = MockPin(5) | ||||
|     assert pin.when_changed is None | ||||
|     fired = Event() | ||||
|     pin.function = 'input' | ||||
|     pin.edges = 'both' | ||||
|     assert pin.edges == 'both' | ||||
|     pin.drive_low() | ||||
|     assert not pin.state | ||||
|     def changed(): | ||||
|         fired.set() | ||||
|     pin.when_changed = changed | ||||
|     pin.drive_high() | ||||
|     assert pin.state | ||||
|     assert fired.wait(0) | ||||
|     fired.clear() | ||||
|     pin.edges = 'falling' | ||||
|     pin.drive_low() | ||||
|     assert not pin.state | ||||
|     assert fired.wait(0) | ||||
|     fired.clear() | ||||
|     pin.drive_high() | ||||
|     assert pin.state | ||||
|     assert not fired.wait(0) | ||||
|     assert pin.edges == 'falling' | ||||
|  | ||||
							
								
								
									
										460
									
								
								tests/test_outputs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								tests/test_outputs.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,460 @@ | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
|     print_function, | ||||
|     division, | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
|  | ||||
| try: | ||||
|     from math import isclose | ||||
| except ImportError: | ||||
|     from gpiozero.compat import isclose | ||||
|  | ||||
| import pytest | ||||
| from time import sleep | ||||
|  | ||||
| from gpiozero.pins.mock import MockPin, MockPWMPin | ||||
| from gpiozero import * | ||||
|  | ||||
|  | ||||
| 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 | ||||
|  | ||||
| def test_output_write_active_high(): | ||||
|     pin = MockPin(2) | ||||
|     device = OutputDevice(pin) | ||||
|     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 | ||||
|  | ||||
| def test_output_write_closed(): | ||||
|     device = OutputDevice(MockPin(2)) | ||||
|     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() | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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) | ||||
|         ]) | ||||
|  | ||||
| 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) | ||||
|         ]) | ||||
|  | ||||
| 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]) | ||||
|  | ||||
| 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]) | ||||
|  | ||||
| def test_output_pwm_bad_initial_value(): | ||||
|     with pytest.raises(ValueError): | ||||
|         PWMOutputDevice(MockPin(2), initial_value=2) | ||||
|  | ||||
| def test_output_pwm_not_supported(): | ||||
|     with pytest.raises(AttributeError): | ||||
|         PWMOutputDevice(MockPin(2)) | ||||
|  | ||||
| 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]) | ||||
|  | ||||
| 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 | ||||
|  | ||||
| def test_output_pwm_write(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     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]) | ||||
|  | ||||
| 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 | ||||
|  | ||||
| def test_output_pwm_bad_value(): | ||||
|     with pytest.raises(ValueError): | ||||
|         PWMOutputDevice(MockPWMPin(2)).value = 2 | ||||
|  | ||||
| def test_output_pwm_write_closed(): | ||||
|     device = PWMOutputDevice(MockPWMPin(2)) | ||||
|     device.close() | ||||
|     with pytest.raises(GPIODeviceClosed): | ||||
|         device.on() | ||||
|  | ||||
| def test_output_pwm_write_silly(): | ||||
|     pin = MockPWMPin(2) | ||||
|     device = PWMOutputDevice(pin) | ||||
|     pin.function = 'input' | ||||
|     with pytest.raises(AttributeError): | ||||
|         device.off() | ||||
|  | ||||
| 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) | ||||
|         ]) | ||||
|  | ||||
| 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) | ||||
|         ]) | ||||
|  | ||||
| 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), | ||||
|         ]) | ||||
|  | ||||
| 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), | ||||
|         ]) | ||||
|  | ||||
| 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]) | ||||
|  | ||||
| def test_rgbled_missing_pins(): | ||||
|     with pytest.raises(ValueError): | ||||
|         RGBLED() | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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) | ||||
|  | ||||
| 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]) | ||||
|  | ||||
| def test_motor_missing_pins(): | ||||
|     with pytest.raises(ValueError): | ||||
|         Motor() | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| def test_motor_bad_value(): | ||||
|     f = MockPWMPin(1) | ||||
|     b = MockPWMPin(2) | ||||
|     device = Motor(f, b) | ||||
|     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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user