2 * Copyright (c) 2010-2020 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.lang.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.Disabled;
32 import org.junit.jupiter.api.Test;
33 import org.junit.jupiter.api.extension.ExtendWith;
34 import org.mockito.ArgumentCaptor;
35 import org.mockito.ArgumentMatchers;
36 import org.mockito.Mock;
37 import org.mockito.Mockito;
38 import org.mockito.Spy;
39 import org.mockito.junit.jupiter.MockitoExtension;
40 import org.mockito.junit.jupiter.MockitoSettings;
41 import org.mockito.quality.Strictness;
42 import org.openhab.binding.bluetooth.BluetoothAdapter;
43 import org.openhab.binding.bluetooth.BluetoothAddress;
44 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
45 import org.openhab.binding.bluetooth.BluetoothCharacteristic.GattCharacteristic;
46 import org.openhab.binding.bluetooth.BluetoothDevice;
47 import org.openhab.binding.bluetooth.MockBluetoothAdapter;
48 import org.openhab.binding.bluetooth.MockBluetoothDevice;
49 import org.openhab.binding.bluetooth.TestUtils;
50 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
51 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
52 import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
53 import org.openhab.core.config.discovery.DiscoveryListener;
54 import org.openhab.core.config.discovery.DiscoveryResult;
55 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
56 import org.openhab.core.thing.ThingTypeUID;
57 import org.openhab.core.thing.ThingUID;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * Tests {@link BluetoothDiscoveryService}.
64 * @author Connor Petty - Initial contribution
66 @ExtendWith(MockitoExtension.class)
67 @MockitoSettings(strictness = Strictness.WARN)
69 @Disabled("Needs to be updated for OH3")
70 public class BluetoothDiscoveryServiceTest {
72 private static final int TIMEOUT = 2000;
74 private final Logger logger = LoggerFactory.getLogger(BluetoothDiscoveryServiceTest.class);
76 private @NonNullByDefault({}) BluetoothDiscoveryService discoveryService;
78 private @Spy @NonNullByDefault({}) MockDiscoveryParticipant participant1 = new MockDiscoveryParticipant();
79 private @Mock @NonNullByDefault({}) DiscoveryListener mockDiscoveryListener;
83 discoveryService = new BluetoothDiscoveryService();
84 discoveryService.addDiscoveryListener(mockDiscoveryListener);
85 discoveryService.addBluetoothDiscoveryParticipant(participant1);
89 public void ignoreDuplicateTest() {
90 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
91 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
92 discoveryService.deviceDiscovered(device);
93 // this second call should not produce another result
94 discoveryService.deviceDiscovered(device);
96 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
97 ArgumentMatchers.same(discoveryService),
98 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
102 public void ignoreOtherDuplicateTest() {
103 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
104 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
105 BluetoothAddress address = TestUtils.randomAddress();
106 BluetoothDevice device1 = mockAdapter1.getDevice(address);
107 BluetoothDevice device2 = mockAdapter2.getDevice(address);
108 discoveryService.deviceDiscovered(device1);
109 discoveryService.deviceDiscovered(device2);
110 // this should not produce another result
111 discoveryService.deviceDiscovered(device1);
113 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
114 ArgumentMatchers.same(discoveryService),
115 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
119 public void ignoreRssiDuplicateTest() {
120 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
121 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
122 discoveryService.deviceDiscovered(device);
123 // changing the rssi should not result in a new discovery
125 discoveryService.deviceDiscovered(device);
127 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
128 ArgumentMatchers.same(discoveryService),
129 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
133 public void nonDuplicateNameTest() throws InterruptedException {
134 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
135 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
136 discoveryService.deviceDiscovered(device);
137 // this second call should produce another result
138 device.setName("sdfad");
139 discoveryService.deviceDiscovered(device);
141 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
142 ArgumentMatchers.same(discoveryService),
143 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
147 public void nonDuplicateTxPowerTest() {
148 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
149 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
150 discoveryService.deviceDiscovered(device);
151 // this second call should produce another result
152 device.setTxPower(10);
153 discoveryService.deviceDiscovered(device);
155 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
156 ArgumentMatchers.same(discoveryService),
157 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
161 public void nonDuplicateManufacturerIdTest() {
162 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
163 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
164 discoveryService.deviceDiscovered(device);
165 // this second call should produce another result
166 device.setManufacturerId(100);
167 discoveryService.deviceDiscovered(device);
169 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
170 ArgumentMatchers.same(discoveryService),
171 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
175 public void useResultFromAnotherAdapterTest() {
176 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
177 BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
178 BluetoothAddress address = TestUtils.randomAddress();
180 discoveryService.deviceDiscovered(mockAdapter1.getDevice(address));
181 discoveryService.deviceDiscovered(mockAdapter2.getDevice(address));
183 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
184 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
185 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
187 List<DiscoveryResult> results = resultCaptor.getAllValues();
188 DiscoveryResult result1 = results.get(0);
189 DiscoveryResult result2 = results.get(1);
191 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
192 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
193 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
194 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
195 assertEquals(result1.getLabel(), result2.getLabel());
196 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
200 public void connectionParticipantTest() {
201 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
202 BluetoothAddress address = TestUtils.randomAddress();
204 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
205 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(address);
206 String deviceName = RandomStringUtils.randomAlphanumeric(10);
207 mockDevice.setDeviceName(deviceName);
209 BluetoothDevice device = Mockito.spy(mockDevice);
211 discoveryService.deviceDiscovered(device);
213 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).connect();
214 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).readCharacteristic(
215 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
216 Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).disconnect();
218 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
219 ArgumentMatchers.same(discoveryService),
220 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
221 && arg.getThingUID().getId().equals(deviceName)));
225 public void multiDiscoverySingleConnectionTest() {
226 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
227 BluetoothAddress address = TestUtils.randomAddress();
229 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
230 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
231 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
232 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
233 String deviceName = RandomStringUtils.randomAlphanumeric(10);
234 mockDevice1.setDeviceName(deviceName);
235 mockDevice2.setDeviceName(deviceName);
237 BluetoothDevice device1 = Mockito.spy(mockDevice1);
238 BluetoothDevice device2 = Mockito.spy(mockDevice2);
240 discoveryService.deviceDiscovered(device1);
242 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
243 ArgumentMatchers.same(discoveryService),
244 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
245 && mockAdapter1.getUID().equals(arg.getBridgeUID())
246 && arg.getThingUID().getId().equals(deviceName)));
248 Mockito.verify(device1, Mockito.times(1)).connect();
249 Mockito.verify(device1, Mockito.times(1)).readCharacteristic(
250 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
251 Mockito.verify(device1, Mockito.times(1)).disconnect();
253 discoveryService.deviceDiscovered(device2);
255 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
256 ArgumentMatchers.same(discoveryService),
257 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
258 && mockAdapter2.getUID().equals(arg.getBridgeUID())
259 && arg.getThingUID().getId().equals(deviceName)));
261 Mockito.verify(device2, Mockito.never()).connect();
262 Mockito.verify(device2, Mockito.never()).readCharacteristic(
263 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
264 Mockito.verify(device2, Mockito.never()).disconnect();
268 public void nonConnectionParticipantTest() {
269 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
270 MockBluetoothDevice mockDevice = mockAdapter1.getDevice(TestUtils.randomAddress());
271 String deviceName = RandomStringUtils.randomAlphanumeric(10);
272 mockDevice.setDeviceName(deviceName);
274 BluetoothDevice device = Mockito.spy(mockDevice);
276 discoveryService.deviceDiscovered(device);
278 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
279 ArgumentMatchers.same(discoveryService),
280 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
281 && !arg.getThingUID().getId().equals(deviceName)));
282 Mockito.verify(device, Mockito.never()).connect();
283 Mockito.verify(device, Mockito.never()).readCharacteristic(
284 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
285 Mockito.verify(device, Mockito.never()).disconnect();
289 public void defaultResultTest() {
290 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
291 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
292 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
293 discoveryService.deviceDiscovered(device);
295 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
296 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
297 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
301 public void removeDefaultDeviceTest() {
302 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
303 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
304 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
305 discoveryService.deviceDiscovered(device);
306 discoveryService.deviceRemoved(device);
308 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
309 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
310 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
312 DiscoveryResult result = resultCaptor.getValue();
314 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
316 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
317 ArgumentMatchers.same(discoveryService),
318 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
322 public void removeUpdatedDefaultDeviceTest() {
323 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
324 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
325 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
326 discoveryService.deviceDiscovered(device);
327 device.setName("somename");
328 discoveryService.deviceDiscovered(device);
330 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
331 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
332 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
334 DiscoveryResult result = resultCaptor.getValue();
336 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
338 discoveryService.deviceRemoved(device);
340 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
341 ArgumentMatchers.same(discoveryService),
342 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
346 public void bluezConnectionTimeoutTest() {
347 Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
349 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
350 BadConnectionDevice device = new BadConnectionDevice(mockAdapter1, TestUtils.randomAddress(), 100);
351 discoveryService.deviceDiscovered(device);
353 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
354 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
355 .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
359 public void replaceOlderDiscoveryTest() {
360 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
362 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
363 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
365 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
367 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
368 Integer manufacturer = device.getManufacturerId();
369 if (manufacturer != null && manufacturer.equals(10)) {
370 // without a device name it should produce a random ThingUID
371 return super.createResult(device);
377 discoveryService.addBluetoothDiscoveryParticipant(participant2);
379 // lets start with producing a default result
380 discoveryService.deviceDiscovered(device);
382 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
383 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
384 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
386 DiscoveryResult result = resultCaptor.getValue();
388 assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
390 device.setManufacturerId(10);
392 // lets start with producing a default result
393 discoveryService.deviceDiscovered(device);
395 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
396 ArgumentMatchers.same(discoveryService),
397 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
399 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
400 ArgumentMatchers.same(discoveryService),
401 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
405 public void recursiveFutureTest() throws InterruptedException {
407 * 1. deviceDiscovered(device1)
408 * 2. cause discovery to pause at participant1
409 * participant1 should make a field non-null for device1 upon unpause
410 * 3. make the same field non-null for device2
411 * 4. deviceDiscovered(device2)
412 * this discovery should be waiting for first discovery to finish
413 * 5. unpause participant
415 * - participant should only have been called once
416 * - thingDiscovered should have been called twice
418 Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
420 AtomicInteger callCount = new AtomicInteger(0);
421 final CountDownLatch pauseLatch = new CountDownLatch(1);
423 BluetoothAddress address = TestUtils.randomAddress();
424 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
425 MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
426 MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
427 MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
428 String deviceName = RandomStringUtils.randomAlphanumeric(10);
430 MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
432 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
435 } catch (InterruptedException e) {
438 ((BluetoothDeviceSnapshot) device).setName(deviceName);
439 callCount.incrementAndGet();
440 return super.createResult(device);
444 discoveryService.addBluetoothDiscoveryParticipant(participant2);
446 discoveryService.deviceDiscovered(mockDevice1);
447 mockDevice2.setName(deviceName);
448 discoveryService.deviceDiscovered(mockDevice2);
450 pauseLatch.countDown();
452 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
453 ArgumentMatchers.same(discoveryService),
454 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
456 assertEquals(1, callCount.get());
460 public void roamingDiscoveryTest() {
461 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
462 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
463 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
465 BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
466 BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
467 discoveryService.deviceDiscovered(device);
469 ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
470 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
471 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
473 List<DiscoveryResult> results = resultCaptor.getAllValues();
474 DiscoveryResult result1 = results.get(0);
475 DiscoveryResult result2 = results.get(1);
477 assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
478 assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
479 assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
480 assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
481 assertEquals(result1.getLabel(), result2.getLabel());
482 assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
486 public void roamingDiscoveryRetractionTest() {
487 RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
488 MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
489 discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
491 MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
492 MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
493 discoveryService.deviceDiscovered(device);
494 device.setName("dasf");
495 discoveryService.deviceDiscovered(device);
497 ArgumentCaptor<ThingUID> resultCaptor = ArgumentCaptor.forClass(ThingUID.class);
498 Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
499 .thingRemoved(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
501 List<ThingUID> results = resultCaptor.getAllValues();
502 ThingUID result1 = results.get(0);
503 ThingUID result2 = results.get(1);
505 assertNotEquals(result1.getBridgeIds(), result2.getBridgeIds());
506 assertThat(result1.getBridgeIds().get(0),
507 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
508 assertThat(result2.getBridgeIds().get(0),
509 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
510 assertEquals(result1.getId(), result2.getId());
513 private class RoamingDiscoveryParticipant implements BluetoothDiscoveryParticipant {
515 private MockBluetoothAdapter roamingAdapter = new MockBluetoothAdapter();
518 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
519 return Collections.emptySet();
523 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
528 public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
533 public void publishAdditionalResults(DiscoveryResult result,
534 BiConsumer<BluetoothAdapter, DiscoveryResult> publisher) {
535 publisher.accept(roamingAdapter, result);
539 private class MockDiscoveryParticipant implements BluetoothDiscoveryParticipant {
541 private ThingTypeUID typeUID;
543 public MockDiscoveryParticipant() {
544 this.typeUID = new ThingTypeUID("mock", RandomStringUtils.randomAlphabetic(6));
548 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
549 return Collections.singleton(typeUID);
553 public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
554 return DiscoveryResultBuilder.create(getThingUID(device)).withLabel(RandomStringUtils.randomAlphabetic(6))
555 .withRepresentationProperty(RandomStringUtils.randomAlphabetic(6))
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));