|
@@ -15,7 +15,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-import pathlib
|
|
+import ctypes
|
|
|
|
+import ctypes.util
|
|
|
|
+import errno
|
|
import re
|
|
import re
|
|
import unittest.mock
|
|
import unittest.mock
|
|
|
|
|
|
@@ -26,101 +28,100 @@ import cc1101._gpio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-@pytest.mark.parametrize(
|
|
+def test__load_libgpiod():
|
|
- ("chip_selector", "chip_selector_formatted"),
|
|
|
|
- (
|
|
|
|
- (0, "/dev/gpiochip0"),
|
|
|
|
- (1, "/dev/gpiochip1"),
|
|
|
|
- ("/dev/gpiochip0", "/dev/gpiochip0"),
|
|
|
|
- (pathlib.Path("/dev/gpiochip1"), "/dev/gpiochip1"),
|
|
|
|
- ),
|
|
|
|
-)
|
|
|
|
-def test_get_line_permission_error(chip_selector, chip_selector_formatted):
|
|
|
|
with unittest.mock.patch(
|
|
with unittest.mock.patch(
|
|
- "gpiod.chip",
|
|
+ "ctypes.util.find_library", return_value=ctypes.util.find_library("c")
|
|
- side_effect=PermissionError("[Errno 13] Permission denied: '/dev/gpiochip0'"),
|
|
+ ) as find_library_mock:
|
|
|
|
+ assert isinstance(cc1101._gpio._load_libgpiod(), ctypes.CDLL)
|
|
|
|
+ find_library_mock.assert_called_once_with("gpiod")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@pytest.mark.parametrize("name", ("GPIO24", "GPIO25"))
|
|
|
|
+def test_line_find_permission_denied(libgpiod_mock, name):
|
|
|
|
+ libgpiod_mock.gpiod_line_find.return_value = 0
|
|
|
|
+ ctypes.set_errno(errno.EACCES)
|
|
|
|
+ with pytest.raises(
|
|
|
|
+ PermissionError,
|
|
|
|
+ match=r"^Failed to access GPIO line {!r}\.\n".format(re.escape(name)),
|
|
):
|
|
):
|
|
- with pytest.raises(
|
|
+ cc1101._gpio.GPIOLine.find(name.encode())
|
|
- PermissionError,
|
|
|
|
- match=r"^Failed to access GPIO chip {}\.".format(
|
|
|
|
- re.escape(chip_selector_formatted)
|
|
|
|
- ),
|
|
|
|
- ):
|
|
|
|
- cc1101._gpio.get_line(chip_selector=chip_selector, line_name="GPIO24")
|
|
|
|
|
|
|
|
|
|
|
|
-def test_get_line_file_not_found():
|
|
+@pytest.mark.parametrize("name", ("GPIO24", "GPIO25"))
|
|
- with unittest.mock.patch(
|
|
+def test_line_find_non_existing(libgpiod_mock, name):
|
|
- "gpiod.chip",
|
|
+ libgpiod_mock.gpiod_line_find.return_value = 0
|
|
- side_effect=FileNotFoundError(
|
|
+ ctypes.set_errno(errno.ENOENT)
|
|
- "[Errno 2] No such file or directory: 'cannot open GPIO device /dev/gpiochip21'"
|
|
+ with pytest.raises(
|
|
- ),
|
|
+ FileNotFoundError,
|
|
|
|
+ match=r"^GPIO line {!r} does not exist\.\n".format(re.escape(name)),
|
|
):
|
|
):
|
|
- with pytest.raises(
|
|
+ cc1101._gpio.GPIOLine.find(name.encode())
|
|
- FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip21\."
|
|
|
|
- ):
|
|
|
|
- cc1101._gpio.get_line(chip_selector=21, line_name="GPIO24")
|
|
|
|
|
|
|
|
|
|
|
|
-def test_get_line_cannot_open():
|
|
+@pytest.mark.parametrize("name", ("GPIO24", "GPIO25"))
|
|
- with unittest.mock.patch(
|
|
+def test_line_find_unknown_error(libgpiod_mock, name):
|
|
- "gpiod.chip",
|
|
+ libgpiod_mock.gpiod_line_find.return_value = 0
|
|
- side_effect=OSError("[Errno 0] Success: 'cannot open GPIO device 42'"),
|
|
+ ctypes.set_errno(errno.ENOANO)
|
|
|
|
+ with pytest.raises(
|
|
|
|
+ OSError,
|
|
|
|
+ match=r"^Failed to open GPIO line {!r}: ENOANO$".format(re.escape(name)),
|
|
):
|
|
):
|
|
- with pytest.raises(
|
|
+ cc1101._gpio.GPIOLine.find(name.encode())
|
|
- FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip42\."
|
|
+
|
|
- ):
|
|
+
|
|
- cc1101._gpio.get_line(chip_selector=42, line_name="GPIO24")
|
|
+def test_line_find(libgpiod_mock):
|
|
-
|
|
+ libgpiod_mock.gpiod_line_find.return_value = 21
|
|
-
|
|
+ line = cc1101._gpio.GPIOLine.find(b"GPIO24")
|
|
-def test_get_line_type_error():
|
|
+ libgpiod_mock.gpiod_line_find.assert_called_once_with(b"GPIO24")
|
|
- with unittest.mock.patch(
|
|
+ assert isinstance(line, cc1101._gpio.GPIOLine)
|
|
- "gpiod.chip",
|
|
+ assert line._pointer.value == 21
|
|
- side_effect=TypeError("iter() returned non-iterator of type 'NoneType'"),
|
|
+
|
|
|
|
+
|
|
|
|
+def test_line_release(libgpiod_mock):
|
|
|
|
+ line = cc1101._gpio.GPIOLine(42)
|
|
|
|
+ del line
|
|
|
|
+ libgpiod_mock.gpiod_line_close_chip.assert_called_once_with(42)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@pytest.mark.parametrize("consumer", (b"CC1101 GDO0", b"test"))
|
|
|
|
+@pytest.mark.parametrize("timeout_seconds", (21, 42))
|
|
|
|
+@pytest.mark.parametrize("reached_timeout", (False, True))
|
|
|
|
+def test_line_wait_for_rising_edge(
|
|
|
|
+ libgpiod_mock, consumer: bytes, timeout_seconds: int, reached_timeout: bool
|
|
|
|
+):
|
|
|
|
+ pointer = ctypes.c_void_p(1234)
|
|
|
|
+ line = cc1101._gpio.GPIOLine(pointer=pointer)
|
|
|
|
+ libgpiod_mock.gpiod_line_request_rising_edge_events.return_value = 0
|
|
|
|
+ libgpiod_mock.gpiod_line_event_wait.return_value = 0 if reached_timeout else 1
|
|
|
|
+ event_occured = line.wait_for_rising_edge(
|
|
|
|
+ consumer=consumer, timeout_seconds=timeout_seconds
|
|
|
|
+ )
|
|
|
|
+ assert event_occured is not reached_timeout
|
|
|
|
+ libgpiod_mock.gpiod_line_request_rising_edge_events.assert_called_once_with(
|
|
|
|
+ pointer, consumer
|
|
|
|
+ )
|
|
|
|
+ assert libgpiod_mock.gpiod_line_event_wait.call_count == 1
|
|
|
|
+ wait_args, wait_kwargs = libgpiod_mock.gpiod_line_event_wait.call_args
|
|
|
|
+ assert wait_args[0] == pointer
|
|
|
|
+ assert wait_args[1].contents.tv_nsec == 0
|
|
|
|
+ assert wait_args[1].contents.tv_sec == timeout_seconds
|
|
|
|
+ assert not wait_args[2:]
|
|
|
|
+ assert not wait_kwargs
|
|
|
|
+ libgpiod_mock.gpiod_line_release.assert_called_once_with(pointer)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@pytest.mark.parametrize("consumer", (b"CC1101 GDO0",))
|
|
|
|
+@pytest.mark.parametrize("timeout_seconds", (21,))
|
|
|
|
+def test_line_wait_for_rising_edge_busy(
|
|
|
|
+ libgpiod_mock, consumer: bytes, timeout_seconds: int
|
|
|
|
+):
|
|
|
|
+ pointer = ctypes.c_void_p(1234)
|
|
|
|
+ line = cc1101._gpio.GPIOLine(pointer=pointer)
|
|
|
|
+ libgpiod_mock.gpiod_line_request_rising_edge_events.return_value = -1
|
|
|
|
+ ctypes.set_errno(errno.EBUSY)
|
|
|
|
+ with pytest.raises(
|
|
|
|
+ OSError,
|
|
|
|
+ match=r"^Request for rising edge event notifications failed \(EBUSY\)."
|
|
|
|
+ r"\nBlocked by another process\?$",
|
|
):
|
|
):
|
|
- with pytest.raises(
|
|
+ line.wait_for_rising_edge(consumer=consumer, timeout_seconds=timeout_seconds)
|
|
- FileNotFoundError, match=r"^Failed to find GPIO chip /dev/gpiochip815\."
|
|
|
|
- ):
|
|
|
|
- cc1101._gpio.get_line(chip_selector="/dev/gpiochip815", line_name="GPIO24")
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-class _LineMock:
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- def __init__(self, holding: bool):
|
|
|
|
- self._holding = holding
|
|
|
|
-
|
|
|
|
- @property
|
|
|
|
- def name(self) -> str:
|
|
|
|
- if not self._holding:
|
|
|
|
- raise RuntimeError("object not holding a GPIO line handle")
|
|
|
|
- return "dummy"
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-@pytest.mark.parametrize("chip_selector", ("/dev/gpiochip0",))
|
|
|
|
-@pytest.mark.parametrize("line_name", ("GPIO24", "GPIO25"))
|
|
|
|
-def test_get_line(chip_selector, line_name):
|
|
|
|
- with unittest.mock.patch("gpiod.chip") as chip_mock:
|
|
|
|
- chip_mock().find_line.return_value = _LineMock(holding=True)
|
|
|
|
- chip_mock.reset_mock()
|
|
|
|
- assert isinstance(
|
|
|
|
- cc1101._gpio.get_line(chip_selector=chip_selector, line_name=line_name),
|
|
|
|
- _LineMock,
|
|
|
|
- )
|
|
|
|
- chip_mock.assert_called_once_with(chip_selector)
|
|
|
|
- chip_mock().find_line.assert_called_once_with(name=line_name)
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-@pytest.mark.parametrize("chip_selector", ("/dev/gpiochip0",))
|
|
|
|
-@pytest.mark.parametrize("line_name", ("GPIO24", "GPIO25"))
|
|
|
|
-def test_get_line_unknown_line(chip_selector, line_name):
|
|
|
|
- with unittest.mock.patch("gpiod.chip") as chip_mock:
|
|
|
|
- chip_mock().find_line.return_value = _LineMock(holding=False)
|
|
|
|
- with pytest.raises(
|
|
|
|
- ValueError,
|
|
|
|
- match=r"Failed to find GPIO line with name {}\.".format(
|
|
|
|
- re.escape(repr(line_name))
|
|
|
|
- ),
|
|
|
|
- ):
|
|
|
|
- cc1101._gpio.get_line(chip_selector=chip_selector, line_name=line_name)
|
|
|