Skip to content
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

Refactor bt_list_adapters.py and add unit tests to it (BugFix) #875

Merged
merged 3 commits into from
Dec 13, 2023
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
85 changes: 65 additions & 20 deletions providers/base/bin/bt_list_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
#
# This file is part of Checkbox.
#
# Copyright 2018 Canonical Ltd.
# Copyright 2018-2023 Canonical Ltd.
#
# Authors:
# Jonathan Cave <jonathan.cave@canonical.com>
# Pierre Equoy <pierre.equoy@canonical.com>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
Expand All @@ -19,28 +20,72 @@
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

import os
import sys
from collections import namedtuple
from pathlib import Path


BTDevice = namedtuple("BTDevice", ["sysfs_name", "device_name"])


def get_node_content(path):
"""Retrieve the content from a sysfs path.

:param path: path to the sysfs node
:type path: str or Path
:return: content of the sysfs node
:rtype: str
"""
with open(path, "r") as f:
content = f.read().strip()
return content


def is_bluetooth_adapter(path):
"""Check if a given sysfs node is a bluetooth adapter.

:param path: Path to the sysfs node
:type path: Path
:return: `True` if it is a bluetooth adapter, `False` otherwise
:rtype: bool
"""
type = get_node_content(path / "type")
if type == "bluetooth":
return True
return False


def get_bluetooth_devices(paths_list):
"""Retrieve information about bluetooth devices from a list of sysfs paths.

:param paths_list: List of sysfs paths to check
:type paths_list: list
:return: List of named tuples containing the sysfs basename and the device
name, for example:
[BTDevice(sysfs_name='rfkill3', device_name='dell-bluetooth'),]
:rtype: list
"""
rf_devices = []
for rfdev in paths_list:
if is_bluetooth_adapter(rfdev):
device_name = get_node_content(rfdev / "name")
btdev = BTDevice(rfdev.name, device_name)
rf_devices.append(btdev)
return rf_devices


def main():
rfkill = '/sys/class/rfkill'
found_adatper = False
for rfdev in os.listdir(rfkill):
typef = os.path.join(rfkill, rfdev, 'type')
type = ''
with open(typef, 'r') as f:
type = f.read().strip()
if type != 'bluetooth':
continue
found_adatper = True
namef = os.path.join(rfkill, rfdev, 'name')
name = ''
with open(namef, 'r') as f:
name = f.read().strip()
print(rfdev, name)
if found_adatper is False:
raise SystemExit('No bluetooth adatpers registered with rfkill')
try:
rf_devices_paths = list(Path("/sys/class/rfkill").iterdir())
except FileNotFoundError:
rf_devices_paths = []
rf_devices = get_bluetooth_devices(rf_devices_paths)
if rf_devices:
for rf_device in rf_devices:
print(" ".join(rf_device))
else:
raise SystemExit("No bluetooth adapters registered with rfkill")


if __name__ == "__main__":
main()
sys.exit(main())
65 changes: 65 additions & 0 deletions providers/base/tests/test_bt_list_adapters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import unittest
from unittest.mock import patch, mock_open
from pathlib import Path
from io import StringIO

import bt_list_adapters


class BTTests(unittest.TestCase):
pieqq marked this conversation as resolved.
Show resolved Hide resolved
@patch("builtins.open", new_callable=mock_open, read_data=" test ")
def test_get_node_content(self, mock_open):
content = bt_list_adapters.get_node_content(Path("test"))
self.assertEqual(content, "test")

@patch("builtins.open", new_callable=mock_open, read_data="bluetooth")
def test_is_bluetooth_adapter(self, mock_open):
self.assertTrue(bt_list_adapters.is_bluetooth_adapter(Path("test")))

@patch("builtins.open", new_callable=mock_open, read_data="not bluetooth")
def test_is_not_bluetooth_adapter(self, mock_open):
self.assertFalse(bt_list_adapters.is_bluetooth_adapter(Path("test")))

def test_bt_adapter_not_found(self):
list_bt_devices = bt_list_adapters.get_bluetooth_devices([])
self.assertEqual(list_bt_devices, [])

@patch("bt_list_adapters.is_bluetooth_adapter")
@patch("bt_list_adapters.get_node_content")
def test_bt_adapter_found(self, mock_content, mock_is_bt_adapter):
pieqq marked this conversation as resolved.
Show resolved Hide resolved
mock_content.return_value = "test"
mock_is_bt_adapter.return_value = True
fake_devices = bt_list_adapters.get_bluetooth_devices([Path("/test")])
self.assertEqual(fake_devices, [("test", "test")])

@patch("bt_list_adapters.get_bluetooth_devices")
@patch("bt_list_adapters.Path.iterdir")
@patch("sys.stdout", new_callable=StringIO)
def test_main(self, mock_stdout, mock_path, mock_bt_devices):
mock_path.return_value = [
"test",
]
btdev = bt_list_adapters.BTDevice("test", "test")
mock_bt_devices.return_value = [
btdev,
]
bt_list_adapters.main()
self.assertEqual(mock_stdout.getvalue().strip(), "test test")

@patch("bt_list_adapters.get_bluetooth_devices")
@patch("bt_list_adapters.Path.iterdir")
@patch("sys.stdout", new_callable=StringIO)
def test_main_no_bt_devices(self, mock_stdout, mock_path, mock_bt_devices):
mock_path.return_value = []
mock_bt_devices.return_value = []
with self.assertRaises(SystemExit):
bt_list_adapters.main()

@patch("bt_list_adapters.get_bluetooth_devices")
@patch("bt_list_adapters.Path.iterdir")
@patch("sys.stdout", new_callable=StringIO)
def test_main_no_rfkill_path(self, mock_stdout, mock_path, mock_bt_devices):
mock_path.side_effect = FileNotFoundError
mock_bt_devices.return_value = []
with self.assertRaises(SystemExit):
bt_list_adapters.main()
Loading