Skip to content
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
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 120
30 changes: 30 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
pull_request:
push:
branches: [master]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[dev]
- name: Lint with flake8
run: |
pip install flake8
flake8 examples tapsdk tests
- name: Run tests
run: pytest -v
15 changes: 15 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,21 @@ The dynamic range of the sensors is determined with the ```set_input_mode``` met

You can find some examples in the [examples folder](examples).

### Testing

To run the tests, first install the development dependencies:

```bash
pip install .[dev]
```

Then run the tests using pytest:

```bash
pytest
```


### Known Issues
An up-to-date list of known issues is available [here](History.md).

Expand Down
2 changes: 1 addition & 1 deletion examples/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async def run(loop):
print("Force Mouse Mode for 5 seconds")
await client.set_input_type(InputType.MOUSE)
await asyncio.sleep(5)

print("Force keyboard Mode for 5 seconds")
await client.set_input_type(InputType.KEYBOARD)
await asyncio.sleep(5)
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
bleak
setuptools
setuptools
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
REQUIRED = [
# linux reqs
'bleak==0.6.4;platform_system=="Linux"',
# macOS reqs
# macOS reqs
'bleak==0.12.1;platform_system=="Darwin"',
# Windows reqs
'pythonnet;platform_system=="Windows"'
Expand Down Expand Up @@ -82,13 +82,13 @@ def run(self):
author_email=EMAIL,
url=URL,
packages=find_packages(exclude=("tests", "examples", "docs")),
# package_data={"tapsdk.backends.dotnet": ["*.dll"]},
install_requires=REQUIRED,
# test_suite="tests",
# tests_require=TEST_REQUIRED,
include_package_data=True,
license="MIT",
python_requires='>=3.7'
python_requires='>=3.9',
extras_require={
"dev": ["pytest", "flake8"]
},
# classifiers=[
# # Trove classifiers
# # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
Expand Down
1 change: 0 additions & 1 deletion tapsdk/tap.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ async def _refresh_input_mode(self):
async def _write_input_mode(self, value):
await self.client.write_gatt_char(tap_mode_characteristic, value)


async def run(self):
stop_event = asyncio.Event()
devices = []
Expand Down
Empty file added tests/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import sys
import types


def pytest_configure(config):
bleak_stub = types.ModuleType("bleak")

class Dummy:
def __init__(self, *args, **kwargs):
pass

bleak_stub.BleakClient = Dummy
bleak_stub.BleakScanner = Dummy
bleak_stub._logger = types.SimpleNamespace(
debug=lambda *a, **k: None,
info=lambda *a, **k: None,
error=lambda *a, **k: None,
)
sys.modules.setdefault("bleak", bleak_stub)

core_mod = types.ModuleType("bleak.backends.corebluetooth.CentralManagerDelegate")
core_mod.CBUUID = type("CBUUID", (), {"UUIDWithString_": staticmethod(lambda x: x)})
core_mod.CentralManagerDelegate = type(
"CentralManagerDelegate",
(),
{"alloc": classmethod(lambda cls: type("Obj", (), {"init": lambda self: None})())},
)
sys.modules.setdefault("bleak.backends.corebluetooth.CentralManagerDelegate", core_mod)
53 changes: 53 additions & 0 deletions tests/test_cross_platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import importlib
import sys
import types
from unittest.mock import patch


def _make_bleak_stub():
bleak_stub = types.ModuleType("bleak")

class Dummy:
def __init__(self, *args, **kwargs):
pass

bleak_stub.BleakClient = Dummy
bleak_stub.BleakScanner = Dummy
bleak_stub._logger = types.SimpleNamespace(
debug=lambda *a, **k: None,
info=lambda *a, **k: None,
error=lambda *a, **k: None,
)
core_mod = types.ModuleType(
"bleak.backends.corebluetooth.CentralManagerDelegate"
)
core_mod.CBUUID = type("CBUUID", (), {"UUIDWithString_": staticmethod(lambda x: x)})
core_mod.CentralManagerDelegate = type(
"CentralManagerDelegate",
(),
{"alloc": classmethod(lambda cls: type("Obj", (), {"init": lambda self: None})())},
)
return bleak_stub, core_mod


def _load_tap(platform_name: str):
bleak_stub, core_stub = _make_bleak_stub()
with patch.dict(
sys.modules,
{
"bleak": bleak_stub,
"bleak.backends.corebluetooth.CentralManagerDelegate": core_stub,
},
):
with patch("platform.system", return_value=platform_name):
if "tapsdk.tap" in sys.modules:
module = importlib.reload(sys.modules["tapsdk.tap"])
else:
module = importlib.import_module("tapsdk.tap")
return module


def test_tapclient_defined_for_all_platforms():
for name in ["Linux", "Windows", "Darwin"]:
module = _load_tap(name)
assert hasattr(module, "TapClient")
17 changes: 17 additions & 0 deletions tests/test_inputmodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from tapsdk.inputmodes import TapInputMode, input_type_command
from tapsdk.enumerations import InputType


def test_input_mode_basic():
assert TapInputMode("text").get_command() == bytearray([0x3, 0xc, 0x0, 0x0])
assert TapInputMode("controller").get_command() == bytearray([0x3, 0xc, 0x0, 0x1])
assert TapInputMode("controller_text").get_command() == bytearray([0x3, 0xc, 0x0, 0x3])


def test_input_mode_raw_with_sensitivity():
mode = TapInputMode("raw", sensitivity=[1, 2, 3])
assert mode.get_command() == bytearray([0x3, 0xc, 0x0, 0xa, 1, 2, 3])


def test_input_type_command():
assert input_type_command(InputType.MOUSE) == bytearray([0x3, 0xd, 0x0, InputType.MOUSE.value])
55 changes: 55 additions & 0 deletions tests/test_parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import tapsdk.parsers as parsers


def test_mouse_data_msg():
data = bytearray([0, 1, 0, 2, 0, 0, 0, 0, 0, 1])
assert parsers.mouse_data_msg(data) == (1, 2, True)


def test_tap_data_msg():
data = bytearray([5])
assert parsers.tap_data_msg(data) == [5]


def test_raw_data_msg():
# 1. packet with one imu message
# IMU message: type=0, timestamp=123, 6 samples (12 bytes)
ts = 123
imu_ts = ts # type bit is 0, so ts stays 123
imu_bytes = imu_ts.to_bytes(4, 'little', signed=False)
imu_payload = b''
imu_samples = [100, -100, 200, -200, 300, -300]
for v in imu_samples:
imu_payload += v.to_bytes(2, 'little', signed=True)
imu_packet = bytearray(imu_bytes + imu_payload)
result = parsers.raw_data_msg(imu_packet)
assert result == [{
'type': 'imu',
'ts': 123,
'payload': imu_samples
}]

# 2. packet with one accl message
# Accl message: type=1, timestamp=456, 15 samples (30 bytes)
accl_ts = (1 << 31) + 456 # set MSB for accl
accl_bytes = accl_ts.to_bytes(4, 'little', signed=False)
accl_samples = list(range(1, 16))
accl_payload = b''
for v in accl_samples:
accl_payload += v.to_bytes(2, 'little', signed=True)
accl_packet = bytearray(accl_bytes + accl_payload)
result = parsers.raw_data_msg(accl_packet)
assert result == [{
'type': 'accl',
'ts': 456,
'payload': accl_samples
}]

# 3. packet with imu message and accl message
# imu first, then accl
combo_packet = bytearray(imu_bytes + imu_payload + accl_bytes + accl_payload)
result = parsers.raw_data_msg(combo_packet)
assert result == [
{'type': 'imu', 'ts': 123, 'payload': imu_samples},
{'type': 'accl', 'ts': 456, 'payload': accl_samples}
]