Linux bluetooth development
 help / color / mirror / Atom feed
From: Kevin Dewald <kevin@simpleble.org>
To: linux-bluetooth@vger.kernel.org
Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
	Kevin Dewald <kevin@simpleble.org>
Subject: [PATCH BlueZ 5/5] test: functional: add bearer connection parameter tests
Date: Mon,  2 Nov 2026 23:27:49 -0800	[thread overview]
Message-ID: <20261103072749.50979-6-kevin@simpleble.org> (raw)
In-Reply-To: <20261103072749.50979-1-kevin@simpleble.org>

This adds test_bearer_le_conn_params, which checks both hosts report the
same connection parameters on the LE bearer while connected and that
they are gone once disconnected, and test_bearer_le_conn_update, which
checks Interval follows a connection update the peripheral asks for,
and documents them in functional-bearer.

Assisted-by: Claude:claude-opus-5.5
---
 doc/functional-bearer.rst      | 43 +++++++++++++++++++
 test/functional/test_bearer.py | 78 ++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+)

diff --git a/doc/functional-bearer.rst b/doc/functional-bearer.rst
index de69b489b..8331a2a6d 100644
--- a/doc/functional-bearer.rst
+++ b/doc/functional-bearer.rst
@@ -62,6 +62,49 @@ test_bearer_le_connect
 	   ``org.bluez.Reason.Remote`` on host1. ``Connected`` is false on
 	   both hosts and ``Role`` is no longer present.
 
+test_bearer_le_conn_params
+--------------------------
+
+:Setup: As above, over LE.
+
+:Steps:
+	1. host0 connects to host1, as in test_bearer_le_connect.
+	2. Both hosts read ``Interval``, ``Latency``, ``SupervisionTimeout``
+	   and ``Subrate`` on the LE bearer.
+	3. host0 calls ``org.bluez.Bearer.LE1.Disconnect``.
+
+:Expected:
+	1. ``Connect`` replies.
+	2. Both hosts report the same values, ``Subrate`` is 1, and the
+	   supervision timeout is longer than twice ``(1 + Latency) *
+	   Interval``, as the Core specification requires, which also
+	   checks the units of the properties.
+	3. ``Disconnect`` replies, and the four properties are no longer
+	   present on either host.
+
+:Notes: The values are not checked against the defaults, as the kernel
+	of host0 may keep connection parameters for host1 from an earlier
+	test on the same VM instance, e.g. after pairing.
+
+test_bearer_le_conn_update
+--------------------------
+
+:Setup: As above, over LE, except host1 runs **bluetoothd(8)** with a
+	connection interval of 100 ms (``MinConnectionInterval`` and
+	``MaxConnectionInterval`` of 80).
+
+:Steps:
+	1. host0 connects to host1, as in test_bearer_le_connect.
+	2. Both hosts read ``Interval`` on the LE bearer.
+
+:Expected:
+	1. ``Connect`` replies.
+	2. ``Interval`` becomes 100000 on both hosts.
+
+:Notes: host0 connects with a shorter interval, which is outside the
+	range of host1, so the kernel of host1 asks host0 to update the
+	connection with an L2CAP Connection Parameter Update Request.
+
 test_bearer_le_connectable
 --------------------------
 
diff --git a/test/functional/test_bearer.py b/test/functional/test_bearer.py
index 69eed15c4..59ce8ef7d 100644
--- a/test/functional/test_bearer.py
+++ b/test/functional/test_bearer.py
@@ -48,6 +48,12 @@ BROADCAST_UUID = "6e2c1d84-7a3b-4f50-9c1e-5b8d2a7f0e13"
 # other host exports, which would need an agent reply in every test
 ARGS = ("-P", "midi")
 
+# host1 wants a 100 ms connection interval (80 units of 1.25 ms), so it asks
+# host0 to update the connection once connected
+UPDATE_CONF = LE_CONF + "[LE]\nMinConnectionInterval = 80\nMaxConnectionInterval = 80\n"
+
+CONN_PARAMS = ("Interval", "Latency", "SupervisionTimeout", "Subrate")
+
 
 class Bearers(HostPlugin, EventPluginMixin):
     """
@@ -188,6 +194,25 @@ def peers(host0, host1):
     return ((host0, host1), (host1, host0))
 
 
+def conn_params(host, remote):
+    """
+    The connection parameters of the LE bearer host has for remote, or
+    None if they are not present.
+    """
+    values = [host.bearers.get(remote.bdaddr, LE_INTERFACE, n) for n in CONN_PARAMS]
+    if None in values:
+        return None
+
+    return dict(zip(CONN_PARAMS, map(int, values)))
+
+
+def connect_le(host0, host1):
+    discover(host0, host1)
+
+    host0.bearers.call(host1.bdaddr, LE_INTERFACE, "Connect")
+    host0.bearers.expect(f"{LE_INTERFACE}.Connect:reply")
+
+
 def disconnect(host0, host1, interface):
     """
     Disconnect the bearer from host0, and check it is reported as
@@ -230,6 +255,59 @@ def test_bearer_le_connect(hosts):
         assert host.bearers.get(remote.bdaddr, LE_INTERFACE, "Role") is None
 
 
+@le_config
+def test_bearer_le_conn_params(hosts):
+    host0, host1 = hosts
+
+    connect_le(host0, host1)
+
+    for host, remote in peers(host0, host1):
+        wait_until(lambda: conn_params(host, remote) is not None)
+
+    # The parameters are not known in advance, as the kernel of host0 may
+    # keep connection parameters for host1 from an earlier test, but both
+    # ends of the connection report the same ones
+    params = conn_params(host0, host1)
+    assert conn_params(host1, host0) == params
+    assert params["Subrate"] == 1
+
+    # The supervision timeout is longer than twice the time between the
+    # connection events the peripheral listens to
+    timeout = params["SupervisionTimeout"] * 1000
+    assert timeout > 2 * (1 + params["Latency"]) * params["Interval"]
+
+    disconnect(host0, host1, LE_INTERFACE)
+
+    for host, remote in peers(host0, host1):
+        for name in CONN_PARAMS:
+            assert host.bearers.get(remote.bdaddr, LE_INTERFACE, name) is None
+
+
+@host_config(
+    [
+        Bluetoothd(conf=LE_CONF, args=ARGS),
+        Agent(capability="NoInputNoOutput"),
+        Bearers(),
+    ],
+    [
+        Bluetoothd(conf=UPDATE_CONF, args=ARGS),
+        LeAdvertiser(),
+        Agent(capability="NoInputNoOutput"),
+        Bearers(),
+    ],
+)
+def test_bearer_le_conn_update(hosts):
+    host0, host1 = hosts
+
+    connect_le(host0, host1)
+
+    for host, remote in peers(host0, host1):
+        wait_until(
+            lambda: host.bearers.get(remote.bdaddr, LE_INTERFACE, "Interval")
+            == "100000"
+        )
+
+
 @host_config(
     [Bluetoothd(conf=SCANNER_CONF, args=ARGS), Agent(), Bearers()],
     # A broadcaster, so neither connectable nor discoverable
-- 
2.54.0 (Apple Git-157)


  parent reply	other threads:[~2026-11-03  7:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  7:27 [PATCH BlueZ 0/5] Add LE connection parameters to org.bluez.Bearer.LE1 Kevin Dewald
2026-11-03  7:27 ` [PATCH BlueZ 1/5] doc/mgmt: Document Connection Subrate for LE connections and updates Kevin Dewald
2026-11-03  8:56   ` Add LE connection parameters to org.bluez.Bearer.LE1 bluez.test.bot
2026-11-03  7:27 ` [PATCH BlueZ 2/5] org.bluez.Bearer: Add connection parameter properties Kevin Dewald
2026-11-03  7:27 ` [PATCH BlueZ 3/5] bearer: " Kevin Dewald
2026-11-03  7:27 ` [PATCH BlueZ 4/5] client: Print LE connection parameters in info Kevin Dewald
2026-11-03  7:27 ` Kevin Dewald [this message]
2026-11-03 16:30 ` [PATCH BlueZ 0/5] Add LE connection parameters to org.bluez.Bearer.LE1 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=20261103072749.50979-6-kevin@simpleble.org \
    --to=kevin@simpleble.org \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    /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