]> git.basschouten.com Git - openhab-addons.git/blob
2345caccf0659213753139e47fd526e3efac9c75
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.bluetooth.discovery.internal;
14
15 import static org.hamcrest.CoreMatchers.*;
16
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.concurrent.CountDownLatch;
21 import java.util.concurrent.atomic.AtomicInteger;
22 import java.util.function.BiConsumer;
23
24 import org.apache.commons.lang.RandomStringUtils;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.mockito.ArgumentCaptor;
33 import org.mockito.ArgumentMatchers;
34 import org.mockito.Mock;
35 import org.mockito.Mockito;
36 import org.mockito.Spy;
37 import org.mockito.junit.MockitoJUnitRunner;
38 import org.openhab.binding.bluetooth.BluetoothAdapter;
39 import org.openhab.binding.bluetooth.BluetoothAddress;
40 import org.openhab.binding.bluetooth.BluetoothBindingConstants;
41 import org.openhab.binding.bluetooth.BluetoothCharacteristic.GattCharacteristic;
42 import org.openhab.binding.bluetooth.BluetoothDevice;
43 import org.openhab.binding.bluetooth.MockBluetoothAdapter;
44 import org.openhab.binding.bluetooth.MockBluetoothDevice;
45 import org.openhab.binding.bluetooth.TestUtils;
46 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryDevice;
47 import org.openhab.binding.bluetooth.discovery.BluetoothDiscoveryParticipant;
48 import org.openhab.binding.bluetooth.notification.BluetoothConnectionStatusNotification;
49 import org.openhab.core.config.discovery.DiscoveryListener;
50 import org.openhab.core.config.discovery.DiscoveryResult;
51 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
52 import org.openhab.core.thing.ThingTypeUID;
53 import org.openhab.core.thing.ThingUID;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * Tests {@link BluetoothDiscoveryService}.
59  *
60  * @author Connor Petty - Initial contribution
61  */
62 @NonNullByDefault
63 @RunWith(MockitoJUnitRunner.class)
64 public class BluetoothDiscoveryServiceTest {
65
66     private static final int TIMEOUT = 2000;
67
68     private final Logger logger = LoggerFactory.getLogger(BluetoothDiscoveryServiceTest.class);
69
70     private @NonNullByDefault({}) BluetoothDiscoveryService discoveryService;
71
72     @Spy
73     private @NonNullByDefault({}) MockDiscoveryParticipant participant1 = new MockDiscoveryParticipant();
74
75     @Mock
76     private @NonNullByDefault({}) DiscoveryListener mockDiscoveryListener;
77
78     @Before
79     public void setup() {
80         discoveryService = new BluetoothDiscoveryService();
81         discoveryService.addDiscoveryListener(mockDiscoveryListener);
82         discoveryService.addBluetoothDiscoveryParticipant(participant1);
83     }
84
85     @Test
86     public void ignoreDuplicateTest() {
87         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
88         BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
89         discoveryService.deviceDiscovered(device);
90         // this second call should not produce another result
91         discoveryService.deviceDiscovered(device);
92
93         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
94                 ArgumentMatchers.same(discoveryService),
95                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
96     }
97
98     @Test
99     public void ignoreOtherDuplicateTest() {
100         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
101         BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
102         BluetoothAddress address = TestUtils.randomAddress();
103         BluetoothDevice device1 = mockAdapter1.getDevice(address);
104         BluetoothDevice device2 = mockAdapter2.getDevice(address);
105         discoveryService.deviceDiscovered(device1);
106         discoveryService.deviceDiscovered(device2);
107         // this should not produce another result
108         discoveryService.deviceDiscovered(device1);
109
110         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
111                 ArgumentMatchers.same(discoveryService),
112                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
113     }
114
115     @Test
116     public void ignoreRssiDuplicateTest() {
117         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
118         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
119         discoveryService.deviceDiscovered(device);
120         // changing the rssi should not result in a new discovery
121         device.setRssi(100);
122         discoveryService.deviceDiscovered(device);
123
124         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
125                 ArgumentMatchers.same(discoveryService),
126                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
127     }
128
129     @Test
130     public void nonDuplicateNameTest() throws InterruptedException {
131         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
132         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
133         discoveryService.deviceDiscovered(device);
134         // this second call should produce another result
135         device.setName("sdfad");
136         discoveryService.deviceDiscovered(device);
137
138         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
139                 ArgumentMatchers.same(discoveryService),
140                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
141     }
142
143     @Test
144     public void nonDuplicateTxPowerTest() {
145         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
146         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
147         discoveryService.deviceDiscovered(device);
148         // this second call should produce another result
149         device.setTxPower(10);
150         discoveryService.deviceDiscovered(device);
151
152         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
153                 ArgumentMatchers.same(discoveryService),
154                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
155     }
156
157     @Test
158     public void nonDuplicateManufacturerIdTest() {
159         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
160         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
161         discoveryService.deviceDiscovered(device);
162         // this second call should produce another result
163         device.setManufacturerId(100);
164         discoveryService.deviceDiscovered(device);
165
166         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
167                 ArgumentMatchers.same(discoveryService),
168                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)));
169     }
170
171     @Test
172     public void useResultFromAnotherAdapterTest() {
173         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
174         BluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
175         BluetoothAddress address = TestUtils.randomAddress();
176
177         discoveryService.deviceDiscovered(mockAdapter1.getDevice(address));
178         discoveryService.deviceDiscovered(mockAdapter2.getDevice(address));
179
180         ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
181         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
182                 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
183
184         List<DiscoveryResult> results = resultCaptor.getAllValues();
185         DiscoveryResult result1 = results.get(0);
186         DiscoveryResult result2 = results.get(1);
187
188         Assert.assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
189         Assert.assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
190         Assert.assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(mockAdapter2.getUID())));
191         Assert.assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
192         Assert.assertEquals(result1.getLabel(), result2.getLabel());
193         Assert.assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
194     }
195
196     @Test
197     public void connectionParticipantTest() {
198         Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
199         BluetoothAddress address = TestUtils.randomAddress();
200
201         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
202         MockBluetoothDevice mockDevice = mockAdapter1.getDevice(address);
203         String deviceName = RandomStringUtils.randomAlphanumeric(10);
204         mockDevice.setDeviceName(deviceName);
205
206         BluetoothDevice device = Mockito.spy(mockDevice);
207
208         discoveryService.deviceDiscovered(device);
209
210         Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).connect();
211         Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).readCharacteristic(
212                 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
213         Mockito.verify(device, Mockito.timeout(TIMEOUT).times(1)).disconnect();
214
215         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
216                 ArgumentMatchers.same(discoveryService),
217                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
218                         && arg.getThingUID().getId().equals(deviceName)));
219     }
220
221     @Test
222     public void multiDiscoverySingleConnectionTest() {
223         Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
224         BluetoothAddress address = TestUtils.randomAddress();
225
226         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
227         MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
228         MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
229         MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
230         String deviceName = RandomStringUtils.randomAlphanumeric(10);
231         mockDevice1.setDeviceName(deviceName);
232         mockDevice2.setDeviceName(deviceName);
233
234         BluetoothDevice device1 = Mockito.spy(mockDevice1);
235         BluetoothDevice device2 = Mockito.spy(mockDevice2);
236
237         discoveryService.deviceDiscovered(device1);
238
239         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
240                 ArgumentMatchers.same(discoveryService),
241                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
242                         && mockAdapter1.getUID().equals(arg.getBridgeUID())
243                         && arg.getThingUID().getId().equals(deviceName)));
244
245         Mockito.verify(device1, Mockito.times(1)).connect();
246         Mockito.verify(device1, Mockito.times(1)).readCharacteristic(
247                 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
248         Mockito.verify(device1, Mockito.times(1)).disconnect();
249
250         discoveryService.deviceDiscovered(device2);
251
252         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
253                 ArgumentMatchers.same(discoveryService),
254                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
255                         && mockAdapter2.getUID().equals(arg.getBridgeUID())
256                         && arg.getThingUID().getId().equals(deviceName)));
257
258         Mockito.verify(device2, Mockito.never()).connect();
259         Mockito.verify(device2, Mockito.never()).readCharacteristic(
260                 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
261         Mockito.verify(device2, Mockito.never()).disconnect();
262     }
263
264     @Test
265     public void nonConnectionParticipantTest() {
266         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
267         MockBluetoothDevice mockDevice = mockAdapter1.getDevice(TestUtils.randomAddress());
268         String deviceName = RandomStringUtils.randomAlphanumeric(10);
269         mockDevice.setDeviceName(deviceName);
270
271         BluetoothDevice device = Mockito.spy(mockDevice);
272
273         discoveryService.deviceDiscovered(device);
274
275         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
276                 ArgumentMatchers.same(discoveryService),
277                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant1.typeUID)
278                         && !arg.getThingUID().getId().equals(deviceName)));
279         Mockito.verify(device, Mockito.never()).connect();
280         Mockito.verify(device, Mockito.never()).readCharacteristic(
281                 ArgumentMatchers.argThat(ch -> ch.getGattCharacteristic() == GattCharacteristic.DEVICE_NAME));
282         Mockito.verify(device, Mockito.never()).disconnect();
283     }
284
285     @Test
286     public void defaultResultTest() {
287         Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
288         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
289         BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
290         discoveryService.deviceDiscovered(device);
291
292         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
293                 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
294                         .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
295     }
296
297     @Test
298     public void removeDefaultDeviceTest() {
299         Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
300         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
301         BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
302         discoveryService.deviceDiscovered(device);
303         discoveryService.deviceRemoved(device);
304
305         ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
306         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
307                 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
308
309         DiscoveryResult result = resultCaptor.getValue();
310
311         Assert.assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
312
313         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
314                 ArgumentMatchers.same(discoveryService),
315                 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
316     }
317
318     @Test
319     public void removeUpdatedDefaultDeviceTest() {
320         Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
321         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
322         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
323         discoveryService.deviceDiscovered(device);
324         device.setName("somename");
325         discoveryService.deviceDiscovered(device);
326
327         ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
328         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
329                 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
330
331         DiscoveryResult result = resultCaptor.getValue();
332
333         Assert.assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
334
335         discoveryService.deviceRemoved(device);
336
337         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
338                 ArgumentMatchers.same(discoveryService),
339                 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
340     }
341
342     @Test
343     public void bluezConnectionTimeoutTest() {
344         Mockito.doReturn(true).when(participant1).requiresConnection(ArgumentMatchers.any());
345
346         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
347         BadConnectionDevice device = new BadConnectionDevice(mockAdapter1, TestUtils.randomAddress(), 100);
348         discoveryService.deviceDiscovered(device);
349
350         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
351                 .thingDiscovered(ArgumentMatchers.same(discoveryService), ArgumentMatchers
352                         .argThat(arg -> arg.getThingTypeUID().equals(BluetoothBindingConstants.THING_TYPE_BEACON)));
353     }
354
355     @Test
356     public void replaceOlderDiscoveryTest() {
357         Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
358
359         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
360         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
361
362         MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
363             @Override
364             public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
365                 Integer manufacturer = device.getManufacturerId();
366                 if (manufacturer != null && manufacturer.equals(10)) {
367                     // without a device name it should produce a random ThingUID
368                     return super.createResult(device);
369                 }
370                 return null;
371             }
372         };
373
374         discoveryService.addBluetoothDiscoveryParticipant(participant2);
375
376         // lets start with producing a default result
377         discoveryService.deviceDiscovered(device);
378
379         ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
380         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1))
381                 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
382
383         DiscoveryResult result = resultCaptor.getValue();
384
385         Assert.assertEquals(BluetoothBindingConstants.THING_TYPE_BEACON, result.getThingTypeUID());
386
387         device.setManufacturerId(10);
388
389         // lets start with producing a default result
390         discoveryService.deviceDiscovered(device);
391
392         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingRemoved(
393                 ArgumentMatchers.same(discoveryService),
394                 ArgumentMatchers.argThat(arg -> arg.equals(result.getThingUID())));
395
396         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(1)).thingDiscovered(
397                 ArgumentMatchers.same(discoveryService),
398                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
399     }
400
401     @Test
402     public void recursiveFutureTest() throws InterruptedException {
403         /*
404          * 1. deviceDiscovered(device1)
405          * 2. cause discovery to pause at participant1
406          * participant1 should make a field non-null for device1 upon unpause
407          * 3. make the same field non-null for device2
408          * 4. deviceDiscovered(device2)
409          * this discovery should be waiting for first discovery to finish
410          * 5. unpause participant
411          * End result:
412          * - participant should only have been called once
413          * - thingDiscovered should have been called twice
414          */
415         Mockito.doReturn(null).when(participant1).createResult(ArgumentMatchers.any());
416
417         AtomicInteger callCount = new AtomicInteger(0);
418         final CountDownLatch pauseLatch = new CountDownLatch(1);
419
420         BluetoothAddress address = TestUtils.randomAddress();
421         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
422         MockBluetoothAdapter mockAdapter2 = new MockBluetoothAdapter();
423         MockBluetoothDevice mockDevice1 = mockAdapter1.getDevice(address);
424         MockBluetoothDevice mockDevice2 = mockAdapter2.getDevice(address);
425         String deviceName = RandomStringUtils.randomAlphanumeric(10);
426
427         MockDiscoveryParticipant participant2 = new MockDiscoveryParticipant() {
428             @Override
429             public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
430                 try {
431                     pauseLatch.await();
432                 } catch (InterruptedException e) {
433                     // do nothing
434                 }
435                 ((BluetoothDeviceSnapshot) device).setName(deviceName);
436                 callCount.incrementAndGet();
437                 return super.createResult(device);
438             }
439         };
440
441         discoveryService.addBluetoothDiscoveryParticipant(participant2);
442
443         discoveryService.deviceDiscovered(mockDevice1);
444         mockDevice2.setName(deviceName);
445         discoveryService.deviceDiscovered(mockDevice2);
446
447         pauseLatch.countDown();
448
449         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2)).thingDiscovered(
450                 ArgumentMatchers.same(discoveryService),
451                 ArgumentMatchers.argThat(arg -> arg.getThingTypeUID().equals(participant2.typeUID)));
452
453         Assert.assertEquals(1, callCount.get());
454     }
455
456     @Test
457     public void roamingDiscoveryTest() {
458         RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
459         MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
460         discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
461
462         BluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
463         BluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
464         discoveryService.deviceDiscovered(device);
465
466         ArgumentCaptor<DiscoveryResult> resultCaptor = ArgumentCaptor.forClass(DiscoveryResult.class);
467         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
468                 .thingDiscovered(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
469
470         List<DiscoveryResult> results = resultCaptor.getAllValues();
471         DiscoveryResult result1 = results.get(0);
472         DiscoveryResult result2 = results.get(1);
473
474         Assert.assertNotEquals(result1.getBridgeUID(), result2.getBridgeUID());
475         Assert.assertThat(result1.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
476         Assert.assertThat(result2.getBridgeUID(), anyOf(is(mockAdapter1.getUID()), is(roamingAdapter.getUID())));
477         Assert.assertEquals(result1.getThingUID().getId(), result2.getThingUID().getId());
478         Assert.assertEquals(result1.getLabel(), result2.getLabel());
479         Assert.assertEquals(result1.getRepresentationProperty(), result2.getRepresentationProperty());
480     }
481
482     @Test
483     public void roamingDiscoveryRetractionTest() {
484         RoamingDiscoveryParticipant roamingParticipant = new RoamingDiscoveryParticipant();
485         MockBluetoothAdapter roamingAdapter = roamingParticipant.roamingAdapter;
486         discoveryService.addBluetoothDiscoveryParticipant(roamingParticipant);
487
488         MockBluetoothAdapter mockAdapter1 = new MockBluetoothAdapter();
489         MockBluetoothDevice device = mockAdapter1.getDevice(TestUtils.randomAddress());
490         discoveryService.deviceDiscovered(device);
491         device.setName("dasf");
492         discoveryService.deviceDiscovered(device);
493
494         ArgumentCaptor<ThingUID> resultCaptor = ArgumentCaptor.forClass(ThingUID.class);
495         Mockito.verify(mockDiscoveryListener, Mockito.timeout(TIMEOUT).times(2))
496                 .thingRemoved(ArgumentMatchers.same(discoveryService), resultCaptor.capture());
497
498         List<ThingUID> results = resultCaptor.getAllValues();
499         ThingUID result1 = results.get(0);
500         ThingUID result2 = results.get(1);
501
502         Assert.assertNotEquals(result1.getBridgeIds(), result2.getBridgeIds());
503         Assert.assertThat(result1.getBridgeIds().get(0),
504                 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
505         Assert.assertThat(result2.getBridgeIds().get(0),
506                 anyOf(is(mockAdapter1.getUID().getId()), is(roamingAdapter.getUID().getId())));
507         Assert.assertEquals(result1.getId(), result2.getId());
508     }
509
510     private class RoamingDiscoveryParticipant implements BluetoothDiscoveryParticipant {
511
512         private MockBluetoothAdapter roamingAdapter = new MockBluetoothAdapter();
513
514         @Override
515         public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
516             return Collections.emptySet();
517         }
518
519         @Override
520         public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
521             return null;
522         }
523
524         @Override
525         public @Nullable ThingUID getThingUID(BluetoothDiscoveryDevice device) {
526             return null;
527         }
528
529         @Override
530         public void publishAdditionalResults(DiscoveryResult result,
531                 BiConsumer<BluetoothAdapter, DiscoveryResult> publisher) {
532             publisher.accept(roamingAdapter, result);
533         }
534     }
535
536     private class MockDiscoveryParticipant implements BluetoothDiscoveryParticipant {
537
538         private ThingTypeUID typeUID;
539
540         public MockDiscoveryParticipant() {
541             this.typeUID = new ThingTypeUID("mock", RandomStringUtils.randomAlphabetic(6));
542         }
543
544         @Override
545         public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
546             return Collections.singleton(typeUID);
547         }
548
549         @Override
550         public @Nullable DiscoveryResult createResult(BluetoothDiscoveryDevice device) {
551             return DiscoveryResultBuilder.create(getThingUID(device)).withLabel(RandomStringUtils.randomAlphabetic(6))
552                     .withRepresentationProperty(RandomStringUtils.randomAlphabetic(6))
553                     .withBridge(device.getAdapter().getUID()).build();
554         }
555
556         @Override
557         public @NonNull ThingUID getThingUID(BluetoothDiscoveryDevice device) {
558             String id = device.getName() != null ? device.getName() : RandomStringUtils.randomAlphabetic(6);
559             return new ThingUID(typeUID, device.getAdapter().getUID(), id);
560         }
561     }
562
563     private class BadConnectionDevice extends MockBluetoothDevice {
564
565         private int sleepTime;
566
567         public BadConnectionDevice(BluetoothAdapter adapter, BluetoothAddress address, int sleepTime) {
568             super(adapter, address);
569             this.sleepTime = sleepTime;
570         }
571
572         @Override
573         public boolean connect() {
574             notifyListeners(BluetoothEventType.CONNECTION_STATE,
575                     new BluetoothConnectionStatusNotification(ConnectionState.CONNECTED));
576             try {
577                 Thread.sleep(sleepTime);
578             } catch (InterruptedException e) {
579                 // do nothing
580             }
581             notifyListeners(BluetoothEventType.CONNECTION_STATE,
582                     new BluetoothConnectionStatusNotification(ConnectionState.DISCONNECTED));
583             return false;
584         }
585     }
586 }