Linux bluetooth development
 help / color / mirror / Atom feed
From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: linux-bluetooth@vger.kernel.org
Subject: [PATCH BlueZ v1 3/6] test: functional: add the true wireless broadcast tests
Date: Mon, 21 Sep 2026 17:01:20 -0400	[thread overview]
Message-ID: <20260921210123.3690449-4-luiz.dentz@gmail.com> (raw)
In-Reply-To: <20260921210123.3690449-1-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Assisted-by: opencode:claude-opus-5
---
 client/scripts/broadcast-sink-left.bt  |  23 ++++++
 client/scripts/broadcast-sink-right.bt |  23 ++++++
 doc/functional-bap.rst                 |  10 ++-
 test/functional/test_bap.py            | 104 +++++++++++++++++++++++++
 4 files changed, 158 insertions(+), 2 deletions(-)
 create mode 100644 client/scripts/broadcast-sink-left.bt
 create mode 100644 client/scripts/broadcast-sink-right.bt

diff --git a/client/scripts/broadcast-sink-left.bt b/client/scripts/broadcast-sink-left.bt
new file mode 100644
index 000000000000..d5ad2ed25abb
--- /dev/null
+++ b/client/scripts/broadcast-sink-left.bt
@@ -0,0 +1,23 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the left side of a true wireless pair. Each side syncs to
+# the Broadcast Source on its own, with no assistant involved.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Left, so only the BIS carrying that channel is synced
+1
+# Supported Context: Media
+4
+#
+#
+# Scan for Broadcast Sources: the sink syncs to the periodic
+# advertising and creates a transport for the BIS matching its own
+# location.
+#
+scan on
diff --git a/client/scripts/broadcast-sink-right.bt b/client/scripts/broadcast-sink-right.bt
new file mode 100644
index 000000000000..58f07dbe67ec
--- /dev/null
+++ b/client/scripts/broadcast-sink-right.bt
@@ -0,0 +1,23 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the right side of a true wireless pair. Each side syncs to
+# the Broadcast Source on its own, with no assistant involved.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Right, so only the BIS carrying that channel is synced
+2
+# Supported Context: Media
+4
+#
+#
+# Scan for Broadcast Sources: the sink syncs to the periodic
+# advertising and creates a transport for the BIS matching its own
+# location.
+#
+scan on
diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 87917065e1eb..7059e0c735f1 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -290,8 +290,9 @@ test_bap_broadcast_earbuds_transport_created
 :Steps:
 	1. Start `bluetoothctl` with the source script on host0.
 	2. Start `bluetoothctl` with the sink scripts on host1 and host2.
-	3. Each side: ``transport.list``, to check that nothing besides
-	   the transport of its own BIS was created.
+	3. Each side: list its **org.bluez.MediaTransport1** objects, to
+	   check that nothing besides the transport of its own BIS was
+	   created.
 
 :Expected:
 	1. ``Endpoint /local/endpoint/ep0 registered``, then two
@@ -307,6 +308,11 @@ test_bap_broadcast_earbuds_transport_created
 :Notes: The BIS index of the path is what tells the two apart, as each
 	side only ever sees its own.
 
+	The objects are listed over D-Bus rather than with
+	``transport.list``: the endpoint prints the transport it was
+	configured with as well, so a listing cannot be told apart from
+	it in the output.
+
 	Both transports of the source have to be acquired, as for the CIS
 	of a CIG: the BIG is only created once every BIS of it is ready.
 
diff --git a/test/functional/test_bap.py b/test/functional/test_bap.py
index 7bd59777b53b..bfcb43c0ad67 100644
--- a/test/functional/test_bap.py
+++ b/test/functional/test_bap.py
@@ -312,6 +312,20 @@ def remote_transport_properties(remote):
     }
 
 
+@mainloop_wrap
+def transport_paths():
+    """Paths of every MediaTransport1 object of the host."""
+    objects = dbus.Interface(
+        get_dbus().get_object("org.bluez", "/"),
+        "org.freedesktop.DBus.ObjectManager",
+    ).GetManagedObjects()
+    return sorted(
+        str(path)
+        for path, props in objects.items()
+        if "org.bluez.MediaTransport1" in props
+    )
+
+
 def add_preset(ctl, name, props, metadata):
     """Make a custom preset from a transport's codec configuration and QoS."""
 
@@ -460,6 +474,96 @@ def test_bap_broadcast_transport_acquire(hosts, source_script):
     )
 
 
+ACQUIRED = r"Acquire successful: fd \d+ MTU \d+:\d+"
+
+# Transport of a given BIS of a broadcast the sink is synced to
+BIS_TRANSPORT = r"Transport (/org/bluez/\S+/bis{}/fd\d+)"
+
+earbuds_host_config = host_config(
+    [Bluetoothd(conf=BAP_CONF), Pexpect()],
+    [Bluetoothd(conf=BAP_CONF), Pexpect()],
+    [Bluetoothd(conf=BAP_CONF), Pexpect()],
+)
+
+
+def start_earbuds_broadcast(hosts):
+    """
+    Source broadcasting one BIS per channel, with the two sides of a
+    true wireless pair scanning for it, each one taking a single
+    channel.
+    """
+    source_host, left_host, right_host = hosts
+
+    source = start_bluetoothctl(source_host, "broadcast-source-2bis.bt")
+
+    # The BIG is only created once every BIS of it is ready, so the
+    # script acquires both transports
+    expect_all(source, [ACQUIRED, ACQUIRED])
+
+    left = start_bluetoothctl(left_host, "broadcast-sink-left.bt")
+    right = start_bluetoothctl(right_host, "broadcast-sink-right.bt")
+
+    return source, left, right
+
+
+def expect_bis_transport(ctl, host, bis):
+    """
+    Transport a side created for the BIS carrying its own channel,
+    checking it is the only one it has.
+    """
+    _, m = ctl.expect(BIS_TRANSPORT.format(bis))
+    transport = m[0].decode("utf-8")
+
+    # Checked over D-Bus rather than with transport.list, as the
+    # endpoint prints the transport it was configured with as well, so
+    # a listing cannot be told apart from it in the output
+    assert host.call(transport_paths) == [transport]
+
+    return transport
+
+
+@earbuds_host_config
+def test_bap_broadcast_earbuds_transport_created(hosts):
+    source_host, left_host, right_host = hosts
+    source, left, right = start_earbuds_broadcast(hosts)
+
+    # Each side syncs to the periodic advertising on its own and only
+    # creates a transport for the BIS whose channel allocation matches
+    # its own location
+    for ctl, host, bis in ((left, left_host, 1), (right, right_host, 2)):
+        transport = expect_bis_transport(ctl, host, bis)
+
+        ctl.send(f"transport.show {transport}\n")
+        ctl.expect(f"Transport {transport}")
+        ctl.expect(r"Codec: 0x06")
+        ctl.expect("State: idle")
+
+
+@earbuds_host_config
+def test_bap_broadcast_earbuds_transport_acquire(hosts):
+    source_host, left_host, right_host = hosts
+    source, left, right = start_earbuds_broadcast(hosts)
+
+    # The two sides sync independently, so each of them is given the
+    # broadcast code of its own, and both are checked
+    for ctl, host, bis in ((left, left_host, 1), (right, right_host, 2)):
+        transport = expect_bis_transport(ctl, host, bis)
+
+        # Selecting the transport syncs to the BIG and starts acquiring
+        ctl.send(f"transport.select {transport}\n")
+        ctl.expect(r"Enter bcode\[value/no\]:")
+        ctl.send(f"{BCAST_CODE}\n")
+
+        expect_all(
+            ctl,
+            [
+                f"Transport {transport} State: broadcasting",
+                ACQUIRED,
+                f"Transport {transport} State: active",
+            ],
+        )
+
+
 past_host_config = host_config(
     [Bluetoothd(conf=BAP_CONF), Pexpect()],
     [Bluetoothd(conf=BAP_CONF), Pexpect()],
-- 
2.55.0


  parent reply	other threads:[~2026-09-21 21:01 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
2026-09-21 23:47   ` Add true wireless BAP broadcast tests bluez.test.bot
2026-09-21 21:01 ` [PATCH BlueZ v1 2/6] doc: describe the true wireless broadcast assistant test case Luiz Augusto von Dentz
2026-09-21 21:01 ` Luiz Augusto von Dentz [this message]
2026-09-21 21:01 ` [PATCH BlueZ v1 4/6] bass: keep the push target per MediaAssistant object Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 5/6] client: connect a device that is already paired Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 6/6] test: functional: add the true wireless broadcast assistant test Luiz Augusto von Dentz
2026-09-22 15:00 ` [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests patchwork-bot+bluetooth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260921210123.3690449-4-luiz.dentz@gmail.com \
    --to=luiz.dentz@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox