Skip to content

Pylint and RTD update patch, and other fixes #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ jobs:
# (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.)
run: |
source actions-ci/install.sh
- name: Pip install pylint, Sphinx, pre-commit
- name: Pip install Sphinx, pre-commit
run: |
pip install --force-reinstall pylint Sphinx sphinx-rtd-theme pre-commit
pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit
- name: Library version
run: git describe --dirty --always --tags
- name: Pre-commit hooks
Expand Down
32 changes: 16 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: pylint-2.7.1
rev: v2.11.1
hooks:
- id: pylint
name: pylint (library code)
types: [python]
exclude: "^(docs/|tests/|examples/|setup.py$)"
- repo: local
hooks:
- id: pylint_examples
name: pylint (examples code)
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
description: Run pylint rules on "examples/*.py" files
entry: /usr/bin/env bash -c
args: ['([[ ! -d "examples" ]] || for example in $(find . -path "./examples/*.py"); do pylint --disable=missing-docstring,invalid-name,consider-using-f-string $example; done)']
language: system
- repo: local
hooks:
- id: pylint_tests
name: pylint (tests code)
types: [python]
files: "^examples/"
args:
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- id: pylint
name: pylint (test code)
description: Run pylint rules on "tests/*.py" files
entry: /usr/bin/env bash -c
args: ['([[ ! -d "tests" ]] || for test in $(find . -path "./tests/*.py"); do pylint --disable=missing-docstring,consider-using-f-string $test; done)']
language: system
types: [python]
files: "^tests/"
args:
- --disable=missing-docstring,consider-using-f-string,duplicate-code
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ ignore-docstrings=yes
ignore-imports=yes

# Minimum lines number of a similarity.
min-similarity-lines=12
min-similarity-lines=4


[BASIC]
Expand Down
5 changes: 5 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

sphinx>=4.0.0
102 changes: 54 additions & 48 deletions tests/test_adafruit_radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,111 +46,115 @@ def test_radio_init_channel():
assert r._channel == 7 # pylint: disable=protected-access


def test_radio_configure_channel(radio):
def test_radio_configure_channel(radio_obj):
"""
If a valid channel argument is passed to the configure method, the Radio
instance's channel is updated to reflect this.
"""
assert radio._channel == 42 # pylint: disable=protected-access
radio.configure(channel=7)
assert radio._channel == 7 # pylint: disable=protected-access
assert radio_obj._channel == 42 # pylint: disable=protected-access
radio_obj.configure(channel=7)
assert radio_obj._channel == 7 # pylint: disable=protected-access


def test_radio_configure_channel_out_of_bounds(radio):
def test_radio_configure_channel_out_of_bounds(
radio_obj,
): # pylint: disable=invalid-name
"""
If a channel not in the range 0-255 is passed into the configure method,
then a ValueError exception is raised.
"""
with pytest.raises(ValueError):
radio.configure(channel=-1)
radio_obj.configure(channel=-1)
with pytest.raises(ValueError):
radio.configure(channel=256)
radio_obj.configure(channel=256)
# Add just-in-bounds checks too.
radio.configure(channel=0)
assert radio._channel == 0 # pylint: disable=protected-access
radio.configure(channel=255)
assert radio._channel == 255 # pylint: disable=protected-access
radio_obj.configure(channel=0)
assert radio_obj._channel == 0 # pylint: disable=protected-access
radio_obj.configure(channel=255)
assert radio_obj._channel == 255 # pylint: disable=protected-access


def test_radio_send(radio):
def test_radio_send(radio_obj):
"""
The send method merely encodes to bytes and calls send_bytes.
"""
radio.send_bytes = mock.MagicMock()
radio_obj.send_bytes = mock.MagicMock()
msg = "Testing 1, 2, 3..."
radio.send(msg)
radio.send_bytes.assert_called_once_with(msg.encode("utf-8"))
radio_obj.send(msg)
radio_obj.send_bytes.assert_called_once_with(msg.encode("utf-8"))


def test_radio_send_bytes_too_long(radio):
def test_radio_send_bytes_too_long(radio_obj):
"""
A ValueError is raised if the message to be sent is too long (defined by
MAX_LENGTH).
"""
msg = bytes(adafruit_ble_radio.MAX_LENGTH + 1)
with pytest.raises(ValueError):
radio.send_bytes(msg)
radio_obj.send_bytes(msg)


def test_radio_send_bytes(radio):
def test_radio_send_bytes(radio_obj):
"""
Ensure the expected message is set on an instance of AdafruitRadio, and
broadcast for AD_DURATION period of time.
"""
radio.uid = 255 # set up for cycle back to 0.
radio_obj.uid = 255 # set up for cycle back to 0.
msg = b"Hello"
with mock.patch("adafruit_ble_radio.time.sleep") as mock_sleep:
radio.send_bytes(msg)
radio_obj.send_bytes(msg)
mock_sleep.assert_called_once_with(adafruit_ble_radio.AD_DURATION)
spy_advertisement = (
adafruit_ble_radio._RadioAdvertisement()
adafruit_ble_radio._RadioAdvertisement() # pylint: disable=protected-access
) # pylint: disable=protected-access
chan = struct.pack("<B", radio._channel) # pylint: disable=protected-access
chan = struct.pack("<B", radio_obj._channel) # pylint: disable=protected-access
uid = struct.pack("<B", 255)
assert spy_advertisement.msg == chan + uid + msg
radio.ble.start_advertising.assert_called_once_with(spy_advertisement)
radio.ble.stop_advertising.assert_called_once_with()
assert radio.uid == 0
radio_obj.ble.start_advertising.assert_called_once_with(spy_advertisement)
radio_obj.ble.stop_advertising.assert_called_once_with()
assert radio_obj.uid == 0


def test_radio_receive_no_message(radio):
def test_radio_receive_no_message(radio_obj):
"""
If no message is received from the receive_bytes method, then None is
returned.
"""
radio.receive_full = mock.MagicMock(return_value=None)
assert radio.receive() is None
radio.receive_full.assert_called_once_with()
radio_obj.receive_full = mock.MagicMock(return_value=None)
assert radio_obj.receive() is None
radio_obj.receive_full.assert_called_once_with()


def test_radio_receive(radio):
def test_radio_receive(radio_obj):
"""
If bytes are received from the receive_bytes method, these are decoded
using utf-8 and returned as a string with null characters stripped from the
end.
"""
# Return value contains message bytes, RSSI (signal strength), timestamp.
msg = b"testing 1, 2, 3\x00\x00\x00\x00\x00\x00"
radio.receive_full = mock.MagicMock(return_value=(msg, -20, 1.2))
assert radio.receive() == "testing 1, 2, 3"
radio_obj.receive_full = mock.MagicMock(return_value=(msg, -20, 1.2))
assert radio_obj.receive() == "testing 1, 2, 3"


def test_radio_receive_full_no_messages(radio):
def test_radio_receive_full_no_messages(radio_obj): # pylint: disable=invalid-name
"""
If no messages are detected by receive_full then it returns None.
"""
radio.ble.start_scan.return_value = []
assert radio.receive_full() is None
radio.ble.start_scan.assert_called_once_with(
adafruit_ble_radio._RadioAdvertisement,
radio_obj.ble.start_scan.return_value = []
assert radio_obj.receive_full() is None
radio_obj.ble.start_scan.assert_called_once_with(
adafruit_ble_radio._RadioAdvertisement, # pylint: disable=protected-access
minimum_rssi=-255,
timeout=1,
extended=True,
)
radio.ble.stop_scan.assert_called_once_with()
radio_obj.ble.stop_scan.assert_called_once_with()


def test_radio_receive_full_duplicate_message(radio):
def test_radio_receive_full_duplicate_message(
radio_obj,
): # pylint: disable=invalid-name
"""
If a duplicate message is detected, then receive_full returns None
(indicating no *new* messages received).
Expand All @@ -159,12 +163,14 @@ def test_radio_receive_full_duplicate_message(radio):
mock_entry.msg = b"*\x00Hello"
mock_entry.address.address_bytes = b"addr"
mock_entry.rssi = -40
radio.ble.start_scan.return_value = [mock_entry]
radio.msg_pool.add((time.monotonic(), 42, 0, b"addr"))
assert radio.receive_full() is None
radio_obj.ble.start_scan.return_value = [mock_entry]
radio_obj.msg_pool.add((time.monotonic(), 42, 0, b"addr"))
assert radio_obj.receive_full() is None


def test_radio_receive_full_and_remove_expired_message_metadata(radio):
def test_radio_receive_full_and_remove_expired_message_metadata(
radio_obj,
): # pylint: disable=invalid-name
"""
Return the non-duplicate message.

Expand All @@ -177,15 +183,15 @@ def test_radio_receive_full_and_remove_expired_message_metadata(radio):
mock_entry.msg = b"*\x01Hello"
mock_entry.address.address_bytes = b"adr2"
mock_entry.rssi = -40
radio.ble.start_scan.return_value = [mock_entry]
radio.msg_pool.add(
radio_obj.ble.start_scan.return_value = [mock_entry]
radio_obj.msg_pool.add(
(time.monotonic() - adafruit_ble_radio.AD_DURATION - 1, 42, 0, b"addr")
)
result = radio.receive_full()
result = radio_obj.receive_full()
assert result[0] == b"Hello"
assert result[1] == -40
assert len(radio.msg_pool) == 1
metadata = radio.msg_pool.pop()
assert len(radio_obj.msg_pool) == 1
metadata = radio_obj.msg_pool.pop()
assert metadata[1] == 42
assert metadata[2] == 1
assert metadata[3] == b"adr2"