123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- # python-cc1101 - Python Library to Transmit RF Signals via CC1101 Transceivers
- #
- # Copyright (C) 2020 Fabian Peter Hammerle <fabian@hammerle.me>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- import contextlib
- import unittest.mock
- import pytest
- import cc1101
- import cc1101.addresses
- # pylint: disable=protected-access
- @pytest.fixture(scope="function")
- def spidev_mock(tmp_path):
- class _SpiDevMock:
- def __init__(self):
- self._file = None
- def open(self, bus, select):
- path = tmp_path.joinpath(f"spidev{bus}.{select}~")
- self._file = path.open("w+")
- def fileno(self):
- # mimic behaviour of spidev.SpiDev.fileno()
- return self._file.fileno() if self._file else -1
- def close(self):
- self._file.close()
- self._file = None # for fileno
- return _SpiDevMock
- # pylint: disable=redefined-outer-name; using fixture
- @contextlib.contextmanager
- def _mock_hardware_access():
- with unittest.mock.patch.object(
- cc1101.CC1101, "_reset"
- ), unittest.mock.patch.object(
- cc1101.CC1101, "_verify_chip"
- ), unittest.mock.patch.object(
- cc1101.CC1101, "_configure_defaults"
- ), unittest.mock.patch.object(
- cc1101.CC1101,
- "get_main_radio_control_state_machine_state",
- return_value=cc1101.MainRadioControlStateMachineState.IDLE,
- ):
- yield
- def test_context_lock(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=True)
- with transceiver:
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver2 = cc1101.CC1101(lock_spi_device=True)
- with pytest.raises(BlockingIOError):
- with transceiver2:
- pass
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver3 = cc1101.CC1101(lock_spi_device=False)
- with transceiver3:
- pass
- with transceiver2: # closing unlocks
- pass
- def test_context_no_lock(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=False)
- with transceiver:
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver2 = cc1101.CC1101(lock_spi_device=True)
- with transceiver2:
- pass
- def test_unlock_spi_device(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=True)
- transceiver2 = cc1101.CC1101(lock_spi_device=True)
- with transceiver: # acquire lock
- with pytest.raises(BlockingIOError):
- with transceiver2:
- pass
- transceiver.unlock_spi_device()
- with transceiver2:
- pass
- def test_unlock_spi_device_double(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=True)
- # verify no error occurs
- with transceiver: # acquire lock
- transceiver.unlock_spi_device()
- transceiver.unlock_spi_device()
- def test_unlock_spi_device_outside_context(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=True)
- # verify no error occurs
- transceiver.unlock_spi_device()
- with transceiver: # acquire lock
- pass
- transceiver.unlock_spi_device()
- def test_unlock_spi_device_no_lock(spidev_mock):
- with _mock_hardware_access():
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver = cc1101.CC1101(lock_spi_device=False)
- with transceiver: # no lock acquired
- # verify no error occurs
- transceiver.unlock_spi_device()
- def test_unlock_on_exception_within_context(spidev_mock):
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver1 = cc1101.CC1101(lock_spi_device=True)
- transceiver2 = cc1101.CC1101(lock_spi_device=True)
- with _mock_hardware_access():
- with pytest.raises(ValueError, match=r"^test$"):
- with transceiver1:
- raise ValueError("test")
- with transceiver2:
- pass # no BlockingIOError
- # does locking still work after exception?
- with transceiver1:
- with pytest.raises(BlockingIOError):
- with transceiver2:
- pass
- with transceiver2:
- with pytest.raises(BlockingIOError):
- with transceiver1:
- pass
- def test_unlock_on_exception_when_entering_context(spidev_mock):
- with unittest.mock.patch("spidev.SpiDev", spidev_mock):
- transceiver1 = cc1101.CC1101(lock_spi_device=True)
- transceiver2 = cc1101.CC1101(lock_spi_device=True)
- with _mock_hardware_access():
- with unittest.mock.patch.object(
- cc1101.CC1101, "_verify_chip", side_effect=ValueError("test")
- ), pytest.raises(ValueError, match=r"^test$"):
- with transceiver1:
- pass # never reached
- with transceiver2:
- pass # no BlockingIOError
- # does locking still work after exception?
- with transceiver1:
- with pytest.raises(BlockingIOError):
- with transceiver2:
- pass
- with transceiver2:
- with pytest.raises(BlockingIOError):
- with transceiver1:
- pass
|