1# BumbleBluetoothTests
2
3Bumble Bluetooth tests are instrumented Android-specific multi-device tests using a reference
4peer device implementing the Pandora APIs.
5
6## Architecture
7
8BumbleBluetoothTests is an Android APK that offers enhanced control over Android compared to Avatar
9by interacting directly with the Device Under Test (DUT) via Android APIs. Instead of mocking every
10API call, it communicates with actual reference devices using gRPC and limits peer device interactions
11to the Pandora APIs.
12
13Here is an overview of the BumbleBluetoothTests architecture:
14![BumbleBluetoothTests architecture](asset/java-bumble-test-setup.png)
15
16A simple LE connection test looks like this:
17
18```kotlin
19// Setup a Bumble Pandora device for the duration of the test.
20// Acting as a Pandora client, it can be interacted with through the Pandora APIs.
21@Rule @JvmField val mBumble = PandoraDevice()
22
23/**
24 * Tests the Bluetooth GATT connection process with a mock callback.
25 * This verifies both successful connection and disconnection events for a
26 * remote Bluetooth device.
27 *
28 * @throws Exception if there's an unexpected error during the test execution.
29 */
30@Test
31fun testGattConnect() {
32    // 1. Advertise the host's Bluetooth capabilities using another
33    //    gRPC call:
34    // - `hostBlocking()` accesses another gRPC service related to the host.
35    //   The following `advertise(...)` sends an advertise request to the server, setting
36    //   specific attributes.
37    mBumble
38        .hostBlocking()
39        .advertise(
40            AdvertiseRequest.newBuilder()
41                .setLegacy(true)
42                .setConnectable(true)
43                .setOwnAddressType(OwnAddressType.RANDOM)
44                .build()
45        )
46
47    // 2. Create a mock callback to handle Bluetooth GATT (Generic Attribute Profile) related events.
48    val gattCallback = mock(BluetoothGattCallback::class.java)
49
50    // 3. Fetch a remote Bluetooth device instance (here, Bumble).
51    val bumbleDevice =
52        bluetoothAdapter.getRemoteLeDevice(
53            Utils.BUMBLE_RANDOM_ADDRESS,
54            BluetoothDevice.ADDRESS_TYPE_RANDOM // Specify address type as RANDOM because the device advertises with this address type.
55        )
56
57    // 4. Connect to the Bumble device and expect a successful connection callback.
58    var bumbleGatt = bumbleDevice.connectGatt(context, false, gattCallback)
59    verify(gattCallback, timeout(TIMEOUT))
60        .onConnectionStateChange(
61            any(),
62            eq(BluetoothGatt.GATT_SUCCESS),
63            eq(BluetoothProfile.STATE_CONNECTED)
64        )
65
66    // 5. Disconnect from the Bumble device and expect a successful disconnection callback.
67    bumbleGatt.disconnect()
68    verify(gattCallback, timeout(TIMEOUT))
69        .onConnectionStateChange(
70            any(),
71            eq(BluetoothGatt.GATT_SUCCESS),
72            eq(BluetoothProfile.STATE_DISCONNECTED)
73        )
74}
75```
76