More work on the tests...

This commit is contained in:
Dave Jones
2016-04-05 09:45:44 +01:00
parent 2a2e77051d
commit 3acf8a0d3a
12 changed files with 397 additions and 28 deletions

View File

@@ -19,6 +19,7 @@ from .exc import (
BadEventHandler,
CompositeDeviceError,
CompositeDeviceBadName,
CompositeDeviceBadOrder,
SPIError,
SPIBadArgs,
EnergenieSocketMissing,

View File

@@ -49,5 +49,5 @@ def median(data):
return data[n // 2]
else:
i = n // 2
return (data[n - 1] + data[n]) / 2
return (data[i - 1] + data[i]) / 2

View File

@@ -21,6 +21,8 @@ from .mixins import (
)
from .exc import (
DeviceClosed,
CompositeDeviceBadName,
CompositeDeviceBadOrder,
GPIOPinMissing,
GPIOPinInUse,
GPIODeviceClosed,
@@ -191,7 +193,7 @@ class GPIOBase(GPIOMeta(nstr('GPIOBase'), (), {})):
method). Once a device is closed you can no longer use any other
methods or properties to control or query the device.
"""
return False
raise NotImplementedError
def _check_open(self):
if self.closed:
@@ -223,7 +225,7 @@ class Device(ValuesMixin, GPIOBase):
ranges (e.g. -1 to +1) and composite devices usually use tuples to
return the states of all their subordinate components.
"""
return 0
raise NotImplementedError
@property
def is_active(self):
@@ -261,8 +263,8 @@ class CompositeDevice(Device):
if self._order is None:
self._order = kwargs.keys()
self._order = tuple(self._order)
for missing_name in set(self._order) - set(kwargs.keys()):
raise ValueError('%s missing from _order' % missing_name)
for missing_name in set(kwargs.keys()) - set(self._order):
raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
super(CompositeDevice, self).__init__()
for name in set(self._order) & set(dir(self)):
raise CompositeDeviceBadName('%s is a reserved name' % name)
@@ -372,9 +374,6 @@ class GPIODevice(Device):
self._check_open()
raise
def _fire_events(self):
pass
def close(self):
super(GPIODevice, self).close()
with _PINS_LOCK:

View File

@@ -22,6 +22,9 @@ class CompositeDeviceError(GPIOZeroError):
class CompositeDeviceBadName(CompositeDeviceError, ValueError):
"Error raised when a composite device is constructed with a reserved name"
class CompositeDeviceBadOrder(CompositeDeviceError, ValueError):
"Error raised when a composite device is constructed with an incomplete order"
class EnergenieSocketMissing(CompositeDeviceError, ValueError):
"Error raised when socket number is not specified"

View File

@@ -11,7 +11,7 @@ import warnings
from time import sleep, time
from threading import Event
from .exc import InputDeviceError, GPIODeviceError, GPIODeviceClosed
from .exc import InputDeviceError, GPIODeviceError, DeviceClosed
from .devices import GPIODevice, CompositeDevice
from .mixins import GPIOQueue, EventsMixin
@@ -161,7 +161,7 @@ class SmoothedInputDevice(EventsMixin, InputDevice):
def __repr__(self):
try:
self._check_open()
except GPIODeviceClosed:
except DeviceClosed:
return super(SmoothedInputDevice, self).__repr__()
else:
if self.partial or self._queue.full.wait(0):
@@ -649,7 +649,9 @@ class DistanceSensor(SmoothedInputDevice):
return self.pin
def _read(self):
# Make sure the echo event is clear
# Make sure the echo pin is low then ensure the echo event is clear
while self.pin.state:
sleep(0.00001)
self._echo.clear()
# Fire the trigger
self._trigger.pin.state = True

View File

@@ -8,7 +8,7 @@ str = type('')
from collections import namedtuple
from time import time
from time import time, sleep
from threading import Thread, Event
try:
from math import isclose
@@ -182,7 +182,7 @@ class MockChargingPin(MockPin):
"""
def __init__(self, number):
super(MockChargingPin, self).__init__()
self.charge_time = 0.01
self.charge_time = 0.01 # dark charging time
self._charge_stop = Event()
self._charge_thread = None
@@ -193,16 +193,51 @@ class MockChargingPin(MockPin):
self._charge_stop.set()
self._charge_thread.join()
self._charge_stop.clear()
self._charge_thread = Thread(target=lambda: self._charged)
self._charge_thread = Thread(target=self._charge)
self._charge_thread.start()
elif value == 'output':
if self.charge_thread:
if self._charge_thread:
self._charge_stop.set()
self._charge_thread.join()
def _charged(self):
def _charge(self):
if not self._charge_stop.wait(self.charge_time):
self.drive_high()
try:
self.drive_high()
except AssertionError:
# Charging pins are typically flipped between input and output
# repeatedly; if another thread has already flipped us to
# output ignore the assertion-error resulting from attempting
# to drive the pin high
pass
class MockTriggerPin(MockPin):
"""
This derivative of :class:`MockPin` is intended to be used with another
:class:`MockPin` to emulate a distance sensor. Set :attr:`echo_pin` to the
corresponding pin instance. When this pin is driven high it will trigger
the echo pin to drive high for the echo time.
"""
def __init__(self, number):
super(MockTriggerPin, self).__init__()
self.echo_pin = None
self.echo_time = 0.04 # longest echo time
self._echo_thread = None
def _set_state(self, value):
super(MockTriggerPin, self)._set_state(value)
if value:
if self._echo_thread:
self._echo_thread.join()
self._echo_thread = Thread(target=self._echo)
self._echo_thread.start()
def _echo(self):
sleep(0.001)
self.echo_pin.drive_high()
sleep(self.echo_time)
self.echo_pin.drive_low()
class MockPWMPin(MockPin):

View File

@@ -16,7 +16,7 @@ try:
except ImportError:
pass
from itertools import count, cycle
from math import sin, cos, floor
from math import sin, cos, floor, radians
try:
from statistics import mean
except ImportError:
@@ -270,7 +270,7 @@ def sin_values():
If you require a wider range than 0 to 1, see :func:`scaled`.
"""
for d in cycle(range(360)):
yield sin(d)
yield sin(radians(d))
def cos_values():
@@ -292,5 +292,5 @@ def cos_values():
If you require a wider range than 0 to 1, see :func:`scaled`.
"""
for d in cycle(range(360)):
yield cos(d)
yield cos(radians(d))

View File

@@ -23,8 +23,8 @@ def _threads_shutdown():
class GPIOThread(Thread):
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
super(GPIOThread, self).__init__(group, target, name, args, kwargs)
self.stopping = Event()
super(GPIOThread, self).__init__(group, target, name, args, kwargs)
self.daemon = True
def start(self):