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.hue.internal.discovery;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertTrue;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.THING_TYPE_BRIDGE;
19 import static org.openhab.core.config.discovery.inbox.InboxPredicates.forThingTypeUID;
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.stream.Collectors;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.core.config.discovery.DiscoveryListener;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryService;
33 import org.openhab.core.config.discovery.inbox.Inbox;
34 import org.openhab.core.test.java.JavaOSGiTest;
35 import org.openhab.core.test.storage.VolatileStorageService;
36 import org.openhab.core.thing.ThingTypeUID;
37 import org.openhab.core.thing.ThingUID;
40 * @author Christoph Knauf - Initial contribution
41 * @author Markus Rathgeb - migrated to plain Java test
43 public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
45 HueBridgeNupnpDiscovery sut;
46 VolatileStorageService volatileStorageService = new VolatileStorageService();
47 DiscoveryListener discoveryListener;
50 final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
51 final String ip1 = "192.168.31.17";
52 final String ip2 = "192.168.30.28";
53 final String sn1 = "001788fffe20057f";
54 final String sn2 = "001788fffe141b41";
55 final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1);
56 final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2);
57 final String validBridgeDiscoveryResult = "[{\"id\":\"" + sn1 + "\",\"internalipaddress\":" + ip1 + "},{\"id\":\""
58 + sn2 + "\",\"internalipaddress\":" + ip2 + "}]";
59 String discoveryResult;
60 String expBridgeDescription = "{\"name\":\"Philips Hue\",\"datastoreversion\":\"113\",\"swversion\":\"1948086000\",\"apiversion\":\"1.48.0\",\"mac\":\"00:11:22:33:44\",\"bridgeid\":\"$SN\",\"factorynew\":false,\"replacesbridgeid\":null,\"modelid\":\"BSB002\",\"starterkitid\":\"\"}";
62 private void checkDiscoveryResult(DiscoveryResult result, String expIp, String expSn) {
63 assertThat(result.getBridgeUID(), nullValue());
64 assertThat(result.getLabel(), is(String.format(HueBridgeNupnpDiscovery.LABEL_PATTERN, expIp)));
65 assertThat(result.getProperties().get("ipAddress"), is(expIp));
66 assertThat(result.getProperties().get("serialNumber"), is(expSn));
69 private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
70 unregisterCurrentDiscoveryListener();
71 this.discoveryListener = discoveryListener;
72 sut.addDiscoveryListener(this.discoveryListener);
75 private void unregisterCurrentDiscoveryListener() {
76 if (this.discoveryListener != null) {
77 sut.removeDiscoveryListener(this.discoveryListener);
81 // Mock class which only overrides the doGetRequest method in order to make the class testable
82 class ConfigurableBridgeNupnpDiscoveryMock extends HueBridgeNupnpDiscovery {
84 protected String doGetRequest(String url) throws IOException {
85 if (url.contains("meethue")) {
86 return discoveryResult;
87 } else if (url.contains(ip1)) {
88 return expBridgeDescription.replaceAll("$SN", sn1);
89 } else if (url.contains(ip2)) {
90 return expBridgeDescription.replaceAll("$SN", sn2);
92 throw new IOException();
98 registerService(volatileStorageService);
100 sut = getService(DiscoveryService.class, HueBridgeNupnpDiscovery.class);
101 assertThat(sut, is(notNullValue()));
103 inbox = getService(Inbox.class);
104 assertThat(inbox, is(notNullValue()));
106 unregisterCurrentDiscoveryListener();
110 public void bridgeThingTypeIsSupported() {
111 assertThat(sut.getSupportedThingTypes().size(), is(1));
112 assertThat(sut.getSupportedThingTypes().iterator().next(), is(THING_TYPE_BRIDGE));
116 public void validBridgesAreDiscovered() {
117 List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
118 .collect(Collectors.toList());
119 for (final DiscoveryResult oldResult : oldResults) {
120 inbox.remove(oldResult.getThingUID());
123 sut = new ConfigurableBridgeNupnpDiscoveryMock();
124 registerService(sut, DiscoveryService.class.getName());
125 discoveryResult = validBridgeDiscoveryResult;
126 final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
127 registerDiscoveryListener(new DiscoveryListener() {
129 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
130 results.put(result.getThingUID(), result);
134 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
138 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
139 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
146 waitForAssert(() -> {
147 assertThat(results.size(), is(2));
148 assertThat(results.get(BRIDGE_THING_UID_1), is(notNullValue()));
149 checkDiscoveryResult(results.get(BRIDGE_THING_UID_1), ip1, sn1);
150 assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue()));
151 checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), ip2, sn2);
153 final List<DiscoveryResult> inboxResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
154 .collect(Collectors.toList());
155 assertTrue(inboxResults.size() >= 2);
157 assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1))
158 .findFirst().orElse(null), is(notNullValue()));
159 assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_2))
160 .findFirst().orElse(null), is(notNullValue()));
165 public void invalidBridgesAreNotDiscovered() {
166 List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
167 .collect(Collectors.toList());
168 for (final DiscoveryResult oldResult : oldResults) {
169 inbox.remove(oldResult.getThingUID());
172 sut = new ConfigurableBridgeNupnpDiscoveryMock();
173 registerService(sut, DiscoveryService.class.getName());
174 final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
175 registerDiscoveryListener(new DiscoveryListener() {
177 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
178 results.put(result.getThingUID(), result);
182 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
186 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
187 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
193 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":}]";
195 waitForAssert(() -> {
196 assertThat(results.size(), is(0));
200 discoveryResult = "[{\"id\":\"\",\"internalipaddress\":192.168.30.22}]";
202 waitForAssert(() -> {
203 assertThat(results.size(), is(0));
207 discoveryResult = "[{\"id\":\"012345678\",\"internalipaddress\":192.168.30.22}]";
209 waitForAssert(() -> {
210 assertThat(results.size(), is(0));
213 // bridge indicator not part of id
214 discoveryResult = "[{\"id\":\"0123456789\",\"internalipaddress\":192.168.30.22}]";
216 waitForAssert(() -> {
217 assertThat(results.size(), is(0));
220 // bridge indicator at wrong position (-1)
221 discoveryResult = "[{\"id\":\"01234" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
222 + "7891\",\"internalipaddress\":192.168.30.22}]";
224 waitForAssert(() -> {
225 assertThat(results.size(), is(0));
228 // bridge indicator at wrong position (+1)
229 discoveryResult = "[{\"id\":\"0123456" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
230 + "7891\",\"internalipaddress\":192.168.30.22}]";
232 waitForAssert(() -> {
233 assertThat(results.size(), is(0));
236 // bridge not reachable
237 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":192.168.30.1}]";
239 waitForAssert(() -> {
240 assertThat(results.size(), is(0));
243 // invalid bridge description
244 expBridgeDescription = "";
245 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1 + "}]";
247 waitForAssert(() -> {
248 assertThat(results.size(), is(0));
251 waitForAssert(() -> {
253 inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(),