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.mockito.Mockito.mock;
19 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
20 import static org.openhab.core.config.discovery.inbox.InboxPredicates.forThingTypeUID;
22 import java.io.IOException;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.stream.Collectors;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test;
32 import org.openhab.core.config.discovery.DiscoveryListener;
33 import org.openhab.core.config.discovery.DiscoveryResult;
34 import org.openhab.core.config.discovery.DiscoveryService;
35 import org.openhab.core.config.discovery.inbox.Inbox;
36 import org.openhab.core.test.java.JavaOSGiTest;
37 import org.openhab.core.test.storage.VolatileStorageService;
38 import org.openhab.core.thing.ThingRegistry;
39 import org.openhab.core.thing.ThingTypeUID;
40 import org.openhab.core.thing.ThingUID;
43 * @author Christoph Knauf - Initial contribution
44 * @author Markus Rathgeb - migrated to plain Java test
46 public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
48 HueBridgeNupnpDiscovery sut;
49 VolatileStorageService volatileStorageService = new VolatileStorageService();
50 DiscoveryListener discoveryListener;
53 final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
54 final String ip1 = "192.168.31.17";
55 final String ip2 = "192.168.30.28";
56 final String sn1 = "001788fffe20057f";
57 final String sn2 = "001788fffe141b41";
58 final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1);
59 final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2);
60 final String validBridgeDiscoveryResult = "[{\"id\":\"" + sn1 + "\",\"internalipaddress\":" + ip1 + "},{\"id\":\""
61 + sn2 + "\",\"internalipaddress\":" + ip2 + "}]";
62 String discoveryResult;
63 String expBridgeDescription = "{\"name\":\"Philips hue\",\"datastoreversion\":\"149\",\"swversion\":\"1957113050\",\"apiversion\":\"1.57.0\",\"mac\":\"00:11:22:33:44\",\"bridgeid\":\"$SN\",\"factorynew\":false,\"replacesbridgeid\":null,\"modelid\":\"BSB002\",\"starterkitid\":\"\"}";
65 private void checkDiscoveryResult(DiscoveryResult result, String expIp, String expSn) {
66 assertThat(result.getBridgeUID(), nullValue());
67 assertThat(result.getLabel(), is(String.format(DISCOVERY_LABEL_PATTERN, expIp)));
68 assertThat(result.getProperties().get("ipAddress"), is(expIp));
69 assertThat(result.getProperties().get("serialNumber"), is(expSn));
72 private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
73 unregisterCurrentDiscoveryListener();
74 this.discoveryListener = discoveryListener;
75 sut.addDiscoveryListener(this.discoveryListener);
78 private void unregisterCurrentDiscoveryListener() {
79 if (this.discoveryListener != null) {
80 sut.removeDiscoveryListener(this.discoveryListener);
84 // Mock class which only overrides the doGetRequest method in order to make the class testable
85 class ConfigurableBridgeNupnpDiscoveryMock extends HueBridgeNupnpDiscovery {
86 public ConfigurableBridgeNupnpDiscoveryMock(ThingRegistry thingRegistry) {
91 protected String doGetRequest(String url) throws IOException {
92 if (url.contains("meethue")) {
93 return discoveryResult;
94 } else if (url.contains(ip1)) {
95 return expBridgeDescription.replaceAll("$SN", sn1);
96 } else if (url.contains(ip2)) {
97 return expBridgeDescription.replaceAll("$SN", sn2);
99 throw new IOException();
103 protected boolean isClip2Supported(@NonNull String ipAddress) {
109 public void setUp() {
110 registerService(volatileStorageService);
112 sut = getService(DiscoveryService.class, HueBridgeNupnpDiscovery.class);
113 assertThat(sut, is(notNullValue()));
115 inbox = getService(Inbox.class);
116 assertThat(inbox, is(notNullValue()));
118 unregisterCurrentDiscoveryListener();
122 public void bridgeThingTypeIsSupported() {
123 assertThat(sut.getSupportedThingTypes().size(), is(2));
124 assertThat(sut.getSupportedThingTypes().contains(THING_TYPE_BRIDGE), is(true));
128 public void validBridgesAreDiscovered() {
129 List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
130 .collect(Collectors.toList());
131 for (final DiscoveryResult oldResult : oldResults) {
132 inbox.remove(oldResult.getThingUID());
135 sut = new ConfigurableBridgeNupnpDiscoveryMock(mock(ThingRegistry.class));
136 registerService(sut, DiscoveryService.class.getName());
137 discoveryResult = validBridgeDiscoveryResult;
138 final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
139 registerDiscoveryListener(new DiscoveryListener() {
141 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
142 results.put(result.getThingUID(), result);
146 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
150 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
151 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
158 waitForAssert(() -> {
159 assertThat(results.size(), is(2));
160 assertThat(results.get(BRIDGE_THING_UID_1), is(notNullValue()));
161 checkDiscoveryResult(results.get(BRIDGE_THING_UID_1), ip1, sn1);
162 assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue()));
163 checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), ip2, sn2);
165 final List<DiscoveryResult> inboxResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
166 .collect(Collectors.toList());
167 assertTrue(inboxResults.size() >= 2);
169 assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1))
170 .findFirst().orElse(null), is(notNullValue()));
171 assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_2))
172 .findFirst().orElse(null), is(notNullValue()));
177 public void invalidBridgesAreNotDiscovered() {
178 List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
179 .collect(Collectors.toList());
180 for (final DiscoveryResult oldResult : oldResults) {
181 inbox.remove(oldResult.getThingUID());
184 sut = new ConfigurableBridgeNupnpDiscoveryMock(mock(ThingRegistry.class));
185 registerService(sut, DiscoveryService.class.getName());
186 final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
187 registerDiscoveryListener(new DiscoveryListener() {
189 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
190 results.put(result.getThingUID(), result);
194 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
198 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
199 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
205 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":}]";
207 waitForAssert(() -> {
208 assertThat(results.size(), is(0));
212 discoveryResult = "[{\"id\":\"\",\"internalipaddress\":192.168.30.22}]";
214 waitForAssert(() -> {
215 assertThat(results.size(), is(0));
219 discoveryResult = "[{\"id\":\"012345678\",\"internalipaddress\":192.168.30.22}]";
221 waitForAssert(() -> {
222 assertThat(results.size(), is(0));
225 // bridge indicator not part of id
226 discoveryResult = "[{\"id\":\"0123456789\",\"internalipaddress\":192.168.30.22}]";
228 waitForAssert(() -> {
229 assertThat(results.size(), is(0));
232 // bridge indicator at wrong position (-1)
233 discoveryResult = "[{\"id\":\"01234" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
234 + "7891\",\"internalipaddress\":192.168.30.22}]";
236 waitForAssert(() -> {
237 assertThat(results.size(), is(0));
240 // bridge indicator at wrong position (+1)
241 discoveryResult = "[{\"id\":\"0123456" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
242 + "7891\",\"internalipaddress\":192.168.30.22}]";
244 waitForAssert(() -> {
245 assertThat(results.size(), is(0));
248 // bridge not reachable
249 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":192.168.30.1}]";
251 waitForAssert(() -> {
252 assertThat(results.size(), is(0));
255 // invalid bridge description
256 expBridgeDescription = "";
257 discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1 + "}]";
259 waitForAssert(() -> {
260 assertThat(results.size(), is(0));
263 waitForAssert(() -> {
265 inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(),