Skip to content

Commit 21d4fcc

Browse files
authored
add I2CScan operation (#13)
1 parent fe280c6 commit 21d4fcc

File tree

5 files changed

+39
-4
lines changed

5 files changed

+39
-4
lines changed

circuitpython_mocks/_mixins.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
I2CRead,
77
I2CWrite,
88
I2CTransfer,
9+
I2CScan,
910
SPIRead,
1011
SPIWrite,
1112
SPITransfer,
@@ -72,6 +73,7 @@ def __init__(self, **kwargs) -> None:
7273
I2CRead,
7374
I2CWrite,
7475
I2CTransfer,
76+
I2CScan,
7577
SPIRead,
7678
SPIWrite,
7779
SPITransfer,

circuitpython_mocks/busio/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
I2CRead,
3131
I2CWrite,
3232
I2CTransfer,
33+
I2CScan,
3334
SPIRead,
3435
SPIWrite,
3536
SPITransfer,
@@ -82,9 +83,18 @@ def __init__(
8283
super().__init__()
8384

8485
def scan(self) -> List[int]:
85-
"""Returns an empty list.
86-
Use :py:meth:`pytest.MonkeyPatch.setattr()` to change this output."""
87-
return []
86+
"""Scan all I2C addresses between 0x08 and 0x77 inclusive and return a list of
87+
those that respond.
88+
89+
.. mock-expects::
90+
91+
This function will check against `I2CScan`
92+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`.
93+
"""
94+
assert self.expectations, "no expectation found for I2C.scan()"
95+
op = self.expectations.popleft()
96+
assert isinstance(op, I2CScan), f"I2CScan operation expected, found {repr(op)}"
97+
return op.expected
8898

8999
def readfrom_into(
90100
self,

circuitpython_mocks/busio/operations.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import List
12
import sys
23
import circuitpython_typing as cir_py_types
34

@@ -140,6 +141,23 @@ def __repr__(self) -> str:
140141
)
141142

142143

144+
class I2CScan:
145+
"""A class to identify a scan operation over a
146+
:py:class:`~circuitpython_mocks.busio.I2C` bus.
147+
148+
The given ``expected`` value will be the result of `I2C.scan()`.
149+
"""
150+
151+
def __init__(self, expected: List[int]) -> None:
152+
for val in expected:
153+
assert val <= 0x7F, f"scan result {val} is not a valid I2C address"
154+
self.expected = expected
155+
156+
def __repr__(self) -> str:
157+
stringify = ", ".join(["%02X" % x for x in self.expected])
158+
return f"<I2CScan expected='[{stringify}]'>"
159+
160+
143161
class SPIRead(_Read):
144162
"""A class to identify a read operation over a
145163
:py:class:`~circuitpython_mocks.busio.SPI` bus."""

docs/busio.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
.. autoclass:: circuitpython_mocks.busio.operations.I2CRead
3131
.. autoclass:: circuitpython_mocks.busio.operations.I2CWrite
3232
.. autoclass:: circuitpython_mocks.busio.operations.I2CTransfer
33+
.. autoclass:: circuitpython_mocks.busio.operations.I2CScan
3334

3435
SPI operations
3536
**************

tests/test_i2c_fixture.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
I2CRead,
33
I2CWrite,
44
I2CTransfer,
5+
I2CScan,
56
)
67

78
pytest_plugins = ["circuitpython_mocks.fixtures"]
@@ -15,7 +16,10 @@ def test_i2c(mock_blinka_imports):
1516
address = 0x42
1617
# do setup
1718
with I2C(board.SCL, board.SDA) as i2c_bus:
18-
assert i2c_bus.scan() == []
19+
i2c_bus.expectations.append(I2CScan([address]))
20+
assert i2c_bus.try_lock()
21+
assert address in i2c_bus.scan()
22+
i2c_bus.unlock()
1923

2024
# set expectation for probing performed by I2CDevice.__init__()
2125
i2c_bus.expectations.append(I2CWrite(address, b""))

0 commit comments

Comments
 (0)