1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-17 00:07:37 +02:00
Files
jc/tests/test_bluetoothctl.py
Jake Ob ebbd4e9320 Fix broken controller parser schema to include power state prop (#652)
This commit fixes issue #627

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2025-04-02 11:32:19 -07:00

396 lines
14 KiB
Python

import json
import re
import unittest
from jc.parsers.bluetoothctl import (
_controller_head_pattern,
_controller_line_pattern,
_device_head_pattern,
_device_line_pattern,
_parse_controller,
_parse_device,
Controller,
Device,
parse,
)
class BluetoothctlTests(unittest.TestCase):
def test_bluetoothctl_nodata(self):
"""
Test 'bluetoothctl' with no data
"""
output=''
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_invalid_call(self):
"""
Test 'bluetoothctl' with output from invalid call
"""
output='Invalid command in menu main: foo'
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_with_invalid_args(self):
"""
Test 'bluetoothctl' with output from invalid arguments
"""
output='Too many arguments: 2 > 1'
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_no_controller(self):
"""
Test 'bluetoothctl' with no controller
"""
output='No default controller available'
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_no_controller_found(self):
"""
Test 'bluetoothctl' with no controller found
"""
output='Controller EB:06:EF:62:B3:33 not available'
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_no_device_found(self):
"""
Test 'bluetoothctl' with no device found
"""
output='Device EB:06:EF:62:B3:33 not available'
self.assertEqual(parse(output, quiet=True), [])
def test_bluetoothctl_controller(self):
"""
Test 'bluetoothctl' with controller
"""
with open("tests/fixtures/generic/bluetoothctl_controller.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
expected = {
"address": "CC:BB:AF:27:6A:E4",
"is_public": True,
"name": "arch",
"alias": "arch",
"class": "0x006c010c",
"powered": "yes",
"discoverable": "no",
"discoverable_timeout": "0x000000b4",
"pairable": "no",
"uuids": [
"Handsfree (0000111e-0000-1000-8000-00805f9b34fb)",
"Audio Source (0000110a-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)",
"PnP Information (00001200-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)",
"Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)"
],
"modalias": "usb:v1D6Bp0246d0542",
"discovering": "no"
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}")
def test_bluetoothctl_controller_2(self):
"""
Test 'bluetoothctl' with controller 2
"""
with open("tests/fixtures/generic/bluetoothctl_controller_2.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
expected = {
"address": "CC:52:AF:17:6A:E4",
"is_public": True,
"name": "starbase",
"alias": "starbase",
"class": "0x006c010c (7078156)",
"powered": "yes",
"power_state": "on",
"discoverable": "no",
"discoverable_timeout": "0x000000b4 (180)",
"pairable": "no",
"uuids": [
"Handsfree (0000111e-0000-1000-8000-00805f9b34fb)",
"Audio Source (0000110a-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)",
"PnP Information (00001200-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)",
"Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)"
],
"modalias": "usb:v1D6Bp0246d054F",
"discovering": "no"
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}")
def test_bluetoothctl_controller_with_manufacturer(self):
"""
Test 'bluetoothctl' with controller having manufacturer attr
"""
with open("tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
expected = {
"manufacturer": "0x0002 (2)",
"version": "0x08 (8)",
"address": "48:A4:72:3C:96:63",
"is_public": True,
"name": "ubuntu",
"alias": "ubuntu",
"class": "0x007c0104 (8126724)",
"powered": "yes",
"discoverable": "yes",
"discoverable_timeout": "0x000000b4 (180)",
"pairable": "yes",
"uuids": [
"Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)",
"OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb)",
"Message Access Server (00001132-0000-1000-8000-00805f9b34fb)",
"PnP Information (00001200-0000-1000-8000-00805f9b34fb)",
"IrMC Sync (00001104-0000-1000-8000-00805f9b34fb)",
"Vendor specific (00005005-0000-1000-8000-0002ee000001)",
"A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)",
"Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)",
"Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)",
"Device Information (0000180a-0000-1000-8000-00805f9b34fb)",
"Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)",
"Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)",
"Audio Source (0000110a-0000-1000-8000-00805f9b34fb)",
"OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb)",
"Handsfree (0000111e-0000-1000-8000-00805f9b34fb)"
],
"modalias": "usb:v1D6Bp0246d0548",
"discovering": "yes"
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}")
def test_bluetoothctl_controllers(self):
"""
Test 'bluetoothctl' with controllers
"""
output='Controller CC:52:AF:A4:6A:E4 arch [default]\n'
output+='Controller CC:53:AF:17:6A:34 logi'
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
self.assertIsNotNone(actual[1])
expected = [
{
"address": "CC:52:AF:A4:6A:E4",
"is_default": True,
"name": "arch"
},
{
"address": "CC:53:AF:17:6A:34",
"name": "logi"
},
]
if actual:
for k, v in expected[0].items():
self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}")
for k, v in expected[1].items():
self.assertEqual(v, actual[1][k], f"Controller regex failed on {k}")
def test_bluetoothctl_device(self):
"""
Test 'bluetoothctl' with device
"""
with open("tests/fixtures/generic/bluetoothctl_device.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
expected = {
"address": "EB:06:EF:62:B3:19",
"is_public": True,
"name": "TaoTronics TT-BH026",
"alias": "TaoTronics TT-BH026",
"class": "0x00240404",
"icon": "audio-headset",
"paired": "no",
"bonded": "no",
"trusted": "no",
"blocked": "no",
"connected": "no",
"legacy_pairing": "no",
"uuids": [
"Advanced Audio Distribu.. (0000110d-0000-1000-8000-00805f9b34fb)",
"Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)",
"A/V Remote Control Cont.. (0000110f-0000-1000-8000-00805f9b34fb)",
"Handsfree (0000111e-0000-1000-8000-00805f9b34fb)",
"Headset (00001108-0000-1000-8000-00805f9b34fb)",
"Headset HS (00001131-0000-1000-8000-00805f9b34fb)"
],
"rssi": -52,
"txpower": 4
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Device regex failed on {k}")
def test_bluetoothctl_device_with_battery(self):
"""
Test 'bluetoothctl' with device that has a battery
"""
with open("tests/fixtures/generic/bluetoothctl_device_with_battery.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0], actual)
expected = {
"name": "WH-1000XM3",
"is_public": True,
"is_random": False,
"address": "67:F6:B4:0E:5C:94",
"alias": "WH-1000XM3",
"appearance": "",
"class": "0x11240404 (2360324)",
"icon": "audio-headset",
"paired": "yes",
"bonded": "yes",
"trusted": "yes",
"blocked": "no",
"connected": "yes",
"legacy_pairing": "no",
"rssi": 0,
"txpower": 0,
"uuids": [
"Vendor specific (fd096fad-eed7-4504-943b-5fa1c0e761b2)",
"Vendor specific (03c57488-f7b6-45a3-8a23-ed4a890075cd)",
"Vendor specific (77a369ae-e453-4ff7-bc84-dc8f411eaa6a)",
"Vendor specific (8c274bd0-e7bd-4ed0-a391-55465e38005c)"
],
"modalias": "usb:v052Cp0DC3d1426",
"battery_percentage": 70
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Device regex failed on {k}")
def test_bluetoothctl_device_random(self):
"""
Test 'bluetoothctl' with device random
"""
with open("tests/fixtures/generic/bluetoothctl_device_random.out", "r") as f:
output = f.read()
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
expected = {
"address": "DF:1C:C3:B4:1A:1F",
"is_random": True,
"name": "M585/M590",
"alias": "M585/M590",
"appearance": "0x03c2",
"icon": "input-mouse",
"paired": "yes",
"bonded": "yes",
"trusted": "no",
"blocked": "no",
"connected": "no",
"legacy_pairing": "no",
"uuids": [
"Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)",
"Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)",
"Device Information (0000180a-0000-1000-8000-00805f9b34fb)",
"Battery Service (0000180f-0000-1000-8000-00805f9b34fb)",
"Human Interface Device (00001812-0000-1000-8000-00805f9b34fb)",
"Vendor specific (00010000-0000-1000-8000-011f2000046d)"
],
"modalias": "usb:v046DpB01Bd0011"
}
if actual:
for k, v in expected.items():
self.assertEqual(v, actual[0][k], f"Device regex failed on {k}")
def test_bluetoothctl_devices(self):
"""
Test 'bluetoothctl' with devices
"""
output='Device EB:06:EF:62:13:19 TaoTronics TT-BH026\n'
output+='Device AC:1F:EA:F8:AA:A1 wacom'
actual = parse(output, quiet=True)
self.assertIsNotNone(actual)
self.assertIsNotNone(actual[0])
self.assertIsNotNone(actual[1])
expected = [
{
"address": "EB:06:EF:62:13:19",
"name": "TaoTronics TT-BH026"
},
{
"address": "AC:1F:EA:F8:AA:A1",
"name": "wacom"
}
]
if actual:
for k, v in expected[0].items():
self.assertEqual(v, actual[0][k], f"Device regex failed on {k}")
for k, v in expected[1].items():
self.assertEqual(v, actual[1][k], f"Device regex failed on {k}")
if __name__ == '__main__':
unittest.main()