2 * Copyright (c) 2010-2020 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;
15 import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
16 import static org.hamcrest.CoreMatchers.*;
17 import static org.junit.Assert.*;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
20 import java.io.IOException;
21 import java.lang.reflect.Field;
22 import java.util.Collection;
23 import java.util.concurrent.atomic.AtomicBoolean;
24 import java.util.concurrent.atomic.AtomicReference;
26 import org.openhab.core.config.core.Configuration;
27 import org.openhab.core.config.discovery.DiscoveryListener;
28 import org.openhab.core.config.discovery.DiscoveryResult;
29 import org.openhab.core.config.discovery.DiscoveryResultFlag;
30 import org.openhab.core.config.discovery.DiscoveryService;
31 import org.openhab.core.thing.Bridge;
32 import org.openhab.core.thing.ThingRegistry;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.ThingStatusInfo;
36 import org.openhab.core.thing.ThingTypeUID;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
39 import org.junit.After;
40 import org.junit.Assert;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService;
44 import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
47 * Tests for {@link HueLightDiscoveryService}.
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
55 public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent {
57 protected HueThingHandlerFactory hueThingHandlerFactory;
58 protected DiscoveryListener discoveryListener;
59 protected ThingRegistry thingRegistry;
60 protected Bridge hueBridge;
61 protected HueBridgeHandler hueBridgeHandler;
62 protected HueLightDiscoveryService discoveryService;
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");
69 registerVolatileStorageService();
71 thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
72 assertThat(thingRegistry, is(notNullValue()));
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");
79 hueBridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID, BRIDGE_THING_UID, null, "Bridge",
82 assertThat(hueBridge, is(notNullValue()));
83 thingRegistry.add(hueBridge);
85 hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class);
86 assertThat(hueBridgeHandler, is(notNullValue()));
88 discoveryService = getService(DiscoveryService.class, HueLightDiscoveryService.class);
89 assertThat(discoveryService, is(notNullValue()));
93 public void cleanUp() {
94 thingRegistry.remove(BRIDGE_THING_UID);
96 assertNull(getService(DiscoveryService.class, HueLightDiscoveryService.class));
100 private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
101 unregisterCurrentDiscoveryListener();
102 this.discoveryListener = discoveryListener;
103 discoveryService.addDiscoveryListener(this.discoveryListener);
106 private void unregisterCurrentDiscoveryListener() {
107 if (this.discoveryListener != null) {
108 discoveryService.removeDiscoveryListener(this.discoveryListener);
113 public void hueLightRegistration() {
114 FullLight light = new FullLight();
116 light.setModelID("LCT001");
117 light.setType("Extended color light");
119 AtomicReference<DiscoveryResult> resultWrapper = new AtomicReference<>();
121 registerDiscoveryListener(new DiscoveryListener() {
123 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
124 resultWrapper.set(result);
128 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
132 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
133 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
138 discoveryService.addLightDiscovery(light);
139 waitForAssert(() -> {
140 assertTrue(resultWrapper.get() != null);
143 final DiscoveryResult result = resultWrapper.get();
144 assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
145 assertThat(result.getThingUID().toString(), is("hue:0210:testBridge:" + light.getId()));
146 assertThat(result.getThingTypeUID(), is(THING_TYPE_EXTENDED_COLOR_LIGHT));
147 assertThat(result.getBridgeUID(), is(hueBridge.getUID()));
148 assertThat(result.getProperties().get(LIGHT_ID), is(light.getId()));
152 public void startSearchIsCalled() {
153 final AtomicBoolean searchHasBeenTriggered = new AtomicBoolean(false);
155 MockedHttpClient mockedHttpClient = new MockedHttpClient() {
158 public Result put(String address, String body) throws IOException {
159 return new Result("", 200);
163 public Result get(String address) throws IOException {
164 if (address.endsWith("testUserName")) {
165 String body = "{\"lights\":{}}";
166 return new Result(body, 200);
167 } else if (address.endsWith("lights") || address.endsWith("sensors")) {
169 return new Result(body, 200);
170 } else if (address.endsWith("testUserName/config")) {
171 String body = "{ \"apiversion\": \"1.26.0\"}";
172 return new Result(body, 200);
174 return new Result("", 404);
179 public Result post(String address, String body) throws IOException {
180 if (address.endsWith("lights")) {
181 String bodyReturn = "{\"success\": {\"/lights\": \"Searching for new devices\"}}";
182 searchHasBeenTriggered.set(true);
183 return new Result(bodyReturn, 200);
185 return new Result("", 404);
190 installHttpClientMock(hueBridgeHandler, mockedHttpClient);
192 ThingStatusInfo online = ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build();
193 waitForAssert(() -> {
194 assertThat(hueBridge.getStatusInfo(), is(online));
197 discoveryService.startScan();
198 waitForAssert(() -> {
199 assertTrue(searchHasBeenTriggered.get());
203 private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttpClient mockedHttpClient) {
204 waitForAssert(() -> {
207 final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("hueBridge");
208 hueBridgeField.setAccessible(true);
209 final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler);
210 assertThat(hueBridgeValue, is(notNullValue()));
212 final Field httpClientField = HueBridge.class.getDeclaredField("http");
213 httpClientField.setAccessible(true);
214 httpClientField.set(hueBridgeValue, mockedHttpClient);
216 final Field usernameField = HueBridge.class.getDeclaredField("username");
217 usernameField.setAccessible(true);
218 usernameField.set(hueBridgeValue, hueBridgeHandler.getThing().getConfiguration().get(USER_NAME));
219 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
220 Assert.fail("Reflection usage error");
223 hueBridgeHandler.initialize();