]> git.basschouten.com Git - openhab-addons.git/blob
164c287cc7c329003d49f1761aea2f4274ba3f58
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.hue.internal.discovery;
14
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;
21
22 import java.io.IOException;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.stream.Collectors;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33 import org.openhab.core.config.discovery.DiscoveryListener;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryService;
36 import org.openhab.core.config.discovery.inbox.Inbox;
37 import org.openhab.core.test.java.JavaOSGiTest;
38 import org.openhab.core.test.storage.VolatileStorageService;
39 import org.openhab.core.thing.ThingRegistry;
40 import org.openhab.core.thing.ThingTypeUID;
41 import org.openhab.core.thing.ThingUID;
42
43 /**
44  * @author Christoph Knauf - Initial contribution
45  * @author Markus Rathgeb - migrated to plain Java test
46  */
47 @NonNullByDefault
48 public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
49
50     private @NonNullByDefault({}) HueBridgeNupnpDiscovery sut;
51     private VolatileStorageService volatileStorageService = new VolatileStorageService();
52     private @Nullable DiscoveryListener discoveryListener;
53     private @NonNullByDefault({}) Inbox inbox;
54
55     private static final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
56     private static final String IP1 = "192.168.31.17";
57     private static final String IP2 = "192.168.30.28";
58     private static final String SN1 = "001788fffe20057f";
59     private static final String SN2 = "001788fffe141b41";
60     private static final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, SN1);
61     private static final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, SN2);
62
63     private final String validBridgeDiscoveryResult = "[{\"id\":\"" + SN1 + "\",\"internalipaddress\":" + IP1
64             + "},{\"id\":\"" + SN2 + "\",\"internalipaddress\":" + IP2 + "}]";
65     private @Nullable String discoveryResult;
66     private 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\":\"\"}";
67
68     private void checkDiscoveryResult(@Nullable DiscoveryResult result, String expIp, String expSn) {
69         if (result == null) {
70             return;
71         }
72         assertThat(result.getBridgeUID(), nullValue());
73         assertThat(result.getLabel(), is(String.format(DISCOVERY_LABEL_PATTERN, expIp)));
74         assertThat(result.getProperties().get("ipAddress"), is(expIp));
75         assertThat(result.getProperties().get("serialNumber"), is(expSn));
76     }
77
78     private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
79         unregisterCurrentDiscoveryListener();
80         this.discoveryListener = discoveryListener;
81         sut.addDiscoveryListener(this.discoveryListener);
82     }
83
84     private void unregisterCurrentDiscoveryListener() {
85         if (this.discoveryListener != null) {
86             sut.removeDiscoveryListener(this.discoveryListener);
87         }
88     }
89
90     // Mock class which only overrides the doGetRequest method in order to make the class testable
91     class ConfigurableBridgeNupnpDiscoveryMock extends HueBridgeNupnpDiscovery {
92         public ConfigurableBridgeNupnpDiscoveryMock(ThingRegistry thingRegistry) {
93             super(thingRegistry);
94         }
95
96         @Override
97         protected @Nullable String doGetRequest(String url) throws IOException {
98             if (url.contains("meethue")) {
99                 return discoveryResult;
100             } else if (url.contains(IP1)) {
101                 return expBridgeDescription.replaceAll("$SN", SN1);
102             } else if (url.contains(IP2)) {
103                 return expBridgeDescription.replaceAll("$SN", SN2);
104             }
105             throw new IOException();
106         }
107
108         @Override
109         protected boolean isClip2Supported(String ipAddress) {
110             return false;
111         }
112     }
113
114     @BeforeEach
115     public void setUp() {
116         registerService(volatileStorageService);
117
118         sut = getService(DiscoveryService.class, HueBridgeNupnpDiscovery.class);
119         assertThat(sut, is(notNullValue()));
120
121         inbox = getService(Inbox.class);
122         assertThat(inbox, is(notNullValue()));
123
124         unregisterCurrentDiscoveryListener();
125     }
126
127     @Test
128     public void bridgeThingTypeIsSupported() {
129         assertThat(sut.getSupportedThingTypes().size(), is(2));
130         assertThat(sut.getSupportedThingTypes().contains(THING_TYPE_BRIDGE), is(true));
131     }
132
133     @Test
134     public void validBridgesAreDiscovered() {
135         List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
136                 .collect(Collectors.toList());
137         for (final DiscoveryResult oldResult : oldResults) {
138             inbox.remove(oldResult.getThingUID());
139         }
140
141         sut = new ConfigurableBridgeNupnpDiscoveryMock(mock(ThingRegistry.class));
142         registerService(sut, DiscoveryService.class.getName());
143         discoveryResult = validBridgeDiscoveryResult;
144         final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
145         registerDiscoveryListener(new DiscoveryListener() {
146             @Override
147             public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
148                 results.put(result.getThingUID(), result);
149             }
150
151             @Override
152             public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
153             }
154
155             @Override
156             public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
157                     @Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
158                 return null;
159             }
160         });
161
162         sut.startScan();
163
164         waitForAssert(() -> {
165             assertThat(results.size(), is(2));
166             assertThat(results.get(BRIDGE_THING_UID_1), is(notNullValue()));
167             checkDiscoveryResult(results.get(BRIDGE_THING_UID_1), IP1, SN1);
168             assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue()));
169             checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), IP2, SN2);
170
171             final List<DiscoveryResult> inboxResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
172                     .collect(Collectors.toList());
173             assertTrue(inboxResults.size() >= 2);
174
175             assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1))
176                     .findFirst().orElse(null), is(notNullValue()));
177             assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_2))
178                     .findFirst().orElse(null), is(notNullValue()));
179         });
180     }
181
182     @Test
183     public void invalidBridgesAreNotDiscovered() {
184         List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
185                 .collect(Collectors.toList());
186         for (final DiscoveryResult oldResult : oldResults) {
187             inbox.remove(oldResult.getThingUID());
188         }
189
190         sut = new ConfigurableBridgeNupnpDiscoveryMock(mock(ThingRegistry.class));
191         registerService(sut, DiscoveryService.class.getName());
192         final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
193         registerDiscoveryListener(new DiscoveryListener() {
194             @Override
195             public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
196                 results.put(result.getThingUID(), result);
197             }
198
199             @Override
200             public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
201             }
202
203             @Override
204             public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
205                     @Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
206                 return null;
207             }
208         });
209
210         // missing ip
211         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":}]";
212         sut.startScan();
213         waitForAssert(() -> {
214             assertThat(results.size(), is(0));
215         });
216
217         // missing id
218         discoveryResult = "[{\"id\":\"\",\"internalipaddress\":192.168.30.22}]";
219         sut.startScan();
220         waitForAssert(() -> {
221             assertThat(results.size(), is(0));
222         });
223
224         // id < 10
225         discoveryResult = "[{\"id\":\"012345678\",\"internalipaddress\":192.168.30.22}]";
226         sut.startScan();
227         waitForAssert(() -> {
228             assertThat(results.size(), is(0));
229         });
230
231         // bridge indicator not part of id
232         discoveryResult = "[{\"id\":\"0123456789\",\"internalipaddress\":192.168.30.22}]";
233         sut.startScan();
234         waitForAssert(() -> {
235             assertThat(results.size(), is(0));
236         });
237
238         // bridge indicator at wrong position (-1)
239         discoveryResult = "[{\"id\":\"01234" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
240                 + "7891\",\"internalipaddress\":192.168.30.22}]";
241         sut.startScan();
242         waitForAssert(() -> {
243             assertThat(results.size(), is(0));
244         });
245
246         // bridge indicator at wrong position (+1)
247         discoveryResult = "[{\"id\":\"0123456" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
248                 + "7891\",\"internalipaddress\":192.168.30.22}]";
249         sut.startScan();
250         waitForAssert(() -> {
251             assertThat(results.size(), is(0));
252         });
253
254         // bridge not reachable
255         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":192.168.30.1}]";
256         sut.startScan();
257         waitForAssert(() -> {
258             assertThat(results.size(), is(0));
259         });
260
261         // invalid bridge description
262         expBridgeDescription = "";
263         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + IP1 + "}]";
264         sut.startScan();
265         waitForAssert(() -> {
266             assertThat(results.size(), is(0));
267         });
268
269         waitForAssert(() -> {
270             assertThat(
271                     inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(),
272                     is(0));
273         });
274     }
275 }