Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

Create a Button Component #57

Merged
merged 12 commits into from Feb 12, 2019
3 changes: 2 additions & 1 deletion j5/components/__init__.py
@@ -1,6 +1,7 @@
"""This module contains components, which are the smallest logical element of hardware."""

from .base import Component
from .button import Button, ButtonInterface
from .led import LED, LEDInterface

__all__ = ["Component", "LED", "LEDInterface"]
__all__ = ["Component", "Button", "ButtonInterface", "LED", "LEDInterface"]
43 changes: 43 additions & 0 deletions j5/components/button.py
@@ -0,0 +1,43 @@
"""Classes for Button."""

from abc import ABCMeta, abstractmethod

from j5.boards import Board
from j5.components import Component


class ButtonInterface(metaclass=ABCMeta):
"""An interface containing the methods required for a button."""

@abstractmethod
def get_button_state(self, board: Board, identifier: int) -> bool:
"""Set the state of a button."""
raise NotImplementedError # pragma: no cover

@abstractmethod
def wait_until_button_pressed(self, board: Board, identifier: int) -> bool:
"""Halt the program until this button is pushed."""
raise NotImplementedError # pragma: no cover


class Button(Component):
"""A button."""

def __init__(self, identifier: int, board: Board, backend: ButtonInterface) -> None:
self._board = board
self._backend = backend
self._identifier = identifier

@staticmethod
def interface_class():
"""Get the interface class that is required to use this component."""
return ButtonInterface

@property
def is_pressed(self) -> bool:
"""Get the current pushed state of the button."""
return self._backend.get_button_state(self._board, self._identifier)

def wait_until_pressed(self):
"""Halt the program until this button is pushed."""
self._backend.wait_until_button_pressed(self._board, self._identifier)
92 changes: 92 additions & 0 deletions tests/components/test_button.py
@@ -0,0 +1,92 @@
"""Tests for the Button Classes."""
from time import sleep, time
from typing import List, Type

from j5.backends import Backend
from j5.boards import Board
from j5.components import Component
from j5.components.button import Button, ButtonInterface


class MockButtonDriver(ButtonInterface):
"""A testing driver for the button component."""

def __init__(self):
self.state = False

def set_button_state(self, new_state: bool):
"""Set the button state for testing purposes."""
self.state = new_state

def get_button_state(self, board: Board, identifier: int) -> bool:
"""Get the state of the button."""
return self.state

def wait_until_button_pressed(self, board: Board, identifier: int) -> bool:
"""Wait until the button was pressed."""
sleep(0.2) # The mock driver always presses the button after 0.2s


class MockButtonBoard(Board):
"""A testing board for the button."""

@property
def name(self) -> str:
"""The name of this board."""
return "Testing Button Board"

@property
def serial(self) -> str:
"""The serial number of this board."""
return "SERIAL"

def make_safe(self):
"""Make this board safe."""
pass

@staticmethod
def supported_components() -> List[Type[Component]]:
"""List the types of component that this Board supports."""
return [Button]

@staticmethod
def discover(backend: Backend) -> List[Board]:
"""Detect all of the boards on a given backend."""
return []


def test_button_interface_implementation():
"""Test that we can implement the button interface."""
MockButtonDriver()


def test_button_instantiation():
"""Test that we can instantiate a button."""
Button(0, MockButtonBoard(), MockButtonDriver())


def test_button_state():
"""Test that we can get the state of the button."""
driver = MockButtonDriver()
button = Button(0, MockButtonBoard(), driver)

assert button.is_pressed is False
driver.set_button_state(True)
assert button.is_pressed is True


def test_button_wait_until_pressed():
"""Test that the button takes at least 0.2 secs to be pressed."""
button = Button(0, MockButtonBoard(), MockButtonDriver())
start_time = time()
button.wait_until_pressed()
end_time = time()
time_taken = end_time - start_time

assert time_taken > 0.2
assert time_taken < 2


def test_button_interface_class():
"""Test that the Button Interface class is a ButtonInterface."""
assert Button.interface_class() is ButtonInterface