diff --git a/gpiozero/boards.py b/gpiozero/boards.py index a42dc5f..b8f06a7 100644 --- a/gpiozero/boards.py +++ b/gpiozero/boards.py @@ -18,6 +18,7 @@ from .exc import ( GPIOPinMissing, EnergenieSocketMissing, EnergenieBadSocket, + OutputDeviceBadValue, ) from .input_devices import Button from .output_devices import OutputDevice, LED, PWMLED, Buzzer, Motor @@ -402,6 +403,8 @@ class LEDBarGraph(LEDCollection): @value.setter def value(self, value): + if not -1 <= value <= 1: + raise OutputDeviceBadValue('LEDBarGraph value must be between -1 and 1') count = len(self) leds = self if value < 0: diff --git a/gpiozero/input_devices.py b/gpiozero/input_devices.py index 3f38a86..05d0949 100644 --- a/gpiozero/input_devices.py +++ b/gpiozero/input_devices.py @@ -165,7 +165,7 @@ class SmoothedInputDevice(EventsMixin, InputDevice): if self.partial or self._queue.full.wait(0): return super(SmoothedInputDevice, self).__repr__() else: - return "" % ( + return "" % ( self.__class__.__name__, self.pin, self.pull_up) @property diff --git a/gpiozero/output_devices.py b/gpiozero/output_devices.py index 037f749..49a1b47 100644 --- a/gpiozero/output_devices.py +++ b/gpiozero/output_devices.py @@ -581,7 +581,7 @@ class RGBLED(SourceMixin, Device): @property def closed(self): - return bool(self._leds) + return len(self._leds) == 0 @property def value(self): diff --git a/tests/test_boards.py b/tests/test_boards.py index 627325e..ed3a0bb 100644 --- a/tests/test_boards.py +++ b/tests/test_boards.py @@ -85,6 +85,36 @@ def test_led_board_on_off(): assert not pin1.state assert pin2.state assert pin3.state + board.toggle(0,1) + assert board.value == (1, 0, 1) + assert pin1.state + assert not pin2.state + assert pin3.state + board.off(2) + assert board.value == (1, 0, 0) + assert pin1.state + assert not pin2.state + assert not pin3.state + board.on(1) + assert board.value == (1, 1, 0) + assert pin1.state + assert pin2.state + assert not pin3.state + board.off(0,1) + assert board.value == (0, 0, 0) + assert not pin1.state + assert not pin2.state + assert not pin3.state + board.on(1,2) + assert board.value == (0, 1, 1) + assert not pin1.state + assert pin2.state + assert pin3.state + board.toggle(0) + assert board.value == (1, 1, 1) + assert pin1.state + assert pin2.state + assert pin3.state def test_led_board_nested(): pin1 = MockPin(2) @@ -321,12 +351,48 @@ def test_led_bar_graph_pwm_value(): pin3.state = 1 assert graph.value == -1/2 +def test_led_bar_graph_bad_value(): + pin1 = MockPin(2) + pin2 = MockPin(3) + pin3 = MockPin(4) + with LEDBarGraph(pin1, pin2, pin3) as graph: + with pytest.raises(ValueError): + graph.value = -2 + with pytest.raises(ValueError): + graph.value = 2 + def test_led_bar_graph_bad_init(): pin1 = MockPin(2) pin2 = MockPin(3) pin3 = MockPin(4) with pytest.raises(TypeError): LEDBarGraph(pin1, pin2, foo=pin3) + with pytest.raises(ValueError): + LEDBarGraph(pin1, pin2, pin3, initial_value=-2) + with pytest.raises(ValueError): + LEDBarGraph(pin1, pin2, pin3, initial_value=2) + +def test_led_bar_graph_initial_value(): + pin1 = MockPin(2) + pin2 = MockPin(3) + pin3 = MockPin(4) + with LEDBarGraph(pin1, pin2, pin3, initial_value=1/3) as graph: + assert graph.value == 1/3 + assert pin1.state and not (pin2.state or pin3.state) + with LEDBarGraph(pin1, pin2, pin3, initial_value=-1/3) as graph: + assert graph.value == -1/3 + assert pin3.state and not (pin1.state or pin2.state) + +def test_led_bar_graph_pwm_initial_value(): + pin1 = MockPWMPin(2) + pin2 = MockPWMPin(3) + pin3 = MockPWMPin(4) + with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=0.5) as graph: + assert graph.value == 0.5 + assert (pin1.state, pin2.state, pin3.state) == (1, 0.5, 0) + with LEDBarGraph(pin1, pin2, pin3, pwm=True, initial_value=-0.5) as graph: + assert graph.value == -0.5 + assert (pin1.state, pin2.state, pin3.state) == (0, 0.5, 1) def test_pi_liter(): pins = [MockPin(n) for n in (4, 17, 27, 18, 22, 23, 24, 25)] diff --git a/tests/test_compat.py b/tests/test_compat.py index a3e6e28..c58fe7c 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -120,6 +120,7 @@ def test_mean(): values = list(values) random.shuffle(values) assert mean(values) == result + assert mean(iter(values)) == result def test_mean_big_data(): c = 1e9 diff --git a/tests/test_inputs.py b/tests/test_inputs.py index ee1d62a..eec55e4 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -34,13 +34,25 @@ def test_input_initial_values(): assert pin.pull == 'down' assert not device.pull_up -def test_input_is_active(): +def test_input_is_active_low(): pin = MockPin(2) with InputDevice(pin, pull_up=True) as device: pin.drive_high() assert not device.is_active + assert repr(device) == '' pin.drive_low() assert device.is_active + assert repr(device) == '' + +def test_input_is_active_high(): + pin = MockPin(2) + with InputDevice(pin, pull_up=False) as device: + pin.drive_high() + assert device.is_active + assert repr(device) == '' + pin.drive_low() + assert not device.is_active + assert repr(device) == '' def test_input_pulled_up(): pin = MockPulledUpPin(2) @@ -83,11 +95,14 @@ def test_input_wait_inactive(): def test_input_smoothed_attrib(): pin = MockPin(2) with SmoothedInputDevice(pin, threshold=0.5, queue_len=5, partial=False) as device: + assert repr(device) == '' assert device.threshold == 0.5 assert device.queue_len == 5 assert not device.partial device._queue.start() assert not device.is_active + with pytest.raises(InputDeviceError): + device.threshold = 1 def test_input_smoothed_values(): pin = MockPin(2) diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 27bc97d..cd3abf2 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -53,6 +53,9 @@ def test_output_write_active_low(): def test_output_write_closed(): with OutputDevice(MockPin(2)) as device: device.close() + assert device.closed + device.close() + assert device.closed with pytest.raises(GPIODeviceClosed): device.on() @@ -294,6 +297,67 @@ def test_output_pwm_fade_foreground(): (0.04, 0), ]) +@pytest.mark.skipif(hasattr(sys, 'pypy_version_info'), + reason='timing is too random on pypy') +def test_output_pwm_pulse_background(): + pin = MockPWMPin(2) + with PWMOutputDevice(pin) as device: + device.pulse(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_pulse_foreground(): + pin = MockPWMPin(2) + with PWMOutputDevice(pin) as device: + device.pulse(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) with PWMOutputDevice(pin) as device: @@ -409,7 +473,7 @@ def test_rgbled_fade_background(): g.assert_states_and_times(expected) b.assert_states_and_times(expected) -def test_output_rgbled_blink_interrupt(): +def test_rgbled_blink_interrupt(): r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) with RGBLED(r, g, b) as device: device.blink(1, 0.1) @@ -419,6 +483,15 @@ def test_output_rgbled_blink_interrupt(): g.assert_states([0, 1, 0]) b.assert_states([0, 1, 0]) +def test_rgbled_close(): + r, g, b = (MockPWMPin(i) for i in (1, 2, 3)) + with RGBLED(r, g, b) as device: + assert not device.closed + device.close() + assert device.closed + device.close() + assert device.closed + def test_motor_missing_pins(): with pytest.raises(ValueError): Motor() @@ -438,6 +511,8 @@ def test_motor_close(): assert device.closed assert device.forward_device.pin is None assert device.backward_device.pin is None + device.close() + assert device.closed def test_motor_value(): f = MockPWMPin(1)