diff --git a/library/tests/conftest.py b/library/tests/conftest.py new file mode 100644 index 0000000..7582e08 --- /dev/null +++ b/library/tests/conftest.py @@ -0,0 +1,38 @@ +"""Test configuration. +These allow the mocking of various Python modules +that might otherwise have runtime side-effects. +""" +import sys +import mock +import pytest + + +@pytest.fixture(scope='function', autouse=False) +def GPIO(): + """Mock RPi.GPIO module.""" + GPIO = mock.MagicMock() + # Fudge for Python < 37 (possibly earlier) + sys.modules['RPi'] = mock.Mock() + sys.modules['RPi'].GPIO = GPIO + sys.modules['RPi.GPIO'] = GPIO + yield GPIO + del sys.modules['RPi'] + del sys.modules['RPi.GPIO'] + + +@pytest.fixture(scope='function', autouse=False) +def spidev(): + """Mock spidev module.""" + spidev = mock.MagicMock() + sys.modules['spidev'] = spidev + yield spidev + del sys.modules['spidev'] + + +@pytest.fixture(scope='function', autouse=False) +def numpy(): + """Mock numpy module.""" + numpy = mock.MagicMock() + sys.modules['numpy'] = numpy + yield numpy + del sys.modules['numpy'] diff --git a/library/tests/test_dimensions.py b/library/tests/test_dimensions.py index 8de9af4..4eb4b61 100644 --- a/library/tests/test_dimensions.py +++ b/library/tests/test_dimensions.py @@ -1,24 +1,16 @@ -import sys -import mock +from tools import force_reimport -def _mock(): - sys.modules['numpy'] = mock.Mock() - sys.modules['spidev'] = mock.Mock() - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - - -def test_128_64_0(): - _mock() +def test_128_64_0(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=0) assert display.width == 128 assert display.height == 64 -def test_128_64_90(): - _mock() +def test_128_64_90(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=90) assert display.width == 64 diff --git a/library/tests/test_features.py b/library/tests/test_features.py new file mode 100644 index 0000000..f8fb7d4 --- /dev/null +++ b/library/tests/test_features.py @@ -0,0 +1,25 @@ +import mock +from tools import force_reimport + + +def test_display(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24) + numpy.dstack().flatten().tolist.return_value = [0xff, 0x00, 0xff, 0x00] + display.display(mock.MagicMock()) + + spidev.SpiDev().xfer3.assert_called_with([0xff, 0x00, 0xff, 0x00]) + + +def test_color565(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + assert ST7735.color565(255, 255, 255) == 0xFFFF + + +def test_image_to_data(GPIO, spidev, numpy): + force_reimport('ST7735') + numpy.dstack().flatten().tolist.return_value = [] + import ST7735 + assert ST7735.image_to_data(mock.MagicMock()) == [] diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index ac8bb04..8b89f47 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -1,13 +1,47 @@ -import sys import mock +from tools import force_reimport -def test_setup(): - sys.modules['numpy'] = mock.Mock() - sys.modules['spidev'] = mock.Mock() - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - +def test_setup(GPIO, spidev, numpy): + force_reimport('ST7735') import ST7735 display = ST7735.ST7735(port=0, cs=0, dc=24) del display + + GPIO.output.assert_has_calls([ + mock.call(24, True), + mock.call(24, False) + ], any_order=True) + + +def test_setup_no_invert(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, invert=False) + del display + + +def test_setup_with_backlight(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, backlight=4) + GPIO.setup.assert_called_with(4, GPIO.OUT) + + display.set_backlight(GPIO.HIGH) + + GPIO.output.assert_has_calls([ + mock.call(4, GPIO.LOW), + mock.call(4, GPIO.HIGH), + # Dozens of falls with True/False here + # due to _init() being called and the display + # setup setting the command/data pin + mock.call(4, GPIO.HIGH) + ], any_order=True) + + +def test_setup_with_reset(GPIO, spidev, numpy): + force_reimport('ST7735') + import ST7735 + display = ST7735.ST7735(port=0, cs=0, dc=24, rst=4) + GPIO.setup.assert_called_with(4, GPIO.OUT) + del display diff --git a/library/tests/tools.py b/library/tests/tools.py new file mode 100644 index 0000000..ddf752d --- /dev/null +++ b/library/tests/tools.py @@ -0,0 +1,23 @@ +import sys + + +def force_reimport(module): + """Force the module under test to be re-imported. + + Because pytest runs all tests within the same scope (this makes me cry) + we have to do some manual housekeeping to avoid tests polluting each other. + + Since conftest.py already does some sys.modules mangling I see no reason not to + do the same thing here. + """ + if "." in module: + steps = module.split(".") + else: + steps = [module] + + for i in range(len(steps)): + module = ".".join(steps[0:i + 1]) + try: + del sys.modules[module] + except KeyError: + pass diff --git a/library/tox.ini b/library/tox.ini index e53234d..b23507c 100644 --- a/library/tox.ini +++ b/library/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{27,35},qa +envlist = py{27,35,37},qa skip_missing_interpreters = True [testenv]