Skip to content

Commit fb5af43

Browse files
authored
improve UART mock (#14)
separate busio docs into separate pages related to each bus
1 parent 21d4fcc commit fb5af43

File tree

11 files changed

+180
-65
lines changed

11 files changed

+180
-65
lines changed

circuitpython_mocks/_mixins.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
SPITransfer,
1313
UARTRead,
1414
UARTWrite,
15+
UARTFlush,
1516
)
1617
from circuitpython_mocks.digitalio.operations import SetState, GetState
1718

@@ -79,6 +80,7 @@ def __init__(self, **kwargs) -> None:
7980
SPITransfer,
8081
UARTRead,
8182
UARTWrite,
83+
UARTFlush,
8284
SetState,
8385
GetState,
8486
]
@@ -90,7 +92,7 @@ def __init__(self, **kwargs) -> None:
9092
9193
Examples that use `expectations` can be found in the
9294
93-
- :doc:`busio` documentation
95+
- :doc:`busio/index` documentation
9496
- :doc:`digitalio` documentation
9597
- :py:func:`~circuitpython_mocks.fixtures.mock_blinka_imports`
9698
(pytest fixture) documentation

circuitpython_mocks/busio/__init__.py

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
55
.. md-tab-item:: I2C
66
7-
.. literalinclude:: ../tests/test_i2c.py
7+
.. literalinclude:: ../../tests/test_i2c.py
88
:language: python
99
1010
.. md-tab-item:: SPI
1111
12-
.. literalinclude:: ../tests/test_spi.py
12+
.. literalinclude:: ../../tests/test_spi.py
1313
:language: python
1414
1515
.. md-tab-item:: UART
1616
17-
.. literalinclude:: ../tests/test_uart.py
17+
.. literalinclude:: ../../tests/test_uart.py
1818
:language: python
1919
"""
2020

@@ -27,6 +27,7 @@
2727
from circuitpython_mocks.busio.operations import (
2828
UARTRead,
2929
UARTWrite,
30+
UARTFlush,
3031
I2CRead,
3132
I2CWrite,
3233
I2CTransfer,
@@ -310,9 +311,11 @@ def __init__(
310311
timeout: float = 1,
311312
receiver_buffer_size: int = 64,
312313
) -> None:
314+
self._baudrate = baudrate
315+
self._timeout = timeout
313316
super().__init__()
314317

315-
def read(self, nbytes: int | None = None) -> bytes | None:
318+
def read(self, nbytes: int | None = None) -> Optional[bytes]:
316319
"""A function that mocks :external:py:meth:`busio.UART.read()`.
317320
318321
.. mock-expects::
@@ -323,12 +326,12 @@ def read(self, nbytes: int | None = None) -> bytes | None:
323326
assert self.expectations, "no expectation found for UART.read()"
324327
op = self.expectations.popleft()
325328
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
326-
length = nbytes or len(op.response)
329+
length = nbytes or 0 if not op.response else len(op.response)
327330
buffer = bytearray(length)
328331
op.assert_response(buffer, 0, length)
329-
return bytes(buffer)
332+
return None if not buffer else bytes(buffer)
330333

331-
def readinto(self, buf: circuitpython_typing.WriteableBuffer) -> int | None:
334+
def readinto(self, buf: circuitpython_typing.WriteableBuffer) -> Optional[int]:
332335
"""A function that mocks :external:py:meth:`busio.UART.readinto()`.
333336
334337
.. mock-expects::
@@ -339,11 +342,11 @@ def readinto(self, buf: circuitpython_typing.WriteableBuffer) -> int | None:
339342
assert self.expectations, "no expectation found for UART.readinto()"
340343
op = self.expectations.popleft()
341344
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
342-
len_buf = len(op.response)
345+
len_buf = 0 if not op.response else len(op.response)
343346
op.assert_response(buf, 0, len_buf)
344347
return len_buf
345348

346-
def readline(self) -> bytes:
349+
def readline(self) -> Optional[bytes]:
347350
"""A function that mocks :external:py:meth:`busio.UART.readline()`.
348351
349352
.. mock-expects::
@@ -354,10 +357,10 @@ def readline(self) -> bytes:
354357
assert self.expectations, "no expectation found for UART.readline()"
355358
op = self.expectations.popleft()
356359
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
357-
len_buf = len(op.response)
360+
len_buf = 0 if not op.response else len(op.response)
358361
buf = bytearray(len_buf)
359362
op.assert_response(buf, 0, len_buf)
360-
return bytes(buf)
363+
return None if buf else bytes(buf)
361364

362365
def write(self, buf: circuitpython_typing.ReadableBuffer) -> int | None:
363366
"""A function that mocks :external:py:meth:`busio.UART.write()`.
@@ -373,3 +376,48 @@ def write(self, buf: circuitpython_typing.ReadableBuffer) -> int | None:
373376
len_buf = len(op.expected)
374377
op.assert_expected(buf, 0, len_buf)
375378
return len(buf) or None
379+
380+
@property
381+
def baudrate(self) -> int:
382+
"""The current baudrate."""
383+
return self._baudrate
384+
385+
@baudrate.setter
386+
def baudrate(self, val: int):
387+
self._baudrate = int(val)
388+
389+
@property
390+
def timeout(self) -> float:
391+
"""The current timeout, in seconds."""
392+
return self._timeout
393+
394+
@timeout.setter
395+
def timeout(self, val: float):
396+
self._timeout = float(val)
397+
398+
@property
399+
def in_waiting(self) -> int:
400+
"""The number of bytes in the input buffer, available to be read.
401+
402+
.. mock-expects::
403+
404+
This property peeks at the number of bytes in next available operation in
405+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`. If a
406+
`UARTRead` operation is not immediately expected, then ``0`` is returned.
407+
"""
408+
if self.expectations and isinstance(self.expectations[0], UARTRead):
409+
return len(self.expectations[0].response)
410+
return 0
411+
412+
def reset_input_buffer(self) -> None:
413+
"""Discard any unread characters in the input buffer.
414+
415+
.. mock-expects::
416+
417+
This function merely checks the immediately queued
418+
:py:class:`~circuitpython_mocks._mixins.Expecting.expectations` for
419+
a `UARTFlush` operation. It does not actually discard any data.
420+
"""
421+
assert self.expectations
422+
op = self.expectations.popleft()
423+
assert isinstance(op, UARTFlush), f"Flush operation expected, found {repr(op)}"

circuitpython_mocks/busio/operations.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Optional
22
import sys
33
import circuitpython_typing as cir_py_types
44

@@ -196,13 +196,46 @@ def assert_transaction(
196196

197197
class UARTRead(_Read):
198198
"""A class to identify a read operation over a
199-
:py:class:`~circuitpython_mocks.busio.UART` bus."""
199+
:py:class:`~circuitpython_mocks.busio.UART` bus.
200200
201-
pass
201+
.. tip::
202+
To emulate a timeout condition, pass a `None` value to the ``response``
203+
parameter.
204+
"""
205+
206+
def __init__(self, response: Optional[bytearray], **kwargs) -> None:
207+
super().__init__(response, **kwargs) # type: ignore[arg-type]
208+
209+
def __repr__(self) -> str:
210+
if not self.response:
211+
return "<Read response='None'>"
212+
return super().__repr__()
213+
214+
def assert_response(
215+
self,
216+
buffer: cir_py_types.ReadableBuffer,
217+
start: int = 0,
218+
end: int = sys.maxsize,
219+
):
220+
if not self.response:
221+
buffer = self.response
222+
return
223+
return super().assert_response(buffer, start, end)
202224

203225

204226
class UARTWrite(_Write):
205227
"""A class to identify a write operation over a
206228
:py:class:`~circuitpython_mocks.busio.UART` bus."""
207229

208230
pass
231+
232+
233+
class UARTFlush:
234+
"""A class to identify a flush operation over a
235+
:py:class:`~circuitpython_mocks.busio.UART` bus.
236+
237+
This operation corresponds to the function
238+
:py:meth:`~circuitpython_mocks.busio.UART.reset_input_buffer()`.
239+
"""
240+
241+
pass

docs/busio.rst

Lines changed: 0 additions & 46 deletions
This file was deleted.

docs/busio/i2c.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
``busio.I2C``
2+
=============
3+
4+
.. autoclass:: circuitpython_mocks.busio.I2C
5+
:members: readfrom_into, writeto, writeto_then_readfrom, scan, try_lock, unlock, deinit
6+
7+
I2C operations
8+
**************
9+
10+
.. autoclass:: circuitpython_mocks.busio.operations.I2CRead
11+
.. autoclass:: circuitpython_mocks.busio.operations.I2CWrite
12+
.. autoclass:: circuitpython_mocks.busio.operations.I2CTransfer
13+
.. autoclass:: circuitpython_mocks.busio.operations.I2CScan

docs/busio/index.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
``busio``
2+
=================
3+
4+
.. automodule:: circuitpython_mocks.busio
5+
6+
Related Pages
7+
-------------
8+
9+
.. toctree::
10+
:maxdepth: 2
11+
12+
i2c
13+
spi
14+
uart

docs/busio/spi.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
``busio.SPI``
2+
=============
3+
4+
.. autoclass:: circuitpython_mocks.busio.SPI
5+
:members: readinto, write, write_readinto, configure, frequency, try_lock, unlock, deinit
6+
7+
SPI operations
8+
**************
9+
10+
.. autoclass:: circuitpython_mocks.busio.operations.SPIRead
11+
.. autoclass:: circuitpython_mocks.busio.operations.SPIWrite
12+
.. autoclass:: circuitpython_mocks.busio.operations.SPITransfer

docs/busio/uart.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
``busio.UART``
2+
==============
3+
4+
.. autoclass:: circuitpython_mocks.busio.UART
5+
:members: readinto, readline, write, in_waiting, reset_input_buffer, timeout, baudrate
6+
7+
.. py:class:: circuitpython_mocks.busio.UART.Parity
8+
9+
A mock enumeration of :external:py:class:`busio.Parity`.
10+
11+
.. py:attribute:: ODD
12+
:type: Parity
13+
.. py:attribute:: EVEN
14+
:type: Parity
15+
16+
UART operations
17+
***************
18+
19+
.. autoclass:: circuitpython_mocks.busio.operations.UARTRead
20+
.. autoclass:: circuitpython_mocks.busio.operations.UARTWrite
21+
.. autoclass:: circuitpython_mocks.busio.operations.UARTFlush

docs/index.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ Pytest fixtures
1919
Mocked API
2020
----------
2121

22+
.. toctree::
23+
:maxdepth: 4
24+
25+
busio/index
26+
2227
.. toctree::
2328
:maxdepth: 2
2429

25-
busio
2630
digitalio
2731
board

tests/test_uart.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
11
from collections import deque
22
from circuitpython_mocks import board, busio
3-
from circuitpython_mocks.busio.operations import UARTRead, UARTWrite
3+
from circuitpython_mocks.busio.operations import UARTRead, UARTWrite, UARTFlush
44

55

66
def test_singleton():
77
board_uart = board.UART()
88
assert hasattr(board_uart, "expectations")
99
assert isinstance(board_uart.expectations, deque)
10-
1110
busio_uart = busio.UART(board.TX, board.RX)
1211
assert board_uart == busio_uart
12+
board_uart.timeout = 0.5
13+
assert 0.5 == busio_uart.timeout
14+
busio_uart.baudrate = 115200
15+
assert 115200 == board_uart.baudrate
1316

1417
board_uart.expectations.append(UARTRead(bytearray(1)))
1518
assert busio_uart.expectations == board_uart.expectations
1619
buffer = bytearray(1)
20+
assert 1 == board_uart.in_waiting
1721
board_uart.readinto(buffer)
1822
assert not busio_uart.expectations
1923

2024
busio_uart.expectations.append(UARTWrite(bytearray(1)))
2125
assert busio_uart.expectations == board_uart.expectations
26+
assert not busio_uart.in_waiting
2227
busio_uart.write(bytearray(1))
2328

29+
busio_uart.expectations.append(UARTFlush())
30+
assert busio_uart.expectations == board_uart.expectations
31+
busio_uart.reset_input_buffer()
32+
2433
# assert all expectations were used
2534
board_uart.done()
2635
assert busio_uart.expectations == board_uart.expectations

0 commit comments

Comments
 (0)