Functional/integration testing
Subsystems (2)
cover Functional/integration testing
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.
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 unit/func_test/test_bluetoothctl_vm.py has some simple cases, and unit/func_test/test_pipewire.py for a more complicated setup
host0(qemu): Pipewire <-> BlueZ <-> kernel
<-> btvirt
host1(qemu): kernel <-> BlueZ <-> PipewireThe framework allows easily passing any data and code between the parent and VM hosts, so writing tests is straightforward.
***
Some examples:
$ unit/test-functional --list -q
unit/func_test/lib/tests/test_rpc.py::test_basic unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2]
$ unit/test-functional -v --no-header ======================================= test session starts ======================================== collected 5 items
unit/func_test/lib/tests/test_rpc.py::test_basic PASSED [ 20%] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] SKIPPED [ 40%] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] SKIPPED (No kernel image) [ 60%]
unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] SKIPPED (No k...) [ 80%]
unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2] SKIPPED (No kernel image) [100%]
=================================== 1 passed, 4 skipped in 0.19s ===================================
$ unit/test-functional --kernel=../linux ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items
unit/func_test/lib/tests/test_rpc.py . [ 20%] unit/func_test/test_bluetoothctl_vm.py . [ 40%] unit/func_test/test_btmgmt_vm.py . [ 60%] unit/func_test/test_bluetoothctl_vm.py . [ 80%] unit/func_test/test_pipewire.py . [100%]
============================== 5 passed in 41.92s ==============================
$ unit/test-functional --kernel=../linux -k test_btmgmt ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.py . [100%]
======================= 1 passed, 4 deselected in 9.15s ========================
$ grep btmgmt test-functional.log
13:15:42 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f27b81ce140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:15:42 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7fd5e35a1010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:15:42 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info$ unit/test-functional --kernel=../linux -k test_btmgmt --log-cli-level=0 ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] -------------------------------- live log setup --------------------------------
13:00:31 INFO func_test.lib.env : Starting btvirt: /usr/bin/stdbuf -o L -e L /home/pauli/prj/external/bluez/build/emulator/btvirt --server=/tmp/bluez-func-test-8t6ychy8
13:00:31 OUT btvirt : Bluetooth emulator ver 5.86
13:00:31 INFO func_test.lib.env : Starting host: /home/pauli/prj/external/bluez/build/tools/test-runner --kernel=../linux/arch/x86/boot/bzImage -u/tmp/bluez-func-test-8t6ychy8/bt-server-bredrle -o -chardev -o socket,id=ser0,path=/tmp/bluez-func-test-8t6ychy8/bluez-func-test-rpc-0,server=on,wait=off -o -device -o virtio-serial -o -device -o virtserialport,chardev=ser0,name=bluez-func-test-rpc -H -- /usr/bin/python3 -P /home/pauli/prj/external/bluez/unit/func_test/lib/runner.py /dev/ttyS2
13:00:31 OUT btvirt : Request for /tmp/bluez-func-test-8t6ychy8/bt-server-bredrle
13:00:32 OUT host.0.0 : early console in extract_kernel
13:00:32 OUT host.0.0 : input_data: 0x000000000425c2c4
...
13:00:39 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f7547472140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:00:39 DEBUG host.0.0.rpc : server: done
13:00:39 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7f77dcc81010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'}
13:00:39 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info
13:00:40 OUT host.0.0.run.out : hci0: Primary controller
13:00:40 OUT host.0.0.run.out : addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000
13:00:40 OUT host.0.0.run.out : supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
13:00:40 OUT host.0.0.run.out : current settings: br/edr
13:00:40 OUT host.0.0.run.out : name
13:00:40 OUT host.0.0.run.out : short name
13:00:40 INFO host.0.0.run : (return code 0)
13:00:40 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
13:00:40 OUT host.0.0 : qemu-system-x86_64: terminating on signal 15 from pid 149047 (python3)
======================= 1 passed, 4 deselected in 8.84s ========================
$ unit/test-functional --kernel=../linux -k test_bluetoothctl_pair --log-cli-level=0 --log-filter=*.bluetoothctl,rpc.* --force-usb ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] -------------------------------- live log setup --------------------------------
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f268712d160>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f268712d2b0>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {}
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f268712d010>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f26871542d0>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f2687154410>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {}
13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f2687154190>,) {}
13:03:20 INFO rpc.host.0.0 : client: wait_load () {}
13:03:21 DEBUG rpc.host.0.0 : client-reply
13:03:21 INFO rpc.host.0.1 : client: wait_load () {}
13:03:21 DEBUG rpc.host.0.1 : client-reply
-------------------------------- live log call ---------------------------------
13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'show\n') {}
13:03:21 DEBUG rpc.host.0.0 : client-reply
13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Powered: yes') {}
...
13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'pair 70:1a:b8:73:99:bb\n') {}
13:03:23 OUT host.0.0.bluetoothctl: pair 70:1a:b8:73:99:bb
13:03:23 DEBUG rpc.host.0.0 : client-reply
13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey (\\d+).*:') {}
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> pair 70:1a:b8:73:99:bb
13:03:23 OUT host.0.0.bluetoothctl: Attempting to pair with 70:1A:B8:73:99:BB
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 device_flags_changed: 70:1A:B8:73:99:BB (BR/EDR)
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> supp: 0x00000007 curr: 0x00000000
13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 type 7 discovering off
13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 70:1A:B8:73:99:BB type BR/EDR connected eir_len 12
13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Connected: yes
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> Request confirmation
13:03:25 DEBUG rpc.host.0.0 : client-reply
13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey 237345') {}
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> hci0 84:5C:F3:77:31:19 type BR/EDR connected eir_len 12
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [NEW] Device 84:5C:F3:77:31:19 BlueZ 5.86
13:03:25 DEBUG rpc.host.0.1 : client-reply
13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {}
13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> Request confirmation
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes
13:03:25 DEBUG rpc.host.0.0 : client-reply
13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {}
13:03:25 OUT host.0.1.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes
13:03:25 DEBUG rpc.host.0.1 : client-reply
13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Pairing successful') {}
13:03:25 OUT host.0.0.bluetoothctl: yes
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> hci0 new_link_key 70:1A:B8:73:99:BB type 0x08 pin_len 0 store_hint 1
13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Bonded: yes
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB AddressType: public
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
13:03:26 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
------------------------------ live log teardown -------------------------------
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:98:FF:qemu-system-x86_64: terminating on signal 15 from pid 149357 (python3)
======================= 1 passed, 4 deselected in 13.22s =======================
$ COLUMNS=80 unit/test-functional -k test_btmgmt --kernel=../linux --trace ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.py
Quoted 2 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
PDB runcall (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(19)test_btmgmt_info()
-> (host,) = hosts
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) p host.bdaddr
'00:aa:01:00:00:42'
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(22)test_btmgmt_info()
-> run,
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(23)test_btmgmt_info()
-> [btmgmt, "--index", "0", "info"],
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(24)test_btmgmt_info()
-> stdout=subprocess.PIPE,
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(25)test_btmgmt_info()
-> stdin=subprocess.DEVNULL,
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(26)test_btmgmt_info()
-> encoding="utf-8",
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) n
Quoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(28)test_btmgmt_info()
-> assert result.returncode == 0
(Pdb) p result
CompletedProcess(args=['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info'], returncode=0, stdout='hci0:\tPrimary controller\n\taddr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000\n\tsupported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver \n\tcurrent settings: br/edr \n\tname \n\tshort name \n') (Pdb) print(result.stdout)
hci0: Primary controller
addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000
supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
current settings: br/edr
name
short name
(Pdb) q
!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Quitting debugger !!!!!!!!!!!!!!!!!!! ======================= 4 deselected in 75.91s (0:01:15) =======================
***
Pauli Virtanen (11):
emulator: btvirt: check pkt lengths, don't get stuck on malformed
emulator: btvirt: allow specifying where server unix sockets are made
test-runner: enable path argument for --unix
test-runner: Add -o/--option option
test-runner: allow source tree root for -k
doc: enable CONFIG_VIRTIO_CONSOLE in tester config
test-runner: use virtio-serial for implementing -u device forwarding
doc: add functional/integration testing documentation
unit: add functional/integration testing framework
unit: func_test: add Pipewire-using smoke tests
build: add functional testing target
Makefile.am | 7 + configure.ac | 17 + doc/ci.config | 1 + doc/test-functional.rst | 374 +++++++++++++++++++ doc/test-runner.rst | 1 + doc/tester.config | 1 + emulator/main.c | 37 +- emulator/server.c | 9 + tools/test-runner.c | 376 ++++++++++++++----- unit/func_test/__init__.py | 0 unit/func_test/conftest.py | 277 ++++++++++++++ unit/func_test/lib/__init__.py | 5 + unit/func_test/lib/env.py | 484 +++++++++++++++++++++++++ unit/func_test/lib/host_plugins.py | 269 ++++++++++++++ unit/func_test/lib/rpc.py | 293 +++++++++++++++ unit/func_test/lib/runner.py | 10 + unit/func_test/lib/tests/__init__.py | 0 unit/func_test/lib/tests/test_rpc.py | 49 +++ unit/func_test/lib/utils.py | 266 ++++++++++++++ unit/func_test/requirements.txt | 3 + unit/func_test/test_bluetoothctl_vm.py | 76 ++++ unit/func_test/test_btmgmt_vm.py | 29 ++ unit/func_test/test_pipewire.py | 137 +++++++ unit/pytest.ini | 7 + unit/test-functional | 8 + 25 files changed, 2631 insertions(+), 105 deletions(-) create mode 100644 doc/test-functional.rst create mode 100644 unit/func_test/__init__.py create mode 100644 unit/func_test/conftest.py create mode 100644 unit/func_test/lib/__init__.py create mode 100644 unit/func_test/lib/env.py create mode 100644 unit/func_test/lib/host_plugins.py create mode 100644 unit/func_test/lib/rpc.py create mode 100644 unit/func_test/lib/runner.py create mode 100644 unit/func_test/lib/tests/__init__.py create mode 100644 unit/func_test/lib/tests/test_rpc.py create mode 100644 unit/func_test/lib/utils.py create mode 100644 unit/func_test/requirements.txt create mode 100644 unit/func_test/test_bluetoothctl_vm.py create mode 100644 unit/func_test/test_btmgmt_vm.py create mode 100644 unit/func_test/test_pipewire.py create mode 100644 unit/pytest.ini create mode 100755 unit/test-functional
Signature · 1 line
2.53.0
PATCH 1/11 emulator: btvirt: check pkt lengths, don't get stuck on malformed
Don't try to parse packet before whole header is received.
If received data has unknown packet type, reset buffer so that we don't get stuck.
--- emulator/server.c | 9 +++++++++ 1 file changed, 9 insertions(+)
emulator/server.c +9 -0
@@ -136,12 +136,20 @@ again: client->pkt_len =0 ;break ;case HCI_ACLDATA_PKT : +if (count <HCI_ACL_HDR_SIZE +1 ) { + client->pkt_offset += len; +return ; + } acl_hdr = (hci_acl_hdr *)(ptr +1 ); client->pkt_expect =HCI_ACL_HDR_SIZE + acl_hdr->dlen +1 ; client->pkt_data =malloc (client->pkt_expect); client->pkt_len =0 ;break ;case HCI_ISODATA_PKT : +if (count <HCI_ISO_HDR_SIZE +1 ) { + client->pkt_offset += len; +return ; + } iso_hdr = (hci_iso_hdr *)(ptr +1 ); client->pkt_expect =HCI_ISO_HDR_SIZE + iso_hdr->dlen +1 ;
@@ -151,6 +159,7 @@ again:default :printf ("packet error, unknown type: %d\n" , client->pkt_type); + client->pkt_offset =0 ;return ; }
Signature · 1 line
2.53.0
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=1059428
---Test result---
Test Summary:
CheckPatch PENDING 0.31 seconds GitLint PENDING 0.27 seconds BuildEll PASS 21.22 seconds BluezMake FAIL 631.87 seconds MakeCheck FAIL 13.18 seconds MakeDistcheck FAIL 82.97 seconds CheckValgrind FAIL 215.70 seconds CheckSmatch FAIL 350.74 seconds bluezmakeextell FAIL 177.68 seconds IncrementalBuild PENDING 0.28 seconds ScanBuild FAIL 489.22 seconds
Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:
##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:
##############################
Test: BluezMake - FAIL
Desc: Build BlueZ
Output:
tools/mgmt-tester.c: In function ‘main’:tools/mgmt-tester.c:12984:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without 12984 | int main(int argc, char *argv[])
| ^~~~
unit/test-avdtp.c: In function ‘main’:
unit/test-avdtp.c:766:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
766 | int main(int argc, char *argv[])
| ^~~~
unit/test-avrcp.c: In function ‘main’:
unit/test-avrcp.c:989:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without
989 | int main(int argc, char *argv[])
| ^~~~/usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4224: all] Error 2
##############################
Test: MakeCheck - FAIL
Desc: Run Bluez Make Check
Output:
/usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make: *** [Makefile:10903: check] Error 2
##############################
Test: MakeDistcheck - FAIL
Desc: Run Bluez Make Distcheck
Output:
/usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/bluez-5.86/_build/sub/../../tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[2]: *** [Makefile:6194: tools/test-runner] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [Makefile:4224: all] Error 2
make: *** [Makefile:10824: distcheck] Error 1
##############################
Test: CheckValgrind - FAIL
Desc: Run Bluez Make Check with Valgrind
Output:
tools/mgmt-tester.c: In function ‘main’:tools/mgmt-tester.c:12984:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without 12984 | int main(int argc, char *argv[])
| ^~~~
/usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:10903: check] Error 2
##############################
Test: CheckSmatch - FAIL
Desc: Run smatch tool with source
Output:
src/shared/crypto.c:271:21: warning: Variable length array is used. src/shared/crypto.c:272:23: warning: Variable length array is used. src/shared/gatt-helpers.c:768:31: warning: Variable length array is used. src/shared/gatt-helpers.c:846:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1339:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1370:23: warning: Variable length array is used. src/shared/gatt-server.c:278:25: warning: Variable length array is used. src/shared/gatt-server.c:618:25: warning: Variable length array is used. src/shared/gatt-server.c:716:25: warning: Variable length array is used. src/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structuressrc/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' src/shared/crypto.c:271:21: warning: Variable length array is used. src/shared/crypto.c:272:23: warning: Variable length array is used. src/shared/gatt-helpers.c:768:31: warning: Variable length array is used. src/shared/gatt-helpers.c:846:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1339:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1370:23: warning: Variable length array is used. src/shared/gatt-server.c:278:25: warning: Variable length array is used. src/shared/gatt-server.c:618:25: warning: Variable length array is used. src/shared/gatt-server.c:716:25: warning: Variable length array is used. src/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structuressrc/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' tools/mesh-cfgtest.c:1453:17: warning: unknown escape sequence: '\%'tools/sco-tester.c: note: in included file:./lib/bluetooth/bluetooth.h:232:15: warning: array of flexible structures ./lib/bluetooth/bluetooth.h:237:31: warning: array of flexible structures tools/bneptest.c:634:39: warning: unknown escape sequence: '\%'tools/seq2bseq.c:57:26: warning: Variable length array is used.tools/obex-client-tool.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' src/advertising.c: note: in included file:./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability src/adv_monitor.c: note: in included file:./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability unit/avctp.c:505:34: warning: Variable length array is used.unit/avctp.c:556:34: warning: Variable length array is used.unit/test-avrcp.c:373:26: warning: Variable length array is used. unit/test-avrcp.c:398:26: warning: Variable length array is used. unit/test-avrcp.c:414:24: warning: Variable length array is used. unit/avrcp-lib.c:1085:34: warning: Variable length array is used. unit/avrcp-lib.c:1583:34: warning: Variable length array is used. unit/avrcp-lib.c:1612:34: warning: Variable length array is used. unit/avrcp-lib.c:1638:34: warning: Variable length array is used.src/advertising.c: note: in included file:./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability src/adv_monitor.c: note: in included file:./src/shared/mgmt.h:95:25: error: redefinition of unsigned int enum mgmt_io_capability mesh/mesh-io-mgmt.c:525:67: warning: Variable length array is used.client/display.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' src/shared/crypto.c:271:21: warning: Variable length array is used. src/shared/crypto.c:272:23: warning: Variable length array is used. src/shared/gatt-helpers.c:768:31: warning: Variable length array is used. src/shared/gatt-helpers.c:846:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1339:31: warning: Variable length array is used. src/shared/gatt-helpers.c:1370:23: warning: Variable length array is used. src/shared/gatt-server.c:278:25: warning: Variable length array is used. src/shared/gatt-server.c:618:25: warning: Variable length array is used. src/shared/gatt-server.c:716:25: warning: Variable length array is used. src/shared/bap.c:312:25: warning: array of flexible structuressrc/shared/bap.c: note: in included file:./src/shared/ascs.h:88:25: warning: array of flexible structuressrc/shared/shell.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' monitor/packet.c:1993:26: warning: Variable length array is used.monitor/packet.c: note: in included file:monitor/bt.h:3866:52: warning: array of flexible structuresmonitor/bt.h:3854:40: warning: array of flexible structuresmonitor/msft.c: note: in included file:monitor/msft.h:88:44: warning: array of flexible structurestools/rctest.c:631:33: warning: non-ANSI function declaration of function 'automated_send_recv' tools/hex2hcd.c:136:26: warning: Variable length array is used.tools/meshctl.c:324:33: warning: non-ANSI function declaration of function 'forget_mesh_devices' tools/mesh-gatt/node.c:456:39: warning: non-ANSI function declaration of function 'node_get_local_node' tools/mesh-gatt/net.c:1239:30: warning: non-ANSI function declaration of function 'get_next_seq' tools/mesh-gatt/net.c:2193:29: warning: non-ANSI function declaration of function 'net_get_default_ttl' tools/mesh-gatt/net.c:2207:26: warning: non-ANSI function declaration of function 'net_get_seq_num' tools/mesh-gatt/prov.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' tools/mesh-gatt/onoff-model.c: note: in included file (through /usr/include/readline/readline.h):/usr/include/readline/rltypedefs.h:35:23: warning: non-ANSI function declaration of function 'Function' /usr/include/readline/rltypedefs.h:36:25: warning: non-ANSI function declaration of function 'VFunction' /usr/include/readline/rltypedefs.h:37:27: warning: non-ANSI function declaration of function 'CPFunction' /usr/include/readline/rltypedefs.h:38:29: warning: non-ANSI function declaration of function 'CPPFunction' ell/log.c:431:65: warning: non-ANSI function declaration of function 'register_debug_section' ell/log.c:439:68: warning: non-ANSI function declaration of function 'free_debug_sections' ell/random.c:60:42: warning: non-ANSI function declaration of function 'l_getrandom_is_supported' ell/cipher.c:660:28: warning: non-ANSI function declaration of function 'init_supported' ell/checksum.c:382:28: warning: non-ANSI function declaration of function 'init_supported' ell/checksum.c:444:47: warning: non-ANSI function declaration of function 'l_checksum_cmac_aes_supported' ell/cipher.c:519:24: warning: Variable length array is used.ell/cert-crypto.c:36:33: warning: Variable length array is used. ell/cert-crypto.c:142:36: warning: Variable length array is used. ell/cert-crypto.c:198:36: warning: Variable length array is used. ell/cert-crypto.c:251:31: warning: Variable length array is used.ell/key.c:550:25: warning: Variable length array is used.ell/dbus-service.c:548:49: warning: non-ANSI function declaration of function '_dbus_object_tree_new' ell/dbus-filter.c:233:46: warning: Variable length array is used.ell/tls.c:45:25: warning: Variable length array is used.ell/tls.c:86:22: warning: Variable length array is used.ell/tls.c:86:46: warning: Variable length array is used.ell/tls.c:1819:26: warning: Variable length array is used.ell/tls-suites.c:1079:25: warning: Variable length array is used. ell/tls-suites.c:1081:34: warning: Variable length array is used. ell/tls-suites.c:1084:41: warning: Variable length array is used. ell/tls-suites.c:1133:41: warning: Variable length array is used. emulator/btdev.c:470:29: warning: Variable length array is used. emulator/bthost.c:700:28: warning: Variable length array is used. emulator/bthost.c:701:32: warning: Variable length array is used. emulator/bthost.c:918:28: warning: Variable length array is used. emulator/bthost.c:952:28: warning: Variable length array is used. emulator/bthost.c:953:32: warning: Variable length array is used./usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4224: all] Error 2
##############################
Test: bluezmakeextell - FAIL
Desc: Build Bluez with External ELL
Output:
/usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:4224: all] Error 2
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:
##############################
Test: ScanBuild - FAIL
Desc: Run Scan Build
Output:
src/shared/gatt-client.c:455:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:700:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1000:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1106:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1300:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1365:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1640:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1645:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1701:56: warning: Use of memory after it is freed
notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2154:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2162:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3340:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3362:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
bap = bt_bap_ref_safe(bap);
^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
return queue_find(stream->bap->streams, NULL, stream);
^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/shared/gatt-client.c:455:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:700:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1000:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1106:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1300:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1365:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1640:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1645:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1701:56: warning: Use of memory after it is freed
notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2154:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2162:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3340:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3362:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
13 warnings generated.
tools/hciattach.c:817:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 10)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:865:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:887:8: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 10)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:909:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:930:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 4)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/hciattach.c:974:7: warning: Although the value stored to 'n' is used in the enclosing expression, the value is never actually read from 'n'
if ((n = read_hci_event(fd, resp, 6)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 warnings generated.
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
bap = bt_bap_ref_safe(bap);
^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
return queue_find(stream->bap->streams, NULL, stream);
^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/oui.c:50:2: warning: Value stored to 'hwdb' is never read
hwdb = udev_hwdb_unref(hwdb);
^ ~~~~~~~~~~~~~~~~~~~~~
src/oui.c:53:2: warning: Value stored to 'udev' is never read
udev = udev_unref(udev);
^ ~~~~~~~~~~~~~~~~
2 warnings generated.
tools/rfcomm.c:234:3: warning: Value stored to 'i' is never read
i = execvp(cmdargv[0], cmdargv);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:234:7: warning: Null pointer passed to 1st parameter expecting 'nonnull'
i = execvp(cmdargv[0], cmdargv);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:354:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/rfcomm.c:497:14: warning: Assigned value is garbage or undefined
req.channel = raddr.rc_channel;
^ ~~~~~~~~~~~~~~~~
tools/rfcomm.c:515:8: warning: Although the value stored to 'fd' is used in the enclosing expression, the value is never actually read from 'fd'
if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 warnings generated.
tools/hcidump.c:180:9: warning: Potential leak of memory pointed to by 'dp'
if (fds[i].fd == sock)
^~~
tools/hcidump.c:248:17: warning: Assigned value is garbage or undefined
dh->ts_sec = htobl(frm.ts.tv_sec);
^ ~~~~~~~~~~~~~~~~~~~~
tools/hcidump.c:326:9: warning: 1st function call argument is an uninitialized value
if (be32toh(dp.flags) & 0x02) {
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:341:20: warning: 1st function call argument is an uninitialized value
frm.data_len = be32toh(dp.len);
^~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:346:14: warning: 1st function call argument is an uninitialized value
opcode = be32toh(dp.flags) & 0xffff;
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:384:17: warning: Assigned value is garbage or undefined
frm.data_len = btohs(dh.len);
^ ~~~~~~~~~~~~~
tools/hcidump.c:394:11: warning: Assigned value is garbage or undefined
frm.len = frm.data_len;
^ ~~~~~~~~~~~~
tools/hcidump.c:398:9: warning: 1st function call argument is an uninitialized value
ts = be64toh(ph.ts);
^~~~~~~~~~~~~~
/usr/include/endian.h:51:22: note: expanded from macro 'be64toh'
# define be64toh(x) __bswap_64 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:403:13: warning: 1st function call argument is an uninitialized value
frm.in = be32toh(dp.flags) & 0x01;
^~~~~~~~~~~~~~~~~
/usr/include/endian.h:46:22: note: expanded from macro 'be32toh'
# define be32toh(x) __bswap_32 (x)
^~~~~~~~~~~~~~
tools/hcidump.c:408:11: warning: Assigned value is garbage or undefined
frm.in = dh.in;
^ ~~~~~
tools/hcidump.c:437:7: warning: Null pointer passed to 1st parameter expecting 'nonnull'
fd = open(file, open_flags, 0644);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 warnings generated.
tools/ciptool.c:351:7: warning: 5th function call argument is an uninitialized value
sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:306:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:344:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
3 warnings generated.
tools/sdptool.c:941:26: warning: Result of 'malloc' is converted to a pointer of type 'uint32_t', which is incompatible with sizeof operand type 'int'
uint32_t *value_int = malloc(sizeof(int));
~~~~~~~~~~ ^~~~~~ ~~~~~~~~~~~
tools/sdptool.c:980:4: warning: 1st function call argument is an uninitialized value
free(allocArray[i]);
^~~~~~~~~~~~~~~~~~~
tools/sdptool.c:3777:2: warning: Potential leak of memory pointed to by 'si.name'
return add_service(0, &si);
^~~~~~~~~~~~~~~~~~~~~~~~~~
tools/sdptool.c:4112:4: warning: Potential leak of memory pointed to by 'context.svc'
return -1;
^~~~~~~~~
4 warnings generated.
tools/avtest.c:243:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:253:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:262:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~tools/avtest.c:276:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~tools/avtest.c:283:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~tools/avtest.c:290:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~tools/avtest.c:297:5: warning: Value stored to 'len' is never read
len = write(sk, buf,
^ ~~~~~~~~~~~~~~tools/avtest.c:309:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:313:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:322:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:326:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:335:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:342:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:364:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:368:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:377:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:381:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:394:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 4);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:398:5: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:405:4: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:415:4: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:580:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:588:3: warning: Value stored to 'len' is never read
len = write(sk, buf, invalid ? 2 : 3);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:602:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 4 + media_transport_size);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/avtest.c:615:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:625:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:637:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:652:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:664:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:673:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 3);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:680:3: warning: Value stored to 'len' is never read
len = write(sk, buf, 2);
^ ~~~~~~~~~~~~~~~~~
tools/avtest.c:716:2: warning: Value stored to 'len' is never read
len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 warnings generated.
tools/btproxy.c:836:15: warning: Null pointer passed to 1st parameter expecting 'nonnull'
tcp_port = atoi(optarg);
^~~~~~~~~~~~
tools/btproxy.c:839:8: warning: Null pointer passed to 1st parameter expecting 'nonnull'
if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
^~~~~~~~~~~~~~
2 warnings generated.
tools/create-image.c:76:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:84:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:92:3: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
tools/create-image.c:105:2: warning: Value stored to 'fd' is never read
fd = -1;
^ ~~
4 warnings generated.
tools/btgatt-client.c:1822:2: warning: Value stored to 'argv' is never read
argv += optind;
^ ~~~~~~
1 warning generated.
tools/check-selftest.c:42:3: warning: Value stored to 'ptr' is never read
ptr = fgets(result, sizeof(result), fp);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/btgatt-server.c:1208:2: warning: Value stored to 'argv' is never read
argv -= optind;
^ ~~~~~~
1 warning generated.
tools/gatt-service.c:294:2: warning: 2nd function call argument is an uninitialized value
chr_write(chr, value, len);
^~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
tools/obex-server-tool.c:133:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
data->fd = open(name, O_WRONLY | O_CREAT | O_NOCTTY, 0600);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/obex-server-tool.c:192:13: warning: Null pointer passed to 1st parameter expecting 'nonnull'
data->fd = open(name, O_RDONLY | O_NOCTTY, 0);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
client/btpclient/btpclientctl.c:402:3: warning: Value stored to 'bit' is never read
bit = 0;
^ ~
client/btpclient/btpclientctl.c:1655:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
memcpy(cp->data, ad_data, ad_len);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdp-client.c:353:14: warning: Access to field 'cb' results in a dereference of a null pointer
(*ctxt)->cb = cb;
~~~~~~~~~~~~^~~~
1 warning generated.
src/sdpd-request.c:211:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
pElem = malloc(sizeof(uint16_t));
^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:239:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
pElem = malloc(sizeof(uint32_t));
^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
src/gatt-database.c:1175:10: warning: Value stored to 'bits' during its initialization is never read
uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
^~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/gatt-client.c:1569:2: warning: Use of memory after it is freed
notify_client_unref(client);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
unit/avrcp-lib.c:1968:3: warning: 1st function call argument is an uninitialized value
g_free(text[i]);
^~~~~~~~~~~~~~~
1 warning generated.
unit/avdtp.c:756:25: warning: Use of memory after it is freed
session->prio_queue = g_slist_remove(session->prio_queue, req);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
unit/avdtp.c:763:24: warning: Use of memory after it is freed
session->req_queue = g_slist_remove(session->req_queue, req);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
profiles/audio/avdtp.c:895:25: warning: Use of memory after it is freed
session->prio_queue = g_slist_remove(session->prio_queue, req);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
profiles/audio/avdtp.c:902:24: warning: Use of memory after it is freed
session->req_queue = g_slist_remove(session->req_queue, req);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
profiles/audio/a2dp.c:442:8: warning: Use of memory after it is freed
if (!cb->resume_cb)
^~~~~~~~~~~~~
profiles/audio/a2dp.c:3354:20: warning: Access to field 'starting' results in a dereference of a null pointer (loaded from variable 'stream')
stream->starting = TRUE;
~~~~~~ ^
profiles/audio/a2dp.c:3357:8: warning: Access to field 'suspending' results in a dereference of a null pointer (loaded from variable 'stream')
if (!stream->suspending && stream->suspend_timer) {
^~~~~~~~~~~~~~~~~~
profiles/audio/a2dp.c:3417:22: warning: Access to field 'suspending' results in a dereference of a null pointer (loaded from variable 'stream')
stream->suspending = TRUE;
~~~~~~ ^
4 warnings generated.
profiles/audio/avrcp.c:1961:2: warning: Value stored to 'operands' is never read
operands += sizeof(*pdu);
^ ~~~~~~~~~~~~
1 warning generated.
attrib/gatt.c:970:2: warning: Potential leak of memory pointed to by 'long_write'
return prepare_write(long_write);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdpd-request.c:211:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint16_t'
pElem = malloc(sizeof(uint16_t));
^~~~~~ ~~~~~~~~~~~~~~~~
src/sdpd-request.c:239:13: warning: Result of 'malloc' is converted to a pointer of type 'char', which is incompatible with sizeof operand type 'uint32_t'
pElem = malloc(sizeof(uint32_t));
^~~~~~ ~~~~~~~~~~~~~~~~
2 warnings generated.
src/sdp-client.c:353:14: warning: Access to field 'cb' results in a dereference of a null pointer
(*ctxt)->cb = cb;
~~~~~~~~~~~~^~~~
1 warning generated.
src/gatt-database.c:1175:10: warning: Value stored to 'bits' during its initialization is never read
uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
^~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
src/sdp-xml.c:126:10: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:306:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
src/sdp-xml.c:344:11: warning: Assigned value is garbage or undefined
buf[1] = data[i + 1];
^ ~~~~~~~~~~~
3 warnings generated.
src/gatt-client.c:1569:2: warning: Use of memory after it is freed
notify_client_unref(client);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
gobex/gobex-header.c:95:2: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
memcpy(to, from, count);
^~~~~~~~~~~~~~~~~~~~~~~1 warning generated.gobex/gobex-transfer.c:423:7: warning: Use of memory after it is freed
if (!g_slist_find(transfers, transfer))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.mesh/main.c:162:3: warning: Value stored to 'optarg' is never read
optarg += strlen("auto");
^ ~~~~~~~~~~~~~~
1 warning generated.
lib/bluetooth/hci.c:97:4: warning: Value stored to 'ptr' is never read
ptr += sprintf(ptr, "%s", m->str);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
client/player.c:2363:8: warning: Null pointer passed to 2nd parameter expecting 'nonnull'
if (!strcmp(ep->path, pattern))
^~~~~~~~~~~~~~~~~~~~~~~~~
client/player.c:3640:16: warning: Null pointer passed to 1st parameter expecting 'nonnull'
codec->name = strdup(name);
^~~~~~~~~~~~
2 warnings generated.
gdbus/watch.c:226:3: warning: Attempt to free released memory
g_free(l->data);
^~~~~~~~~~~~~~~
1 warning generated.
lib/bluetooth/sdp.c:509:17: warning: Dereference of undefined pointer value
uint8_t dtd = *(uint8_t *) dtds[i];
^~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:539:17: warning: Dereference of undefined pointer value
uint8_t dtd = *(uint8_t *) dtds[i];
^~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:1885:26: warning: Potential leak of memory pointed to by 'ap'
for (; pdlist; pdlist = pdlist->next) {
^~~~~~
lib/bluetooth/sdp.c:1899:6: warning: Potential leak of memory pointed to by 'pds'
ap = sdp_list_append(ap, pds);
~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:1944:10: warning: Potential leak of memory pointed to by 'u'
*seqp = sdp_list_append(*seqp, u);
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:2049:4: warning: Potential leak of memory pointed to by 'lang'
sdp_list_free(*langSeq, free);
^~~~~~~~~~~~~
lib/bluetooth/sdp.c:2138:9: warning: Potential leak of memory pointed to by 'profDesc'
return 0;
^
lib/bluetooth/sdp.c:3270:8: warning: Potential leak of memory pointed to by 'pSvcRec'
pSeq = sdp_list_append(pSeq, pSvcRec);
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:3271:9: warning: Potential leak of memory pointed to by 'pSeq'
pdata += sizeof(uint32_t);
~~~~~~^~~~~~~~~~~~~~~~~~~
lib/bluetooth/sdp.c:4607:13: warning: Potential leak of memory pointed to by 'rec_list'
} while (scanned < attr_list_len && pdata_len > 0);
^~~~~~~
lib/bluetooth/sdp.c:4903:40: warning: Potential leak of memory pointed to by 'tseq'
for (d = sdpdata->val.dataseq; d; d = d->next) {
^
lib/bluetooth/sdp.c:4939:8: warning: Potential leak of memory pointed to by 'subseq'
tseq = sdp_list_append(tseq, subseq);
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 warnings generated.
src/shared/gatt-client.c:455:21: warning: Use of memory after it is freed
gatt_db_unregister(op->client->db, op->db_id);
^~~~~~~~~~
src/shared/gatt-client.c:700:2: warning: Use of memory after it is freed
discovery_op_complete(op, false, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1000:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1106:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1300:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1365:2: warning: Use of memory after it is freed
discovery_op_complete(op, success, att_ecode);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1640:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1645:2: warning: Use of memory after it is freed
discover_all(op);
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:1701:56: warning: Use of memory after it is freed
notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
~~~~~~~~~~~~~~~~~~~ ^
src/shared/gatt-client.c:2154:6: warning: Use of memory after it is freed
if (read_db_hash(op)) {
^~~~~~~~~~~~~~~~
src/shared/gatt-client.c:2162:8: warning: Use of memory after it is freed
discovery_op_ref(op),
^~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3340:2: warning: Use of memory after it is freed
complete_write_long_op(req, success, 0, false);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/gatt-client.c:3362:2: warning: Use of memory after it is freed
request_unref(req);
^~~~~~~~~~~~~~~~~~
13 warnings generated.
src/shared/bap.c:1529:8: warning: Use of memory after it is freed
bap = bt_bap_ref_safe(bap);
^~~~~~~~~~~~~~~~~~~~
src/shared/bap.c:2340:20: warning: Use of memory after it is freed
return queue_find(stream->bap->streams, NULL, stream);
^~~~~~~~~~~~~~~~~~~~
2 warnings generated.
monitor/l2cap.c:1676:4: warning: Value stored to 'data' is never read
data += len;
^ ~~~
monitor/l2cap.c:1677:4: warning: Value stored to 'size' is never read
size -= len;
^ ~~~
2 warnings generated.
monitor/hwdb.c:59:2: warning: Value stored to 'hwdb' is never read
hwdb = udev_hwdb_unref(hwdb);
^ ~~~~~~~~~~~~~~~~~~~~~
monitor/hwdb.c:64:2: warning: Value stored to 'udev' is never read
udev = udev_unref(udev);
^ ~~~~~~~~~~~~~~~~
monitor/hwdb.c:106:2: warning: Value stored to 'hwdb' is never read
hwdb = udev_hwdb_unref(hwdb);
^ ~~~~~~~~~~~~~~~~~~~~~
monitor/hwdb.c:111:2: warning: Value stored to 'udev' is never read
udev = udev_unref(udev);
^ ~~~~~~~~~~~~~~~~
4 warnings generated.
tools/bluemoon.c:1102:8: warning: Null pointer passed to 1st parameter expecting 'nonnull'
if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
^~~~~~~~~~~~~~
1 warning generated.
tools/meshctl.c:326:19: warning: Access to field 'mesh_devices' results in a dereference of a null pointer (loaded from variable 'default_ctrl')
g_list_free_full(default_ctrl->mesh_devices, g_free);
^~~~~~~~~~~~~~~~~~~~~~~~~~
tools/meshctl.c:762:2: warning: 2nd function call argument is an uninitialized value
bt_shell_printf("Attempting to disconnect from %s\n", addr);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/meshctl.c:1957:2: warning: Value stored to 'len' is never read
len = len + extra + strlen("local_node.json");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
In file included from tools/mesh-gatt/crypto.c:32:
./src/shared/util.h:244:9: warning: 1st function call argument is an uninitialized value
return be32_to_cpu(get_unaligned((const uint32_t *) ptr));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./src/shared/util.h:33:26: note: expanded from macro 'be32_to_cpu'
#define be32_to_cpu(val) bswap_32(val)
^~~~~~~~~~~~~
/usr/include/byteswap.h:34:21: note: expanded from macro 'bswap_32'
#define bswap_32(x) __bswap_32 (x)
^~~~~~~~~~~~~~
In file included from tools/mesh-gatt/crypto.c:32:
./src/shared/util.h:254:9: warning: 1st function call argument is an uninitialized value
return be64_to_cpu(get_unaligned((const uint64_t *) ptr));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./src/shared/util.h:34:26: note: expanded from macro 'be64_to_cpu'
#define be64_to_cpu(val) bswap_64(val)
^~~~~~~~~~~~~
/usr/include/byteswap.h:37:21: note: expanded from macro 'bswap_64'
#define bswap_64(x) __bswap_64 (x)
^~~~~~~~~~~~~~
2 warnings generated.
ell/util.c:853:8: warning: The left operand of '>' is a garbage value
if (x > UINT8_MAX)
~ ^
ell/util.c:871:8: warning: The left operand of '>' is a garbage value
if (x > UINT16_MAX)
~ ^
2 warnings generated.
ell/pem.c:131:8: warning: Dereference of null pointer (loaded from variable 'eol')
if (*eol == '\r' || *eol == '\n')
^~~~
ell/pem.c:166:18: warning: Dereference of null pointer (loaded from variable 'eol')
if (buf_len && *eol == '\r' && *buf_ptr == '\n') {
^~~~
ell/pem.c:166:34: warning: Dereference of null pointer (loaded from variable 'buf_ptr')
if (buf_len && *eol == '\r' && *buf_ptr == '\n') {
^~~~~~~~ell/pem.c:304:11: warning: 1st function call argument is an uninitialized value
result = pem_load_buffer(file.data, file.st.st_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ell/pem.c:469:9: warning: 1st function call argument is an uninitialized value
list = l_pem_load_certificate_list_from_data(file.data,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 warnings generated.ell/cert.c:645:41: warning: Access to field 'asn1_len' results in a dereference of a null pointer (loaded from variable 'cert')
key = l_key_new(L_KEY_RSA, cert->asn1, cert->asn1_len);
^~~~~~~~~~~~~~1 warning generated.ell/gvariant-util.c:143:18: warning: The left operand of '>' is a garbage value
if (alignment > max_alignment)
~~~~~~~~~ ^ell/gvariant-util.c:456:5: warning: Dereference of null pointer
!children[0].fixed_size) {
^~~~~~~~~~~~~~~~~~~~~~
2 warnings generated.
ell/ecc-external.c:77:11: warning: Assigned value is garbage or undefined
dest[i] = src[i];
^ ~~~~~~
ell/ecc-external.c:160:18: warning: The right operand of '-' is a garbage value
diff = left[i] - right[i] - borrow;
^ ~~~~~~~~
ell/ecc-external.c:227:14: warning: 2nd function call argument is an uninitialized value
product = mul_64_64(left[i], right[k - i]);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ell/ecc-external.c:408:9: warning: Assigned value is garbage or undefined
tmp[1] = product[3];
^ ~~~~~~~~~~
ell/ecc-external.c:435:22: warning: The left operand of '&' is a garbage value
tmp[1] = product[3] & 0xffffffff00000000ull;
~~~~~~~~~~ ^
ell/ecc-external.c:483:22: warning: The left operand of '&' is a garbage value
tmp[1] = product[5] & 0xffffffff00000000ull;
~~~~~~~~~~ ^
ell/ecc-external.c:688:28: warning: The left operand of '>>' is a garbage value
tmp[i] = (product[8 + i] >> 9) | (product[9 + i] << 55);
~~~~~~~~~~~~~~ ^
7 warnings generated.
In file included from tools/parser/l2cap.c:24:
tools/parser/parser.h:121:16: warning: Dereference of null pointer
time_t t = f->ts.tv_sec;
^~~~~~~~~~~~
tools/parser/parser.h:129:18: warning: Dereference of null pointer
(long long)f->ts.tv_sec,
^~~~~~~~~~~~
/usr/include/x86_64-linux-gnu/bits/stdio2.h:111:42: note: expanded from macro 'printf'
__printf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
^~~~~~~~~~~
In file included from tools/parser/l2cap.c:24:
tools/parser/parser.h:132:18: warning: Access to field 'in' results in a dereference of a null pointer (loaded from variable 'f')
printf("%c ", (f->in ? '>' : '<'));
^~~~~
/usr/include/x86_64-linux-gnu/bits/stdio2.h:111:42: note: expanded from macro 'printf'
__printf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
^~~~~~~~~~~
3 warnings generated.
In file included from tools/parser/sdp.c:24:
tools/parser/parser.h:121:16: warning: Dereference of null pointer
time_t t = f->ts.tv_sec;
^~~~~~~~~~~~
tools/parser/parser.h:129:18: warning: Dereference of null pointer
(long long)f->ts.tv_sec,
^~~~~~~~~~~~
/usr/include/x86_64-linux-gnu/bits/stdio2.h:111:42: note: expanded from macro 'printf'
__printf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
^~~~~~~~~~~
In file included from tools/parser/sdp.c:24:
tools/parser/parser.h:132:18: warning: Access to field 'in' results in a dereference of a null pointer (loaded from variable 'f')
printf("%c ", (f->in ? '>' : '<'));
^~~~~
/usr/include/x86_64-linux-gnu/bits/stdio2.h:111:42: note: expanded from macro 'printf'
__printf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
^~~~~~~~~~~
3 warnings generated.
In file included from tools/parser/ppp.c:22:
tools/parser/parser.h:159:2: warning: Undefined or garbage value returned to caller
return *u8_ptr;
^~~~~~~~~~~~~~
tools/parser/ppp.c:108:30: warning: The left operand of '&' is a garbage value
if (*((uint8_t *) frm->ptr) & 0x80)
~~~~~~~~~~~~~~~~~~~~~~~ ^
2 warnings generated.
emulator/serial.c:150:2: warning: Assigned value is garbage or undefined
enum btdev_type uninitialized_var(type);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/serial.c:150:36: warning: Value stored to 'type' during its initialization is never read
enum btdev_type uninitialized_var(type);
^~~~
emulator/serial.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
^ ~
emulator/serial.c:213:2: warning: Assigned value is garbage or undefined
enum btdev_type uninitialized_var(dev_type);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/serial.c:213:36: warning: Value stored to 'dev_type' during its initialization is never read
enum btdev_type uninitialized_var(dev_type);
^~~~~~~~
emulator/serial.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
^ ~
4 warnings generated.
emulator/server.c:218:2: warning: Assigned value is garbage or undefined
enum btdev_type uninitialized_var(type);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
emulator/server.c:218:36: warning: Value stored to 'type' during its initialization is never read
enum btdev_type uninitialized_var(type);
^~~~
emulator/server.c:36:30: note: expanded from macro 'uninitialized_var'
#define uninitialized_var(x) x = x
^ ~
2 warnings generated.
emulator/b1ee.c:258:3: warning: Potential leak of memory pointed to by 'server_port'
int opt;
^~~~~~~
emulator/b1ee.c:258:3: warning: Potential leak of memory pointed to by 'sniffer_port'
int opt;
^~~~~~~
emulator/b1ee.c:289:2: warning: Value stored to 'argc' is never read
argc = argc - optind;
^ ~~~~~~~~~~~~~3 warnings generated./usr/bin/ld: tools/test-runner.o: in function `start_controller_forward':
/github/workspace/src/src/tools/test-runner.c:989: undefined reference to `openpty'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:6194: tools/test-runner] Error 1
make[1]: *** Waiting for unfinished jobs....emulator/btdev.c:6620:20: warning: Access to field 'link' results in a dereference of a null pointer (loaded from variable 'acl')
le_past_received(acl->link, pa);
^~~~~~~~~
emulator/btdev.c:6720:25: warning: Access to field 'link' results in a dereference of a null pointer (loaded from variable 'acl')
le_past_info_received(acl->link, ea);
^~~~~~~~~2 warnings generated.
make: *** [Makefile:4224: all] Error 2
---
Regards,
Linux Bluetooth
PATCH 2/11 emulator: btvirt: allow specifying where server unix sockets are made
In reply to Pauli VirtanenMake --server to take optional path name where to create the various server sockets.
--- emulator/main.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-)
emulator/main.c +24 -13
@@ -18,6 +18,7 @@#include <stdbool.h> #include <getopt.h> #include <sys/uio.h> +#include <limits.h> #include "src/shared/mainloop.h" #include "src/shared/util.h"
@@ -46,7 +47,7 @@ static void usage(void)printf ("options:\n" "\t-d Enable debug\n" "\t-S Create local serial port\n" -"\t-s Create local server sockets\n" +"\t-s[path=/tmp] Create local server sockets\n" "\t-t[port=45550] Create a TCP server\n" "\t-l[num] Number of local controllers\n" "\t-L Create LE only controller\n"
@@ -60,7 +61,7 @@ static void usage(void)static const struct option main_options[] = { {"debug" , no_argument,NULL ,'d' }, {"serial" , no_argument,NULL ,'S' }, - {"server" , no_argument,NULL ,'s' }, + {"server" , optional_argument,NULL ,'s' }, {"tcp" , optional_argument,NULL ,'t' }, {"local" , optional_argument,NULL ,'l' }, {"le" , no_argument,NULL ,'L' },
@@ -88,6 +89,7 @@ int main(int argc, char *argv[])struct server *server5;bool debug_enabled = false;bool server_enabled = false; +const char *server_path ="/tmp" ;uint16_t tcp_port =0 ;bool serial_enabled = false;int letest_count =0 ;
@@ -100,7 +102,7 @@ int main(int argc, char *argv[])for (;;) {int opt; - opt =getopt_long (argc, argv,"dSst::l::LBAU::T::vh" , + opt =getopt_long (argc, argv,"dSs::t::l::LBAU::T::vh" , main_options,NULL );if (opt <0 )break ;
@@ -114,6 +116,8 @@ int main(int argc, char *argv[])break ;case 's' : server_enabled = true; +if (optarg) + server_path = optarg;break ;case 't' :if (optarg)
@@ -196,28 +200,35 @@ int main(int argc, char *argv[]) }if (server_enabled) { - server1 =server_open_unix (SERVER_TYPE_BREDRLE , -"/tmp/bt-server-bredrle" ); +char path[PATH_MAX ]; + +snprintf (path,sizeof (path),"%s/%s" , server_path, +"bt-server-bredrle" ); + server1 =server_open_unix (SERVER_TYPE_BREDRLE , path);if (!server1)fprintf (stderr,"Failed to open BR/EDR/LE server\n" ); - server2 =server_open_unix (SERVER_TYPE_BREDR , -"/tmp/bt-server-bredr" ); +snprintf (path,sizeof (path),"%s/%s" , server_path, +"bt-server-bredr" ); + server2 =server_open_unix (SERVER_TYPE_BREDR , path);if (!server2)fprintf (stderr,"Failed to open BR/EDR server\n" ); - server3 =server_open_unix (SERVER_TYPE_AMP , -"/tmp/bt-server-amp" ); +snprintf (path,sizeof (path),"%s/%s" , server_path, +"bt-server-amp" ); + server3 =server_open_unix (SERVER_TYPE_AMP , path);if (!server3)fprintf (stderr,"Failed to open AMP server\n" ); - server4 =server_open_unix (SERVER_TYPE_LE , -"/tmp/bt-server-le" ); +snprintf (path,sizeof (path),"%s/%s" , server_path, +"bt-server-le" ); + server4 =server_open_unix (SERVER_TYPE_LE , path);if (!server4)fprintf (stderr,"Failed to open LE server\n" ); - server5 =server_open_unix (SERVER_TYPE_MONITOR , -"/tmp/bt-server-mon" ); +snprintf (path,sizeof (path),"%s/%s" , server_path, +"bt-server-mon" ); + server5 =server_open_unix (SERVER_TYPE_MONITOR , path);if (!server5)fprintf (stderr,"Failed to open monitor server\n" ); }
Signature · 1 line
2.53.0
PATCH 3/11 test-runner: enable path argument for --unix
In reply to Pauli VirtanenAllow specifying the path for the controller socket to be used.
--- tools/test-runner.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
tools/test-runner.c +8 -6
@@ -54,6 +54,7 @@ static bool start_monitor = false;static bool qemu_host_cpu = false;static int num_devs =0 ;static int num_emulator =0 ; +static const char *device_path ="/tmp/bt-server-bredr" ;static const char *qemu_binary =NULL ;static const char *kernel_image =NULL ;static char *audio_server;
@@ -313,11 +314,10 @@ static void start_qemu(void) argv[pos++] = (char *) cmdline;for (i =0 ; i < num_devs; i++) { -const char *path ="/tmp/bt-server-bredr" ;char *chrdev, *serdev; - chrdev =alloca (48 +strlen (path)); -sprintf (chrdev,"socket,path=%s,id=bt%d" , path, i); + chrdev =alloca (48 +strlen (device_path)); +sprintf (chrdev,"socket,path=%s,id=bt%d" , device_path, i); serdev =alloca (48 );sprintf (serdev,"pci-serial,chardev=bt%d" , i);
@@ -1198,7 +1198,7 @@ static void usage(void)"\t-m, --monitor Start btmon\n" "\t-l, --emulator[=num] Start btvirt\n" "\t-A, --audio[=path] Start audio server\n" -"\t-u, --unix [path] Provide serial device\n" +"\t-u, --unix[=path] Provide serial device\n" "\t-U, --usb [qemu_args] Provide USB device\n" "\t-q, --qemu <path> QEMU binary\n" "\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n"
@@ -1211,7 +1211,7 @@ static const struct option main_options[] = { { "auto", no_argument, NULL, 'a' }, { "dbus", no_argument, NULL, 'b' }, { "dbus-session", no_argument, NULL, 's' }, - { "unix", no_argument, NULL, 'u' }, + { "unix", optional_argument, NULL, 'u' }, { "daemon", no_argument, NULL, 'd' }, { "emulator", no_argument, NULL, 'l' }, { "monitor", no_argument, NULL, 'm' },
@@ -1239,7 +1239,7 @@ int main(int argc, char *argv[])for (;;) {int opt; - opt =getopt_long (argc, argv,"aubdsl::mq:Hk:A::U:vh" , + opt =getopt_long (argc, argv,"au::bdsl::mq:Hk:A::U:vh" , main_options,NULL );if (opt <0 )break ;
@@ -1250,6 +1250,8 @@ int main(int argc, char *argv[])break ;case 'u' : num_devs =1 ; +if (optarg) + device_path = optarg;break ;case 'b' : start_dbus = true;
Signature · 1 line
2.53.0
PATCH 4/11 test-runner: Add -o/--option option
In reply to Pauli VirtanenAllow passing arbitrary arguments to QEMU.
--- tools/test-runner.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-)
tools/test-runner.c +19 -3
@@ -41,6 +41,7 @@#endif #define CMDLINE_MAX (2048 * 10) +#define EXTRA_OPT_MAX 64static const char *own_binary;static char **test_argv;
@@ -59,6 +60,8 @@ static const char *qemu_binary = NULL;static const char *kernel_image =NULL ;static char *audio_server;static char *usb_dev; +static char *extra_opts[EXTRA_OPT_MAX ]; +static int num_extra_opts;static const char *qemu_table[] = {"qemu-system-x86_64" ,
@@ -291,7 +294,8 @@ static void start_qemu(void) argv =alloca (sizeof (qemu_argv) + (sizeof (char *) * (6 + (num_devs *4 ))) + - (sizeof (char *) * (usb_dev ?4 :0 ))); + (sizeof (char *) * (usb_dev ?4 :0 )) + + (sizeof (char *) * num_extra_opts));memcpy (argv, qemu_argv,sizeof (qemu_argv)); pos = (sizeof (qemu_argv) /sizeof (char *)) -1 ;
@@ -335,6 +339,9 @@ static void start_qemu(void) argv[pos++] = usb_dev; } +for (i =0 ; i < num_extra_opts; ++i) + argv[pos++] = extra_opts[i]; + argv[pos] =NULL ;execve (argv[0 ], argv, qemu_envp);
@@ -1199,10 +1206,11 @@ static void usage(void)"\t-l, --emulator[=num] Start btvirt\n" "\t-A, --audio[=path] Start audio server\n" "\t-u, --unix[=path] Provide serial device\n" -"\t-U, --usb [qemu_args] Provide USB device\n" +"\t-U, --usb <qemu_args> Provide USB device\n" "\t-q, --qemu <path> QEMU binary\n" "\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n" "\t-k, --kernel <image> Kernel image (bzImage)\n" +"\t-o, --option <opt> Additional argument passed to QEMU\n" "\t-h, --help Show help options\n"); }
@@ -1220,6 +1228,7 @@ static const struct option main_options[] = { { "kernel", required_argument, NULL, 'k' }, { "audio", optional_argument, NULL, 'A' }, { "usb", required_argument, NULL, 'U' }, + { "option", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { }
@@ -1239,7 +1248,7 @@ int main(int argc, char *argv[])for (;;) {int opt; - opt =getopt_long (argc, argv,"au::bdsl::mq:Hk:A::U:vh" , + opt =getopt_long (argc, argv,"au::bdsl::mq:Hk:A::U:o:vh" , main_options,NULL );if (opt <0 )break ;
@@ -1284,6 +1293,13 @@ int main(int argc, char *argv[])case 'U' : usb_dev = optarg;break ; +case 'o' : +if (num_extra_opts >=EXTRA_OPT_MAX ) { +fprintf (stderr,"Too many -o\n" ); +return EXIT_FAILURE ; + } + extra_opts[num_extra_opts++] = optarg; +break ;case 'v' :printf ("%s\n" ,VERSION );return EXIT_SUCCESS ;
Signature · 1 line
2.53.0
PATCH 5/11 test-runner: allow source tree root for -k
In reply to Pauli VirtanenAllow passing source tree root for -k option, look up kernel below it.
--- tools/test-runner.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-)
tools/test-runner.c +28 -14
@@ -93,18 +93,31 @@ static const char *kernel_table[] = {NULL }; -static const char *find_kernel (void ) +static bool find_kernel (const char *root,char path[PATH_MAX ]) { +struct stat st;int i; -for (i =0 ; kernel_table[i]; i++) { -struct stat st; - -if (!stat (kernel_table[i], &st)) -return kernel_table[i]; +if (root) { +snprintf (path,PATH_MAX ,"%s" , root); +if (stat (path, &st)) +return false; +if (!(st.st_mode &S_IFDIR )) +return true; } -return NULL ; +for (i =0 ; kernel_table[i]; i++) { +if (root) +snprintf (path,PATH_MAX ,"%s/%s" , root, + kernel_table[i]); +else +snprintf (path,PATH_MAX ,"%s" , + kernel_table[i]); +if (!stat (path, &st)) +return true; + } + +return false; } static const struct {
@@ -1209,7 +1222,7 @@ static void usage(void)"\t-U, --usb <qemu_args> Provide USB device\n" "\t-q, --qemu <path> QEMU binary\n" "\t-H, --qemu-host-cpu Use host CPU (requires KVM support)\n" -"\t-k, --kernel <image> Kernel image (bzImage)\n" +"\t-k, --kernel <image> Kernel bzImage or source tree path\n" "\t-o, --option <opt> Additional argument passed to QEMU\n" "\t-h, --help Show help options\n"); }
@@ -1236,6 +1249,8 @@ static const struct option main_options[] = {int main (int argc,char *argv[]) { +char kernel_path[PATH_MAX ]; +if (getpid () ==1 &&getppid () ==0 ) {prepare_sandbox ();run_tests ();
@@ -1335,14 +1350,13 @@ int main(int argc, char *argv[]) } } -if (!kernel_image) { - kernel_image =find_kernel (); -if (!kernel_image) { -fprintf (stderr,"No default kernel image found\n" ); -return EXIT_FAILURE ; - } +if (!find_kernel (kernel_image, kernel_path)) { +fprintf (stderr,"No kernel image found\n" ); +return EXIT_FAILURE ; } + kernel_image = kernel_path; +printf ("Using QEMU binary %s\n" , qemu_binary);printf ("Using kernel image %s\n" , kernel_image);
Signature · 1 line
2.53.0
PATCH 6/11 doc: enable CONFIG_VIRTIO_CONSOLE in tester config
In reply to Pauli VirtanenEnable kernel option that allows using -device virtserialport in qemu. This is easier to make work reliably than pci-serial channel.
--- doc/ci.config | 1 + doc/test-runner.rst | 1 + doc/tester.config | 1 + 3 files changed, 3 insertions(+)
doc/ci.config +1 -0
@@ -6,6 +6,7 @@ CONFIG_VIRTIO=y CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_CONSOLE=y CONFIG_NET=y CONFIG_INET=y
doc/test-runner.rst +1 -0
@@ -45,6 +45,7 @@ option (like the Bluetooth subsystem) can be enabled on top of this. CONFIG_VIRTIO=y CONFIG_VIRTIO_PCI=y + CONFIG_VIRTIO_CONSOLE=y CONFIG_NET=y CONFIG_INET=y
doc/tester.config +1 -0
@@ -1,6 +1,7 @@ CONFIG_PCI=y CONFIG_VIRTIO=y CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_CONSOLE=y CONFIG_NET=y CONFIG_INET=y
Signature · 1 line
2.53.0
PATCH 7/11 test-runner: use virtio-serial for implementing -u device forwarding
In reply to Pauli VirtanenUsing pci-serial to forward eg. btvirt sockets is unreliable, as qemu or kernel seems to be sometimes dropping part of the sent data or insert spurious \0 bytes, leading to sporadic errors like:
kernel: Bluetooth: hci0: command 0x0c52 tx timeout
kernel: Bluetooth: hci0: Opcode 0x0c52 failed: -110
btvirt: packet error, unknown type: 0This appears to occur most often when host system is under load, e.g. due to multiple test-runners running at the same time. The problem is not specific to btvirt, but seems to be in the qemu serial device layer vs. kernel interaction.
Change test-runner to use virtserialport to forward the btvirt connection inside the VM, as virtio-serial doesn't appear to have these problems.
Since it's not a TTY device, we have to do vport <-> tty-with-hci-ldisc forwarding of the data in test-runner, so this becomes a bit more involved.
--- tools/test-runner.c | 300 +++++++++++++++++++++++++++++++++----------- 1 file changed, 230 insertions(+), 70 deletions(-)
tools/test-runner.c +230 -70
@@ -24,6 +24,9 @@#include <getopt.h> #include <poll.h> #include <limits.h> +#include <dirent.h> +#include <pty.h> +#include <stdint.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/types.h>
@@ -306,7 +309,7 @@ static void start_qemu(void) testargs); argv =alloca (sizeof (qemu_argv) + - (sizeof (char *) * (6 + (num_devs *4 ))) + + (sizeof (char *) * (8 + (num_devs *4 ))) + (sizeof (char *) * (usb_dev ?4 :0 )) + (sizeof (char *) * num_extra_opts));memcpy (argv, qemu_argv,sizeof (qemu_argv));
@@ -330,14 +333,17 @@ static void start_qemu(void) argv[pos++] ="-append" ; argv[pos++] = (char *) cmdline; + argv[pos++] ="-device" ; + argv[pos++] ="virtio-serial" ; +for (i =0 ; i < num_devs; i++) {char *chrdev, *serdev; chrdev =alloca (48 +strlen (device_path));sprintf (chrdev,"socket,path=%s,id=bt%d" , device_path, i); - serdev =alloca (48 ); -sprintf (serdev,"pci-serial,chardev=bt%d" , i); + serdev =alloca (64 ); +sprintf (serdev,"virtserialport,chardev=bt%d,name=bt.%d" , i, i); argv[pos++] ="-chardev" ; argv[pos++] = chrdev;
@@ -360,65 +366,12 @@ static void start_qemu(void)execve (argv[0 ], argv, qemu_envp); } -static int open_serial (const char *path) -{ -struct termios ti; -int fd, saved_ldisc, ldisc =N_HCI ; - - fd =open (path,O_RDWR |O_NOCTTY ); -if (fd <0 ) { -perror ("Failed to open serial port" ); -return -1 ; - } - -if (tcflush (fd,TCIOFLUSH ) <0 ) { -perror ("Failed to flush serial port" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { -perror ("Failed get serial line discipline" ); -close (fd); -return -1 ; - } - -/* Switch TTY to raw mode */ -memset (&ti,0 ,sizeof (ti)); -cfmakeraw (&ti); - - ti.c_cflag |= (B115200 |CLOCAL |CREAD ); - -/* Set flow control */ - ti.c_cflag |=CRTSCTS ; - -if (tcsetattr (fd,TCSANOW , &ti) <0 ) { -perror ("Failed to set serial port settings" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { -perror ("Failed set serial line discipline" ); -close (fd); -return -1 ; - } - -printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); - -return fd; -} - -static int attach_proto (const char *path,unsigned int proto, +static int attach_proto (int fd,unsigned int proto,unsigned int mandatory_flags,unsigned int optional_flags) {unsigned int flags = mandatory_flags | optional_flags; -int fd, dev_id; - - fd =open_serial (path); -if (fd <0 ) -return -1 ; +int dev_id;if (ioctl (fd,HCIUARTSETFLAGS , flags) <0 ) {if (errno ==EINVAL ) {
@@ -895,13 +848,222 @@ static int start_audio_server(pid_t pids[2])return 0 ; } +static bool find_attach_dev (char path[PATH_MAX ]) +{ +const char *vport_path ="/sys/class/virtio-ports" ; +struct dirent *entry; +DIR *dir; + + dir =opendir (vport_path); +if (!dir) +return false; + +while ((entry =readdir (dir)) !=NULL ) { +FILE *f; +char buf[64 ]; +size_t size; + +snprintf (path,PATH_MAX ,"%s/%s/name" , vport_path, + entry->d_name); + f =fopen (path,"r" ); +if (!f) +continue ; + + size =fread (buf,1 ,sizeof (buf) -1 , f); + buf[size] =0 ; + +fclose (f); + +if (strncmp (buf,"bt." ,3 ) ==0 ) { +snprintf (path,PATH_MAX ,"/dev/%s" , entry->d_name); +closedir (dir); +return true; + } + } + +closedir (dir); +return false; +} + +static void copy_fd_bidi (int src,int dst) +{ +fd_set rfds, wfds; +int fd[2 ] = { src, dst }; +uint8_t buf[2 ][4096 ]; +size_t size[2 ] = {0 ,0 }; +size_t pos[2 ] = {0 ,0 }; +int i, ret; + +/* Simple copying of data src <-> dst to both directions */ + +for (i =0 ; i <2 ; ++i) { +int flags =fcntl (fd[i],F_GETFL ); + +if (fcntl (fd[i],F_SETFL , flags |O_NONBLOCK ) <0 ) { +perror ("fcntl" ); + goto error; + } + } + +while (1 ) { +FD_ZERO (&rfds); +FD_ZERO (&wfds); + +for (i =0 ; i <2 ; ++i) { +if (size[i]) +FD_SET (fd[i], &wfds); +else +FD_SET (fd[1 - i], &rfds); + } + + ret =select (FD_SETSIZE , &rfds, &wfds,NULL ,NULL ); +if (ret <0 ) { +if (errno ==EINTR ) +continue ; +perror ("select" ); + goto error; + } + +for (i =0 ; i <2 ; ++i) { +ssize_t s; + +if (!size[i] &&FD_ISSET (fd[1 - i], &rfds)) { + s =read (fd[1 - i], buf[i],sizeof (buf[i])); +if (s >=0 ) { + size[i] = s; + pos[i] =0 ; + }else if (errno ==EINTR ) { +/* ok */ + }else { +perror ("read" ); + goto error; + } + + } + +if (size[i]) { + s =write (fd[i], buf[i] + pos[i], size[i]); +if (s >=0 ) { + size[i] -= s; + pos[i] += s; + }else if (errno ==EINTR || errno ==EAGAIN + || errno ==EWOULDBLOCK ) { +/* ok */ + }else { +perror ("write" ); + goto error; + } + } + } + } +return ; + +error: +fprintf (stderr,"Bluetooth controller forward terminated with error\n" ); +exit (1 ); +} + +static int start_controller_forward (const char *path,pid_t *controller_pid) +{ +struct termios ti; +pid_t pid; +int src =-1 , dst =-1 , fd =-1 ; +int ret, saved_ldisc, ldisc =N_HCI ; + +/* virtio-serial ports cannot be used for HCI line disciple, so +* openpty() serial device and forward data to/from it. +*/ + + src =open (path,O_RDWR ); +if (src <0 ) + goto error; + +/* Raw mode TTY */ +memset (&ti,0 ,sizeof (ti)); +cfmakeraw (&ti); + ti.c_cflag |=B115200 |CLOCAL |CREAD ; + +/* With flow control */ + ti.c_cflag |=CRTSCTS ; + + ret =openpty (&dst, &fd,NULL , &ti,NULL ); +if (ret <0 ) + goto error; + +if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { +perror ("Failed get serial line discipline" ); + goto error; + } + +if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { +perror ("Failed set serial line discipline" ); + goto error; + } + +printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); + + pid =fork (); +if (pid <0 ) { +perror ("Failed to fork new process" ); + goto error; + }else if (pid ==0 ) { +close (fd); +copy_fd_bidi (src, dst); +exit (0 ); + } + + *controller_pid = pid; + +close (src); +close (dst); +return fd; + +error: +if (src >=0 ) +close (src); +if (dst >=0 ) +close (dst); +if (fd >=0 ) +close (fd); +return -1 ; +} + +static int attach_controller (pid_t *controller_pid) +{ +unsigned int basic_flags, extra_flags; +char path[PATH_MAX ]; +int fd; + + *controller_pid =-1 ; + +if (!find_attach_dev (path)) { +printf ("Failed to find Bluetooth controller virtio\n" ); +return -1 ; + } + +printf ("Forwarding Bluetooth controller from %s\n" , path); + + fd =start_controller_forward (path, controller_pid); +if (fd <0 ) { +printf ("Failed to forward Bluetooth controller\n" ); +return -1 ; + } + + basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); + extra_flags = (1 <<HCI_UART_VND_DETECT ); + +printf ("Attaching Bluetooth controller\n" ); + +return attach_proto (fd,HCI_UART_H4 , basic_flags, extra_flags); +} +static void run_command (char *cmdname,char *home) {char *argv[9 ], *envp[3 ];int pos =0 , idx =0 ;int serial_fd;pid_t pid, dbus_pid, daemon_pid, monitor_pid, emulator_pid, - dbus_session_pid, audio_pid[2 ]; + dbus_session_pid, audio_pid[2 ], controller_pid;int i;if (!home) {
@@ -910,18 +1072,11 @@ static void run_command(char *cmdname, char *home) }if (num_devs) { -const char *node ="/dev/ttyS1" ; -unsigned int basic_flags, extra_flags; - -printf ("Attaching BR/EDR controller to %s\n" , node); - - basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); - extra_flags = (1 <<HCI_UART_VND_DETECT ); - - serial_fd =attach_proto (node,HCI_UART_H4 , basic_flags, - extra_flags); - }else + serial_fd =attach_controller (&controller_pid); + }else { serial_fd =-1 ; + controller_pid =-1 ; + }if (start_dbus) {create_dbus_system_conf ();
@@ -1063,6 +1218,11 @@ start_next: monitor_pid =-1 ; } +if (corpse == controller_pid) { +printf ("Controller terminated\n" ); + controller_pid =-1 ; + } +for (i =0 ; i <2 ; ++i) {if (corpse == audio_pid[i]) {printf ("Audio server %d terminated\n" , i);
Signature · 1 line
2.53.0
Luiz Augusto von Dentz
maintainerRe: [PATCH BlueZ 07/11] test-runner: use virtio-serial for implementing -u device forwarding
Hi Pauli,
Quoted 8 lines · Pauli Virtanen
Using pci-serial to forward eg. btvirt sockets is unreliable, as qemu or kernel seems to be sometimes dropping part of the sent data or insert spurious \0 bytes, leading to sporadic errors like:
kernel: Bluetooth: hci0: command 0x0c52 tx timeout kernel: Bluetooth: hci0: Opcode 0x0c52 failed: -110 btvirt: packet error, unknown type: 0
Interesting, so this comes down the pci-serial being unreliable? There have been occurrences of this even without attaching any controller with the VM, LL Privacy tests in mgmt-tester do occasionally timeout like above, but perhaps that is a different issue.
Quoted 5 lines
This appears to occur most often when host system is under load, e.g. due to multiple test-runners running at the same time. The problem is not specific to btvirt, but seems to be in the qemu serial device layer vs. kernel interaction.
Quoted 7 lines
Change test-runner to use virtserialport to forward the btvirt connection inside the VM, as virtio-serial doesn't appear to have these problems.
Since it's not a TTY device, we have to do vport <-> tty-with-hci-ldisc forwarding of the data in test-runner, so this becomes a bit more involved.
I wonder if we could just use virtio_bt
(https://github.com/torvalds/linux/blob/master/drivers/bluetooth/virtio_bt.c)
here, instead of using a serial, so we don't have to copy the data both ways? Or perhaps the problem is that this was never completed upstream @Marcel Holtmann?
Quoted 378 lines
--- tools/test-runner.c | 300 +++++++++++++++++++++++++++++++++----------- 1 file changed, 230 insertions(+), 70 deletions(-)tools/test-runner.c +230
-71@@ -24,6 +24,9 @@#include <getopt.h> #include <poll.h> #include <limits.h> +#include <dirent.h> +#include <pty.h> +#include <stdint.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/types.h> @@ -306,7 +309,7 @@ static void start_qemu(void) testargs); argv =alloca (sizeof (qemu_argv) + - (sizeof (char *) * (6 + (num_devs *4 ))) + + (sizeof (char *) * (8 + (num_devs *4 ))) + (sizeof (char *) * (usb_dev ?4 :0 )) + (sizeof (char *) * num_extra_opts));memcpy (argv, qemu_argv,sizeof (qemu_argv));@@ -330,14 +333,17 @@ static void start_qemu(void) argv[pos++] ="-append" ; argv[pos++] = (char *) cmdline; + argv[pos++] ="-device" ; + argv[pos++] ="virtio-serial" ; +for (i =0 ; i < num_devs; i++) {char *chrdev, *serdev; chrdev =alloca (48 +strlen (device_path));sprintf (chrdev,"socket,path=%s,id=bt%d" , device_path, i); - serdev =alloca (48 ); -sprintf (serdev,"pci-serial,chardev=bt%d" , i); + serdev =alloca (64 ); +sprintf (serdev,"virtserialport,chardev=bt%d,name=bt.%d" , i, i); argv[pos++] ="-chardev" ; argv[pos++] = chrdev;@@ -360,65 +366,12 @@ static void start_qemu(void)execve (argv[0 ], argv, qemu_envp); } -static int open_serial (const char *path) -{ -struct termios ti; -int fd, saved_ldisc, ldisc =N_HCI ; - - fd =open (path,O_RDWR |O_NOCTTY ); -if (fd <0 ) { -perror ("Failed to open serial port" ); -return -1 ; - } - -if (tcflush (fd,TCIOFLUSH ) <0 ) { -perror ("Failed to flush serial port" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { -perror ("Failed get serial line discipline" ); -close (fd); -return -1 ; - } - -/* Switch TTY to raw mode */ -memset (&ti,0 ,sizeof (ti)); -cfmakeraw (&ti); - - ti.c_cflag |= (B115200 |CLOCAL |CREAD ); - -/* Set flow control */ - ti.c_cflag |=CRTSCTS ; - -if (tcsetattr (fd,TCSANOW , &ti) <0 ) { -perror ("Failed to set serial port settings" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { -perror ("Failed set serial line discipline" ); -close (fd); -return -1 ; - } - -printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); - -return fd; -} - -static int attach_proto (const char *path,unsigned int proto, +static int attach_proto (int fd,unsigned int proto,unsigned int mandatory_flags,unsigned int optional_flags) {unsigned int flags = mandatory_flags | optional_flags; -int fd, dev_id; - - fd =open_serial (path); -if (fd <0 ) -return -1 ; +int dev_id;if (ioctl (fd,HCIUARTSETFLAGS , flags) <0 ) {if (errno ==EINVAL ) {@@ -895,13 +848,222 @@ static int start_audio_server(pid_t pids[2])return 0 ; } +static bool find_attach_dev (char path[PATH_MAX ]) +{ +const char *vport_path ="/sys/class/virtio-ports" ; +struct dirent *entry; +DIR *dir; + + dir =opendir (vport_path); +if (!dir) +return false; + +while ((entry =readdir (dir)) !=NULL ) { +FILE *f; +char buf[64 ]; +size_t size; + +snprintf (path,PATH_MAX ,"%s/%s/name" , vport_path, + entry->d_name); + f =fopen (path,"r" ); +if (!f) +continue ; + + size =fread (buf,1 ,sizeof (buf) -1 , f); + buf[size] =0 ; + +fclose (f); + +if (strncmp (buf,"bt." ,3 ) ==0 ) { +snprintf (path,PATH_MAX ,"/dev/%s" , entry->d_name); +closedir (dir); +return true; + } + } + +closedir (dir); +return false; +} + +static void copy_fd_bidi (int src,int dst) +{ +fd_set rfds, wfds; +int fd[2 ] = { src, dst }; +uint8_t buf[2 ][4096 ]; +size_t size[2 ] = {0 ,0 }; +size_t pos[2 ] = {0 ,0 }; +int i, ret; + +/* Simple copying of data src <-> dst to both directions */ + +for (i =0 ; i <2 ; ++i) { +int flags =fcntl (fd[i],F_GETFL ); + +if (fcntl (fd[i],F_SETFL , flags |O_NONBLOCK ) <0 ) { +perror ("fcntl" ); + goto error; + } + } + +while (1 ) { +FD_ZERO (&rfds); +FD_ZERO (&wfds); + +for (i =0 ; i <2 ; ++i) { +if (size[i]) +FD_SET (fd[i], &wfds); +else +FD_SET (fd[1 - i], &rfds); + } + + ret =select (FD_SETSIZE , &rfds, &wfds,NULL ,NULL ); +if (ret <0 ) { +if (errno ==EINTR ) +continue ; +perror ("select" ); + goto error; + } + +for (i =0 ; i <2 ; ++i) { +ssize_t s; + +if (!size[i] &&FD_ISSET (fd[1 - i], &rfds)) { + s =read (fd[1 - i], buf[i],sizeof (buf[i])); +if (s >=0 ) { + size[i] = s; + pos[i] =0 ; + }else if (errno ==EINTR ) { +/* ok */ + }else { +perror ("read" ); + goto error; + } + + } + +if (size[i]) { + s =write (fd[i], buf[i] + pos[i], size[i]); +if (s >=0 ) { + size[i] -= s; + pos[i] += s; + }else if (errno ==EINTR || errno ==EAGAIN + || errno ==EWOULDBLOCK ) { +/* ok */ + }else { +perror ("write" ); + goto error; + } + } + } + } +return ; + +error: +fprintf (stderr,"Bluetooth controller forward terminated with error\n" ); +exit (1 ); +} + +static int start_controller_forward (const char *path,pid_t *controller_pid) +{ +struct termios ti; +pid_t pid; +int src =-1 , dst =-1 , fd =-1 ; +int ret, saved_ldisc, ldisc =N_HCI ; + +/* virtio-serial ports cannot be used for HCI line disciple, so +* openpty() serial device and forward data to/from it. +*/ + + src =open (path,O_RDWR ); +if (src <0 ) + goto error; + +/* Raw mode TTY */ +memset (&ti,0 ,sizeof (ti)); +cfmakeraw (&ti); + ti.c_cflag |=B115200 |CLOCAL |CREAD ; + +/* With flow control */ + ti.c_cflag |=CRTSCTS ; + + ret =openpty (&dst, &fd,NULL , &ti,NULL ); +if (ret <0 ) + goto error; + +if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { +perror ("Failed get serial line discipline" ); + goto error; + } + +if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { +perror ("Failed set serial line discipline" ); + goto error; + } + +printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); + + pid =fork (); +if (pid <0 ) { +perror ("Failed to fork new process" ); + goto error; + }else if (pid ==0 ) { +close (fd); +copy_fd_bidi (src, dst); +exit (0 ); + } + + *controller_pid = pid; + +close (src); +close (dst); +return fd; + +error: +if (src >=0 ) +close (src); +if (dst >=0 ) +close (dst); +if (fd >=0 ) +close (fd); +return -1 ; +} + +static int attach_controller (pid_t *controller_pid) +{ +unsigned int basic_flags, extra_flags; +char path[PATH_MAX ]; +int fd; + + *controller_pid =-1 ; + +if (!find_attach_dev (path)) { +printf ("Failed to find Bluetooth controller virtio\n" ); +return -1 ; + } + +printf ("Forwarding Bluetooth controller from %s\n" , path); + + fd =start_controller_forward (path, controller_pid); +if (fd <0 ) { +printf ("Failed to forward Bluetooth controller\n" ); +return -1 ; + } + + basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); + extra_flags = (1 <<HCI_UART_VND_DETECT ); + +printf ("Attaching Bluetooth controller\n" ); + +return attach_proto (fd,HCI_UART_H4 , basic_flags, extra_flags); +} +static void run_command (char *cmdname,char *home) {char *argv[9 ], *envp[3 ];int pos =0 , idx =0 ;int serial_fd;pid_t pid, dbus_pid, daemon_pid, monitor_pid, emulator_pid, - dbus_session_pid, audio_pid[2 ]; + dbus_session_pid, audio_pid[2 ], controller_pid;int i;if (!home) {@@ -910,18 +1072,11 @@ static void run_command(char *cmdname, char *home) }if (num_devs) { -const char *node ="/dev/ttyS1" ; -unsigned int basic_flags, extra_flags; - -printf ("Attaching BR/EDR controller to %s\n" , node); - - basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); - extra_flags = (1 <<HCI_UART_VND_DETECT ); - - serial_fd =attach_proto (node,HCI_UART_H4 , basic_flags, - extra_flags); - }else + serial_fd =attach_controller (&controller_pid); + }else { serial_fd =-1 ; + controller_pid =-1 ; + }if (start_dbus) {create_dbus_system_conf ();@@ -1063,6 +1218,11 @@ start_next: monitor_pid =-1 ; } +if (corpse == controller_pid) { +printf ("Controller terminated\n" ); + controller_pid =-1 ; + } +for (i =0 ; i <2 ; ++i) {if (corpse == audio_pid[i]) {printf ("Audio server %d terminated\n" , i); --2.53.0
Signature · 1 line
Luiz Augusto von Dentz
Re: [PATCH BlueZ 07/11] test-runner: use virtio-serial for implementing -u device forwarding
Hi,
ma, 2026-03-02 kello 16:03 -0500, Luiz Augusto von Dentz kirjoitti:
Quoted 11 lines · Pauli Virtanen
Quoted 8 lines
Using pci-serial to forward eg. btvirt sockets is unreliable, as qemu or kernel seems to be sometimes dropping part of the sent data or insert spurious \0 bytes, leading to sporadic errors like:
kernel: Bluetooth: hci0: command 0x0c52 tx timeout kernel: Bluetooth: hci0: Opcode 0x0c52 failed: -110 btvirt: packet error, unknown type: 0Interesting, so this comes down the pci-serial being unreliable?
The sporadic corruptions are not specific to btvirt (occur also with the test rpc channel here if setup via serial chardev), are not present with virtio-serial, and appear only when the parent machine is under load. I did not find the root cause for this.
It sounds strange to me, I'd expect qemu serial be heavily used.
It could be test-runner.c is setting up the serial channel wrong for the -u option, but I did not find if it could be fixed.
Quoted 4 lines
There
have been occurrences of this even without attaching any controller with the VM, LL Privacy tests in mgmt-tester do occasionally timeout like above, but perhaps that is a different issue.
mgmt-tester btdev connects via vhci and runs inside the vm, so this sounds like different issue.
Quoted 13 lines
Quoted 5 lines
This appears to occur most often when host system is under load, e.g. due to multiple test-runners running at the same time. The problem is not specific to btvirt, but seems to be in the qemu serial device layer vs. kernel interaction.
Quoted 7 lines
Change test-runner to use virtserialport to forward the btvirt connection inside the VM, as virtio-serial doesn't appear to have these problems.
Since it's not a TTY device, we have to do vport <-> tty-with-hci-ldisc forwarding of the data in test-runner, so this becomes a bit more involved.
Quoted 5 lines
I wonder if we could just use
virtio_bt
(https://github.com/torvalds/linux/blob/master/drivers/bluetooth/virtio_bt.c)
here, instead of using a serial, so we don't have to copy the data both ways? Or perhaps the problem is that this was never completed upstream @Marcel Holtmann?
At first sight it looks like Qemu does not support virtio-bluetooth.
Quoted 379 lines
Quoted 378 lines
--- tools/test-runner.c | 300 +++++++++++++++++++++++++++++++++----------- 1 file changed, 230 insertions(+), 70 deletions(-)tools/test-runner.c +230
-71@@ -24,6 +24,9 @@#include <getopt.h> #include <poll.h> #include <limits.h> +#include <dirent.h> +#include <pty.h> +#include <stdint.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/types.h> @@ -306,7 +309,7 @@ static void start_qemu(void) testargs); argv =alloca (sizeof (qemu_argv) + - (sizeof (char *) * (6 + (num_devs *4 ))) + + (sizeof (char *) * (8 + (num_devs *4 ))) + (sizeof (char *) * (usb_dev ?4 :0 )) + (sizeof (char *) * num_extra_opts));memcpy (argv, qemu_argv,sizeof (qemu_argv));@@ -330,14 +333,17 @@ static void start_qemu(void) argv[pos++] ="-append" ; argv[pos++] = (char *) cmdline; + argv[pos++] ="-device" ; + argv[pos++] ="virtio-serial" ; +for (i =0 ; i < num_devs; i++) {char *chrdev, *serdev; chrdev =alloca (48 +strlen (device_path));sprintf (chrdev,"socket,path=%s,id=bt%d" , device_path, i); - serdev =alloca (48 ); -sprintf (serdev,"pci-serial,chardev=bt%d" , i); + serdev =alloca (64 ); +sprintf (serdev,"virtserialport,chardev=bt%d,name=bt.%d" , i, i); argv[pos++] ="-chardev" ; argv[pos++] = chrdev;@@ -360,65 +366,12 @@ static void start_qemu(void)execve (argv[0 ], argv, qemu_envp); } -static int open_serial (const char *path) -{ -struct termios ti; -int fd, saved_ldisc, ldisc =N_HCI ; - - fd =open (path,O_RDWR |O_NOCTTY ); -if (fd <0 ) { -perror ("Failed to open serial port" ); -return -1 ; - } - -if (tcflush (fd,TCIOFLUSH ) <0 ) { -perror ("Failed to flush serial port" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { -perror ("Failed get serial line discipline" ); -close (fd); -return -1 ; - } - -/* Switch TTY to raw mode */ -memset (&ti,0 ,sizeof (ti)); -cfmakeraw (&ti); - - ti.c_cflag |= (B115200 |CLOCAL |CREAD ); - -/* Set flow control */ - ti.c_cflag |=CRTSCTS ; - -if (tcsetattr (fd,TCSANOW , &ti) <0 ) { -perror ("Failed to set serial port settings" ); -close (fd); -return -1 ; - } - -if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { -perror ("Failed set serial line discipline" ); -close (fd); -return -1 ; - } - -printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); - -return fd; -} - -static int attach_proto (const char *path,unsigned int proto, +static int attach_proto (int fd,unsigned int proto,unsigned int mandatory_flags,unsigned int optional_flags) {unsigned int flags = mandatory_flags | optional_flags; -int fd, dev_id; - - fd =open_serial (path); -if (fd <0 ) -return -1 ; +int dev_id;if (ioctl (fd,HCIUARTSETFLAGS , flags) <0 ) {if (errno ==EINVAL ) {@@ -895,13 +848,222 @@ static int start_audio_server(pid_t pids[2])return 0 ; } +static bool find_attach_dev (char path[PATH_MAX ]) +{ +const char *vport_path ="/sys/class/virtio-ports" ; +struct dirent *entry; +DIR *dir; + + dir =opendir (vport_path); +if (!dir) +return false; + +while ((entry =readdir (dir)) !=NULL ) { +FILE *f; +char buf[64 ]; +size_t size; + +snprintf (path,PATH_MAX ,"%s/%s/name" , vport_path, + entry->d_name); + f =fopen (path,"r" ); +if (!f) +continue ; + + size =fread (buf,1 ,sizeof (buf) -1 , f); + buf[size] =0 ; + +fclose (f); + +if (strncmp (buf,"bt." ,3 ) ==0 ) { +snprintf (path,PATH_MAX ,"/dev/%s" , entry->d_name); +closedir (dir); +return true; + } + } + +closedir (dir); +return false; +} + +static void copy_fd_bidi (int src,int dst) +{ +fd_set rfds, wfds; +int fd[2 ] = { src, dst }; +uint8_t buf[2 ][4096 ]; +size_t size[2 ] = {0 ,0 }; +size_t pos[2 ] = {0 ,0 }; +int i, ret; + +/* Simple copying of data src <-> dst to both directions */ + +for (i =0 ; i <2 ; ++i) { +int flags =fcntl (fd[i],F_GETFL ); + +if (fcntl (fd[i],F_SETFL , flags |O_NONBLOCK ) <0 ) { +perror ("fcntl" ); + goto error; + } + } + +while (1 ) { +FD_ZERO (&rfds); +FD_ZERO (&wfds); + +for (i =0 ; i <2 ; ++i) { +if (size[i]) +FD_SET (fd[i], &wfds); +else +FD_SET (fd[1 - i], &rfds); + } + + ret =select (FD_SETSIZE , &rfds, &wfds,NULL ,NULL ); +if (ret <0 ) { +if (errno ==EINTR ) +continue ; +perror ("select" ); + goto error; + } + +for (i =0 ; i <2 ; ++i) { +ssize_t s; + +if (!size[i] &&FD_ISSET (fd[1 - i], &rfds)) { + s =read (fd[1 - i], buf[i],sizeof (buf[i])); +if (s >=0 ) { + size[i] = s; + pos[i] =0 ; + }else if (errno ==EINTR ) { +/* ok */ + }else { +perror ("read" ); + goto error; + } + + } + +if (size[i]) { + s =write (fd[i], buf[i] + pos[i], size[i]); +if (s >=0 ) { + size[i] -= s; + pos[i] += s; + }else if (errno ==EINTR || errno ==EAGAIN + || errno ==EWOULDBLOCK ) { +/* ok */ + }else { +perror ("write" ); + goto error; + } + } + } + } +return ; + +error: +fprintf (stderr,"Bluetooth controller forward terminated with error\n" ); +exit (1 ); +} + +static int start_controller_forward (const char *path,pid_t *controller_pid) +{ +struct termios ti; +pid_t pid; +int src =-1 , dst =-1 , fd =-1 ; +int ret, saved_ldisc, ldisc =N_HCI ; + +/* virtio-serial ports cannot be used for HCI line disciple, so +* openpty() serial device and forward data to/from it. +*/ + + src =open (path,O_RDWR ); +if (src <0 ) + goto error; + +/* Raw mode TTY */ +memset (&ti,0 ,sizeof (ti)); +cfmakeraw (&ti); + ti.c_cflag |=B115200 |CLOCAL |CREAD ; + +/* With flow control */ + ti.c_cflag |=CRTSCTS ; + + ret =openpty (&dst, &fd,NULL , &ti,NULL ); +if (ret <0 ) + goto error; + +if (ioctl (fd,TIOCGETD , &saved_ldisc) <0 ) { +perror ("Failed get serial line discipline" ); + goto error; + } + +if (ioctl (fd,TIOCSETD , &ldisc) <0 ) { +perror ("Failed set serial line discipline" ); + goto error; + } + +printf ("Switched line discipline from %d to %d\n" , saved_ldisc, ldisc); + + pid =fork (); +if (pid <0 ) { +perror ("Failed to fork new process" ); + goto error; + }else if (pid ==0 ) { +close (fd); +copy_fd_bidi (src, dst); +exit (0 ); + } + + *controller_pid = pid; + +close (src); +close (dst); +return fd; + +error: +if (src >=0 ) +close (src); +if (dst >=0 ) +close (dst); +if (fd >=0 ) +close (fd); +return -1 ; +} + +static int attach_controller (pid_t *controller_pid) +{ +unsigned int basic_flags, extra_flags; +char path[PATH_MAX ]; +int fd; + + *controller_pid =-1 ; + +if (!find_attach_dev (path)) { +printf ("Failed to find Bluetooth controller virtio\n" ); +return -1 ; + } + +printf ("Forwarding Bluetooth controller from %s\n" , path); + + fd =start_controller_forward (path, controller_pid); +if (fd <0 ) { +printf ("Failed to forward Bluetooth controller\n" ); +return -1 ; + } + + basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); + extra_flags = (1 <<HCI_UART_VND_DETECT ); + +printf ("Attaching Bluetooth controller\n" ); + +return attach_proto (fd,HCI_UART_H4 , basic_flags, extra_flags); +} +static void run_command (char *cmdname,char *home) {char *argv[9 ], *envp[3 ];int pos =0 , idx =0 ;int serial_fd;pid_t pid, dbus_pid, daemon_pid, monitor_pid, emulator_pid, - dbus_session_pid, audio_pid[2 ]; + dbus_session_pid, audio_pid[2 ], controller_pid;int i;if (!home) {@@ -910,18 +1072,11 @@ static void run_command(char *cmdname, char *home) }if (num_devs) { -const char *node ="/dev/ttyS1" ; -unsigned int basic_flags, extra_flags; - -printf ("Attaching BR/EDR controller to %s\n" , node); - - basic_flags = (1 <<HCI_UART_RESET_ON_INIT ); - extra_flags = (1 <<HCI_UART_VND_DETECT ); - - serial_fd =attach_proto (node,HCI_UART_H4 , basic_flags, - extra_flags); - }else + serial_fd =attach_controller (&controller_pid); + }else { serial_fd =-1 ; + controller_pid =-1 ; + }if (start_dbus) {create_dbus_system_conf ();@@ -1063,6 +1218,11 @@ start_next: monitor_pid =-1 ; } +if (corpse == controller_pid) { +printf ("Controller terminated\n" ); + controller_pid =-1 ; + } +for (i =0 ; i <2 ; ++i) {if (corpse == audio_pid[i]) {printf ("Audio server %d terminated\n" , i); --2.53.0
Signature · 1 line
Pauli Virtanen
PATCH 8/11 doc: add functional/integration testing documentation
In reply to Pauli VirtanenAdd documentation for functional/integration test suite.
--- doc/test-functional.rst | 374 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 doc/test-functional.rst
doc/test-functional.rst new file +374 -0
@@ -0,0 +1,374 @@ +=============== +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. + +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: + +:--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`. + +:--vm-timeout=<seconds>: Specify timeout for communication with VM hosts. + +:--log-filter=[+-]<pattern>,[+-]<pattern>,...: Allow/deny lists + for filtering logging output. The pattern is a shell glob matching + to the logger names. + +:--build-dir=<path>: Path to build directory where to search for BlueZ + executables. + +:--list: Output brief lists of existing tests. + +Tests that require kernel image or USB controllers are skipped if none +are available. Normally, tests use `btvirt`. + + +REQUIREMENTS +============ + +Python +------ + +The following Python packages are required: + +.. code-block:: + + pytest + pexpect + dbus-python + +To install them via pip:: + + python3 -m pip install -r unit/func_test/requirements.txt + +On Fedora / RHEL:: + + 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)**. Simplest setup is + +.. code-block:: + + cp ../bluez/doc/tester.config .config + make olddefconfig + make -j8 + +USB +--- + +Some tests may require a hardware controller instead of the virtual `btvirt` one. + + +EXAMPLES +======== + +Run all tests +------------- + +.. code-block:: + + $ unit/test-functional --kernel=/pathto/bzImage + + $ export FUNCTIONAL_TESTING_KERNEL=/pathto/bzImage + $ unit/test-functional + +Show output during run +---------------------- + +.. code-block:: + + $ unit/test-functional --log-cli-level=0 + +Show only specific loggers: + +.. code-block:: + + $ unit/test-functional --log-cli-level=0 --log-filter=rpc,host + + $ unit/test-functional --log-cli-level=0 --log-filter=*.bluetoothctl + +Filter out loggers: + +.. code-block:: + + $ unit/test-functional --log-cli-level=0 --log-filter=-host + + $ unit/test-functional --log-cli-level=0 --log-filter=host,-host.*.1 + +Run selected tests +------------------ + +.. code-block:: + + $ unit/test-functional unit/func_test/test_cli_simple.py::test_bluetoothctl_script_show + + $ unit/test-functional -k test_bluetoothctl_script_show + + $ unit/test-functional -k 'test_btmgmt or test_bluetoothctl' + +Don't run tests with a given marker: + +.. code-block:: + + $ unit/test-functional -m "not pipewire" + +Don't run known-failing tests: + +.. code-block:: + + $ unit/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:: + + $ unit/test-functional -x --ff + +List all tests +-------------- + +.. code-block:: + + $ unit/test-functional --list + +Show errors from know-failing test +---------------------------------- + +.. code-block:: + + $ unit/test-functional --runxfail -k test_btmgmt_info + +Redirect USB devices +-------------------- + +.. code-block:: + + $ unit/test-functional --usb=hci0,hci1 + + $ export FUNCTIONAL_TESTING_CONTROLLERS=hci0,hci1 + $ unit/test-functional + + +WRITING TESTS +============= + +The functional tests are written in files (test modules) names +`unit/func_test/test_*.py`. They are written using standard Pytest +style. See https://docs.pytest.org/en/stable/getting-started.html + +Example: Virtual machines +------------------------- + +.. code-block:: python + + from .lib import host_config, Bluetoothd, Bluetoothctl + + @host_config( + [Bluetoothd(), Bluetoothctl()], + [Bluetoothd(), Bluetoothctl()], + ) + def test_bluetoothctl_pair(hosts): + host0, host1 = hosts + + host0.bluetoothctl.send("show\n") + host0.bluetoothctl.expect("Powered: yes") + + host1.bluetoothctl.send("show\n") + host1.bluetoothctl.expect("Powered: yes") + + 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") + +The test declares a VM setup with two Qemu instances, where both hosts +run bluetoothd and start a bluetoothctl process. The Qemu instances +have `btvirt` virtual BT controllers and can see each other. + +The test itself runs on the parent host. + +The `host0/1.bluetoothctl.*` commands invoke RPC calls to one of the +the two VM instances. In this case, they are controlling the +`bluetoothctl` process using `pexpect` library to deal with its +command line. + +When the test body finishes executing, the test passes. Or, it fails +if any ``assert`` statement fails or an error is raised. For example, +above ``RemoteError`` due to bluetoothctl not proceeding as expected +in pairing is possible. + +The host configuration (bluetoothd + bluetoothctl above) is torn down +between test (SIGTERM/SIGKILL sent etc.). + +By default the VM instance itself continues running, and may be used +for other tests that share the same VM setup. + +Generally, the framework automatically orders the tests so that the VM +setup does not need to be restarted unless needed. + + +Example host plugin +------------------- + +The `host.bluetoothctl` implementation used above is as follows: + +.. code-block:: python + + from .lib import HostPlugin + + class Bluetoothctl(HostPlugin): + # Declare a unique name: + + name = "bluetoothctl" + + # Declare dependencies on other plugins + + depends = [Bluetoothd()] + + # The following is to be run on parent host outside VMs: + + def __init__(self): + self.exe = utils.find_exe("client", "bluetoothctl") + + # These run inside VM on plugin setup / teardown: + + def setup(self, impl): + self.logger = utils.LogStream("bluetoothctl") + self.ctl = pexpect.spawn(self.exe, logfile=self.logger.stream) + + def teardown(self): + self.ctl.terminate() + self.ctl.wait() + + # These declare the custom RPC-callable methods of the plugin: + + def expect(self, *a, **kw): + ret = self.ctl.expect(*a, **kw) + log.debug("pexpect: found") + return ret, self.ctl.match.groups() + + def expect_prompt(self): + prompt = "\\[[a-zA-Z0-9. -]+\\]>" + return self.expect(prompt) + + def send(self, *a, **kw): + return self.ctl.send(*a, **kw) + +Host plugins are for injecting code to run on the VM side test hosts. +The host plugins have scope of one test. The VM side test framework +sends SIGTERM and SIGKILL to all processes in the test process group +to reset the state between each test. + +The plugins are declared by inheriting from `HostPlugin`. Their +`__init__()` is supposed to only store declarative configuration on +`self` and runs on parent side early in the test discovery phase. The +`setup()` and `teardown()` methods run on VM-side at host environment +start and end. All other methods can be invoked via RPC by the parent +tester, and any values returned by them are passed via RPC back to the +parent. + +To load a plugin to a VM host, pass it to `host_config()` in the +declaration of a given test. + +Reference +--------- + +In addition to standard Pytest features, the following items are +available in the `.lib` submodule. + +TODO: not complete + +host_config +~~~~~~~~~~~ + +.. code-block:: + + def host_config(*host_setup, hw=False) + +Declare host configuration. + +- \*host_setup: each argument is a list of plugins to be loaded on a host. + The number of arguments specifies the number of hosts. + +- hw (bool): whether to require hardware BT controller + +find_exe +~~~~~~~~ + +.. code-block:: + + from .lib import find_exe + bluetoothctl = find_exe("client", "bluetoothctl") + +Find absolute path to the given executable, either within BlueZ build +directory or on host. + +RemoteError +~~~~~~~~~~~ + +.. code-block:: + + from .lib import RemoteError + + try: + host.call(foo) + except RemoteError as exc: + print(" ".join(exc.traceback)) + original_exception = exc.exc + +Exception raised on the VM side, passed through RPC. Properties: +`traceback` is a list of traceback lines and `exc` is the original +exception instance raised on the remote side.
Signature · 1 line
2.53.0
PATCH 9/11 unit: add functional/integration testing framework
In reply to Pauli VirtanenAdd 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.
This uses tools/test-runner to launch VM instances, but does not use the intra-VM setup code to allow reusing the same VM instances for multiple tests.
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 (more convenient than Python/unittest)
- Grouping tests to minimize VM reboots
- Redirecting USB controllers to use for testing
There is no requirement that the tests spawn VM instances, the test runner can be used for any tests written in Python.
--- unit/func_test/__init__.py | 0 unit/func_test/conftest.py | 277 ++++++++++++++ unit/func_test/lib/__init__.py | 5 + unit/func_test/lib/env.py | 484 +++++++++++++++++++++++++ unit/func_test/lib/host_plugins.py | 269 ++++++++++++++ unit/func_test/lib/rpc.py | 293 +++++++++++++++ unit/func_test/lib/runner.py | 10 + unit/func_test/lib/tests/__init__.py | 0 unit/func_test/lib/tests/test_rpc.py | 49 +++ unit/func_test/lib/utils.py | 266 ++++++++++++++ unit/func_test/requirements.txt | 3 + unit/func_test/test_bluetoothctl_vm.py | 76 ++++ unit/func_test/test_btmgmt_vm.py | 29 ++ unit/pytest.ini | 6 + unit/test-functional | 8 + 15 files changed, 1775 insertions(+) create mode 100644 unit/func_test/__init__.py create mode 100644 unit/func_test/conftest.py create mode 100644 unit/func_test/lib/__init__.py create mode 100644 unit/func_test/lib/env.py create mode 100644 unit/func_test/lib/host_plugins.py create mode 100644 unit/func_test/lib/rpc.py create mode 100644 unit/func_test/lib/runner.py create mode 100644 unit/func_test/lib/tests/__init__.py create mode 100644 unit/func_test/lib/tests/test_rpc.py create mode 100644 unit/func_test/lib/utils.py create mode 100644 unit/func_test/requirements.txt create mode 100644 unit/func_test/test_bluetoothctl_vm.py create mode 100644 unit/func_test/test_btmgmt_vm.py create mode 100644 unit/pytest.ini create mode 100755 unit/test-functional
unit/func_test/__init__.py new file +0 -0
unit/func_test/conftest.py new file +277 -0
277 lines not on this page · raw message
unit/func_test/lib/__init__.py new file +5 -0
@@ -0,0 +1,5 @@ +from .rpc import RemoteError + +from .env import * +from .utils import * +from .host_plugins import *
unit/func_test/lib/env.py new file +484 -0
484 lines not on this page · raw message
unit/func_test/lib/host_plugins.py new file +269 -0
269 lines not on this page · raw message
unit/func_test/lib/rpc.py new file +293 -0
293 lines not on this page · raw message
unit/func_test/lib/runner.py new file +10 -0
@@ -0,0 +1,10 @@ +#!/usr/bin/python3 -P +# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*- +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent / ".." / "..")) + +import func_test.lib.env + +sys.exit(func_test.lib.env._main_runner())
unit/func_test/lib/tests/__init__.py new file +0 -0
unit/func_test/lib/tests/test_rpc.py new file +49 -0
49 lines not on this page · raw message
unit/func_test/lib/utils.py new file +266 -0
266 lines not on this page · raw message
unit/func_test/requirements.txt new file +3 -0
@@ -0,0 +1,3 @@ +pytest +pexpect +dbus-python
unit/func_test/test_bluetoothctl_vm.py new file +76 -0
76 lines not on this page · raw message
unit/func_test/test_btmgmt_vm.py new file +29 -0
@@ -0,0 +1,29 @@ +# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*- +""" +Tests for btmgmt using VM instances +""" +import sys +import pytest +import subprocess +import tempfile + +from .lib import host_config, find_exe, run + +pytestmark = [pytest.mark.vm] + +btmgmt = find_exe("tools", "btmgmt") + + +@host_config([]) +def test_btmgmt_info(hosts): + (host,) = hosts + + 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
unit/pytest.ini new file +6 -0
@@ -0,0 +1,6 @@ +[pytest] +log_format = %(asctime)s %(levelname)-6s %(name)-20s: %(message)s +log_level = 0 +log_file = test-functional.log +markers = + vm: tests requiring VM image
unit/test-functional new file +8 -0
@@ -0,0 +1,8 @@ +#!/bin/sh +# +# Examples: +# +# ./test-functional +# ./test-functional --log-cli-level=0 -s +# +exec python3 -m pytest "$(dirname "$0")"/func_test "$@"
Signature · 1 line
2.53.0
PATCH 10/11 unit: func_test: add Pipewire-using smoke tests
In reply to Pauli VirtanenAdd test for Pipewire hosts connecting A2DP/HFP BREDR services to each other
--- unit/func_test/test_pipewire.py | 137 ++++++++++++++++++++++++++++++++ unit/pytest.ini | 1 + 2 files changed, 138 insertions(+) create mode 100644 unit/func_test/test_pipewire.py
unit/func_test/test_pipewire.py new file +137 -0
@@ -0,0 +1,137 @@ +# -*- coding: utf-8; mode: python; eval: (blacken-mode); -*- +""" +Tests for Pipewire audio +""" +import sys +import os +import pytest +import subprocess +import tempfile +import time +import logging +import json +import dbus +from pathlib import Path + +from .lib import ( + HostPlugin, + host_config, + find_exe, + Bluetoothd, + Bluetoothctl, + DbusSession, + LogStream, +) + +pytestmark = [pytest.mark.vm, pytest.mark.pipewire] + +log = logging.getLogger(__name__) + + +class Pipewire(HostPlugin): + name = "pipewire" + depends = [DbusSession(), Bluetoothd()] + + def __init__( + self, + uuids=( + "0000110a-0000-1000-8000-00805f9b34fb", + "0000110b-0000-1000-8000-00805f9b34fb", + ), + ): + self.uuids = tuple(uuids) + try: + self.exe_pw = find_exe("", "pipewire") + self.exe_wp = find_exe("", "wireplumber") + self.exe_dump = find_exe("", "pw-dump") + except FileNotFoundError: + pytest.skip("skip", allow_module_level=True, reason="Pipewire not found") + + def setup(self, impl): + self.tmpdir = tempfile.TemporaryDirectory(prefix="pipewire-", dir="/run") + conf_dir = Path(self.tmpdir.name) / "config" + runtime_dir = Path(self.tmpdir.name) / "runtime" + + conf_dir.mkdir() + runtime_dir.mkdir() + + environ = dict(os.environ) + + environ["XDG_CONFIG_HOME"] = str(conf_dir) + environ["XDG_STATE_HOME"] = str(runtime_dir) + environ["XDG_RUNTIME_HOME"] = str(runtime_dir) + environ["PIPEWIRE_RUNTIME_DIR"] = str(runtime_dir) + environ["PIPEWIRE_DEBUG"] = "2" + environ["WIREPLUMBER_DEBUG"] = "3" + + os.environ["PIPEWIRE_RUNTIME_DIR"] = str(runtime_dir) + + log.info("Start pipewire") + + self.logger = LogStream("pipewire") + self.pw = subprocess.Popen( + self.exe_pw, + env=environ, + stdout=self.logger.stream, + stderr=subprocess.STDOUT, + ) + self.wp = subprocess.Popen( + self.exe_wp, + env=environ, + stdout=self.logger.stream, + stderr=subprocess.STDOUT, + ) + + # Wait for Pipewire's bluetooth services + bus = dbus.SystemBus() + adapter = dbus.Interface( + bus.get_object("org.bluez", "/org/bluez/hci0"), + "org.freedesktop.DBus.Properties", + ) + while True: + uuids = [str(uuid) for uuid in adapter.Get("org.bluez.Adapter1", "UUIDs")] + if all(uuid in uuids for uuid in self.uuids): + break + time.sleep(0.1) + + log.info("Pipewire ready") + + def pw_dump(self): + ret = subprocess.run(["pw-dump"], stdout=subprocess.PIPE, encoding="utf-8") + return ret.stdout + + def teardown(self): + log.info("Stop pipewire") + self.pw.terminate() + self.wp.terminate() + self.pw.wait() + self.wp.wait() + self.tmpdir.cleanup() + + +@host_config( + [Bluetoothctl(), Pipewire()], + [Bluetoothctl(), Pipewire()], +) +def test_pipewire(hosts): + from .test_bluetoothctl_vm import test_bluetoothctl_pair + + host0, host1 = hosts + + # Pair first + test_bluetoothctl_pair(hosts) + + # Connect + host1.bluetoothctl.send(f"trust {host0.bdaddr}\n") + + host0.bluetoothctl.send(f"scan off\n") + host0.bluetoothctl.send(f"connect {host1.bdaddr}\n") + + # Wait for pipewire devices to appear + for j in range(20): + text = host0.pipewire.pw_dump() + if "bluez_output." in text: + break + time.sleep(1) + else: + assert False, "no pipewire devices seen within timeout"
unit/pytest.ini +1 -0
@@ -3,4 +3,5 @@ log_format = %(asctime)s %(levelname)-6s %(name)-20s: %(message)s log_level = 0 log_file = test-functional.log markers = + pipewire: tests requiring Pipewire vm: tests requiring VM image
Signature · 1 line
2.53.0
PATCH 11/11 build: add functional testing target
In reply to Pauli VirtanenThis 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 | 7 +++++++ configure.ac | 17 +++++++++++++++++ 2 files changed, 24 insertions(+)
Makefile.am +7 -0
@@ -786,6 +786,13 @@ endif TESTS = $(unit_tests) AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69 +check-functional: all + $(srcdir)/unit/test-functional -v --kernel="$(FUNCTIONAL_TESTING_KERNEL)" --build-dir="$(top_builddir)" + +if FUNCTIONAL_TESTING +check: check-functional +endif + if DBUS_RUN_SESSION AM_TESTS_ENVIRONMENT += dbus-run-session -- endif
configure.ac +17 -0
@@ -405,6 +405,23 @@ 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 + AC_MSG_CHECKING([pytest and dependencies]) + python3 -m pip install --dry-run --no-index -r "${srcdir}/unit/func_test/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}])
Signature · 1 line
2.53.0
Luiz Augusto von Dentz
maintainerHi Pauli,
Quoted 307 lines · 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.
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.rstfor various examples.Also
unit/func_test/test_bluetoothctl_vm.pyhas some simple cases, andunit/func_test/test_pipewire.pyfor a more complicated setuphost0(qemu): Pipewire <-> BlueZ <-> kernel <-> btvirt host1(qemu): kernel <-> BlueZ <-> PipewireThe framework allows easily passing any data and code between the parent and VM hosts, so writing tests is straightforward.
***
Some examples:
$ unit/test-functional --list -qunit/func_test/lib/tests/test_rpc.py::test_basic unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2]
$ unit/test-functional -v --no-header ======================================= test session starts ======================================== collected 5 itemsunit/func_test/lib/tests/test_rpc.py::test_basic PASSED [ 20%] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] SKIPPED [ 40%] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] SKIPPED (No kernel image) [ 60%]unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] SKIPPED (No k...) [ 80%]
unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2] SKIPPED (No kernel image) [100%]=================================== 1 passed, 4 skipped in 0.19s ===================================
$ unit/test-functional --kernel=../linux ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 itemsunit/func_test/lib/tests/test_rpc.py . [ 20%] unit/func_test/test_bluetoothctl_vm.py . [ 40%] unit/func_test/test_btmgmt_vm.py . [ 60%] unit/func_test/test_bluetoothctl_vm.py . [ 80%] unit/func_test/test_pipewire.py . [100%]============================== 5 passed in 41.92s ==============================
$ unit/test-functional --kernel=../linux -k test_btmgmt ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.py. [100%]======================= 1 passed, 4 deselected in 9.15s ========================
$ grep btmgmt test-functional.log 13:15:42 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f27b81ce140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:15:42 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7fd5e35a1010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:15:42 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info$ unit/test-functional --kernel=../linux -k test_btmgmt --log-cli-level=0 ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selectedunit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] -------------------------------- live log setup --------------------------------
13:00:31 INFO func_test.lib.env : Starting btvirt: /usr/bin/stdbuf -o L -e L /home/pauli/prj/external/bluez/build/emulator/btvirt --server=/tmp/bluez-func-test-8t6ychy8 13:00:31 OUT btvirt : Bluetooth emulator ver 5.86 13:00:31 INFO func_test.lib.env : Starting host: /home/pauli/prj/external/bluez/build/tools/test-runner --kernel=../linux/arch/x86/boot/bzImage -u/tmp/bluez-func-test-8t6ychy8/bt-server-bredrle -o -chardev -o socket,id=ser0,path=/tmp/bluez-func-test-8t6ychy8/bluez-func-test-rpc-0,server=on,wait=off -o -device -o virtio-serial -o -device -o virtserialport,chardev=ser0,name=bluez-func-test-rpc -H -- /usr/bin/python3 -P /home/pauli/prj/external/bluez/unit/func_test/lib/runner.py /dev/ttyS2 13:00:31 OUT btvirt : Request for /tmp/bluez-func-test-8t6ychy8/bt-server-bredrle 13:00:32 OUT host.0.0 : early console in extract_kernel 13:00:32 OUT host.0.0 : input_data: 0x000000000425c2c4 ... 13:00:39 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f7547472140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:00:39 DEBUG host.0.0.rpc : server: done 13:00:39 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7f77dcc81010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:00:39 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info 13:00:40 OUT host.0.0.run.out : hci0: Primary controller 13:00:40 OUT host.0.0.run.out : addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000 13:00:40 OUT host.0.0.run.out : supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver 13:00:40 OUT host.0.0.run.out : current settings: br/edr 13:00:40 OUT host.0.0.run.out : name 13:00:40 OUT host.0.0.run.out : short name 13:00:40 INFO host.0.0.run : (return code 0) 13:00:40 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
13:00:40 OUT host.0.0 : qemu-system-x86_64: terminating on signal 15 from pid 149047 (python3)
======================= 1 passed, 4 deselected in 8.84s ========================$ unit/test-functional --kernel=../linux -k test_bluetoothctl_pair --log-cli-level=0 --log-filter=*.bluetoothctl,rpc.* --force-usb ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selectedunit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] -------------------------------- live log setup --------------------------------
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f268712d160>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f268712d2b0>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f268712d010>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f26871542d0>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f2687154410>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f2687154190>,) {} 13:03:20 INFO rpc.host.0.0 : client: wait_load () {} 13:03:21 DEBUG rpc.host.0.0 : client-reply 13:03:21 INFO rpc.host.0.1 : client: wait_load () {} 13:03:21 DEBUG rpc.host.0.1 : client-reply -------------------------------- live log call --------------------------------- 13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'show\n') {} 13:03:21 DEBUG rpc.host.0.0 : client-reply 13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Powered: yes') {} ... 13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'pair 70:1a:b8:73:99:bb\n') {} 13:03:23 OUT host.0.0.bluetoothctl: pair 70:1a:b8:73:99:bb 13:03:23 DEBUG rpc.host.0.0 : client-reply 13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey (\\d+).*:') {} 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> pair 70:1a:b8:73:99:bb 13:03:23 OUT host.0.0.bluetoothctl: Attempting to pair with 70:1A:B8:73:99:BB 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 device_flags_changed: 70:1A:B8:73:99:BB (BR/EDR) 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> supp: 0x00000007 curr: 0x00000000 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 type 7 discovering off 13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 70:1A:B8:73:99:BB type BR/EDR connected eir_len 12 13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Connected: yes 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> Request confirmation 13:03:25 DEBUG rpc.host.0.0 : client-reply 13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey 237345') {} 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> hci0 84:5C:F3:77:31:19 type BR/EDR connected eir_len 12 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [NEW] Device 84:5C:F3:77:31:19 BlueZ 5.86 13:03:25 DEBUG rpc.host.0.1 : client-reply 13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {} 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> Request confirmation 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes 13:03:25 DEBUG rpc.host.0.0 : client-reply 13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {} 13:03:25 OUT host.0.1.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes 13:03:25 DEBUG rpc.host.0.1 : client-reply 13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Pairing successful') {} 13:03:25 OUT host.0.0.bluetoothctl: yes 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> hci0 new_link_key 70:1A:B8:73:99:BB type 0x08 pin_len 0 store_hint 1 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Bonded: yes 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB AddressType: public 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110c-0000-1000-8000-00805f9b34fb 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110e-0000-1000-8000-00805f9b34fb 13:03:26 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
------------------------------ live log teardown -------------------------------
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:98:FF:qemu-system-x86_64: terminating on signal 15 from pid 149357 (python3)======================= 1 passed, 4 deselected in 13.22s =======================
$ COLUMNS=80 unit/test-functional -k test_btmgmt --kernel=../linux --trace ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.pyQuoted 2 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
PDB runcall (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(19)test_btmgmt_info()
-> (host,) = hosts
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) p host.bdaddr
'00:aa:01:00:00:42'
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(22)test_btmgmt_info()
-> run,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(23)test_btmgmt_info()
-> [btmgmt, "--index", "0", "info"],
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(24)test_btmgmt_info()
-> stdout=subprocess.PIPE,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(25)test_btmgmt_info()
-> stdin=subprocess.DEVNULL,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(26)test_btmgmt_info()
-> encoding="utf-8",
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(28)test_btmgmt_info()
-> assert result.returncode == 0
(Pdb) p result
CompletedProcess(args=['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info'], returncode=0, stdout='hci0:\tPrimary controller\n\taddr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000\n\tsupported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver \n\tcurrent settings: br/edr \n\tname \n\tshort name \n') (Pdb) print(result.stdout)
hci0: Primary controller
addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class0x000000
supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
current settings: br/edr
name
short name
(Pdb) q!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Quitting debugger !!!!!!!!!!!!!!!!!!! ======================= 4 deselected in 75.91s (0:01:15) =======================
***
Pauli Virtanen (11):
emulator: btvirt: check pkt lengths, don't get stuck on malformed
emulator: btvirt: allow specifying where server unix sockets are made
test-runner: enable path argument for --unix
test-runner: Add -o/--option option
test-runner: allow source tree root for -k
doc: enableCONFIG_VIRTIO_CONSOLEin tester config
test-runner: use virtio-serial for implementing -u device forwarding
doc: add functional/integration testing documentation
unit: add functional/integration testing framework
unit:func_test: add Pipewire-using smoke tests
build: add functional testing targetMakefile.am | 7 + configure.ac | 17 + doc/ci.config | 1 + doc/test-functional.rst | 374 +++++++++++++++++++ doc/test-runner.rst | 1 + doc/tester.config | 1 + emulator/main.c | 37 +- emulator/server.c | 9 + tools/test-runner.c | 376 ++++++++++++++----- unit/func_test/__init__.py | 0 unit/func_test/conftest.py | 277 ++++++++++++++ unit/func_test/lib/__init__.py | 5 + unit/func_test/lib/env.py | 484 +++++++++++++++++++++++++ unit/func_test/lib/host_plugins.py | 269 ++++++++++++++ unit/func_test/lib/rpc.py | 293 +++++++++++++++ unit/func_test/lib/runner.py | 10 + unit/func_test/lib/tests/__init__.py | 0 unit/func_test/lib/tests/test_rpc.py | 49 +++ unit/func_test/lib/utils.py | 266 ++++++++++++++ unit/func_test/requirements.txt | 3 + unit/func_test/test_bluetoothctl_vm.py | 76 ++++ unit/func_test/test_btmgmt_vm.py | 29 ++ unit/func_test/test_pipewire.py | 137 +++++++ unit/pytest.ini | 7 + unit/test-functional | 8 + 25 files changed, 2631 insertions(+), 105 deletions(-) create mode 100644 doc/test-functional.rst create mode 100644 unit/func_test/__init__.py create mode 100644 unit/func_test/conftest.py create mode 100644 unit/func_test/lib/__init__.py create mode 100644 unit/func_test/lib/env.py create mode 100644 unit/func_test/lib/host_plugins.py create mode 100644 unit/func_test/lib/rpc.py create mode 100644 unit/func_test/lib/runner.py create mode 100644 unit/func_test/lib/tests/__init__.py create mode 100644 unit/func_test/lib/tests/test_rpc.py create mode 100644 unit/func_test/lib/utils.py create mode 100644 unit/func_test/requirements.txt create mode 100644 unit/func_test/test_bluetoothctl_vm.py create mode 100644 unit/func_test/test_btmgmt_vm.py create mode 100644 unit/func_test/test_pipewire.py create mode 100644 unit/pytest.ini create mode 100755 unit/test-functional
Quoted 2 lines
--
2.53.0
Looks like a fairly solid start, that said I wouldn't mix these with unit test, that is more of a unit/whitebox style testing not really meant for end-to-end testing, Id probably have this under test/functional or test/pytest, anyway we could possibly remove the existing python tests and just convert them, or perhaps move them to examples since they are just demostrating how to use our D-Bus APIs using python rather than testing a specific use-case, etc.
Regarding pytest, Im not really an expert in the python testing frameworks so I will probably need to do some digging to see what are the options and there might be companies that already emply similar testing environment so I guess it is a good chance to make an attempt to convince more people to contribute upstream so we can consolidate in a single framework for end-to-end testing.
Signature · 1 line
Luiz Augusto von Dentz
Hi,
ma, 2026-03-02 kello 16:18 -0500, Luiz Augusto von Dentz kirjoitti:
Quoted 310 lines
Hi Pauli,
Quoted 307 lines · 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.
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.rstfor various examples.Also
unit/func_test/test_bluetoothctl_vm.pyhas some simple cases, andunit/func_test/test_pipewire.pyfor a more complicated setuphost0(qemu): Pipewire <-> BlueZ <-> kernel <-> btvirt host1(qemu): kernel <-> BlueZ <-> PipewireThe framework allows easily passing any data and code between the parent and VM hosts, so writing tests is straightforward.
***
Some examples:
$ unit/test-functional --list -qunit/func_test/lib/tests/test_rpc.py::test_basic unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2]
$ unit/test-functional -v --no-header ======================================= test session starts ======================================== collected 5 itemsunit/func_test/lib/tests/test_rpc.py::test_basic PASSED [ 20%] unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_script_show[hosts1-vm1] SKIPPED [ 40%] unit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] SKIPPED (No kernel image) [ 60%]unit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] SKIPPED (No k...) [ 80%]
unit/func_test/test_pipewire.py::test_pipewire[hosts3-vm2] SKIPPED (No kernel image) [100%]=================================== 1 passed, 4 skipped in 0.19s ===================================
$ unit/test-functional --kernel=../linux ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 itemsunit/func_test/lib/tests/test_rpc.py . [ 20%] unit/func_test/test_bluetoothctl_vm.py . [ 40%] unit/func_test/test_btmgmt_vm.py . [ 60%] unit/func_test/test_bluetoothctl_vm.py . [ 80%] unit/func_test/test_pipewire.py . [100%]============================== 5 passed in 41.92s ==============================
$ unit/test-functional --kernel=../linux -k test_btmgmt ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.py. [100%]======================= 1 passed, 4 deselected in 9.15s ========================
$ grep btmgmt test-functional.log 13:15:42 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f27b81ce140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:15:42 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7fd5e35a1010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:15:42 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info$ unit/test-functional --kernel=../linux -k test_btmgmt --log-cli-level=0 ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selectedunit/func_test/test_btmgmt_vm.py::test_btmgmt_info[hosts2-vm1] -------------------------------- live log setup --------------------------------
13:00:31 INFO func_test.lib.env : Starting btvirt: /usr/bin/stdbuf -o L -e L /home/pauli/prj/external/bluez/build/emulator/btvirt --server=/tmp/bluez-func-test-8t6ychy8 13:00:31 OUT btvirt : Bluetooth emulator ver 5.86 13:00:31 INFO func_test.lib.env : Starting host: /home/pauli/prj/external/bluez/build/tools/test-runner --kernel=../linux/arch/x86/boot/bzImage -u/tmp/bluez-func-test-8t6ychy8/bt-server-bredrle -o -chardev -o socket,id=ser0,path=/tmp/bluez-func-test-8t6ychy8/bluez-func-test-rpc-0,server=on,wait=off -o -device -o virtio-serial -o -device -o virtserialport,chardev=ser0,name=bluez-func-test-rpc -H -- /usr/bin/python3 -P /home/pauli/prj/external/bluez/unit/func_test/lib/runner.py /dev/ttyS2 13:00:31 OUT btvirt : Request for /tmp/bluez-func-test-8t6ychy8/bt-server-bredrle 13:00:32 OUT host.0.0 : early console in extract_kernel 13:00:32 OUT host.0.0 : input_data: 0x000000000425c2c4 ... 13:00:39 INFO rpc.host.0.0 : client: call_plugin ('call', '__call__', <function run at 0x7f7547472140>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:00:39 DEBUG host.0.0.rpc : server: done 13:00:39 INFO host.0.0.rpc : server: call_plugin ('call', '__call__', <function run at 0x7f77dcc81010>, ['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info']) {'stdout': -1, 'stdin': -3, 'encoding': 'utf-8'} 13:00:39 INFO host.0.0.run : $ /home/pauli/prj/external/bluez/build/tools/btmgmt --index 0 info 13:00:40 OUT host.0.0.run.out : hci0: Primary controller 13:00:40 OUT host.0.0.run.out : addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000 13:00:40 OUT host.0.0.run.out : supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver 13:00:40 OUT host.0.0.run.out : current settings: br/edr 13:00:40 OUT host.0.0.run.out : name 13:00:40 OUT host.0.0.run.out : short name 13:00:40 INFO host.0.0.run : (return code 0) 13:00:40 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
13:00:40 OUT host.0.0 : qemu-system-x86_64: terminating on signal 15 from pid 149047 (python3)
======================= 1 passed, 4 deselected in 8.84s ========================$ unit/test-functional --kernel=../linux -k test_bluetoothctl_pair --log-cli-level=0 --log-filter=*.bluetoothctl,rpc.* --force-usb ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selectedunit/func_test/test_bluetoothctl_vm.py::test_bluetoothctl_pair[hosts0-vm2] -------------------------------- live log setup --------------------------------
13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f268712d160>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f268712d2b0>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {} 13:03:20 INFO rpc.host.0.0 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f268712d010>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bdaddr object at 0x7f26871542d0>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Call object at 0x7f2687154410>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.DbusSystem object at 0x7f2687aa30e0>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothd object at 0x7f2687aa3230>,) {} 13:03:20 INFO rpc.host.0.1 : client: start_load (<func_test.lib.host_plugins.Bluetoothctl object at 0x7f2687154190>,) {} 13:03:20 INFO rpc.host.0.0 : client: wait_load () {} 13:03:21 DEBUG rpc.host.0.0 : client-reply 13:03:21 INFO rpc.host.0.1 : client: wait_load () {} 13:03:21 DEBUG rpc.host.0.1 : client-reply -------------------------------- live log call --------------------------------- 13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'show\n') {} 13:03:21 DEBUG rpc.host.0.0 : client-reply 13:03:21 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Powered: yes') {} ... 13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'pair 70:1a:b8:73:99:bb\n') {} 13:03:23 OUT host.0.0.bluetoothctl: pair 70:1a:b8:73:99:bb 13:03:23 DEBUG rpc.host.0.0 : client-reply 13:03:23 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey (\\d+).*:') {} 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> pair 70:1a:b8:73:99:bb 13:03:23 OUT host.0.0.bluetoothctl: Attempting to pair with 70:1A:B8:73:99:BB 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 device_flags_changed: 70:1A:B8:73:99:BB (BR/EDR) 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> supp: 0x00000007 curr: 0x00000000 13:03:23 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 type 7 discovering off 13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> hci0 70:1A:B8:73:99:BB type BR/EDR connected eir_len 12 13:03:25 OUT host.0.0.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Connected: yes 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> Request confirmation 13:03:25 DEBUG rpc.host.0.0 : client-reply 13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'expect', 'Confirm passkey 237345') {} 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> hci0 84:5C:F3:77:31:19 type BR/EDR connected eir_len 12 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [NEW] Device 84:5C:F3:77:31:19 BlueZ 5.86 13:03:25 DEBUG rpc.host.0.1 : client-reply 13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {} 13:03:25 OUT host.0.1.bluetoothctl: [bluetoothctl]> [BlueZ 5.86]> Request confirmation 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes 13:03:25 DEBUG rpc.host.0.0 : client-reply 13:03:25 INFO rpc.host.0.1 : client: call_plugin ('bluetoothctl', 'send', 'yes\n') {} 13:03:25 OUT host.0.1.bluetoothctl: [BlueZ 5.86]> [agent] Confirm passkey 237345 (yes/no): yes 13:03:25 DEBUG rpc.host.0.1 : client-reply 13:03:25 INFO rpc.host.0.0 : client: call_plugin ('bluetoothctl', 'expect', 'Pairing successful') {} 13:03:25 OUT host.0.0.bluetoothctl: yes 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> hci0 new_link_key 70:1A:B8:73:99:BB type 0x08 pin_len 0 store_hint 1 13:03:25 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB Bonded: yes 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB AddressType: public 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110c-0000-1000-8000-00805f9b34fb 13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:73:99:BB UUIDs: 0000110e-0000-1000-8000-00805f9b34fb 13:03:26 DEBUG rpc.host.0.0 : client-replyPASSED [100%]
------------------------------ live log teardown -------------------------------
13:03:26 OUT host.0.0.bluetoothctl: [BlueZ 5.86]> [CHG] Device 70:1A:B8:98:FF:qemu-system-x86_64: terminating on signal 15 from pid 149357 (python3)======================= 1 passed, 4 deselected in 13.22s =======================
$ COLUMNS=80 unit/test-functional -k test_btmgmt --kernel=../linux --trace ============================= test session starts ============================== platform linux -- Python 3.14.3, pytest-8.3.5, pluggy-1.6.0 rootdir: /home/pauli/prj/external/bluez/unit configfile: pytest.ini plugins: cov-5.0.0, forked-1.6.0, rerunfailures-15.0, timeout-2.4.0, xdist-3.7.0, hypothesis-6.123.0, flaky-3.8.1, anyio-4.12.1 collected 5 items / 4 deselected / 1 selected
unit/func_test/test_btmgmt_vm.pyQuoted 2 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
PDB runcall (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(19)test_btmgmt_info()
-> (host,) = hosts
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) p host.bdaddr
'00:aa:01:00:00:42'
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(22)test_btmgmt_info()
-> run,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(23)test_btmgmt_info()
-> [btmgmt, "--index", "0", "info"],
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(24)test_btmgmt_info()
-> stdout=subprocess.PIPE,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(25)test_btmgmt_info()
-> stdin=subprocess.DEVNULL,
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(26)test_btmgmt_info()
-> encoding="utf-8",
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(21)test_btmgmt_info()
-> result = host.call(
(Pdb) nQuoted 1 line
/home/pauli/prj/external/bluez/unit/func_test/test_btmgmt_vm.py(28)test_btmgmt_info()
-> assert result.returncode == 0
(Pdb) p result
CompletedProcess(args=['/home/pauli/prj/external/bluez/build/tools/btmgmt', '--index', '0', 'info'], returncode=0, stdout='hci0:\tPrimary controller\n\taddr 00:AA:01:00:00:42 version 11 manufacturer 1521 class 0x000000\n\tsupported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver \n\tcurrent settings: br/edr \n\tname \n\tshort name \n') (Pdb) print(result.stdout)
hci0: Primary controller
addr 00:AA:01:00:00:42 version 11 manufacturer 1521 class0x000000
supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
current settings: br/edr
name
short name
(Pdb) q!!!!!!!!!!!!!!!!!!! _pytest.outcomes.Exit: Quitting debugger !!!!!!!!!!!!!!!!!!! ======================= 4 deselected in 75.91s (0:01:15) =======================
***
Pauli Virtanen (11):
emulator: btvirt: check pkt lengths, don't get stuck on malformed
emulator: btvirt: allow specifying where server unix sockets are made
test-runner: enable path argument for --unix
test-runner: Add -o/--option option
test-runner: allow source tree root for -k
doc: enableCONFIG_VIRTIO_CONSOLEin tester config
test-runner: use virtio-serial for implementing -u device forwarding
doc: add functional/integration testing documentation
unit: add functional/integration testing framework
unit:func_test: add Pipewire-using smoke tests
build: add functional testing targetMakefile.am | 7 + configure.ac | 17 + doc/ci.config | 1 + doc/test-functional.rst | 374 +++++++++++++++++++ doc/test-runner.rst | 1 + doc/tester.config | 1 + emulator/main.c | 37 +- emulator/server.c | 9 + tools/test-runner.c | 376 ++++++++++++++----- unit/func_test/__init__.py | 0 unit/func_test/conftest.py | 277 ++++++++++++++ unit/func_test/lib/__init__.py | 5 + unit/func_test/lib/env.py | 484 +++++++++++++++++++++++++ unit/func_test/lib/host_plugins.py | 269 ++++++++++++++ unit/func_test/lib/rpc.py | 293 +++++++++++++++ unit/func_test/lib/runner.py | 10 + unit/func_test/lib/tests/__init__.py | 0 unit/func_test/lib/tests/test_rpc.py | 49 +++ unit/func_test/lib/utils.py | 266 ++++++++++++++ unit/func_test/requirements.txt | 3 + unit/func_test/test_bluetoothctl_vm.py | 76 ++++ unit/func_test/test_btmgmt_vm.py | 29 ++ unit/func_test/test_pipewire.py | 137 +++++++ unit/pytest.ini | 7 + unit/test-functional | 8 + 25 files changed, 2631 insertions(+), 105 deletions(-) create mode 100644 doc/test-functional.rst create mode 100644 unit/func_test/__init__.py create mode 100644 unit/func_test/conftest.py create mode 100644 unit/func_test/lib/__init__.py create mode 100644 unit/func_test/lib/env.py create mode 100644 unit/func_test/lib/host_plugins.py create mode 100644 unit/func_test/lib/rpc.py create mode 100644 unit/func_test/lib/runner.py create mode 100644 unit/func_test/lib/tests/__init__.py create mode 100644 unit/func_test/lib/tests/test_rpc.py create mode 100644 unit/func_test/lib/utils.py create mode 100644 unit/func_test/requirements.txt create mode 100644 unit/func_test/test_bluetoothctl_vm.py create mode 100644 unit/func_test/test_btmgmt_vm.py create mode 100644 unit/func_test/test_pipewire.py create mode 100644 unit/pytest.ini create mode 100755 unit/test-functional
Quoted 7 lines
Quoted 2 lines
--
2.53.0Looks like a fairly solid start, that said I wouldn't mix these with unit test, that is more of a unit/whitebox style testing not really meant for end-to-end testing, Id probably have this under test/functional or test/pytest,
Ok.
Quoted 4 lines
anyway we could possibly remove the
existing python tests and just convert them, or perhaps move them to examples since they are just demostrating how to use our D-Bus APIs using python rather than testing a specific use-case, etc.
Most of these appear example test clients, and don't have the server side. I guess there's some value in keeping them, and just put the automated test suite to subfolder there.
Quoted 6 lines
Regarding pytest, Im not really an expert in the python testing frameworks so I will probably need to do some digging to see what are the options and there might be companies that already emply similar testing environment so I guess it is a good chance to make an attempt to convince more people to contribute upstream so we can consolidate in a single framework for end-to-end testing.
Pytest is a generic test framework; I'd say there are only two relevant choices for these in Python. It is widely used eg. by big parts of the Python machine learning / scientific stack (Pytorch, numpy, etc) and other projects, should be safe choice.
For specifically this kind of end-to-end or integration testing with multiple VM images + controller redirection, which are the custom parts here, it seemed not so easy to find something open source that's immediately relevant.
There are things for embedded / hardware testing (openhtf, pytest- embedded) but not clear those are immediately helpful here. There are of course applications for controlling fleets of VM/Docker images (eg. Ansible), but these are typically via network and probably need less spartan VM environment.
Signature · 1 line
Pauli Virtanen