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.apache.commons.lang3.RandomStringUtils;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test;
32 import org.junit.jupiter.api.extension.ExtendWith;
33 import org.mockito.ArgumentCaptor;
34 import org.mockito.ArgumentMatchers;
35 import org.mockito.Mock;
36 import org.mockito.Mockito;
37 import org.mockito.Spy;
38 import org.mockito.junit.jupiter.MockitoExtension;
39 import org.mockito.junit.jupiter.MockitoSettings;
40 import org.mockito.quality.Strictness;
41 import org.openhab.binding.bluetooth.BluetoothAdapter;
42 import org.openhab.binding.bluetooth.BluetoothAddress;
43 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
44 import org.openhab.binding.bluetooth.BluetoothCharacteristic.GattCharacteristic;
45 import org.openhab.binding.bluetooth.BluetoothDevice;
46 import org.openhab.binding.bluetooth.MockBluetoothAdapter;
47 import org.openhab.binding.bluetooth.MockBluetoothDevice;
48 import org.openhab.binding.bluetooth.TestUtils;
49 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
50 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
51 import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
52 import org.openhab.core.config.discovery.DiscoveryListener;
53 import org.openhab.core.config.discovery.DiscoveryResult;
54 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
55 import org.openhab.core.thing.ThingTypeUID;
56 import org.openhab.core.thing.ThingUID;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * Tests {@link BluetoothDiscoveryService}.
63 * @author Connor Petty - Initial contribution
65 @ExtendWith(MockitoExtension.class)
66 @MockitoSettings(strictness = Strictness.LENIENT)
68 public class BluetoothDiscoveryServiceTest {
70 private static final int TIMEOUT = 2000;
72 private final Logger logger = LoggerFactory.getLogger(BluetoothDiscoveryServiceTest.class);
74 private @NonNullByDefault({}) BluetoothDiscoveryService discoveryService;
76 private @Spy @NonNullByDefault({}) MockDiscoveryParticipant participant1 = new MockDiscoveryParticipant();
77 private @Mock @NonNullByDefault({}) DiscoveryListener mockDiscoveryListener;
81 discoveryService = new BluetoothDiscoveryService();
82 discoveryService.addDiscoveryListener(mockDiscoveryListener);
83 discoveryService.addBluetoothDiscoveryParticipant(participant1);
87 public void ignoreDuplicateTest() {
88 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
89 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
90 discoveryService.deviceDiscovered(device);
91 // this second call should not produce another result
92 discoveryService.deviceDiscovered(device);
94 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
95 ArgumentMatchers.same(discoveryService),
96 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
100 public void ignoreOtherDuplicateTest() {
101 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
102 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
103 BluetoothAddress address = TestUtils.randomAddress();
104 BluetoothDevice device1 = mockAdapter1.getDevice(address);
105 BluetoothDevice device2 = mockAdapter2.getDevice(address);
106 discoveryService.deviceDiscovered(device1);
107 discoveryService.deviceDiscovered(device2);
108 // this should not produce another result
109 discoveryService.deviceDiscovered(device1);
111 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
112 ArgumentMatchers.same(discoveryService),
113 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
117 public void ignoreRssiDuplicateTest() {
118 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
119 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
120 discoveryService.deviceDiscovered(device);
121 // changing the rssi should not result in a new discovery
123 discoveryService.deviceDiscovered(device);
125 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
126 ArgumentMatchers.same(discoveryService),
127 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
131 public void nonDuplicateNameTest() throws InterruptedException {
132 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
133 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
134 discoveryService.deviceDiscovered(device);
135 // this second call should produce another result
136 device.setName("sdfad");
137 discoveryService.deviceDiscovered(device);
139 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
140 ArgumentMatchers.same(discoveryService),
141 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
145 public void nonDuplicateTxPowerTest() {
146 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
147 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
148 discoveryService.deviceDiscovered(device);
149 // this second call should produce another result
150 device.setTxPower(10);
151 discoveryService.deviceDiscovered(device);
153 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
154 ArgumentMatchers.same(discoveryService),
155 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
159 public void nonDuplicateManufacturerIdTest() {
160 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
161 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
162 discoveryService.deviceDiscovered(device);
163 // this second call should produce another result
164 device.setManufacturerId(100);
165 discoveryService.deviceDiscovered(device);
167 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
168 ArgumentMatchers.same(discoveryService),
169 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
173 public void useResultFromAnotherAdapterTest() {
174 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
175 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
176 BluetoothAddress address = TestUtils.randomAddress();
178 discoveryService.deviceDiscovered(mockAdapter1.getDevice(address));
179 discoveryService.deviceDiscovered(mockAdapter2.getDevice(address));
181 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
182 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
183 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
185 List<DiscoveryResult> results = resultCaptor.getAllValues();
186 DiscoveryResult result1 = results.get(0);
187 DiscoveryResult result2 = results.get(1);
189 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
190 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
191 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
192 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
193 assertEquals(result1.getLabel(), result2.getLabel());
194 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
198 public void connectionParticipantTest() {
199 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
200 BluetoothAddress address = TestUtils.randomAddress();
202 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
203 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(address);
204 String deviceName = RandomStringUtils.randomAlphanumeric(10);
205 mockDevice.setDeviceName(deviceName);
207 BluetoothDevice device = Mockito.spy(mockDevice);
209 discoveryService.deviceDiscovered(device);
211 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).connect();
212 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).readCharacteristic(
213 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
214 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).disconnect();
216 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
217 ArgumentMatchers.same(discoveryService),
218 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
219 && arg.getThingUID().getId().equals(deviceName)));
223 public void multiDiscoverySingleConnectionTest() {
224 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
225 BluetoothAddress address = TestUtils.randomAddress();
227 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
228 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
229 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
230 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
231 String deviceName = RandomStringUtils.randomAlphanumeric(10);
232 mockDevice1.setDeviceName(deviceName);
233 mockDevice2.setDeviceName(deviceName);
235 BluetoothDevice device1 = Mockito.spy(mockDevice1);
236 BluetoothDevice device2 = Mockito.spy(mockDevice2);
238 discoveryService.deviceDiscovered(device1);
240 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
241 ArgumentMatchers.same(discoveryService),
242 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
243 && mockAdapter1.getUID().equals(arg.getBridgeUID())
244 && arg.getThingUID().getId().equals(deviceName)));
246 Mockito.verify(device1, Mockito.times(1)).connect();
247 Mockito.verify(device1, Mockito.times(1)).readCharacteristic(
248 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
249 Mockito.verify(device1, Mockito.times(1)).disconnect();
251 discoveryService.deviceDiscovered(device2);
253 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
254 ArgumentMatchers.same(discoveryService),
255 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
256 && mockAdapter2.getUID().equals(arg.getBridgeUID())
257 && arg.getThingUID().getId().equals(deviceName)));
259 Mockito.verify(device2, Mockito.never()).connect();
260 Mockito.verify(device2, Mockito.never()).readCharacteristic(
261 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
262 Mockito.verify(device2, Mockito.never()).disconnect();
266 public void nonConnectionParticipantTest() {
267 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
268 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(TestUtils.randomAddress());
269 String deviceName = RandomStringUtils.randomAlphanumeric(10);
270 mockDevice.setDeviceName(deviceName);
272 BluetoothDevice device = Mockito.spy(mockDevice);
274 discoveryService.deviceDiscovered(device);
276 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
277 ArgumentMatchers.same(discoveryService),
278 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
279 && !arg.getThingUID().getId().equals(deviceName)));
280 Mockito.verify(device, Mockito.never()).connect();
281 Mockito.verify(device, Mockito.never()).readCharacteristic(
282 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
283 Mockito.verify(device, Mockito.never()).disconnect();
287 public void defaultResultTest() {
288 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
289 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
290 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
291 discoveryService.deviceDiscovered(device);
293 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
294 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
295 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
299 public void removeDefaultDeviceTest() {
300 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
301 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
302 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
303 discoveryService.deviceDiscovered(device);
304 discoveryService.deviceRemoved(device);
306 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
307 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
308 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
310 DiscoveryResult result = resultCaptor.getValue();
312 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
314 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
315 ArgumentMatchers.same(discoveryService),
316 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
320 public void removeUpdatedDefaultDeviceTest() {
321 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
322 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
323 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
324 discoveryService.deviceDiscovered(device);
325 device.setName("somename");
326 discoveryService.deviceDiscovered(device);
328 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
329 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
330 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
332 DiscoveryResult result = resultCaptor.getValue();
334 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
336 discoveryService.deviceRemoved(device);
338 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
339 ArgumentMatchers.same(discoveryService),
340 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
344 public void bluezConnectionTimeoutTest() {
345 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
347 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
348 BadConnectionDevice device = new BadConnectionDevice(mockAdapter1, TestUtils.randomAddress(), 100);
349 discoveryService.deviceDiscovered(device);
351 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
352 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
353 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
357 public void replaceOlderDiscoveryTest() {
358 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
360 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
361 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
363 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
365 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
366 Integer manufacturer = device.getManufacturerId();
367 if (manufacturer != null && manufacturer.equals(10)) {
368 // without a device name it should produce a random ThingUID
369 return super.createResult(device);
375 discoveryService.addBluetoothDiscoveryParticipant(participant2);
377 // lets start with producing a default result
378 discoveryService.deviceDiscovered(device);
380 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
381 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
382 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
384 DiscoveryResult result = resultCaptor.getValue();
386 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
388 device.setManufacturerId(10);
390 // lets start with producing a default result
391 discoveryService.deviceDiscovered(device);
393 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
394 ArgumentMatchers.same(discoveryService),
395 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
397 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
398 ArgumentMatchers.same(discoveryService),
399 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
403 public void recursiveFutureTest() throws InterruptedException {
405 * 1. deviceDiscovered(device1)
406 * 2. cause discovery to pause at participant1
407 * participant1 should make a field non-null for device1 upon unpause
408 * 3. make the same field non-null for device2
409 * 4. deviceDiscovered(device2)
410 * this discovery should be waiting for first discovery to finish
411 * 5. unpause participant
413 * - participant should only have been called once
414 * - thingDiscovered should have been called twice
416 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
418 AtomicInteger callCount = new AtomicInteger(0);
419 final CountDownLatch pauseLatch = new CountDownLatch(1);
421 BluetoothAddress address = TestUtils.randomAddress();
422 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
423 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
424 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
425 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
426 String deviceName = RandomStringUtils.randomAlphanumeric(10);
428 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
430 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
433 } catch (InterruptedException e) {
436 ((BluetoothDeviceSnapshot) device).setName(deviceName);
437 callCount.incrementAndGet();
438 return super.createResult(device);
442 discoveryService.addBluetoothDiscoveryParticipant(participant2);
444 discoveryService.deviceDiscovered(mockDevice1);
445 mockDevice2.setName(deviceName);
446 discoveryService.deviceDiscovered(mockDevice2);
448 pauseLatch.countDown();
450 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
451 ArgumentMatchers.same(discoveryService),
452 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
454 assertEquals(1, callCount.get());
458 public void roamingDiscoveryTest() {
459 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
460 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
461 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
463 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
464 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
465 discoveryService.deviceDiscovered(device);
467 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
468 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
469 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
471 List<DiscoveryResult> results = resultCaptor.getAllValues();
472 DiscoveryResult result1 = results.get(0);
473 DiscoveryResult result2 = results.get(1);
475 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
476 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
477 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
478 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
479 assertEquals(result1.getLabel(), result2.getLabel());
480 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
484 public void roamingDiscoveryRetractionTest() {
485 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
486 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
487 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
489 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
490 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
491 discoveryService.deviceDiscovered(device);
492 device.setName("dasf");
493 discoveryService.deviceDiscovered(device);
495 ArgumentCaptor<ThingUID> resultCaptor = ArgumentCaptor.forClass(ThingUID.class);
496 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
497 .thingRemoved(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
499 List<ThingUID> results = resultCaptor.getAllValues();
500 ThingUID result1 = results.get(0);
501 ThingUID result2 = results.get(1);
503 assertNotEquals(result1.getBridgeIds(), result2.getBridgeIds());
504 assertThat(result1.getBridgeIds().get(0),
505 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
506 assertThat(result2.getBridgeIds().get(0),
507 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
508 assertEquals(result1.getId(), result2.getId());
511 private class RoamingDiscoveryParticipant implements BluetoothDiscoveryParticipant {
513 private MockBluetoothAdapter roamingAdapter = new MockBluetoothAdapter();
516 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
517 return Collections.emptySet();
521 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
526 public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
531 public void publishAdditionalResults(DiscoveryResult result,
532 BiConsumer<BluetoothAdapter, DiscoveryResult> publisher) {
533 publisher.accept(roamingAdapter, result);
537 private class MockDiscoveryParticipant implements BluetoothDiscoveryParticipant {
539 private ThingTypeUID typeUID;
541 public MockDiscoveryParticipant() {
542 this.typeUID = new ThingTypeUID(BluetoothBindingConstants.BINDING_ID,
543 RandomStringUtils.randomAlphabetic(6));
547 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
548 return Collections.singleton(typeUID);
552 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
553 String repProp = RandomStringUtils.randomAlphabetic(6);
554 return DiscoveryResultBuilder.create(getThingUID(device)).withLabel(RandomStringUtils.randomAlphabetic(6))
555 .withProperty(repProp, RandomStringUtils.randomAlphabetic(6)).withRepresentationProperty(repProp)
556 .withBridge(device.getAdapter().getUID()).build();
560 public @NonNull ThingUID getThingUID(BluetoothDiscoveryDevice device) {
561 String deviceName = device.getName();
562 String id = deviceName != null ? deviceName : RandomStringUtils.randomAlphabetic(6);
563 return new ThingUID(typeUID, device.getAdapter().getUID(), id);
567 private class BadConnectionDevice extends MockBluetoothDevice {
569 private int sleepTime;
571 public BadConnectionDevice(BluetoothAdapter adapter, BluetoothAddress address, int sleepTime) {
572 super(adapter, address);
573 this.sleepTime = sleepTime;
577 public boolean connect() {
578 notifyListeners(BluetoothEventType.CONNECTION_STATE,
579 new BluetoothConnectionStatusNotification(ConnectionState.CONNECTED));
581 Thread.sleep(sleepTime);
582 } catch (InterruptedException e) {
585 notifyListeners(BluetoothEventType.CONNECTION_STATE,
586 new BluetoothConnectionStatusNotification(ConnectionState.DISCONNECTED));