/**
* 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.discovery;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.smarthome.binding.hue.HueBindingConstants;
import org.eclipse.smarthome.config.discovery.DiscoveryListener;
import org.eclipse.smarthome.config.discovery.DiscoveryResult;
import org.eclipse.smarthome.config.discovery.DiscoveryService;
import org.eclipse.smarthome.config.discovery.inbox.Inbox;
import org.eclipse.smarthome.config.discovery.inbox.InboxFilterCriteria;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.eclipse.smarthome.test.java.JavaOSGiTest;
import org.eclipse.smarthome.test.storage.VolatileStorageService;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author Christoph Knauf - Initial contribution
* @author Markus Rathgeb - migrated to plain Java test
*/
public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
HueBridgeNupnpDiscovery sut;
VolatileStorageService volatileStorageService = new VolatileStorageService();
DiscoveryListener discoveryListener;
Inbox inbox;
final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
final String ip1 = "192.168.31.17";
final String ip2 = "192.168.30.28";
final String sn1 = "00178820057f";
final String sn2 = "001788141b41";
final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1);
final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2);
final InboxFilterCriteria inboxFilter = new InboxFilterCriteria(BRIDGE_THING_TYPE_UID, null);
final String validBridgeDiscoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1
+ "},{\"id\":\"001788fffe141b41\",\"internalipaddress\":" + ip2 + "}]";
String discoveryResult;
String expBridgeDescription = "" + //
"<?xml version=\"1.0\"?>" + //
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" + //
" <specVersion>" + //
" <major>1</major>" + //
" <minor>0</minor>" + //
" </specVersion>" + //
" <URLBase>http://$IP:80/</URLBase>" + //
" <device>" + //
" <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>" + //
" <friendlyName>Philips hue ($IP)</friendlyName>" + //
" <manufacturer>Royal Philips Electronics</manufacturer>" + //
" <manufacturerURL>http://www.philips.com</manufacturerURL>" + //
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>" + //
"<modelName>Philips hue bridge 2012</modelName>" + //
"<modelNumber>1000000000000</modelNumber>" + //
"<modelURL>http://www.meethue.com</modelURL>" + //
" <serialNumber>93eadbeef13</serialNumber>" + //
" <UDN>uuid:01234567-89ab-cdef-0123-456789abcdef</UDN>" + //
" <serviceList>" + //
" <service>" + //
" <serviceType>(null)</serviceType>" + //
" <serviceId>(null)</serviceId>" + //
" <controlURL>(null)</controlURL>" + //
" <eventSubURL>(null)</eventSubURL>" + //
" <SCPDURL>(null)</SCPDURL>" + //
" </service>" + //
" </serviceList>" + //
" <presentationURL>index.html</presentationURL>" + //
" <iconList>" + //
" <icon>" + //
" <mimetype>image/png</mimetype>" + //
" <height>48</height>" + //
" <width>48</width>" + //
" <depth>24</depth>" + //
" <url>hue_logo_0.png</url>" + //
" </icon>" + //
" <icon>" + //
" <mimetype>image/png</mimetype>" + //
" <height>120</height>" + //
" <width>120</width>" + //
" <depth>24</depth>" + //
" <url>hue_logo_3.png</url>" + //
" </icon>" + //
" </iconList>" + //
" </device>" + //
"</root>";
private void checkDiscoveryResult(DiscoveryResult result, String expIp, String expSn) {
assertThat(result.getBridgeUID(), nullValue());
assertThat(result.getLabel(), is(HueBridgeNupnpDiscovery.LABEL_PATTERN.replace("IP", expIp)));
assertThat(result.getProperties().get("ipAddress"), is(expIp));
assertThat(result.getProperties().get("serialNumber"), is(expSn));
}
private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
unregisterCurrentDiscoveryListener();
this.discoveryListener = discoveryListener;
sut.addDiscoveryListener(this.discoveryListener);
}
private void unregisterCurrentDiscoveryListener() {
if (this.discoveryListener != null) {
sut.removeDiscoveryListener(this.discoveryListener);
}
}
// Mock class which only overrides the doGetRequest method in order to make the class testable
class ConfigurableBridgeNupnpDiscoveryMock extends HueBridgeNupnpDiscovery {
@Override
protected String doGetRequest(String url) throws IOException {
if (url.contains("meethue")) {
return discoveryResult;
} else if (url.contains(ip1)) {
return expBridgeDescription.replaceAll("$IP", ip1);
} else if (url.contains(ip2)) {
return expBridgeDescription.replaceAll("$IP", ip2);
}
throw new IOException();
}
}
@Before
public void setUp() {
registerService(volatileStorageService);
sut = getService(DiscoveryService.class, HueBridgeNupnpDiscovery.class);
assertThat(sut, is(notNullValue()));
inbox = getService(Inbox.class);
assertThat(inbox, is(notNullValue()));
unregisterCurrentDiscoveryListener();
}
@Test
public void bridgeThingTypeIsSupported() {
assertThat(sut.getSupportedThingTypes().size(), is(1));
assertThat(sut.getSupportedThingTypes().iterator().next(), is(HueBindingConstants.THING_TYPE_BRIDGE));
}
@Test
public void validBridgesAreDiscovered() {
List<DiscoveryResult> oldResults = inbox.get(inboxFilter);
for (final DiscoveryResult oldResult : oldResults) {
inbox.remove(oldResult.getThingUID());
}
sut = new ConfigurableBridgeNupnpDiscoveryMock();
registerService(sut, DiscoveryService.class.getName());
discoveryResult = validBridgeDiscoveryResult;
final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
registerDiscoveryListener(new DiscoveryListener() {
@Override
public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
results.put(result.getThingUID(), result);
}
@Override
public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
}
@Override
public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
Collection<ThingTypeUID> thingTypeUIDs) {
return null;
}
});
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(2));
assertThat(results.get(BRIDGE_THING_UID_1), is(notNullValue()));
checkDiscoveryResult(results.get(BRIDGE_THING_UID_1), ip1, sn1);
assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue()));
checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), ip2, sn2);
final List<DiscoveryResult> inboxResults = inbox.get(inboxFilter);
assertTrue(inboxResults.size() >= 2);
assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1))
.findFirst().orElse(null), is(notNullValue()));
assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_2))
.findFirst().orElse(null), is(notNullValue()));
});
}
@Test
public void invalidBridgesAreNotDiscovered() {
List<DiscoveryResult> oldResults = inbox.get(inboxFilter);
for (final DiscoveryResult oldResult : oldResults) {
inbox.remove(oldResult.getThingUID());
}
sut = new ConfigurableBridgeNupnpDiscoveryMock();
registerService(sut, DiscoveryService.class.getName());
final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
registerDiscoveryListener(new DiscoveryListener() {
@Override
public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
results.put(result.getThingUID(), result);
}
@Override
public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
}
@Override
public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
Collection<ThingTypeUID> thingTypeUIDs) {
return null;
}
});
// missing ip
discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// missing id
discoveryResult = "[{\"id\":\"\",\"internalipaddress\":192.168.30.22}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// id < 10
discoveryResult = "[{\"id\":\"012345678\",\"internalipaddress\":192.168.30.22}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// bridge indicator not part of id
discoveryResult = "[{\"id\":\"0123456789\",\"internalipaddress\":192.168.30.22}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// bridge indicator at wrong position (-1)
discoveryResult = "[{\"id\":\"01234" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
+ "7891\",\"internalipaddress\":192.168.30.22}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// bridge indicator at wrong position (+1)
discoveryResult = "[{\"id\":\"0123456" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
+ "7891\",\"internalipaddress\":192.168.30.22}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// bridge not reachable
discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":192.168.30.1}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
// invalid bridge description
expBridgeDescription = "";
;
discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1 + "}]";
sut.startScan();
waitForAssert(() -> {
assertThat(results.size(), is(0));
});
waitForAssert(() -> {
assertThat(inbox.get(inboxFilter).size(), is(0));
});
}
}