2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.bluetooth.discovery.internal;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
19 import java.util.Collections;
20 import java.util.List;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import java.util.function.BiConsumer;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.junit.jupiter.api.extension.ExtendWith;
31 import org.mockito.ArgumentCaptor;
32 import org.mockito.ArgumentMatchers;
33 import org.mockito.Mock;
34 import org.mockito.Mockito;
35 import org.mockito.Spy;
36 import org.mockito.junit.jupiter.MockitoExtension;
37 import org.mockito.junit.jupiter.MockitoSettings;
38 import org.mockito.quality.Strictness;
39 import org.openhab.binding.bluetooth.BluetoothAdapter;
40 import org.openhab.binding.bluetooth.BluetoothAddress;
41 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
42 import org.openhab.binding.bluetooth.BluetoothCharacteristic.GattCharacteristic;
43 import org.openhab.binding.bluetooth.BluetoothDevice;
44 import org.openhab.binding.bluetooth.MockBluetoothAdapter;
45 import org.openhab.binding.bluetooth.MockBluetoothDevice;
46 import org.openhab.binding.bluetooth.TestUtils;
47 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
48 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
49 import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
50 import org.openhab.binding.bluetooth.util.StringUtil;
51 import org.openhab.core.config.discovery.DiscoveryListener;
52 import org.openhab.core.config.discovery.DiscoveryResult;
53 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
54 import org.openhab.core.thing.ThingTypeUID;
55 import org.openhab.core.thing.ThingUID;
58 * Tests {@link BluetoothDiscoveryService}.
60 * @author Connor Petty - Initial contribution
62 @ExtendWith(MockitoExtension.class)
63 @MockitoSettings(strictness = Strictness.LENIENT)
65 public class BluetoothDiscoveryServiceTest {
67 private static final int TIMEOUT = 2000;
69 private @NonNullByDefault({}) BluetoothDiscoveryService discoveryService;
71 private @Spy @NonNullByDefault({}) MockDiscoveryParticipant participant1 = new MockDiscoveryParticipant();
72 private @Mock @NonNullByDefault({}) DiscoveryListener mockDiscoveryListener;
76 discoveryService = new BluetoothDiscoveryService();
77 discoveryService.addDiscoveryListener(mockDiscoveryListener);
78 discoveryService.addBluetoothDiscoveryParticipant(participant1);
82 public void ignoreDuplicateTest() {
83 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
84 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
85 discoveryService.deviceDiscovered(device);
86 // this second call should not produce another result
87 discoveryService.deviceDiscovered(device);
89 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
90 ArgumentMatchers.same(discoveryService),
91 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
95 public void ignoreOtherDuplicateTest() {
96 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
97 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
98 BluetoothAddress address = TestUtils.randomAddress();
99 BluetoothDevice device1 = mockAdapter1.getDevice(address);
100 BluetoothDevice device2 = mockAdapter2.getDevice(address);
101 discoveryService.deviceDiscovered(device1);
102 discoveryService.deviceDiscovered(device2);
103 // this should not produce another result
104 discoveryService.deviceDiscovered(device1);
106 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
107 ArgumentMatchers.same(discoveryService),
108 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
112 public void ignoreRssiDuplicateTest() {
113 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
114 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
115 discoveryService.deviceDiscovered(device);
116 // changing the rssi should not result in a new discovery
118 discoveryService.deviceDiscovered(device);
120 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
121 ArgumentMatchers.same(discoveryService),
122 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
126 public void nonDuplicateNameTest() throws InterruptedException {
127 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
128 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
129 discoveryService.deviceDiscovered(device);
130 // this second call should produce another result
131 device.setName("sdfad");
132 discoveryService.deviceDiscovered(device);
134 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
135 ArgumentMatchers.same(discoveryService),
136 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
140 public void nonDuplicateTxPowerTest() {
141 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
142 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
143 discoveryService.deviceDiscovered(device);
144 // this second call should produce another result
145 device.setTxPower(10);
146 discoveryService.deviceDiscovered(device);
148 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
149 ArgumentMatchers.same(discoveryService),
150 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
154 public void nonDuplicateManufacturerIdTest() {
155 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
156 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
157 discoveryService.deviceDiscovered(device);
158 // this second call should produce another result
159 device.setManufacturerId(100);
160 discoveryService.deviceDiscovered(device);
162 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
163 ArgumentMatchers.same(discoveryService),
164 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
168 public void useResultFromAnotherAdapterTest() {
169 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
170 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
171 BluetoothAddress address = TestUtils.randomAddress();
173 discoveryService.deviceDiscovered(mockAdapter1.getDevice(address));
174 discoveryService.deviceDiscovered(mockAdapter2.getDevice(address));
176 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
177 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
178 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
180 List<DiscoveryResult> results = resultCaptor.getAllValues();
181 DiscoveryResult result1 = results.get(0);
182 DiscoveryResult result2 = results.get(1);
184 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
185 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
186 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
187 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
188 assertEquals(result1.getLabel(), result2.getLabel());
189 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
193 public void connectionParticipantTest() {
194 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
195 BluetoothAddress address = TestUtils.randomAddress();
197 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
198 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(address);
199 String deviceName = StringUtil.randomAlphanummeric(10);
200 mockDevice.setDeviceName(deviceName);
202 BluetoothDevice device = Mockito.spy(mockDevice);
204 discoveryService.deviceDiscovered(device);
206 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).connect();
207 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).readCharacteristic(
208 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
209 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).disconnect();
211 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
212 ArgumentMatchers.same(discoveryService),
213 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
214 && arg.getThingUID().getId().equals(deviceName)));
218 public void multiDiscoverySingleConnectionTest() {
219 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
220 BluetoothAddress address = TestUtils.randomAddress();
222 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
223 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
224 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
225 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
226 String deviceName = StringUtil.randomAlphanummeric(10);
227 mockDevice1.setDeviceName(deviceName);
228 mockDevice2.setDeviceName(deviceName);
230 BluetoothDevice device1 = Mockito.spy(mockDevice1);
231 BluetoothDevice device2 = Mockito.spy(mockDevice2);
233 discoveryService.deviceDiscovered(device1);
235 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
236 ArgumentMatchers.same(discoveryService),
237 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
238 && mockAdapter1.getUID().equals(arg.getBridgeUID())
239 && arg.getThingUID().getId().equals(deviceName)));
241 Mockito.verify(device1, Mockito.times(1)).connect();
242 Mockito.verify(device1, Mockito.times(1)).readCharacteristic(
243 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
244 Mockito.verify(device1, Mockito.times(1)).disconnect();
246 discoveryService.deviceDiscovered(device2);
248 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
249 ArgumentMatchers.same(discoveryService),
250 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
251 && mockAdapter2.getUID().equals(arg.getBridgeUID())
252 && arg.getThingUID().getId().equals(deviceName)));
254 Mockito.verify(device2, Mockito.never()).connect();
255 Mockito.verify(device2, Mockito.never()).readCharacteristic(
256 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
257 Mockito.verify(device2, Mockito.never()).disconnect();
261 public void nonConnectionParticipantTest() {
262 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
263 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(TestUtils.randomAddress());
264 String deviceName = StringUtil.randomAlphanummeric(10);
265 mockDevice.setDeviceName(deviceName);
267 BluetoothDevice device = Mockito.spy(mockDevice);
269 discoveryService.deviceDiscovered(device);
271 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
272 ArgumentMatchers.same(discoveryService),
273 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
274 && !arg.getThingUID().getId().equals(deviceName)));
275 Mockito.verify(device, Mockito.never()).connect();
276 Mockito.verify(device, Mockito.never()).readCharacteristic(
277 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
278 Mockito.verify(device, Mockito.never()).disconnect();
282 public void defaultResultTest() {
283 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
284 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
285 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
286 discoveryService.deviceDiscovered(device);
288 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
289 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
290 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
294 public void removeDefaultDeviceTest() {
295 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
296 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
297 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
298 discoveryService.deviceDiscovered(device);
299 discoveryService.deviceRemoved(device);
301 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
302 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
303 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
305 DiscoveryResult result = resultCaptor.getValue();
307 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
309 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
310 ArgumentMatchers.same(discoveryService),
311 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
315 public void removeUpdatedDefaultDeviceTest() {
316 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
317 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
318 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
319 discoveryService.deviceDiscovered(device);
320 device.setName("somename");
321 discoveryService.deviceDiscovered(device);
323 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
324 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
325 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
327 DiscoveryResult result = resultCaptor.getValue();
329 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
331 discoveryService.deviceRemoved(device);
333 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
334 ArgumentMatchers.same(discoveryService),
335 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
339 public void bluezConnectionTimeoutTest() {
340 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
342 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
343 BadConnectionDevice device = new BadConnectionDevice(mockAdapter1, TestUtils.randomAddress(), 100);
344 discoveryService.deviceDiscovered(device);
346 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
347 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
348 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
352 public void replaceOlderDiscoveryTest() {
353 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
355 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
356 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
358 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
360 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
361 Integer manufacturer = device.getManufacturerId();
362 if (manufacturer != null && manufacturer.equals(10)) {
363 // without a device name it should produce a random ThingUID
364 return super.createResult(device);
370 discoveryService.addBluetoothDiscoveryParticipant(participant2);
372 // lets start with producing a default result
373 discoveryService.deviceDiscovered(device);
375 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
376 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
377 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
379 DiscoveryResult result = resultCaptor.getValue();
381 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
383 device.setManufacturerId(10);
385 // lets start with producing a default result
386 discoveryService.deviceDiscovered(device);
388 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
389 ArgumentMatchers.same(discoveryService),
390 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
392 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
393 ArgumentMatchers.same(discoveryService),
394 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
398 public void recursiveFutureTest() throws InterruptedException {
400 * 1. deviceDiscovered(device1)
401 * 2. cause discovery to pause at participant1
402 * participant1 should make a field non-null for device1 upon unpause
403 * 3. make the same field non-null for device2
404 * 4. deviceDiscovered(device2)
405 * this discovery should be waiting for first discovery to finish
406 * 5. unpause participant
408 * - participant should only have been called once
409 * - thingDiscovered should have been called twice
411 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
413 AtomicInteger callCount = new AtomicInteger(0);
414 final CountDownLatch pauseLatch = new CountDownLatch(1);
416 BluetoothAddress address = TestUtils.randomAddress();
417 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
418 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
419 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
420 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
421 String deviceName = StringUtil.randomAlphanummeric(10);
423 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
425 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
428 } catch (InterruptedException e) {
431 ((BluetoothDeviceSnapshot) device).setName(deviceName);
432 callCount.incrementAndGet();
433 return super.createResult(device);
437 discoveryService.addBluetoothDiscoveryParticipant(participant2);
439 discoveryService.deviceDiscovered(mockDevice1);
440 mockDevice2.setName(deviceName);
441 discoveryService.deviceDiscovered(mockDevice2);
443 pauseLatch.countDown();
445 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
446 ArgumentMatchers.same(discoveryService),
447 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
449 assertEquals(1, callCount.get());
453 public void roamingDiscoveryTest() {
454 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
455 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
456 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
458 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
459 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
460 discoveryService.deviceDiscovered(device);
462 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
463 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
464 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
466 List<DiscoveryResult> results = resultCaptor.getAllValues();
467 DiscoveryResult result1 = results.get(0);
468 DiscoveryResult result2 = results.get(1);
470 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
471 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
472 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
473 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
474 assertEquals(result1.getLabel(), result2.getLabel());
475 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
479 public void roamingDiscoveryRetractionTest() {
480 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
481 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
482 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
484 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
485 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
486 discoveryService.deviceDiscovered(device);
487 device.setName("dasf");
488 discoveryService.deviceDiscovered(device);
490 ArgumentCaptor<ThingUID> resultCaptor = ArgumentCaptor.forClass(ThingUID.class);
491 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
492 .thingRemoved(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
494 List<ThingUID> results = resultCaptor.getAllValues();
495 ThingUID result1 = results.get(0);
496 ThingUID result2 = results.get(1);
498 assertNotEquals(result1.getBridgeIds(), result2.getBridgeIds());
499 assertThat(result1.getBridgeIds().get(0),
500 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
501 assertThat(result2.getBridgeIds().get(0),
502 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
503 assertEquals(result1.getId(), result2.getId());
506 private class RoamingDiscoveryParticipant implements BluetoothDiscoveryParticipant {
508 private MockBluetoothAdapter roamingAdapter = new MockBluetoothAdapter();
511 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
512 return Collections.emptySet();
516 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
521 public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
526 public void publishAdditionalResults(DiscoveryResult result,
527 BiConsumer<BluetoothAdapter, DiscoveryResult> publisher) {
528 publisher.accept(roamingAdapter, result);
532 private class MockDiscoveryParticipant implements BluetoothDiscoveryParticipant {
534 private ThingTypeUID typeUID;
536 public MockDiscoveryParticipant() {
537 this.typeUID = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID, StringUtil.randomAlphabetic(6));
541 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
542 return Set.of(typeUID);
546 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
547 String repProp = StringUtil.randomAlphabetic(6);
548 ThingUID thingUID = getThingUID(device);
549 if (thingUID == null) {
552 return DiscoveryResultBuilder.create(thingUID).withLabel(StringUtil.randomAlphabetic(6))
553 .withProperty(repProp, StringUtil.randomAlphabetic(6)).withRepresentationProperty(repProp)
554 .withBridge(device.getAdapter().getUID()).build();
558 public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
559 String deviceName = device.getName();
560 String id = deviceName != null ? deviceName : StringUtil.randomAlphabetic(6);
561 return new ThingUID(typeUID, device.getAdapter().getUID(), id);
565 private class BadConnectionDevice extends MockBluetoothDevice {
567 private int sleepTime;
569 public BadConnectionDevice(BluetoothAdapter adapter, BluetoothAddress address, int sleepTime) {
570 super(adapter, address);
571 this.sleepTime = sleepTime;
575 public boolean connect() {
576 notifyListeners(BluetoothEventType.CONNECTION_STATE,
577 new BluetoothConnectionStatusNotification(ConnectionState.CONNECTED));
579 Thread.sleep(sleepTime);
580 } catch (InterruptedException e) {
583 notifyListeners(BluetoothEventType.CONNECTION_STATE,
584 new BluetoothConnectionStatusNotification(ConnectionState.DISCONNECTED));