mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-05-13 23:50:29 -04:00
Add tests that check PipeWire <-> PipeWire bluetooth audio streaming for A2DP, BAP, HFP. The tests use Qemu VMs and don't require Bluetooth support from HW / kernel. Full VM images are not required; similarly to BlueZ kernel tester these use (read-only) mount of host filesystem. A monolithic kernel image with suitable config is required. The bluetoothd binary installed on host is used if found; otherwise tests are skipped. These test depend on https://github.com/pv/pytest-bluezenv which manages the VM setup. To launch: python3 -m pip install pytest-bluezenv meson devenv -C builddir -w . python3 -m pytest test --kernel-build -v which also builds a kernel image with required options.
95 lines
2.9 KiB
Python
95 lines
2.9 KiB
Python
# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
|
|
# SPDX-FileCopyrightText: Copyright © 2026 Pauli Virtanen
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import pytest
|
|
import warnings
|
|
|
|
from pytest_bluezenv import Bluetoothd
|
|
|
|
|
|
@pytest.fixture
|
|
def paired_hosts(hosts, host_setup):
|
|
"""
|
|
Provide two hosts, paired with each other, and the first one Central
|
|
"""
|
|
|
|
le = any(
|
|
"ControllerMode = le" in (p.conf or "")
|
|
for plugins in host_setup["setup"]
|
|
for p in plugins
|
|
if isinstance(p, Bluetoothd)
|
|
)
|
|
|
|
if le:
|
|
yield from _pair_le(hosts)
|
|
else:
|
|
yield from _pair_bredr(hosts)
|
|
|
|
|
|
def _pair_bredr(hosts):
|
|
host0, host1 = hosts
|
|
|
|
host0.bluetoothctl.send("scan on\n")
|
|
host0.bluetoothctl.expect(f"Controller {host0.bdaddr.upper()} Discovering: yes")
|
|
|
|
host1.bluetoothctl.send("pairable on\n")
|
|
host1.bluetoothctl.expect("Changing pairable on succeeded")
|
|
host1.bluetoothctl.send("discoverable on\n")
|
|
host1.bluetoothctl.expect(f"Controller {host1.bdaddr.upper()} Discoverable: yes")
|
|
|
|
host0.bluetoothctl.expect(f"Device {host1.bdaddr.upper()}")
|
|
host0.bluetoothctl.send(f"pair {host1.bdaddr}\n")
|
|
|
|
idx, m = host0.bluetoothctl.expect(r"Confirm passkey (\d+).*:")
|
|
key = m[0].decode("utf-8")
|
|
|
|
host1.bluetoothctl.expect(f"Confirm passkey {key}")
|
|
|
|
host0.bluetoothctl.send("yes\n")
|
|
host1.bluetoothctl.send("yes\n")
|
|
|
|
host0.bluetoothctl.expect("Pairing successful")
|
|
|
|
yield hosts
|
|
|
|
|
|
def _pair_le(hosts):
|
|
host0, host1 = hosts
|
|
|
|
host0.bluetoothctl.send("scan on\n")
|
|
host0.bluetoothctl.expect(f"Controller {host0.bdaddr.upper()} Discovering: yes")
|
|
|
|
host1.bluetoothctl.send("advertise on\n")
|
|
host1.bluetoothctl.expect("Advertising object registered")
|
|
|
|
host0.bluetoothctl.expect(f"Device {host1.bdaddr.upper()}")
|
|
host0.bluetoothctl.send(f"pair {host1.bdaddr.upper()}\n")
|
|
|
|
# BUG!: if controller is power cycled off/on at boot (before bluetoothd)
|
|
# BUG!: which is what the tester here does,
|
|
# BUG!: bluetoothd MGMT command to enable Secure Connections Host Support
|
|
# BUG!: fails and we are left with legacy passkey. It seems we get randomly
|
|
# BUG!: one of these depending on what state controller/kernel were before
|
|
# BUG!: btmgmt power off/on
|
|
|
|
idx, m = host0.bluetoothctl.expect(
|
|
[r"\[agent\].*Passkey:.*m(\d+)", r"Confirm passkey (\d+).*:"]
|
|
)
|
|
key = m[0].decode("utf-8")
|
|
|
|
if idx == 0:
|
|
warnings.warn(
|
|
"BUG: we got passkey authentication, bluetoothd/kernel should be fixed"
|
|
)
|
|
host1.bluetoothctl.expect(r"\[agent\] Enter passkey \(number in 0-999999\):")
|
|
host1.bluetoothctl.send(f"{key}\n")
|
|
else:
|
|
host1.bluetoothctl.expect(f"Confirm passkey {key}")
|
|
|
|
host0.bluetoothctl.send("yes\n")
|
|
host1.bluetoothctl.send("yes\n")
|
|
|
|
host0.bluetoothctl.expect("Pairing successful")
|
|
|
|
yield hosts
|