/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.binding.hue.internal;
import static org.eclipse.smarthome.binding.hue.HueBindingConstants.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.smarthome.binding.hue.handler.HueBridgeHandler;
import org.eclipse.smarthome.binding.hue.internal.discovery.HueLightDiscoveryService;
import org.eclipse.smarthome.binding.hue.test.AbstractHueOSGiTest;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.config.discovery.DiscoveryListener;
import org.eclipse.smarthome.config.discovery.DiscoveryResult;
import org.eclipse.smarthome.config.discovery.DiscoveryResultFlag;
import org.eclipse.smarthome.config.discovery.DiscoveryService;
import org.eclipse.smarthome.core.thing.Bridge;
import org.eclipse.smarthome.core.thing.ThingRegistry;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.ThingStatusInfo;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.eclipse.smarthome.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.eclipse.smarthome.test.AsyncResultWrapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for {@link HueLightDiscoveryService}.
*
* @author Kai Kreuzer - Initial contribution
* @author Andre Fuechsel - added test 'assert start search is called()'
* - modified tests after introducing the generic thing types
* @author Denis Dudnik - switched to internally integrated source of Jue library
* @author Markus Rathgeb - migrated to plain Java test
*/
public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTest {
protected HueThingHandlerFactory hueThingHandlerFactory;
protected DiscoveryListener discoveryListener;
protected ThingRegistry thingRegistry;
protected Bridge hueBridge;
protected HueBridgeHandler hueBridgeHandler;
protected HueLightDiscoveryService discoveryService;
protected final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
protected final ThingUID BRIDGE_THING_UID = new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge");
@Before
public void setUp() {
registerVolatileStorageService();
thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
assertThat(thingRegistry, is(notNullValue()));
Configuration configuration = new Configuration();
configuration.put(HOST, "1.2.3.4");
configuration.put(USER_NAME, "testUserName");
configuration.put(SERIAL_NUMBER, "testSerialNumber");
hueBridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID, BRIDGE_THING_UID, null, "Bridge",
configuration);
assertThat(hueBridge, is(notNullValue()));
thingRegistry.add(hueBridge);
hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class);
assertThat(hueBridgeHandler, is(notNullValue()));
discoveryService = getService(DiscoveryService.class, HueLightDiscoveryService.class);
assertThat(discoveryService, is(notNullValue()));
}
@After
public void cleanUp() {
thingRegistry.remove(BRIDGE_THING_UID);
}
private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
unregisterCurrentDiscoveryListener();
this.discoveryListener = discoveryListener;
discoveryService.addDiscoveryListener(this.discoveryListener);
}
private void unregisterCurrentDiscoveryListener() {
if (this.discoveryListener != null) {
discoveryService.removeDiscoveryListener(this.discoveryListener);
}
}
@Test
public void hueLightRegistration() {
FullLight light = new FullLight();
light.setId("1");
light.setModelID("LCT001");
light.setType("Extended color light");
AsyncResultWrapper<DiscoveryResult> resultWrapper = new AsyncResultWrapper<DiscoveryResult>();
registerDiscoveryListener(new DiscoveryListener() {
@Override
public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
resultWrapper.set(result);
}
@Override
public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
}
@Override
public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
Collection<ThingTypeUID> thingTypeUIDs) {
return null;
}
});
discoveryService.onLightAdded(null, light);
waitForAssert(() -> {
assertTrue(resultWrapper.isSet());
});
final DiscoveryResult result = resultWrapper.getWrappedObject();
assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
assertThat(result.getThingUID().toString(), is("hue:0210:testBridge:" + light.getId()));
assertThat(result.getThingTypeUID(), is(THING_TYPE_EXTENDED_COLOR_LIGHT));
assertThat(result.getBridgeUID(), is(hueBridge.getUID()));
assertThat(result.getProperties().get(LIGHT_ID), is(light.getId()));
}
@Test
public void startSearchIsCalled() {
final AtomicBoolean searchHasBeenTriggered = new AtomicBoolean(false);
AsyncResultWrapper<String> addressWrapper = new AsyncResultWrapper<String>();
AsyncResultWrapper<String> bodyWrapper = new AsyncResultWrapper<String>();
MockedHttpClient mockedHttpClient = new MockedHttpClient() {
@Override
public Result put(String address, String body) throws IOException {
addressWrapper.set(address);
bodyWrapper.set(body);
return new Result("", 200);
}
@Override
public Result get(String address) throws IOException {
if (address.endsWith("testUserName/")) {
String body = "{\"lights\":{}}";
return new Result(body, 200);
} else {
return null;
}
}
@Override
public Result post(String address, String body) throws IOException {
if (address.endsWith("lights")) {
String bodyReturn = "{\"success\": {\"/lights\": \"Searching for new devices\"}}";
searchHasBeenTriggered.set(true);
return new Result(bodyReturn, 200);
} else {
return null;
}
}
};
installHttpClientMock(hueBridgeHandler, mockedHttpClient);
ThingStatusInfo online = ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build();
waitForAssert(() -> {
assertThat(hueBridge.getStatusInfo(), is(online));
});
discoveryService.startScan();
waitForAssert(() -> {
assertTrue(searchHasBeenTriggered.get());
});
}
private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttpClient mockedHttpClient) {
waitForAssert(() -> {
try {
// mock HttpClient
final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("bridge");
hueBridgeField.setAccessible(true);
final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler);
assertThat(hueBridgeValue, is(notNullValue()));
final Field httpClientField = HueBridge.class.getDeclaredField("http");
httpClientField.setAccessible(true);
httpClientField.set(hueBridgeValue, mockedHttpClient);
final Field usernameField = HueBridge.class.getDeclaredField("username");
usernameField.setAccessible(true);
usernameField.set(hueBridgeValue, hueBridgeHandler.getThing().getConfiguration().get(USER_NAME));
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
Assert.fail("Reflection usage error");
}
});
hueBridgeHandler.initialize();
}
}