# Path: luma/core/interface/serial.py
# class spi(bitbang):
#     """
#     Wraps an `SPI <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_
#     (Serial Peripheral Interface) bus to provide :py:func:`data` and
#     :py:func:`command` methods.
# 
#     :param spi: SPI implementation (must be compatible with `spidev <https://pypi.org/project/spidev>`_)
#     :param gpio: GPIO interface (must be compatible with `RPi.GPIO <https://pypi.org/project/RPi.GPIO>`__).
#         For slaves that don't need reset or D/C functionality, supply a
#         :py:class:`noop` implementation instead.
#     :param port: SPI port, usually 0 (default) or 1.
#     :type port: int
#     :param device: SPI device, usually 0 (default) or 1.
#     :type device: int
#     :param bus_speed_hz: SPI bus speed, defaults to 8MHz.
#     :type bus_speed_hz: int
#     :param transfer_size: Maximum amount of bytes to transfer in one go. Some implementations
#         only support a maximum of 64 or 128 bytes, whereas RPi/py-spidev supports
#         4096 (default).
#     :type transfer_size: int
#     :param gpio_DC: The GPIO pin to connect data/command select (DC) to (defaults to 24).
#     :type gpio_DC: int
#     :param gpio_RST: The GPIO pin to connect reset (RES / RST) to (defaults to 25).
#     :type gpio_RST: int
#     :param spi_mode: SPI mode as two bit pattern of clock polarity and phase [CPOL|CPHA], 0-3 (default:None)
#     :type spi_mode: int
#     :param reset_hold_time: The number of seconds to hold reset active. Some devices may require
#         a duration of 100ms or more to fully reset the display (default:0)
#     :type reset_hold_time: float
#     :param reset_release_time: The number of seconds to delay afer reset. Some devices may require
#         a duration of 150ms or more after reset was triggered before the device can accept the
#         initialization sequence (default:0)
#     :type reset_release_time: float
#     :raises luma.core.error.DeviceNotFoundError: SPI device could not be found.
#     :raises luma.core.error.UnsupportedPlatform: GPIO access not available.
#     """
#     def __init__(self, spi=None, gpio=None, port=0, device=0,
#                  bus_speed_hz=8000000, transfer_size=4096,
#                  gpio_DC=24, gpio_RST=25, spi_mode=None,
#                  reset_hold_time=0, reset_release_time=0, **kwargs):
#         assert(bus_speed_hz in [mhz * 1000000 for mhz in [0.5, 1, 2, 4, 8, 16, 20, 24, 28, 32, 36, 40, 44, 48, 50, 52]])
# 
#         bitbang.__init__(self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
# 
#         try:
#             self._spi = spi or self.__spidev__()
#             self._spi.open(port, device)
#             if spi_mode:
#                 self._spi.mode = spi_mode
#             if "cs_high" in kwargs:
#                 import warnings
#                 warnings.warn(
#                     "SPI cs_high is no longer supported in kernel 5.4.51 and beyond, so setting parameter cs_high is now ignored!",
#                     RuntimeWarning
#                 )
#         except (IOError, OSError) as e:
#             if e.errno == errno.ENOENT:
#                 raise luma.core.error.DeviceNotFoundError('SPI device not found')
#             else:  # pragma: no cover
#                 raise
# 
#         self._spi.max_speed_hz = bus_speed_hz
# 
#     def _write_bytes(self, data):
#         self._spi.writebytes(data)
# 
#     def cleanup(self):
#         """
#         Clean up SPI & GPIO resources.
#         """
#         self._spi.close()
#         super(spi, self).cleanup()


import pytest
import luma.core.error
from unittest.mock import Mock, call
from luma.core.interface.serial import spi
from helpers import get_spidev, rpi_gpio_missing, fib, assert_only_cleans_whats_setup
    device = 2
    bus_speed = 16000000
    dc = 17
    rst = 11

    spi(gpio=gpio, spi=spidev, port=port, device=device, bus_speed_hz=16000000,
        gpio_DC=dc, gpio_RST=rst)
    verify_spi_init(port, device, bus_speed, dc, rst)
    gpio.output.assert_has_calls([
        call(rst, gpio.LOW),
        call(rst, gpio.HIGH)
    ])


def test_init_invalid_bus_speed():
    with pytest.raises(AssertionError):
        spi(gpio=gpio, spi=spidev, port=5, device=2, bus_speed_hz=942312)


def test_command():
    cmds = [3, 1, 4, 2]
    serial = spi(gpio=gpio, spi=spidev, port=9, device=1)
    serial.command(*cmds)
    verify_spi_init(9, 1)
    gpio.output.assert_has_calls([call(25, gpio.HIGH), call(24, gpio.LOW)])
    spidev.writebytes.assert_called_once_with(cmds)


def test_data():
    data = list(fib(100))

# Path: luma/core/interface/serial.py
# class spi(bitbang):
#     """
#     Wraps an `SPI <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_
#     (Serial Peripheral Interface) bus to provide :py:func:`data` and
#     :py:func:`command` methods.
# 
#     :param spi: SPI implementation (must be compatible with `spidev <https://pypi.org/project/spidev>`_)
#     :param gpio: GPIO interface (must be compatible with `RPi.GPIO <https://pypi.org/project/RPi.GPIO>`__).
#         For slaves that don't need reset or D/C functionality, supply a
#         :py:class:`noop` implementation instead.
#     :param port: SPI port, usually 0 (default) or 1.
#     :type port: int
#     :param device: SPI device, usually 0 (default) or 1.
#     :type device: int
#     :param bus_speed_hz: SPI bus speed, defaults to 8MHz.
#     :type bus_speed_hz: int
#     :param transfer_size: Maximum amount of bytes to transfer in one go. Some implementations
#         only support a maximum of 64 or 128 bytes, whereas RPi/py-spidev supports
#         4096 (default).
#     :type transfer_size: int
#     :param gpio_DC: The GPIO pin to connect data/command select (DC) to (defaults to 24).
#     :type gpio_DC: int
#     :param gpio_RST: The GPIO pin to connect reset (RES / RST) to (defaults to 25).
#     :type gpio_RST: int
#     :param spi_mode: SPI mode as two bit pattern of clock polarity and phase [CPOL|CPHA], 0-3 (default:None)
#     :type spi_mode: int
#     :param reset_hold_time: The number of seconds to hold reset active. Some devices may require
#         a duration of 100ms or more to fully reset the display (default:0)
#     :type reset_hold_time: float
#     :param reset_release_time: The number of seconds to delay afer reset. Some devices may require
#         a duration of 150ms or more after reset was triggered before the device can accept the
#         initialization sequence (default:0)
#     :type reset_release_time: float
#     :raises luma.core.error.DeviceNotFoundError: SPI device could not be found.
#     :raises luma.core.error.UnsupportedPlatform: GPIO access not available.
#     """
#     def __init__(self, spi=None, gpio=None, port=0, device=0,
#                  bus_speed_hz=8000000, transfer_size=4096,
#                  gpio_DC=24, gpio_RST=25, spi_mode=None,
#                  reset_hold_time=0, reset_release_time=0, **kwargs):
#         assert(bus_speed_hz in [mhz * 1000000 for mhz in [0.5, 1, 2, 4, 8, 16, 20, 24, 28, 32, 36, 40, 44, 48, 50, 52]])
# 
#         bitbang.__init__(self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
# 
#         try:
#             self._spi = spi or self.__spidev__()
#             self._spi.open(port, device)
#             if spi_mode:
#                 self._spi.mode = spi_mode
#             if "cs_high" in kwargs:
#                 import warnings
#                 warnings.warn(
#                     "SPI cs_high is no longer supported in kernel 5.4.51 and beyond, so setting parameter cs_high is now ignored!",
#                     RuntimeWarning
#                 )
#         except (IOError, OSError) as e:
#             if e.errno == errno.ENOENT:
#                 raise luma.core.error.DeviceNotFoundError('SPI device not found')
#             else:  # pragma: no cover
#                 raise
# 
#         self._spi.max_speed_hz = bus_speed_hz
# 
#     def _write_bytes(self, data):
#         self._spi.writebytes(data)
# 
#     def cleanup(self):
#         """
#         Clean up SPI & GPIO resources.
#         """
#         self._spi.close()
#         super(spi, self).cleanup()


# Path: tests/test_spi.py
import pytest
import luma.core.error
from unittest.mock import Mock, call
from luma.core.interface.serial import spi
from helpers import get_spidev, rpi_gpio_missing, fib, assert_only_cleans_whats_setup

    device = 2
    bus_speed = 16000000
    dc = 17
    rst = 11

    spi(gpio=gpio, spi=spidev, port=port, device=device, bus_speed_hz=16000000,
        gpio_DC=dc, gpio_RST=rst)
    verify_spi_init(port, device, bus_speed, dc, rst)
    gpio.output.assert_has_calls([
        call(rst, gpio.LOW),
        call(rst, gpio.HIGH)
    ])


def test_init_invalid_bus_speed():
    with pytest.raises(AssertionError):
        spi(gpio=gpio, spi=spidev, port=5, device=2, bus_speed_hz=942312)


def test_command():
    cmds = [3, 1, 4, 2]
    serial = spi(gpio=gpio, spi=spidev, port=9, device=1)
    serial.command(*cmds)
    verify_spi_init(9, 1)
    gpio.output.assert_has_calls([call(25, gpio.HIGH), call(24, gpio.LOW)])
    spidev.writebytes.assert_called_once_with(cmds)


def test_data():
    data = list(fib(100))

    serial = spi(gpio=gpio, spi=spidev, port=9, device=1)