]> git.basschouten.com Git - openhab-addons.git/blob
3ff98df726b1d9105f0c7302b547cbd1deb79b43
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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;
14
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
19 import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
20
21 import java.io.IOException;
22 import java.lang.reflect.Field;
23 import java.util.Collection;
24 import java.util.concurrent.atomic.AtomicBoolean;
25 import java.util.concurrent.atomic.AtomicReference;
26
27 import org.junit.jupiter.api.AfterEach;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
31 import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
32 import org.openhab.core.config.core.Configuration;
33 import org.openhab.core.config.discovery.DiscoveryListener;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryResultFlag;
36 import org.openhab.core.config.discovery.DiscoveryService;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ThingRegistry;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingStatusInfo;
42 import org.openhab.core.thing.ThingTypeUID;
43 import org.openhab.core.thing.ThingUID;
44 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
45
46 /**
47  * Tests for {@link HueDeviceDiscoveryService}.
48  *
49  * @author Kai Kreuzer - Initial contribution
50  * @author Andre Fuechsel - added test 'assert start search is called()'
51  *         - modified tests after introducing the generic thing types
52  * @author Denis Dudnik - switched to internally integrated source of Jue library
53  * @author Markus Rathgeb - migrated to plain Java test
54  */
55 public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent {
56
57     protected HueThingHandlerFactory hueThingHandlerFactory;
58     protected DiscoveryListener discoveryListener;
59     protected ThingRegistry thingRegistry;
60     protected Bridge hueBridge;
61     protected HueBridgeHandler hueBridgeHandler;
62     protected HueDeviceDiscoveryService discoveryService;
63
64     protected final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
65     protected final ThingUID BRIDGE_THING_UID = new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge");
66
67     @BeforeEach
68     public void setUp() {
69         registerVolatileStorageService();
70
71         thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
72         assertThat(thingRegistry, is(notNullValue()));
73
74         Configuration configuration = new Configuration();
75         configuration.put(HOST, "1.2.3.4");
76         configuration.put(USER_NAME, "testUserName");
77         configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
78
79         hueBridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID, BRIDGE_THING_UID, null, "Bridge",
80                 configuration);
81
82         assertThat(hueBridge, is(notNullValue()));
83         thingRegistry.add(hueBridge);
84
85         hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class);
86         assertThat(hueBridgeHandler, is(notNullValue()));
87
88         discoveryService = getService(DiscoveryService.class, HueDeviceDiscoveryService.class);
89         assertThat(discoveryService, is(notNullValue()));
90     }
91
92     @AfterEach
93     public void cleanUp() {
94         thingRegistry.remove(BRIDGE_THING_UID);
95         waitForAssert(() -> {
96             assertNull(getService(DiscoveryService.class, HueDeviceDiscoveryService.class));
97         });
98     }
99
100     private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
101         unregisterCurrentDiscoveryListener();
102         this.discoveryListener = discoveryListener;
103         discoveryService.addDiscoveryListener(this.discoveryListener);
104     }
105
106     private void unregisterCurrentDiscoveryListener() {
107         if (this.discoveryListener != null) {
108             discoveryService.removeDiscoveryListener(this.discoveryListener);
109         }
110     }
111
112     @Test
113     public void hueLightRegistration() {
114         FullLight light = new FullLight();
115         light.setId("1");
116         light.setUniqueID("AA:BB:CC:DD:EE:FF:00:11-XX");
117         light.setModelID("LCT001");
118         light.setType("Extended color light");
119
120         AtomicReference<DiscoveryResult> resultWrapper = new AtomicReference<>();
121
122         registerDiscoveryListener(new DiscoveryListener() {
123             @Override
124             public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
125                 resultWrapper.set(result);
126             }
127
128             @Override
129             public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
130             }
131
132             @Override
133             public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
134                     Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
135                 return null;
136             }
137         });
138
139         discoveryService.addLightDiscovery(light);
140         waitForAssert(() -> {
141             assertTrue(resultWrapper.get() != null);
142         });
143
144         final DiscoveryResult result = resultWrapper.get();
145         assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
146         assertThat(result.getThingUID().toString(), is("hue:0210:testBridge:" + light.getId()));
147         assertThat(result.getThingTypeUID(), is(THING_TYPE_EXTENDED_COLOR_LIGHT));
148         assertThat(result.getBridgeUID(), is(hueBridge.getUID()));
149         assertThat(result.getProperties().get(LIGHT_ID), is(light.getId()));
150     }
151
152     @Test
153     public void startSearchIsCalled() {
154         final AtomicBoolean searchHasBeenTriggered = new AtomicBoolean(false);
155
156         MockedHttpClient mockedHttpClient = new MockedHttpClient() {
157
158             @Override
159             public Result put(String address, String body) throws IOException {
160                 return new Result("", 200);
161             }
162
163             @Override
164             public Result get(String address) throws IOException {
165                 if (address.endsWith("testUserName")) {
166                     String body = "{\"lights\":{}}";
167                     return new Result(body, 200);
168                 } else if (address.endsWith("lights") || address.endsWith("sensors") || address.endsWith("groups")) {
169                     String body = "{}";
170                     return new Result(body, 200);
171                 } else if (address.endsWith("testUserName/config")) {
172                     String body = "{ \"apiversion\": \"1.26.0\"}";
173                     return new Result(body, 200);
174                 } else {
175                     return new Result("", 404);
176                 }
177             }
178
179             @Override
180             public Result post(String address, String body) throws IOException {
181                 if (address.endsWith("lights")) {
182                     String bodyReturn = "{\"success\": {\"/lights\": \"Searching for new devices\"}}";
183                     searchHasBeenTriggered.set(true);
184                     return new Result(bodyReturn, 200);
185                 } else {
186                     return new Result("", 404);
187                 }
188             }
189         };
190
191         installHttpClientMock(hueBridgeHandler, mockedHttpClient);
192
193         ThingStatusInfo online = ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build();
194         waitForAssert(() -> {
195             assertThat(hueBridge.getStatusInfo(), is(online));
196         });
197
198         discoveryService.startScan();
199         waitForAssert(() -> {
200             assertTrue(searchHasBeenTriggered.get());
201         });
202     }
203
204     private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttpClient mockedHttpClient) {
205         waitForAssert(() -> {
206             try {
207                 // mock HttpClient
208                 final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("hueBridge");
209                 hueBridgeField.setAccessible(true);
210                 final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler);
211                 assertThat(hueBridgeValue, is(notNullValue()));
212
213                 final Field httpClientField = HueBridge.class.getDeclaredField("http");
214                 httpClientField.setAccessible(true);
215                 httpClientField.set(hueBridgeValue, mockedHttpClient);
216
217                 final Field usernameField = HueBridge.class.getDeclaredField("username");
218                 usernameField.setAccessible(true);
219                 usernameField.set(hueBridgeValue, hueBridgeHandler.getThing().getConfiguration().get(USER_NAME));
220             } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
221                 fail("Reflection usage error");
222             }
223         });
224         hueBridgeHandler.initialize();
225     }
226 }