added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.astro.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,49 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.astro
-runrequires: bnd.identity;id='org.openhab.binding.astro.tests'
#
# done
#
-runbundles: \
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
com.google.gson;version='[2.8.2,2.8.3)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.binding.astro;version='[2.5.9,2.5.10)',\
org.openhab.binding.astro.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.astro.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Astro Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.astro</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.astro.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Astro Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.astro</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.handler.test;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_SUN;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import java.time.ZoneId;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.junit.Test;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.handler.SunHandler;
import org.openhab.binding.astro.internal.model.Sun;
/**
* OSGi test for the {@link AstroThingHandler}
* <p>
* This class tests the commands for the astro thing.
*
* @author Petar Valchev - Initial implementation
* @author Svilen Valkanov - Reworked to plain unit tests
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public class AstroCommandTest {
@Test
public void testRefreshCommandUpdatesTheStateOfTheChannels() {
ThingUID thingUID = new ThingUID(THING_TYPE_SUN, TEST_SUN_THING_ID);
ChannelUID channelUID = new ChannelUID(thingUID, DEFAULT_TEST_CHANNEL_ID);
Channel channel = ChannelBuilder.create(channelUID, DEFAULT_IMEM_TYPE).build();
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, INTERVAL_DEFAULT_VALUE);
Thing thing = mock(Thing.class);
when(thing.getConfiguration()).thenReturn(thingConfiguration);
when(thing.getUID()).thenReturn(thingUID);
when(thing.getChannel(DEFAULT_TEST_CHANNEL_ID)).thenReturn(channel);
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
CronScheduler cronScheduler = mock(CronScheduler.class);
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
AstroThingHandler sunHandler = spy(new SunHandler(thing, cronScheduler, timeZoneProvider));
// Required from the AstroThingHandler to send the status update
doReturn(true).when(callback).isChannelLinked(eq(channelUID));
doReturn(new Sun()).when(sunHandler).getPlanet();
sunHandler.setCallback(callback);
sunHandler.handleCommand(channelUID, RefreshType.REFRESH);
verify(callback, times(1)).stateUpdated(eq(channelUID), any(State.class));
}
}

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.handler.test;
import static org.mockito.Mockito.*;
import static org.openhab.binding.astro.internal.AstroBindingConstants.THING_TYPE_SUN;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import java.time.ZoneId;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.scheduler.CronScheduler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.junit.Test;
import org.openhab.binding.astro.internal.handler.AstroThingHandler;
import org.openhab.binding.astro.internal.handler.SunHandler;
/**
* Tests for the {@link AstroThingHandler}
* <p>
* This class tests the required configuration for the astro thing.
*
* @author Petar Valchev - Initial implementation
* @author Svilen Valkanov - Reworked to plain unit tests, removed irrelevant tests
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public class AstroValidConfigurationTest {
private final String NULL_LONGITUDE = "51.2,null";
private final String NULL_LATITUDE = "null,25.4";
@Test
public void testIfGeolocationIsProvidedForASunThing_theThingStatusBecomesONLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, INTERVAL_DEFAULT_VALUE);
assertThingStatus(thingConfiguration, ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
@Test
public void testIfGeolocationIsProvidedForAMoonThing_theThingStatusBecomesONLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, INTERVAL_DEFAULT_VALUE);
assertThingStatus(thingConfiguration, ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
@Test
public void testIfGeolocationForASunThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, null);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfGeolocationForAMoonThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, null);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheLatitudeForASunThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, NULL_LATITUDE);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheLatitudeForAMoonThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, NULL_LATITUDE);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheLongitudeForASunThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, NULL_LONGITUDE);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheLongitudeForAMoonThingIsNull_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, NULL_LONGITUDE);
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheIntervalForASunThingIsLessThan1_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, new Integer(0));
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheIntervalForAMoonThingIsLessThan1_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, new Integer(0));
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheIntervalForASunThingIsGreaterThan86400_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, new Integer(86401));
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
@Test
public void testIfTheIntervalForAMoonThingIsGreaterThan86400_theThingStatusBecomesOFFLINE() {
Configuration thingConfiguration = new Configuration();
thingConfiguration.put(GEOLOCATION_PROPERTY, GEOLOCATION_VALUE);
thingConfiguration.put(INTERVAL_PROPERTY, new Integer(86401));
assertThingStatus(thingConfiguration, ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
private void assertThingStatus(Configuration configuration, ThingStatus expectedStatus,
ThingStatusDetail expectedStatusDetail) {
ThingUID thingUID = new ThingUID(THING_TYPE_SUN, TEST_SUN_THING_ID);
Thing thing = mock(Thing.class);
when(thing.getConfiguration()).thenReturn(configuration);
when(thing.getUID()).thenReturn(thingUID);
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
CronScheduler cronScheduler = mock(CronScheduler.class);
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.systemDefault());
ThingHandler sunHandler = new SunHandler(thing, cronScheduler, timeZoneProvider);
sunHandler.setCallback(callback);
sunHandler.initialize();
ThingStatusInfo expectedThingStatus = new ThingStatusInfo(expectedStatus, expectedStatusDetail, null);
verify(callback, times(1)).statusUpdated(thing, expectedThingStatus);
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test;
import static org.junit.Assert.assertEquals;
import static org.openhab.binding.astro.internal.AstroBindingConstants.*;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import static org.openhab.binding.astro.test.cases.AstroParametrizedTestCases.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.types.State;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.openhab.binding.astro.internal.calc.MoonCalc;
import org.openhab.binding.astro.internal.calc.SunCalc;
import org.openhab.binding.astro.internal.config.AstroChannelConfig;
import org.openhab.binding.astro.internal.model.Planet;
import org.openhab.binding.astro.internal.util.PropertyUtils;
import org.openhab.binding.astro.test.cases.AstroParametrizedTestCases;
/**
* Tests for the Astro Channels state
*
* @See {@link AstroParametrizedTestCases}
* @author Petar Valchev - Initial implementation
* @author Svilen Valkanov - Reworked to plain unit tests
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
@RunWith(Parameterized.class)
public class AstroStateTest {
private String thingID;
private String channelId;
private State expectedState;
// These test result timestamps are adapted for the +03:00 time zone
private static final ZoneId ZONE_ID = ZoneId.of("+03:00");
public AstroStateTest(String thingID, String channelId, State expectedState) {
this.thingID = thingID;
this.channelId = channelId;
this.expectedState = expectedState;
}
@Parameters
public static List<Object[]> data() {
AstroParametrizedTestCases cases = new AstroParametrizedTestCases();
return cases.getCases();
}
@Test
public void testParametrized() {
try {
assertStateUpdate(thingID, channelId, expectedState);
} catch (Exception e) {
// do nothing
}
}
private void assertStateUpdate(String thingID, String channelId, State expectedState) throws Exception {
ChannelUID testItemChannelUID = new ChannelUID(getThingUID(thingID), channelId);
State state = PropertyUtils.getState(testItemChannelUID, new AstroChannelConfig(), getPlanet(thingID), ZONE_ID);
assertEquals(expectedState, state);
}
private ThingUID getThingUID(String thingID) {
switch (thingID) {
case (TEST_SUN_THING_ID):
return new ThingUID(THING_TYPE_SUN, thingID);
case (TEST_MOON_THING_ID):
return new ThingUID(THING_TYPE_MOON, thingID);
default:
return null;
}
}
private Planet getPlanet(String thingID) {
LocalDateTime time = LocalDateTime.of(TEST_YEAR, TEST_MONTH, TEST_DAY, 0, 0);
ZonedDateTime zonedTime = ZonedDateTime.ofLocal(time, ZONE_ID, null);
Calendar calendar = GregorianCalendar.from(zonedTime);
switch (thingID) {
case (TEST_SUN_THING_ID):
SunCalc sunCalc = new SunCalc();
return sunCalc.getSunInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE, null, false);
case (TEST_MOON_THING_ID):
MoonCalc moonCalc = new MoonCalc();
return moonCalc.getMoonInfo(calendar, TEST_LATITUDE, TEST_LONGITUDE);
default:
return null;
}
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test.cases;
import java.math.BigDecimal;
/**
* Contains some test data used across different tests
*
* @author Svilen Valkanov - Initial implementation
* @author Christoph Weitkamp - Migrated tests to pure Java
*/
public final class AstroBindingTestsData {
public static final String TEST_SUN_THING_ID = "testSunThingId";
public static final String TEST_MOON_THING_ID = "testMoonThingId";
public static final String TEST_ITEM_NAME = "testItem";
public static final String DEFAULT_IMEM_TYPE = "DateTime";
public static final String DEFAULT_TEST_CHANNEL_ID = "rise#start";
public static final String GEOLOCATION_PROPERTY = "geolocation";
public static final String GEOLOCATION_VALUE = "51.2,25.4";
public static final String INTERVAL_PROPERTY = "interval";
public static final BigDecimal INTERVAL_DEFAULT_VALUE = new BigDecimal(300);
}

View File

@@ -0,0 +1,218 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.astro.test.cases;
import static org.openhab.binding.astro.test.cases.AstroBindingTestsData.*;
import java.util.Arrays;
import java.util.List;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.binding.astro.test.AstroStateTest;
/**
* Test cases used in the {@link AstroStateTest}
*
* @author Petar Valchev - Initial contribution
* @author Svilen Valakanov - Added test data from
* <a href="http://www.suncalc.net">http://www.suncalc.net</a> and
* <a href="http://www.mooncalc.org">http://www.mooncalc.org</a>
* @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
* @author Christoph Weitkamp - Introduced UoM and migrated tests to pure Java
*/
public final class AstroParametrizedTestCases {
public static final double TEST_LATITUDE = 22.4343;
public static final double TEST_LONGITUDE = 54.3225;
public static final int TEST_YEAR = 2016;
public static final int TEST_MONTH = 2;
public static final int TEST_DAY = 29;
public Object[][] cases = new Object[42][3];
public AstroParametrizedTestCases() {
cases[0][0] = TEST_SUN_THING_ID;
cases[0][1] = "rise#start";
cases[0][2] = new DateTimeType("2016-02-29T05:46:00+03:00");
cases[1][0] = TEST_SUN_THING_ID;
cases[1][1] = "rise#end";
cases[1][2] = new DateTimeType("2016-02-29T05:48:00+03:00");
cases[2][0] = TEST_SUN_THING_ID;
cases[2][1] = "rise#duration";
cases[2][2] = new QuantityType<>(2, SmartHomeUnits.MINUTE);
cases[3][0] = TEST_SUN_THING_ID;
cases[3][1] = "set#start";
cases[3][2] = new DateTimeType("2016-02-29T17:25:00+03:00");
cases[4][0] = TEST_SUN_THING_ID;
cases[4][1] = "set#end";
cases[4][2] = new DateTimeType("2016-02-29T17:27:00+03:00");
cases[5][0] = TEST_SUN_THING_ID;
cases[5][1] = "set#duration";
cases[5][2] = new QuantityType<>(2, SmartHomeUnits.MINUTE);
cases[6][0] = TEST_SUN_THING_ID;
cases[6][1] = "noon#start";
cases[6][2] = new DateTimeType("2016-02-29T11:37:00+03:00");
cases[7][0] = TEST_SUN_THING_ID;
cases[7][1] = "noon#end";
cases[7][2] = new DateTimeType("2016-02-29T11:38:00+03:00");
cases[8][0] = TEST_SUN_THING_ID;
cases[8][1] = "noon#duration";
cases[8][2] = new QuantityType<>(1, SmartHomeUnits.MINUTE);
cases[9][0] = TEST_SUN_THING_ID;
cases[9][1] = "night#start";
cases[9][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[10][0] = TEST_SUN_THING_ID;
cases[10][1] = "night#end";
cases[10][2] = new DateTimeType("2016-03-01T04:31:00+03:00");
cases[11][0] = TEST_SUN_THING_ID;
cases[11][1] = "night#duration";
cases[11][2] = new QuantityType<>(589, SmartHomeUnits.MINUTE);
cases[12][0] = TEST_SUN_THING_ID;
cases[12][1] = "morningNight#start";
cases[12][2] = new DateTimeType("2016-02-29T00:00:00+03:00");
cases[13][0] = TEST_SUN_THING_ID;
cases[13][1] = "morningNight#end";
cases[13][2] = new DateTimeType("2016-02-29T04:32:00+03:00");
cases[14][0] = TEST_SUN_THING_ID;
cases[14][1] = "morningNight#duration";
cases[14][2] = new QuantityType<>(272, SmartHomeUnits.MINUTE);
cases[15][0] = TEST_SUN_THING_ID;
cases[15][1] = "astroDawn#start";
cases[15][2] = new DateTimeType("2016-02-29T04:32:00+03:00");
cases[16][0] = TEST_SUN_THING_ID;
cases[16][1] = "astroDawn#end";
cases[16][2] = new DateTimeType("2016-02-29T04:58:00+03:00");
cases[17][0] = TEST_SUN_THING_ID;
cases[17][1] = "astroDawn#duration";
cases[17][2] = new QuantityType<>(26, SmartHomeUnits.MINUTE);
cases[18][0] = TEST_SUN_THING_ID;
cases[18][1] = "nauticDawn#start";
cases[18][2] = new DateTimeType("2016-02-29T04:58:00+03:00");
cases[19][0] = TEST_SUN_THING_ID;
cases[19][1] = "nauticDawn#end";
cases[19][2] = new DateTimeType("2016-02-29T05:24:00+03:00");
cases[20][0] = TEST_SUN_THING_ID;
cases[20][1] = "nauticDawn#duration";
cases[20][2] = new QuantityType<>(26, SmartHomeUnits.MINUTE);
cases[21][0] = TEST_SUN_THING_ID;
cases[21][1] = "civilDawn#start";
cases[21][2] = new DateTimeType("2016-02-29T05:24:00+03:00");
cases[22][0] = TEST_SUN_THING_ID;
cases[22][1] = "civilDawn#end";
cases[22][2] = new DateTimeType("2016-02-29T05:46:00+03:00");
cases[23][0] = TEST_SUN_THING_ID;
cases[23][1] = "civilDawn#duration";
cases[23][2] = new QuantityType<>(22, SmartHomeUnits.MINUTE);
cases[24][0] = TEST_SUN_THING_ID;
cases[24][1] = "astroDusk#start";
cases[24][2] = new DateTimeType("2016-02-29T18:16:00+03:00");
cases[25][0] = TEST_SUN_THING_ID;
cases[25][1] = "astroDusk#end";
cases[25][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[26][0] = TEST_SUN_THING_ID;
cases[26][1] = "astroDusk#duration";
cases[26][2] = new QuantityType<>(26, SmartHomeUnits.MINUTE);
cases[27][0] = TEST_SUN_THING_ID;
cases[27][1] = "nauticDusk#start";
cases[27][2] = new DateTimeType("2016-02-29T17:50:00+03:00");
cases[28][0] = TEST_SUN_THING_ID;
cases[28][1] = "nauticDusk#end";
cases[28][2] = new DateTimeType("2016-02-29T18:16:00+03:00");
cases[29][0] = TEST_SUN_THING_ID;
cases[29][1] = "nauticDusk#duration";
cases[29][2] = new QuantityType<>(26, SmartHomeUnits.MINUTE);
cases[30][0] = TEST_SUN_THING_ID;
cases[30][1] = "civilDusk#start";
cases[30][2] = new DateTimeType("2016-02-29T17:27:00+03:00");
cases[31][0] = TEST_SUN_THING_ID;
cases[31][1] = "civilDusk#end";
cases[31][2] = new DateTimeType("2016-02-29T17:50:00+03:00");
cases[32][0] = TEST_SUN_THING_ID;
cases[32][1] = "civilDusk#duration";
cases[32][2] = new QuantityType<>(23, SmartHomeUnits.MINUTE);
cases[33][0] = TEST_SUN_THING_ID;
cases[33][1] = "eveningNight#start";
cases[33][2] = new DateTimeType("2016-02-29T18:42:00+03:00");
cases[34][0] = TEST_SUN_THING_ID;
cases[34][1] = "eveningNight#end";
cases[34][2] = new DateTimeType("2016-03-01T00:00:00+03:00");
cases[35][0] = TEST_SUN_THING_ID;
cases[35][1] = "eveningNight#duration";
cases[35][2] = new QuantityType<>(318, SmartHomeUnits.MINUTE);
cases[36][0] = TEST_SUN_THING_ID;
cases[36][1] = "daylight#start";
cases[36][2] = new DateTimeType("2016-02-29T05:48:00+03:00");
cases[37][0] = TEST_SUN_THING_ID;
cases[37][1] = "daylight#end";
cases[37][2] = new DateTimeType("2016-02-29T17:25:00+03:00");
cases[38][0] = TEST_SUN_THING_ID;
cases[38][1] = "daylight#duration";
cases[38][2] = new QuantityType<>(697, SmartHomeUnits.MINUTE);
cases[39][0] = TEST_MOON_THING_ID;
cases[39][1] = "rise#start";
cases[39][2] = new DateTimeType("2016-02-29T23:00:00+03:00");
cases[40][0] = TEST_MOON_THING_ID;
cases[40][1] = "rise#end";
cases[40][2] = new DateTimeType("2016-02-29T23:00:00+03:00");
cases[41][0] = TEST_MOON_THING_ID;
cases[41][1] = "rise#duration";
cases[41][2] = new QuantityType<>(0, SmartHomeUnits.MINUTE);
}
public List<Object[]> getCases() {
return Arrays.asList(cases);
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.avmfritz.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,64 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.avmfritz
-runrequires: bnd.identity;id='org.openhab.binding.avmfritz.tests'
#
# done
#
-runbundles: \
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.commons.exec;version='[1.1.0,1.1.1)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.jupnp;version='[2.5.2,2.5.3)',\
org.openhab.core.config.discovery.upnp;version='[2.5.0,2.5.1)',\
org.openhab.core.io.net;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
com.google.gson;version='[2.8.2,2.8.3)',\
org.apache.xbean.bundleutils;version='[4.12.0,4.12.1)',\
org.apache.xbean.finder;version='[4.12.0,4.12.1)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
org.objectweb.asm;version='[7.1.0,7.1.1)',\
org.objectweb.asm.commons;version='[7.1.0,7.1.1)',\
org.objectweb.asm.tree;version='[7.1.0,7.1.1)',\
org.eclipse.jetty.client;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.api;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.client;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.common;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.xml;version='[9.4.20,9.4.21)',\
org.ops4j.pax.web.pax-web-api;version='[7.2.11,7.2.12)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.openhab.binding.avmfritz;version='[2.5.9,2.5.10)',\
org.openhab.binding.avmfritz.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.avmfritz.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: AVM FRITZ! Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.avmfritz</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.avmfritz.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: AVM FRITZ! Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.avmfritz</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,839 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.avmfritz.internal.discovery;
import static org.openhab.core.thing.Thing.*;
import static org.junit.Assert.*;
import static org.openhab.binding.avmfritz.internal.AVMFritzBindingConstants.*;
import java.io.StringReader;
import java.util.Collection;
import java.util.Collections;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.DiscoveryListener;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultFlag;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.avmfritz.internal.dto.AVMFritzBaseModel;
import org.openhab.binding.avmfritz.internal.dto.DeviceListModel;
import org.openhab.binding.avmfritz.internal.handler.AVMFritzThingHandlerOSGiTest;
import org.openhab.binding.avmfritz.internal.util.JAXBUtils;
/**
* Tests for {@link AVMFritzDiscoveryService}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class AVMFritzDiscoveryServiceOSGiTest extends AVMFritzThingHandlerOSGiTest {
private static final ThingUID BRIGE_THING_ID = new ThingUID("avmfritz:fritzbox:1");
private @Nullable DiscoveryResult discoveryResult;
private @NonNullByDefault({}) AVMFritzDiscoveryService discovery;
private final DiscoveryListener listener = new DiscoveryListener() {
@Override
public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
discoveryResult = null;
}
@Override
public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
discoveryResult = result;
}
@Override
public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
@Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
return Collections.emptyList();
}
};
@Override
@Before
public void setUp() {
super.setUp();
discovery = new AVMFritzDiscoveryService();
discovery.setThingHandler(bridgeHandler);
discovery.addDiscoveryListener(listener);
}
@After
public void cleanUp() {
discoveryResult = null;
}
@Test
public void correctSupportedTypes() {
assertEquals(12, discovery.getSupportedThingTypes().size());
assertTrue(discovery.getSupportedThingTypes().contains(DECT100_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(DECT200_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(DECT210_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(DECT300_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(DECT301_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(DECT400_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(PL546E_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(COMETDECT_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_CONTACT_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(HAN_FUN_SWITCH_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(GROUP_HEATING_THING_TYPE));
assertTrue(discovery.getSupportedThingTypes().contains(GROUP_SWITCH_THING_TYPE));
}
@Test
public void invalidDiscoveryResult() throws JAXBException {
// attribute productname is important for a valid discovery result
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0954669\" id=\"20\" functionbitmask=\"1280\" fwversion=\"03.86\" manufacturer=\"AVM\" productname=\"\">" +
"<present>1</present>" +
"<name>FRITZ!DECT Repeater 100 #5</name>" +
"<temperature>" +
"<celsius>230</celsius>" +
"<offset>0</offset>" +
"</temperature>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNull(discoveryResult);
}
@Test
public void validDECTRepeater100Result() throws JAXBException {
//@formatter:off
final String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0954669\" id=\"20\" functionbitmask=\"1280\" fwversion=\"03.86\" manufacturer=\"AVM\" productname=\"FRITZ!DECT Repeater 100\">" +
"<present>1</present>" +
"<name>FRITZ!DECT Repeater 100 #5</name>" +
"<temperature>" +
"<celsius>230</celsius>" +
"<offset>0</offset>" +
"</temperature>" +
"</device>" +
"</devicelist>";
//@formatter:on
final Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
assertEquals(1, device.getPresent());
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_DECT_Repeater_100:1:087610954669"), discoveryResult.getThingUID());
assertEquals(DECT100_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610954669", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("20", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610954669", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.86", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
final String deviceNotPresentXml = xml.replace("<present>1</present>", "<present>0</present>");
devices = (DeviceListModel) u.unmarshal(new StringReader(deviceNotPresentXml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
device = devices.getDevicelist().get(0);
assertNotNull(device);
assertEquals(0, device.getPresent());
discovery.onDeviceAdded(device);
assertNull(discoveryResult);
}
@Test
public void validDECT200DiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0000434\" id=\"17\" functionbitmask=\"2944\" fwversion=\"03.83\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 200\">" +
"<present>1</present>" +
"<name>FRITZ!DECT 200 #1</name>" +
"<switch>" +
"<state>0</state>" +
"<mode>manuell</mode>" +
"<lock>0</lock>" +
"<devicelock>1</devicelock>" +
"</switch>" +
"<powermeter>" +
"<voltage>232850</voltage>" +
"<power>45</power>" +
"<energy>166</energy>" +
"</powermeter>" +
"<temperature>" +
"<celsius>255</celsius>" +
"<offset>0</offset>" +
"</temperature>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_DECT_200:1:087610000434"), discoveryResult.getThingUID());
assertEquals(DECT200_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610000434", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("17", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610000434", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.83", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validDECT210DiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0000434\" id=\"17\" functionbitmask=\"2944\" fwversion=\"03.83\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 210\">" +
"<present>1</present>" +
"<name>FRITZ!DECT 210 #1</name>" +
"<switch>" +
"<state>0</state>" +
"<mode>manuell</mode>" +
"<lock>0</lock>" +
"<devicelock>1</devicelock>" +
"</switch>" +
"<powermeter>" +
"<voltage>232850</voltage>" +
"<power>45</power>" +
"<energy>166</energy>" +
"</powermeter>" +
"<temperature>" +
"<celsius>255</celsius>" +
"<offset>0</offset>" +
"</temperature>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_DECT_210:1:087610000434"), discoveryResult.getThingUID());
assertEquals(DECT210_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610000434", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("17", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610000434", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.83", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validCometDECTDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0000435\" id=\"18\" functionbitmask=\"320\" fwversion=\"03.50\" manufacturer=\"AVM\" productname=\"Comet DECT\">" +
"<present>1</present>" +
"<name>Comet DECT #1</name>" +
"<temperature>" +
"<celsius>220</celsius>" +
"<offset>-10</offset>" +
"</temperature>" +
"<hkr>" +
"<tist>44</tist>" +
"<tsoll>42</tsoll>" +
"<absenk>28</absenk>" +
"<komfort>42</komfort>" +
"<lock>0</lock>" +
"<devicelock>0</devicelock>" +
"<errorcode>0</errorcode>" +
"<batterylow>0</batterylow>" +
"<nextchange>" +
"<endperiod>1484341200</endperiod>" +
"<tchange>28</tchange>" +
"</nextchange>" +
"</hkr>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:Comet_DECT:1:087610000435"), discoveryResult.getThingUID());
assertEquals(COMETDECT_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610000435", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("18", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610000435", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.50", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validDECT300DiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0000435\" id=\"18\" functionbitmask=\"320\" fwversion=\"03.50\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 300\">" +
"<present>1</present>" +
"<name>FRITZ!DECT 300 #1</name>" +
"<temperature>" +
"<celsius>220</celsius>" +
"<offset>-10</offset>" +
"</temperature>" +
"<hkr>" +
"<tist>44</tist>" +
"<tsoll>42</tsoll>" +
"<absenk>28</absenk>" +
"<komfort>42</komfort>" +
"<lock>0</lock>" +
"<devicelock>0</devicelock>" +
"<errorcode>0</errorcode>" +
"<batterylow>0</batterylow>" +
"<nextchange>" +
"<endperiod>1484341200</endperiod>" +
"<tchange>28</tchange>" +
"</nextchange>" +
"</hkr>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_DECT_300:1:087610000435"), discoveryResult.getThingUID());
assertEquals(DECT300_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610000435", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("18", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610000435", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.50", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validDECT301DiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"08761 0000435\" id=\"18\" functionbitmask=\"320\" fwversion=\"03.50\" manufacturer=\"AVM\" productname=\"FRITZ!DECT 301\">" +
"<present>1</present>" +
"<name>FRITZ!DECT 301 #1</name>" +
"<temperature>" +
"<celsius>220</celsius>" +
"<offset>-10</offset>" +
"</temperature>" +
"<hkr>" +
"<tist>44</tist>" +
"<tsoll>42</tsoll>" +
"<absenk>28</absenk>" +
"<komfort>42</komfort>" +
"<lock>0</lock>" +
"<devicelock>0</devicelock>" +
"<errorcode>0</errorcode>" +
"<batterylow>0</batterylow>" +
"<nextchange>" +
"<endperiod>1484341200</endperiod>" +
"<tchange>28</tchange>" +
"</nextchange>" +
"</hkr>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_DECT_301:1:087610000435"), discoveryResult.getThingUID());
assertEquals(DECT301_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("087610000435", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("18", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("087610000435", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("03.50", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validPowerline546EDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"5C:49:79:F0:A3:84\" id=\"19\" functionbitmask=\"640\" fwversion=\"06.92\" manufacturer=\"AVM\" productname=\"FRITZ!Powerline 546E\">" +
"<present>1</present>" +
"<name>FRITZ!Powerline 546E #1</name>" +
"<switch>" +
"<state>0</state>" +
"<mode>manuell</mode>" +
"<lock>0</lock>" +
"<devicelock>1</devicelock>" +
"</switch>" +
"<powermeter>" +
"<voltage>232850</voltage>" +
"<power>0</power>" +
"<energy>2087</energy>" +
"</powermeter>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_Powerline_546E:1:5C_49_79_F0_A3_84"), discoveryResult.getThingUID());
assertEquals(PL546E_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("5C:49:79:F0:A3:84", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("19", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("5C:49:79:F0:A3:84", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("06.92", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void invalidHANFUNContactDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11934 0059578\" id=\"406\" functionbitmask=\"1\" fwversion=\"00.00\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #2</name>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNull(discoveryResult);
}
@Test
public void validHANFUNMagneticContactDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11934 0059578-1\" id=\"2000\" functionbitmask=\"8208\" fwversion=\"0.0\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #2: Unit #2</name>" +
"<etsiunitinfo>" +
"<etsideviceid>406</etsideviceid>" +
"<unittype>513</unittype>" +
"<interfaces>256</interfaces>" +
"</etsiunitinfo>" +
"<alert>" +
"<state/>" +
"</alert>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:HAN_FUN_CONTACT:1:119340059578_1"), discoveryResult.getThingUID());
assertEquals(HAN_FUN_CONTACT_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("119340059578-1", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("0x0feb", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("2000", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("119340059578-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validHANFUNOpticalContactDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11934 0059578-1\" id=\"2001\" functionbitmask=\"8208\" fwversion=\"0.0\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #3: Unit #3</name>" +
"<etsiunitinfo>" +
"<etsideviceid>406</etsideviceid>" +
"<unittype>514</unittype>" +
"<interfaces>256</interfaces>" +
"</etsiunitinfo>" +
"<alert>" +
"<state/>" +
"</alert>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:HAN_FUN_CONTACT:1:119340059578_1"), discoveryResult.getThingUID());
assertEquals(HAN_FUN_CONTACT_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("119340059578-1", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("0x0feb", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("2001", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("119340059578-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validHANFUNMotionSensorDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11934 0059578-1\" id=\"2002\" functionbitmask=\"8208\" fwversion=\"0.0\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #3: Unit #3</name>" +
"<etsiunitinfo>" +
"<etsideviceid>408</etsideviceid>" +
"<unittype>515</unittype>" +
"<interfaces>32513,256</interfaces>" +
"</etsiunitinfo>" +
"<alert>" +
"<state>0</state>" +
"</alert>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:HAN_FUN_CONTACT:1:119340059578_1"), discoveryResult.getThingUID());
assertEquals(HAN_FUN_CONTACT_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("119340059578-1", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("0x0feb", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("2002", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("119340059578-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validHANFUNMSmokeDetectorDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11324 0059952-1\" id=\"2003\" functionbitmask=\"8208\" fwversion=\"0.0\" manufacturer=\"0x2c3c\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #4: Unit #4</name>" +
"<etsiunitinfo>" +
"<etsideviceid>407</etsideviceid>" +
"<unittype>516</unittype>" +
"<interfaces>256</interfaces>" +
"</etsiunitinfo>" +
"<alert>" +
"<state>0</state>" +
"</alert>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:HAN_FUN_CONTACT:1:113240059952_1"), discoveryResult.getThingUID());
assertEquals(HAN_FUN_CONTACT_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("113240059952-1", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("0x2c3c", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("2003", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("113240059952-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validHANFUNSwitchtDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<device identifier=\"11934 0059578-1\" id=\"2001\" functionbitmask=\"8200\" fwversion=\"0.0\" manufacturer=\"0x0feb\" productname=\"HAN-FUN\">" +
"<present>1</present>" +
"<name>HAN-FUN #2: Unit #2</name>" +
"<etsiunitinfo>" +
"<etsideviceid>412</etsideviceid>" +
"<unittype>273</unittype>" +
"<interfaces>772</interfaces>" +
"</etsiunitinfo>" +
"<button>" +
"<lastpressedtimestamp>1529590797</lastpressedtimestamp>" +
"</button>" +
"</device>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:HAN_FUN_SWITCH:1:119340059578_1"), discoveryResult.getThingUID());
assertEquals(HAN_FUN_SWITCH_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("119340059578-1", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("0x0feb", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("2001", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("119340059578-1", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("0.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validHeatingGroupDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<group identifier=\"F0:A3:7F-900\" id=\"20000\" functionbitmask=\"4160\" fwversion=\"1.0\" manufacturer=\"AVM\" productname=\"\">" +
"<present>1</present>" +
"<name>Schlafzimmer</name>" +
"<hkr>" +
"<tist>0</tist>" +
"<tsoll>253</tsoll>" +
"<absenk>33</absenk>" +
"<komfort>40</komfort>" +
"<lock>1</lock>" +
"<devicelock>0</devicelock>" +
"<errorcode>0</errorcode>" +
"<batterylow>0</batterylow>" +
"<windowopenactiv>0</windowopenactiv>" +
"<battery>1</battery>" +
"<nextchange>" +
"<endperiod>1546293600</endperiod>" +
"<tchange>33</tchange>" +
"</nextchange>" +
"<summeractive>1</summeractive>" +
"<holidayactive>0</holidayactive>" +
"</hkr>" +
"<groupinfo>" +
"<masterdeviceid>1000</masterdeviceid>" +
"<members>20000</members>" +
"</groupinfo>" +
"</group>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_GROUP_HEATING:1:F0_A3_7F_900"), discoveryResult.getThingUID());
assertEquals(GROUP_HEATING_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("F0:A3:7F-900", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("20000", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("F0:A3:7F-900", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("1.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals("1000", discoveryResult.getProperties().get(PROPERTY_MASTER));
assertEquals("20000", discoveryResult.getProperties().get(PROPERTY_MEMBERS));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
@Test
public void validSwitchGroupDiscoveryResult() throws JAXBException {
//@formatter:off
String xml =
"<devicelist version=\"1\">" +
"<group identifier=\"F0:A3:7F-900\" id=\"20001\" functionbitmask=\"6784\" fwversion=\"1.0\" manufacturer=\"AVM\" productname=\"\">" +
"<present>1</present>" +
"<name>Schlafzimmer</name>" +
"<switch>" +
"<state>1</state>" +
"<mode>manuell</mode>" +
"<lock>0</lock>" +
"<devicelock>0</devicelock>" +
"</switch>" +
"<powermeter>" +
"<voltage>232850</voltage>" +
"<power>0</power>" +
"<energy>2087</energy>" +
"</powermeter>" +
"<groupinfo>" +
"<masterdeviceid>1000</masterdeviceid>" +
"<members>20000</members>" +
"</groupinfo>" +
"</group>" +
"</devicelist>";
//@formatter:on
Unmarshaller u = JAXBUtils.JAXBCONTEXT_DEVICES.createUnmarshaller();
DeviceListModel devices = (DeviceListModel) u.unmarshal(new StringReader(xml));
assertNotNull(devices);
assertEquals(1, devices.getDevicelist().size());
AVMFritzBaseModel device = devices.getDevicelist().get(0);
assertNotNull(device);
discovery.onDeviceAdded(device);
assertNotNull(discoveryResult);
assertEquals(DiscoveryResultFlag.NEW, discoveryResult.getFlag());
assertEquals(new ThingUID("avmfritz:FRITZ_GROUP_SWITCH:1:F0_A3_7F_900"), discoveryResult.getThingUID());
assertEquals(GROUP_SWITCH_THING_TYPE, discoveryResult.getThingTypeUID());
assertEquals(BRIGE_THING_ID, discoveryResult.getBridgeUID());
assertEquals("F0:A3:7F-900", discoveryResult.getProperties().get(CONFIG_AIN));
assertEquals("AVM", discoveryResult.getProperties().get(PROPERTY_VENDOR));
assertEquals("20001", discoveryResult.getProperties().get(PROPERTY_MODEL_ID));
assertEquals("F0:A3:7F-900", discoveryResult.getProperties().get(PROPERTY_SERIAL_NUMBER));
assertEquals("1.0", discoveryResult.getProperties().get(PROPERTY_FIRMWARE_VERSION));
assertEquals("1000", discoveryResult.getProperties().get(PROPERTY_MASTER));
assertEquals("20000", discoveryResult.getProperties().get(PROPERTY_MEMBERS));
assertEquals(CONFIG_AIN, discoveryResult.getRepresentationProperty());
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.avmfritz.internal.handler;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.openhab.binding.avmfritz.internal.AVMFritzBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.ThingProvider;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.openhab.binding.avmfritz.internal.AVMFritzDynamicCommandDescriptionProvider;
/**
* Tests for {@link AVMFritzThingHandlerOSGiTest}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public abstract class AVMFritzThingHandlerOSGiTest extends JavaOSGiTest {
private static HttpClient httpClient = new HttpClient();
private VolatileStorageService volatileStorageService = new VolatileStorageService();
private @NonNullByDefault({}) ManagedThingProvider managedThingProvider;
protected @NonNullByDefault({}) Bridge bridge;
protected @NonNullByDefault({}) BoxHandler bridgeHandler;
@BeforeClass
public static void setUpClass() throws Exception {
httpClient.start();
}
@Before
public void setUp() {
registerService(volatileStorageService);
managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
assertNotNull("Could not get ManagedThingProvider", managedThingProvider);
bridge = buildBridge();
assertNotNull(bridge.getConfiguration());
managedThingProvider.add(bridge);
ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
bridgeHandler = new BoxHandler(bridge, httpClient, mock(AVMFritzDynamicCommandDescriptionProvider.class));
assertNotNull(bridgeHandler);
bridgeHandler.setCallback(callback);
assertNull(bridge.getHandler());
bridge.setHandler(bridgeHandler);
assertNotNull(bridge.getHandler());
bridgeHandler.initialize();
}
@After
public void tearDown() {
if (bridge != null) {
managedThingProvider.remove(bridge.getUID());
}
unregisterService(volatileStorageService);
}
@AfterClass
public static void tearDownClass() throws Exception {
httpClient.stop();
}
private Bridge buildBridge() {
Map<String, Object> properties = new HashMap<>();
properties.put(CONFIG_IP_ADDRESS, "fritz.box");
properties.put(CONFIG_PROTOCOL, "http");
properties.put(CONFIG_USER, "user");
properties.put(CONFIG_PASSWORD, "password");
properties.put(CONFIG_POLLING_INTERVAL, 15);
properties.put(CONFIG_SYNC_TIMEOUT, 2000);
return BridgeBuilder.create(BRIDGE_THING_TYPE, "1").withLabel(BOX_MODEL_NAME)
.withConfiguration(new Configuration(properties)).build();
}
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.feed.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,66 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.feed
-runrequires: \
bnd.identity;id='org.openhab.binding.feed.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml',\
bnd.identity;id='org.apache.felix.configadmin',\
osgi.identity;filter:='(&(osgi.identity=org.ops4j.pax.web.pax-web-runtime)(version>=7.2.3))'
-runvm: -Dorg.osgi.service.http.port=${org.osgi.service.http.port}
#
# done
#
-runbundles: \
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.apache.xbean.bundleutils;version='[4.12.0,4.12.1)',\
org.apache.xbean.finder;version='[4.12.0,4.12.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
org.objectweb.asm;version='[7.1.0,7.1.1)',\
org.objectweb.asm.commons;version='[7.1.0,7.1.1)',\
org.objectweb.asm.tree;version='[7.1.0,7.1.1)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.xml;version='[9.4.20,9.4.21)',\
org.ops4j.pax.swissbox.optional.jcl;version='[1.8.3,1.8.4)',\
org.ops4j.pax.web.pax-web-api;version='[7.2.11,7.2.12)',\
org.ops4j.pax.web.pax-web-jetty;version='[7.2.11,7.2.12)',\
org.ops4j.pax.web.pax-web-runtime;version='[7.2.11,7.2.12)',\
org.ops4j.pax.web.pax-web-spi;version='[7.2.11,7.2.12)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
org.openhab.binding.feed;version='[2.5.9,2.5.10)',\
org.openhab.binding.feed.tests;version='[2.5.9,2.5.10)',\

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.feed.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Feed Binding Tests</name>
<properties>
<org.osgi.service.http.port>9090</org.osgi.service.http.port>
</properties>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.feed</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.jdom</artifactId>
<version>2.0.6_1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>reserve-network-port</id>
<goals>
<goal>reserve-network-port</goal>
</goals>
<phase>process-resources</phase>
<configuration>
<portNames>
<portName>org.osgi.service.http.port</portName>
</portNames>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.feed.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Feed Binding Tests</name>
<properties>
<org.osgi.service.http.port>9090</org.osgi.service.http.port>
</properties>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.feed</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.jdom</artifactId>
<version>2.0.6_1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>reserve-network-port</id>
<goals>
<goal>reserve-network-port</goal>
</goals>
<phase>process-resources</phase>
<configuration>
<portNames>
<portName>org.osgi.service.http.port</portName>
</portNames>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,419 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.feed.test;
import static java.lang.Thread.sleep;
import static org.openhab.core.thing.ThingStatus.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.math.BigDecimal;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.StateChangeListener;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingProvider;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.openhab.binding.feed.internal.FeedBindingConstants;
import org.openhab.binding.feed.internal.handler.FeedHandler;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
/**
* Tests for {@link FeedHandler}
*
* @author Svilen Valkanov - Initial contribution
* @author Wouter Born - Migrate Groovy to Java tests
*/
public class FeedHandlerTest extends JavaOSGiTest {
// Servlet URL configuration
private static final String MOCK_SERVLET_PROTOCOL = "http";
private static final String MOCK_SERVLET_HOSTNAME = "localhost";
private static final int MOCK_SERVLET_PORT = Integer.getInteger("org.osgi.service.http.port", 8080);
private static final String MOCK_SERVLET_PATH = "/test/feed";
// Files used for the test as input. They are located in /src/test/resources directory
/**
* The default mock content in the test is RSS 2.0 format, as this is the most popular format
*/
private static final String DEFAULT_MOCK_CONTENT = "rss_2.0.xml";
/**
* One new entry is added to {@link #DEFAULT_MOCK_CONTENT}
*/
private static final String MOCK_CONTENT_CHANGED = "rss_2.0_changed.xml";
private static final String ITEM_NAME = "testItem";
private static final String THING_NAME = "testFeedThing";
/**
* Default auto refresh interval for the test is 1 Minute.
*/
private static final int DEFAULT_TEST_AUTOREFRESH_TIME = 1;
/**
* It is updated from mocked {@link StateChangeListener#stateUpdated() }
*/
private StringType currentItemState = null;
// Required services for the test
private ManagedThingProvider managedThingProvider;
private VolatileStorageService volatileStorageService;
private ThingRegistry thingRegistry;
private FeedServiceMock servlet;
private Thing feedThing;
private FeedHandler feedHandler;
private ChannelUID channelUID;
/**
* This class is used as a mock for HTTP web server, serving XML feed content.
*/
class FeedServiceMock extends HttpServlet {
private static final long serialVersionUID = -7810045624309790473L;
String feedContent;
int httpStatus;
public FeedServiceMock(String feedContentFile) {
super();
try {
setFeedContent(feedContentFile);
} catch (IOException e) {
throw new IllegalArgumentException("Error loading feed content from: " + feedContentFile);
}
// By default the servlet returns HTTP Status code 200 OK
this.httpStatus = HttpStatus.OK_200;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getOutputStream().println(feedContent);
// Recommended RSS MIME type - http://www.rssboard.org/rss-mime-type-application.txt
// Atom MIME type is - application/atom+xml
// Other MIME types - text/plan, text/xml, text/html are tested and accepted as well
response.setContentType("application/rss+xml");
response.setStatus(httpStatus);
}
public void setFeedContent(String feedContentFile) throws IOException {
String path = "input/" + feedContentFile;
feedContent = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream(path));
}
}
@Before
public void setUp() {
volatileStorageService = new VolatileStorageService();
registerService(volatileStorageService);
managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
assertThat(managedThingProvider, is(notNullValue()));
thingRegistry = getService(ThingRegistry.class);
assertThat(thingRegistry, is(notNullValue()));
registerFeedTestServlet();
}
@After
public void tearDown() {
currentItemState = null;
if (feedThing != null) {
// Remove the feed thing. The handler will be also disposed automatically
Thing removedThing = thingRegistry.forceRemove(feedThing.getUID());
assertThat("The feed thing cannot be deleted", removedThing, is(notNullValue()));
}
unregisterFeedTestServlet();
// Wait for FeedHandler to be unregistered
waitForAssert(() -> {
feedHandler = (FeedHandler) feedThing.getHandler();
assertThat(feedHandler, is(nullValue()));
});
}
private void registerFeedTestServlet() {
HttpService httpService = getService(HttpService.class);
assertThat(httpService, is(notNullValue()));
servlet = new FeedServiceMock(DEFAULT_MOCK_CONTENT);
try {
httpService.registerServlet(MOCK_SERVLET_PATH, servlet, null, null);
} catch (ServletException | NamespaceException e) {
throw new IllegalStateException("Failed to register feed test servlet", e);
}
}
private void unregisterFeedTestServlet() {
HttpService httpService = getService(HttpService.class);
assertThat(httpService, is(notNullValue()));
httpService.unregister(MOCK_SERVLET_PATH);
servlet = null;
}
private String generateURLString(String protocol, String hostname, int port, String path) {
return protocol + "://" + hostname + ":" + port + path;
}
private void initializeDefaultFeedHandler() {
String mockServletURL = generateURLString(MOCK_SERVLET_PROTOCOL, MOCK_SERVLET_HOSTNAME, MOCK_SERVLET_PORT,
MOCK_SERVLET_PATH);
// One minute update time is used for the tests
BigDecimal defaultTestRefreshInterval = new BigDecimal(DEFAULT_TEST_AUTOREFRESH_TIME);
initializeFeedHandler(mockServletURL, defaultTestRefreshInterval);
}
private void initializeFeedHandler(String url) {
initializeFeedHandler(url, null);
}
private void initializeFeedHandler(String url, BigDecimal refreshTime) {
// Set up configuration
Configuration configuration = new Configuration();
configuration.put((FeedBindingConstants.URL), url);
configuration.put((FeedBindingConstants.REFRESH_TIME), refreshTime);
// Create Feed Thing
ThingUID feedUID = new ThingUID(FeedBindingConstants.FEED_THING_TYPE_UID, THING_NAME);
channelUID = new ChannelUID(feedUID, FeedBindingConstants.CHANNEL_LATEST_DESCRIPTION);
Channel channel = ChannelBuilder.create(channelUID, "String").build();
feedThing = ThingBuilder.create(FeedBindingConstants.FEED_THING_TYPE_UID, feedUID)
.withConfiguration(configuration).withChannel(channel).build();
managedThingProvider.add(feedThing);
// Wait for FeedHandler to be registered
waitForAssert(() -> {
feedHandler = (FeedHandler) feedThing.getHandler();
assertThat("FeedHandler is not registered", feedHandler, is(notNullValue()));
});
// This will ensure that the configuration is read before the channelLinked() method in FeedHandler is called !
waitForAssert(() -> {
assertThat(feedThing.getStatus(), anyOf(is(ONLINE), is(OFFLINE)));
}, 60000, DFL_SLEEP_TIME);
initializeItem(channelUID);
}
private void initializeItem(ChannelUID channelUID) {
// Create new item
ItemRegistry itemRegistry = getService(ItemRegistry.class);
assertThat(itemRegistry, is(notNullValue()));
StringItem newItem = new StringItem(ITEM_NAME);
// Add item state change listener
StateChangeListener updateListener = new StateChangeListener() {
@Override
public void stateChanged(Item item, State oldState, State newState) {
}
@Override
public void stateUpdated(Item item, State state) {
currentItemState = (StringType) state;
}
};
newItem.addStateChangeListener(updateListener);
itemRegistry.add(newItem);
// Add item channel link
ManagedItemChannelLinkProvider itemChannelLinkProvider = getService(ManagedItemChannelLinkProvider.class);
assertThat(itemChannelLinkProvider, is(notNullValue()));
itemChannelLinkProvider.add(new ItemChannelLink(ITEM_NAME, channelUID));
}
private void testIfItemStateIsUpdated(boolean commandReceived, boolean contentChanged)
throws IOException, InterruptedException {
initializeDefaultFeedHandler();
waitForAssert(() -> {
assertThat("Feed Thing can not be initialized", feedThing.getStatus(), is(equalTo(ONLINE)));
assertThat("Item's state is not updated on initialize", currentItemState, is(notNullValue()));
});
assertThat(currentItemState, is(instanceOf(StringType.class)));
StringType firstItemState = currentItemState;
if (contentChanged) {
// The content on the mocked server should be changed
servlet.setFeedContent(MOCK_CONTENT_CHANGED);
}
if (commandReceived) {
// Before this time has expired, the refresh command will no trigger a request to the server
sleep(FeedBindingConstants.MINIMUM_REFRESH_TIME);
feedHandler.handleCommand(channelUID, RefreshType.REFRESH);
} else {
// The auto refresh task will handle the update after the default wait time
sleep(DEFAULT_TEST_AUTOREFRESH_TIME * 60 * 1000);
}
waitForAssert(() -> {
assertThat("Error occurred while trying to connect to server. Content is not downloaded!",
feedThing.getStatus(), is(equalTo(ONLINE)));
});
waitForAssert(() -> {
if (contentChanged) {
assertThat("Content is not updated!", currentItemState, not(equalTo(firstItemState)));
} else {
assertThat(currentItemState, is(equalTo(firstItemState)));
}
});
}
@Test
public void assertThatInvalidConfigurationFallsBackToDefaultValues() {
String mockServletURL = generateURLString(MOCK_SERVLET_PROTOCOL, MOCK_SERVLET_HOSTNAME, MOCK_SERVLET_PORT,
MOCK_SERVLET_PATH);
BigDecimal defaultTestRefreshInterval = new BigDecimal(-10);
initializeFeedHandler(mockServletURL, defaultTestRefreshInterval);
}
@Test
@Category(SlowTests.class)
public void assertThatItemsStateIsNotUpdatedOnAutoRefreshIfContentIsNotChanged()
throws IOException, InterruptedException {
boolean commandReceived = false;
boolean contentChanged = false;
testIfItemStateIsUpdated(commandReceived, contentChanged);
}
@Test
@Category(SlowTests.class)
public void assertThatItemsStateIsUpdatedOnAutoRefreshIfContentChanged() throws IOException, InterruptedException {
boolean commandReceived = false;
boolean contentChanged = true;
testIfItemStateIsUpdated(commandReceived, contentChanged);
}
@Test
public void assertThatThingsStatusIsUpdatedWhenHTTP500ErrorCodeIsReceived() throws InterruptedException {
testIfThingStatusIsUpdated(HttpStatus.INTERNAL_SERVER_ERROR_500);
}
@Test
public void assertThatThingsStatusIsUpdatedWhenHTTP401ErrorCodeIsReceived() throws InterruptedException {
testIfThingStatusIsUpdated(HttpStatus.UNAUTHORIZED_401);
}
@Test
public void assertThatThingsStatusIsUpdatedWhenHTTP403ErrorCodeIsReceived() throws InterruptedException {
testIfThingStatusIsUpdated(HttpStatus.FORBIDDEN_403);
}
@Test
public void assertThatThingsStatusIsUpdatedWhenHTTP404ErrorCodeIsReceived() throws InterruptedException {
testIfThingStatusIsUpdated(HttpStatus.NOT_FOUND_404);
}
private void testIfThingStatusIsUpdated(Integer serverStatus) throws InterruptedException {
initializeDefaultFeedHandler();
servlet.httpStatus = serverStatus;
// Before this time has expired, the refresh command will no trigger a request to the server
sleep(FeedBindingConstants.MINIMUM_REFRESH_TIME);
// Invalid channel UID is used for the test, because otherwise
feedHandler.handleCommand(channelUID, RefreshType.REFRESH);
waitForAssert(() -> {
assertThat(feedThing.getStatus(), is(equalTo(OFFLINE)));
});
servlet.httpStatus = HttpStatus.OK_200;
// Before this time has expired, the refresh command will no trigger a request to the server
sleep(FeedBindingConstants.MINIMUM_REFRESH_TIME);
feedHandler.handleCommand(channelUID, RefreshType.REFRESH);
waitForAssert(() -> {
assertThat(feedThing.getStatus(), is(equalTo(ONLINE)));
});
}
@Test
public void createThingWithInvalidUrlProtocol() {
String invalidProtocol = "gdfs";
String invalidURL = generateURLString(invalidProtocol, MOCK_SERVLET_HOSTNAME, MOCK_SERVLET_PORT,
MOCK_SERVLET_PATH);
initializeFeedHandler(invalidURL);
waitForAssert(() -> {
assertThat(feedThing.getStatus(), is(equalTo(OFFLINE)));
assertThat(feedThing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@Test
public void createThingWithInvalidUrlHostname() {
String invalidHostname = "invalidhost";
String invalidURL = generateURLString(MOCK_SERVLET_PROTOCOL, invalidHostname, MOCK_SERVLET_PORT,
MOCK_SERVLET_PATH);
initializeFeedHandler(invalidURL);
waitForAssert(() -> {
assertThat(feedThing.getStatus(), is(equalTo(OFFLINE)));
assertThat(feedThing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.COMMUNICATION_ERROR)));
}, 30000, DFL_SLEEP_TIME);
}
@Test
public void createThingWithInvalidUrlPath() {
String invalidPath = "/invalid/path";
String invalidURL = generateURLString(MOCK_SERVLET_PROTOCOL, MOCK_SERVLET_HOSTNAME, MOCK_SERVLET_PORT,
invalidPath);
initializeFeedHandler(invalidURL);
waitForAssert(() -> {
assertThat(feedThing.getStatus(), is(equalTo(OFFLINE)));
assertThat(feedThing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.COMMUNICATION_ERROR)));
});
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.feed.test;
/**
* This interface is used to mark tests that take too much time
*
* @author Svilen Valkanov
*/
public interface SlowTests {
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="windows-1252"?>
<rss version="2.0">
<channel>
<title>Sample Feed - Favorite RSS Related Software &amp; Resources</title>
<description>Take a look at some of FeedForAll&apos;s favorite software and resources for learning more about RSS.</description>
<link>http://www.feedforall.com</link>
<category domain="www.dmoz.com">Computers/Software/Internet/Site Management/Content Management</category>
<copyright>Copyright 2004 NotePage, Inc.</copyright>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<language>en-us</language>
<lastBuildDate>Mon, 1 Nov 2004 13:17:17 -0500</lastBuildDate>
<managingEditor>marketing@feedforall.com</managingEditor>
<pubDate>Tue, 26 Oct 2004 14:06:44 -0500</pubDate>
<webMaster>webmaster@feedforall.com</webMaster>
<generator>FeedForAll Beta1 (0.0.1.8)</generator>
<image>
<url>http://www.feedforall.com/feedforall-temp.gif</url>
<title>FeedForAll Sample Feed</title>
<link>http://www.feedforall.com/industry-solutions.htm</link>
<description>FeedForAll Sample Feed</description>
<width>144</width>
<height>117</height>
</image>
<item>
<title>RSS Resources</title>
<description>Be sure to take a look at some of our favorite RSS Resources&lt;br&gt;
&lt;a href=&quot;http://www.rss-specifications.com&quot;&gt;RSS Specifications&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;http://www.blog-connection.com&quot;&gt;Blog Connection&lt;/a&gt;&lt;br&gt;
&lt;br&gt;</description>
<link>http://www.feedforall.com</link>
<pubDate>Tue, 26 Oct 2004 14:01:01 -0500</pubDate>
</item>
<item>
<title>Recommended Desktop Feed Reader Software</title>
<description>&lt;b&gt;FeedDemon&lt;/b&gt; enables you to quickly read and gather information from hundreds of web sites - without having to visit them. Don&apos;t waste any more time checking your favorite web sites for updates. Instead, use FeedDemon and make them come to you. &lt;br&gt;
More &lt;a href=&quot;http://store.esellerate.net/a.asp?c=1_SKU5139890208_AFL403073819&quot;&gt;FeedDemon Information&lt;/a&gt;</description>
<link>http://www.feedforall.com/feedforall-partners.htm</link>
<pubDate>Tue, 26 Oct 2004 14:03:25 -0500</pubDate>
</item>
<item>
<title>Recommended Web Based Feed Reader Software</title>
<description>&lt;b&gt;FeedScout&lt;/b&gt; enables you to view RSS/ATOM/RDF feeds from different sites directly in Internet Explorer. You can even set your Home Page to show favorite feeds. Feed Scout is a plug-in for Internet Explorer, so you won&apos;t have to learn anything except for how to press 2 new buttons on Internet Explorer toolbar. &lt;br&gt;
More &lt;a href=&quot;http://www.bytescout.com/feedscout.html&quot;&gt;Information on FeedScout&lt;/a&gt;&lt;br&gt;
</description>
<link>http://www.feedforall.com/feedforall-partners.htm</link>
<pubDate>Tue, 26 Oct 2004 14:06:44 -0500</pubDate>
</item>
</channel>
</rss>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="windows-1252"?>
<rss version="2.0">
<channel>
<title>Sample Feed - Favorite RSS Related Software &amp; Resources</title>
<description>Take a look at some of FeedForAll&apos;s favorite software and resources for learning more about RSS.</description>
<link>http://www.feedforall.com</link>
<category domain="www.dmoz.com">Computers/Software/Internet/Site Management/Content Management</category>
<copyright>Copyright 2004 NotePage, Inc.</copyright>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<language>en-us</language>
<lastBuildDate>Mon, 1 Nov 2004 13:17:17 -0500</lastBuildDate>
<managingEditor>marketing@feedforall.com</managingEditor>
<pubDate>Tue, 26 Oct 2004 14:06:44 -0500</pubDate>
<webMaster>webmaster@feedforall.com</webMaster>
<generator>FeedForAll Beta1 (0.0.1.8)</generator>
<image>
<url>http://www.feedforall.com/feedforall-temp.gif</url>
<title>FeedForAll Sample Feed</title>
<link>http://www.feedforall.com/industry-solutions.htm</link>
<description>FeedForAll Sample Feed</description>
<width>144</width>
<height>117</height>
</image>
<item>
<title>RSS Resources</title>
<description>New Description. Be sure to take a look at some of our favorite RSS Resources&lt;br&gt;
&lt;a href=&quot;http://www.rss-specifications.com&quot;&gt;RSS Specifications&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;http://www.blog-connection.com&quot;&gt;Blog Connection&lt;/a&gt;&lt;br&gt;
&lt;br&gt;</description>
<link>http://www.feedforall.com</link>
<pubDate>Tue, 26 Oct 2004 14:01:01 -0500</pubDate>
</item>
<item>
<title>Recommended Desktop Feed Reader Software</title>
<description>&lt;b&gt;FeedDemon&lt;/b&gt; enables you to quickly read and gather information from hundreds of web sites - without having to visit them. Don&apos;t waste any more time checking your favorite web sites for updates. Instead, use FeedDemon and make them come to you. &lt;br&gt;
More &lt;a href=&quot;http://store.esellerate.net/a.asp?c=1_SKU5139890208_AFL403073819&quot;&gt;FeedDemon Information&lt;/a&gt;</description>
<link>http://www.feedforall.com/feedforall-partners.htm</link>
<pubDate>Tue, 26 Oct 2004 14:03:25 -0500</pubDate>
</item>
<item>
<title>Recommended Web Based Feed Reader Software</title>
<description>&lt;b&gt;FeedScout&lt;/b&gt; enables you to view RSS/ATOM/RDF feeds from different sites directly in Internet Explorer. You can even set your Home Page to show favorite feeds. Feed Scout is a plug-in for Internet Explorer, so you won&apos;t have to learn anything except for how to press 2 new buttons on Internet Explorer toolbar. &lt;br&gt;
More &lt;a href=&quot;http://www.bytescout.com/feedscout.html&quot;&gt;Information on FeedScout&lt;/a&gt;&lt;br&gt;
</description>
<link>http://www.feedforall.com/feedforall-partners.htm</link>
<pubDate>Tue, 26 Oct 2004 14:06:44 -0500</pubDate>
</item>
<item>
<title>Some new item</title>
<description>&lt;b&gt;FeedScout&lt;/b&gt; enables you to view RSS/ATOM/RDF feeds from different sites directly in Internet Explorer. You can even set your Home Page to show favorite feeds. Feed Scout is a plug-in for Internet Explorer, so you won&apos;t have to learn anything except for how to press 2 new buttons on Internet Explorer toolbar. &lt;br&gt;
More &lt;a href=&quot;http://www.bytescout.com/feedscout.html&quot;&gt;Information on FeedScout&lt;/a&gt;&lt;br&gt;
</description>
<link>http://www.feedforall.com/</link>
<pubDate>Tue, 26 Oct 2004 18:06:44 -0500</pubDate>
</item>
</channel>
</rss>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.hue.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,67 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.hue
-runrequires: \
bnd.identity;id='org.openhab.binding.hue.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
#
# done
#
-runbundles: \
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.exec;version='[1.1.0,1.1.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.apache.xbean.bundleutils;version='[4.12.0,4.12.1)',\
org.apache.xbean.finder;version='[4.12.0,4.12.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.jupnp;version='[2.5.2,2.5.3)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery.upnp;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.io.net;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
org.objectweb.asm;version='[7.1.0,7.1.1)',\
org.objectweb.asm.commons;version='[7.1.0,7.1.1)',\
org.objectweb.asm.tree;version='[7.1.0,7.1.1)',\
org.eclipse.jetty.client;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.api;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.client;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.websocket.common;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.xml;version='[9.4.20,9.4.21)',\
org.ops4j.pax.web.pax-web-api;version='[7.2.11,7.2.12)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.openhab.binding.hue;version='[2.5.9,2.5.10)',\
org.openhab.binding.hue.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.hue.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: hue Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.hue</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.hue.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: hue Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.hue</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal;
import static org.junit.Assert.*;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.test.java.JavaOSGiTest;
/**
* @author Markus Rathgeb - migrated to plain Java test
*/
public class AbstractHueOSGiTestParent extends JavaOSGiTest {
/**
* Gets the handler of a thing if it fits to a specific type.
*
* @param thing the thing
* @param clazz type of thing handler
* @return the thing handler
*/
protected <T extends ThingHandler> T getThingHandler(Thing thing, Class<T> clazz) {
return waitForAssert(() -> {
final ThingHandler tmp = thing.getHandler();
if (clazz.isInstance(tmp)) {
return clazz.cast(tmp);
} else {
assertNotNull(tmp);
assertEquals(clazz, tmp.getClass());
throw new RuntimeException();
}
});
}
}

View File

@@ -0,0 +1,225 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal;
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.DiscoveryListener;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultFlag;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService;
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
/**
* 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 AbstractHueOSGiTestParent {
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(PROPERTY_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);
waitForAssert(() -> {
assertNull(getService(DiscoveryService.class, HueLightDiscoveryService.class));
});
}
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");
AtomicReference<DiscoveryResult> resultWrapper = new AtomicReference<>();
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, ThingUID bridgeUID) {
return null;
}
});
discoveryService.addLightDiscovery(light);
waitForAssert(() -> {
assertTrue(resultWrapper.get() != null);
});
final DiscoveryResult result = resultWrapper.get();
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);
MockedHttpClient mockedHttpClient = new MockedHttpClient() {
@Override
public Result put(String address, String body) throws IOException {
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 if (address.endsWith("lights") || address.endsWith("sensors")) {
String body = "{}";
return new Result(body, 200);
} else if (address.endsWith("testUserName/config")) {
String body = "{ \"apiversion\": \"1.26.0\"}";
return new Result(body, 200);
} else {
return new Result("", 404);
}
}
@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 new Result("", 404);
}
}
};
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("hueBridge");
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();
}
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal;
/**
* @author Denis Dudnik - Initial contribution
*/
public class MockedHttpClient extends HttpClient {
}

View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal.discovery;
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
import java.net.MalformedURLException;
import java.net.URL;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultFlag;
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.jupnp.model.ValidationException;
import org.jupnp.model.meta.DeviceDetails;
import org.jupnp.model.meta.ManufacturerDetails;
import org.jupnp.model.meta.ModelDetails;
import org.jupnp.model.meta.RemoteDevice;
import org.jupnp.model.meta.RemoteDeviceIdentity;
import org.jupnp.model.meta.RemoteService;
import org.jupnp.model.types.DeviceType;
import org.jupnp.model.types.UDN;
/**
* Tests for {@link org.openhab.binding.hue.internal.discovery.HueBridgeDiscoveryParticipant}.
*
* @author Kai Kreuzer - Initial contribution
* @author Thomas Höfer - Added representation
* @author Markus Rathgeb - migrated to plain Java test
*/
public class HueBridgeDiscoveryParticipantOSGITest extends JavaOSGiTest {
UpnpDiscoveryParticipant discoveryParticipant;
RemoteDevice hueDevice;
RemoteDevice otherDevice;
@Before
public void setUp() {
discoveryParticipant = getService(UpnpDiscoveryParticipant.class, HueBridgeDiscoveryParticipant.class);
assertThat(discoveryParticipant, is(notNullValue()));
try {
final RemoteService remoteService = null;
hueDevice = new RemoteDevice(
new RemoteDeviceIdentity(new UDN("123"), 60, new URL("http://hue"), null, null),
new DeviceType("namespace", "type"),
new DeviceDetails(new URL("http://1.2.3.4/"), "Hue Bridge", new ManufacturerDetails("Philips"),
new ModelDetails("Philips hue bridge"), "serial123", "upc", null),
remoteService);
otherDevice = new RemoteDevice(
new RemoteDeviceIdentity(new UDN("567"), 60, new URL("http://acme"), null, null),
new DeviceType("namespace", "type"), new DeviceDetails("Some Device",
new ManufacturerDetails("Taiwan"), new ModelDetails("$%&/"), "serial567", "upc"),
remoteService);
} catch (final ValidationException | MalformedURLException ex) {
Assert.fail("Internal test error.");
}
}
@After
public void cleanUp() {
}
@Test
public void correctSupportedTypes() {
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().size(), is(1));
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().iterator().next(), is(THING_TYPE_BRIDGE));
}
@Test
public void correctThingUID() {
assertThat(discoveryParticipant.getThingUID(hueDevice), is(new ThingUID("hue:bridge:serial123")));
}
@Test
public void validDiscoveryResult() {
final DiscoveryResult result = discoveryParticipant.createResult(hueDevice);
assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
assertThat(result.getThingUID(), is(new ThingUID("hue:bridge:serial123")));
assertThat(result.getThingTypeUID(), is(THING_TYPE_BRIDGE));
assertThat(result.getBridgeUID(), is(nullValue()));
assertThat(result.getProperties().get(HOST), is("1.2.3.4"));
assertThat(result.getProperties().get(PROPERTY_SERIAL_NUMBER), is("serial123"));
assertThat(result.getRepresentationProperty(), is(PROPERTY_SERIAL_NUMBER));
}
@Test
public void noThingUIDForUnknownDevice() {
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
}
@Test
public void noDiscoveryResultForUnknownDevice() {
assertThat(discoveryParticipant.createResult(otherDevice), is(nullValue()));
}
}

View File

@@ -0,0 +1,303 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal.discovery;
import static org.openhab.core.config.discovery.inbox.InboxPredicates.forThingTypeUID;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.openhab.binding.hue.internal.HueBindingConstants.THING_TYPE_BRIDGE;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.openhab.core.config.discovery.DiscoveryListener;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.config.discovery.inbox.Inbox;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.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 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(THING_TYPE_BRIDGE));
}
@Test
public void validBridgesAreDiscovered() {
List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
.collect(Collectors.toList());
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, ThingUID bridgeUID) {
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.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
.collect(Collectors.toList());
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.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
.collect(Collectors.toList());
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, ThingUID bridgeUID) {
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.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(),
is(0));
});
}
}

View File

@@ -0,0 +1,252 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.hue.internal.handler;
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.*;
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
import static org.openhab.binding.hue.internal.config.HueBridgeConfig.HTTP;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.ScheduledExecutorService;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.core.status.ConfigStatusMessage;
import org.openhab.core.common.ThreadPoolManager;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.hue.internal.AbstractHueOSGiTestParent;
import org.openhab.binding.hue.internal.HueBridge;
import org.openhab.binding.hue.internal.HueConfigStatusMessage;
import org.openhab.binding.hue.internal.exceptions.ApiException;
import org.openhab.binding.hue.internal.exceptions.LinkButtonException;
import org.openhab.binding.hue.internal.exceptions.UnauthorizedException;
/**
* Tests for {@link HueBridgeHandler}.
*
* @author Oliver Libutzki - Initial contribution
* @author Michael Grammling - Initial contribution
* @author Denis Dudnik - switched to internally integrated source of Jue library
*/
public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
private final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "bridge");
private static final String TEST_USER_NAME = "eshTestUser";
private static final String DUMMY_HOST = "1.2.3.4";
private ThingRegistry thingRegistry;
private ScheduledExecutorService scheduler;
@Before
public void setUp() {
registerVolatileStorageService();
thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
assertNotNull(thingRegistry);
scheduler = ThreadPoolManager.getScheduledPool("hueBridgeTest");
}
@Test
public void assertThatANewUserIsAddedToConfigIfNotExistingYet() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
@Override
public String link(String deviceType) throws IOException, ApiException {
return TEST_USER_NAME;
}
});
hueBridgeHandler.onNotAuthenticated();
assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
}
@Test
public void assertThatAnExistingUserIsUsedIfAuthenticationWasSuccessful() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(USER_NAME, TEST_USER_NAME);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
@Override
public void authenticate(String userName) throws IOException, ApiException {
}
});
hueBridgeHandler.onNotAuthenticated();
assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
}
@Test
public void assertCorrectStatusIfAuthenticationFailedForOldUser() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(USER_NAME, "notAuthenticatedUser");
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
@Override
public void authenticate(String userName) throws IOException, ApiException {
throw new UnauthorizedException();
}
});
hueBridgeHandler.onNotAuthenticated();
assertEquals("notAuthenticatedUser", bridge.getConfiguration().get(USER_NAME));
assertEquals(ThingStatus.OFFLINE, bridge.getStatus());
assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
}
@Test
public void verifyStatusIfLinkButtonIsNotPressed() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
@Override
public String link(String deviceType) throws IOException, ApiException {
throw new LinkButtonException();
}
});
hueBridgeHandler.onNotAuthenticated();
assertNull(bridge.getConfiguration().get(USER_NAME));
assertEquals(ThingStatus.OFFLINE, bridge.getStatus());
assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
}
@Test
public void verifyStatusIfNewUserCannotBeCreated() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
@Override
public String link(String deviceType) throws IOException, ApiException {
throw new ApiException();
}
});
hueBridgeHandler.onNotAuthenticated();
assertNull(bridge.getConfiguration().get(USER_NAME));
assertEquals(ThingStatus.OFFLINE, bridge.getStatus());
assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
}
@Test
public void verifyOfflineIsSetWithoutBridgeOfflineStatus() {
Configuration configuration = new Configuration();
configuration.put(HOST, DUMMY_HOST);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
hueBridgeHandler.thingUpdated(bridge);
hueBridgeHandler.onConnectionLost();
assertEquals(ThingStatus.OFFLINE, bridge.getStatus());
assertNotEquals(ThingStatusDetail.BRIDGE_OFFLINE, bridge.getStatusInfo().getStatusDetail());
}
@Test
public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsNull() {
Configuration configuration = new Configuration();
configuration.put(HOST, null);
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
}
@Test
public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsAnEmptyString() {
Configuration configuration = new Configuration();
configuration.put(HOST, "");
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
Bridge bridge = createBridgeThing(configuration);
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
}
private Bridge createBridgeThing(Configuration configuration) {
Bridge bridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID,
new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge"), null, "Bridge", configuration);
assertNotNull(bridge);
thingRegistry.add(bridge);
return bridge;
}
private void injectBridge(HueBridgeHandler hueBridgeHandler, HueBridge bridge) {
try {
Field hueBridgeField = hueBridgeHandler.getClass().getDeclaredField("hueBridge");
hueBridgeField.setAccessible(true);
hueBridgeField.set(hueBridgeHandler, bridge);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.max.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,53 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.max
-runrequires: \
bnd.identity;id='org.openhab.binding.max.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
#
# done
#
-runbundles: \
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.commons.net;version='[3.3.0,3.3.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.openhab.binding.max;version='[2.5.9,2.5.10)',\
org.openhab.binding.max.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.max.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: MaxCube Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.max</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.max.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: MaxCube Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.max</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,12 @@
H:KEQ0565026,0b5951,0113,00000000,4eed6795,01,32,0f0113,0f34,03,0000
M:00,01,VgIFAQhiYWRrYW1lcgsNowIMU3R1ZGVlcmthbWVyB7bnAwlXb29ua2FtZXIL6aIEDFN6b25qYSBLYW1lcgjDSQUGWm9sZGVyCMHWCAILDaNLRVEwNTQ0MjQyEUJhZGthbWVyIFJhZGlhdG9yAQEHtudLRVEwMTQ1MTcyFVJhZGlhdG9yIFN0dWRlZXJrYW1lcgIDDhXMTEVRMDAxNTM0MBlXYWxsIFRoZXJtb3N0YXQgV29vbmthbWVyAwEL6aJLRVE5MDE1NDMyG1BsdWcgQWRhcHRlciBNdXVydmVyd2FybWluZwMFBDNvSkVRMDM4MDg3OBdFY28gU3dpdGNoIFN0dWRlZXJrYW1lcgAEDnX2S0VRMTEwNDM4MBpXaW5kb3cgU2Vuc29yIFN0dWRlZXJrYW1lcgIBCMNJS0VRMDY0ODk0ORJUaGVybW9zdGFhdCBTem9uamEEAQjB1ktFUTA2NDkzMTIRU3R1ZGVlcmthbWVyIElybWEFAQ==
C:0b5951,7QtZUQATAf9LRVEwNTY1MDI2AQsABEAAAAAAAAAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAsABEAAAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAGh0dHA6Ly9tYXguZXEtMy5kZTo4MC9jdWJlADAvbG9va3VwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENFVAAACgADAAAOEENFU1QAAwACAAAcIA==
C:0b0da3,0gsNowIBEABLRVEwNTQ0MjQyLCQ9CQcYAzAM/wBIYViRSP1ZFE0gTSBNIEUgRSBFIEUgRSBFIEhhWJFQ/VkVUSBRIFEgRSBFIEUgRSBFIEUgSFBYWkj+WRRNIE0gTSBFIEUgRSBFIEUgRSBIUFhaSP5ZFE0gTSBNIEUgRSBFIEUgRSBFIEhQWFpI/lkUTSBNIE0gRSBFIEUgRSBFIEUgSFBYWkj+WRRNIE0gTSBFIEUgRSBFIEUgRSBIUFhaSP5ZFE0gTSBNIEUgRSBFIEUgRSBFIA==
C:08c1d6,0gjB1gEFGP9LRVEwNjQ5MzEyKyE9CQcYAzAM/wBEeFUgVSBVIFUgVSBVIEUgRSBFIEUgRSBFIER4VRZFIEUgRSBFIEUgRSBFIEUgRSBFIEUgRFFEYkTkTQ9FIEUgRSBFIEUgRSBFIEUgRSBEUURiRORND0UgRSBFIEUgRSBFIEUgRSBFIERRRGJE5E0PRSBFIEUgRSBFIEUgRSBFIEUgRFFEYkTkTQ9FIEUgRSBFIEUgRSBFIEUgRSBEUURiRORRGEUgRSBFIEUgRSBFIEUgRSBFIA==
C:0e75f6,EQ519gQCEABLRVExMTA0Mzgw
C:04336f,EQQzbwUAEg9KRVEwMzgwODc4
C:0be9a2,0gvpogEDEapLRVE5MDE1NDMyKyE9CQcYAzAM/wBEbFkgWSBZIFkgWSBZIEUgRSBFIEUgRSBFIERgWSBZIFkgWSBZIFkgRSBFIEUgRSBFIEUgRFFUYUTYWSBZIFkgWSBFIEUgRSBFIEUgRSBEUVRhRNhZIFkgWSBZIEUgRSBFIEUgRSBFIERRVGFE2FkgWSBZIFkgRSBFIEUgRSBFIEUgRFFUYUTYWSBZIFkgWSBFIEUgRSBFIEUgRSBEUVRhRNhZIFkgWSBZIEUgRSBFIEUgRSBFIA==
C:0e15cc,zg4VzAMDEP9MRVEwMDE1MzQwKyE9CURsWSBZIFkgWSBZIFkgRSBFIEUgRSBFIEUgRGBZIFkgWSBZIFkgWSBFIEUgRSBFIEUgRSBEUVRhRNhZIFkgWSBZIEUgRSBFIEUgRSBFIERRVGFE2FkgWSBZIFkgRSBFIEUgRSBFIEUgRFFUYUTYWSBZIFkgWSBFIEUgRSBFIEUgRSBEUVRhRNhZIFkgWSBZIEUgRSBFIEUgRSBFIERRVGFE2FkgWSBZIFkgRSBFIEUgRSBFIEUgBxgw
C:08c349,0gjDSQEEGP9LRVEwNjQ4OTQ5KiA9CQcYAzAM/wBEbFUKRSBFIEUgRSBFIEUgRSBFIEUgRSBFIERgVQlFIEUgRSBFIEUgRSBFIEUgRSBFIEUgRFFUYUS0VQNFIEUgRSBFIEUgRSBFIEUgRSBEUVRhRLRVA0UgRSBFIEUgRSBFIEUgRSBFIERRVGFEtFUDRSBFIEUgRSBFIEUgRSBFIEUgRFFUYUS0VQNFIEUgRSBFIEUgRSBFIEUgRSBEUVRhRLRVCkUgRSBFIEUgRSBFIEUgRSBFIA==
C:07b6e7,0ge25wECGP9LRVEwMTQ1MTcyKyE9CQcYAzAM/wBEflUaRSBFIEUgRSBFIEUgRSBFIEUgRSBFIER+VRpFIEUgRSBFIEUgRSBFIEUgRSBFIEUgRFRUcEjSVRJJIEkgSSBFIEUgRSBFIEUgRSBEVFRwSNJVEkkgSSBJIEUgRSBFIEUgRSBFIERUVG9U01URSSBJIEkgRSBFIEUgRSBFIEUgRFRUcEjSVRJJIEkgSSBFIEUgRSBFIEUgRSBEVFRwSNJVEkkgSSBJIEUgRSBFIEUgRSBFIA==
L:CwsNowkSGE0kALAACwjB1gkSGGAiAAAABg519gkSEAYEM28JAAALC+miCRIYACIAAAAMDhXMCRIYBCIAAACtCwjDSQkSGGQqAGMACwe25wkSGGQkAAAA

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.max.test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
import static org.openhab.binding.max.internal.MaxBindingConstants.*;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.max.internal.MaxBindingConstants;
import org.openhab.binding.max.internal.handler.MaxCubeBridgeHandler;
/**
* Tests for {@link MaxCubeBridgeHandler}.
*
* @author Marcel Verpaalen - Initial contribution
* @author Wouter Born - Migrate Groovy to Java tests
*/
public class MaxCubeBridgeHandlerOSGiTest extends JavaOSGiTest {
private static final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, BRIDGE_MAXCUBE);
private ThingRegistry thingRegistry;
private VolatileStorageService volatileStorageService = new VolatileStorageService();
private Bridge maxBridge;
@Before
public void setUp() {
registerService(volatileStorageService);
thingRegistry = getService(ThingRegistry.class);
assertThat(thingRegistry, is(notNullValue()));
}
@After
public void tearDown() {
if (maxBridge != null) {
thingRegistry.remove(maxBridge.getUID());
// TODO: Due to synchronization issues the handler is currently not unset. To be fixed by PR #1789.
// waitForAssert(() -> assertThat(maxBridge.getHandler(), is(nullValue())));
}
unregisterService(volatileStorageService);
}
@Test
public void maxCubeBridgeHandlerIsCreated() {
MaxCubeBridgeHandler maxBridgeHandler = getService(ThingHandler.class, MaxCubeBridgeHandler.class);
assertThat(maxBridgeHandler, is(nullValue()));
Configuration configuration = new Configuration();
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "KEQ0565026");
configuration.put(MaxBindingConstants.PROPERTY_IP_ADDRESS, "192.168.3.100");
ThingUID cubeUid = new ThingUID(BRIDGE_THING_TYPE_UID, "testCube");
maxBridge = BridgeBuilder.create(BRIDGE_THING_TYPE_UID, cubeUid).withConfiguration(configuration).build();
thingRegistry.add(maxBridge);
// wait for MaxCubeBridgeHandler to be registered
waitForAssert(() -> assertThat(maxBridge.getHandler(), is(notNullValue())));
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.modbus.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab2-addons

View File

@@ -0,0 +1,74 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.modbus
-runrequires: \
bnd.identity;id='org.openhab.binding.modbus.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml',\
bnd.identity;id='ch.qos.logback.core',\
bnd.identity;id='ch.qos.logback.classic'
# 1) We would like to use the "volatile" storage only, drop other storage
# 2) We use logback (see logback.xml), drop others logging implementation
# to avoid conflicts
-runblacklist: \
bnd.identity;id='org.openhab.core.storage.json',\
bnd.identity;id='org.openhab.core.storage.mapdb',\
bnd.identity;id='slf4j.simple',\
bnd.identity;id='org.apache.log4j'
-runproperties: \
logback.configurationFile=file:${.}/logback.xml
#
# done
#
-runbundles: \
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
com.google.gson;version='[2.8.2,2.8.3)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
com.neuronrobotics.nrjavaserial;version='[3.15.0,3.15.1)',\
org.openhab.core.transform;version='[2.5.0,2.5.1)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.openhab.binding.modbus;version='[2.5.9,2.5.10)',\
org.openhab.binding.modbus.tests;version='[2.5.9,2.5.10)',\
org.openhab.io.transport.modbus;version='[2.5.9,2.5.10)',\
org.apache.commons.commons-pool2;version='[2.8.1,2.8.2)'

View File

@@ -0,0 +1,19 @@
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.openhab.binding.modbus" level="trace" />
<logger name="org.openhab.binding.modbus.tests" level="trace" />
<logger name="org.openhab.io.transport.modbus" level="trace" />
<logger name="net.wimpi.modbus" level="trace" />
<logger name="org.openhab.core.internal.items.ItemUpdater" level="trace" />
<logger name="org.openhab.core.internal.items.ItemStateConverterImpl" level="debug" />
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.modbus.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Modbus Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.modbus</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.transport.modbus</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.modbus.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Modbus Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.modbus</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.transport.modbus</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.modbus.tests;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemProvider;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.ManagedItemProvider;
import org.openhab.core.items.events.ItemStateEvent;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingProvider;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.ItemChannelLinkProvider;
import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
import org.openhab.core.transform.TransformationService;
import org.openhab.core.types.State;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.modbus.internal.ModbusHandlerFactory;
import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
import org.openhab.io.transport.modbus.ModbusManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractModbusOSGiTest extends JavaOSGiTest {
private static class StateSubscriber implements EventSubscriber {
private final Logger logger = LoggerFactory.getLogger(StateSubscriber.class);
public Map<String, List<State>> stateUpdates = new HashMap<>();
@Override
public Set<@NonNull String> getSubscribedEventTypes() {
return Collections.singleton(ItemStateEvent.TYPE);
}
@Override
public @Nullable EventFilter getEventFilter() {
return null;
}
@Override
public void receive(Event event) {
// Expecting only state updates in the tests
assertThat(event, is(instanceOf(ItemStateEvent.class)));
ItemStateEvent stateEvent = (ItemStateEvent) event;
logger.trace("Captured event: {} of type {}. Payload: {}", event,
stateEvent.getItemState().getClass().getSimpleName(), event.getPayload());
stateUpdates.computeIfAbsent(stateEvent.getItemName(), (item) -> new ArrayList<>())
.add(stateEvent.getItemState());
}
}
private final Logger logger = LoggerFactory.getLogger(AbstractModbusOSGiTest.class);
@Mock
protected @NonNullByDefault({}) ModbusManager mockedModbusManager;
protected @NonNullByDefault({}) ManagedThingProvider thingProvider;
protected @NonNullByDefault({}) ManagedItemProvider itemProvider;
protected @NonNullByDefault({}) ManagedItemChannelLinkProvider itemChannelLinkProvider;
protected @NonNullByDefault({}) ItemRegistry itemRegistry;
protected @NonNullByDefault({}) CoreItemFactory coreItemFactory;
private @NonNullByDefault({}) ModbusManager realModbusManager;
private Set<Item> addedItems = new HashSet<>();
private Set<Thing> addedThings = new HashSet<>();
private Set<ItemChannelLink> addedLinks = new HashSet<>();
private StateSubscriber stateSubscriber = new StateSubscriber();
@Mock
protected @NonNullByDefault({}) ModbusCommunicationInterface comms;
public AbstractModbusOSGiTest() {
super();
}
/**
* Before each test, configure mocked services
*/
@Before
public void setUpAbstractModbusOSGiTest() {
logger.debug("setUpAbstractModbusOSGiTest BEGIN");
registerVolatileStorageService();
registerService(mockedModbusManager);
registerService(stateSubscriber);
swapModbusManagerToMocked();
thingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
assertThat("Could not get ManagedThingProvider", thingProvider, is(notNullValue()));
itemProvider = getService(ItemProvider.class, ManagedItemProvider.class);
assertThat("Could not get ManagedItemProvider", itemProvider, is(notNullValue()));
itemChannelLinkProvider = getService(ItemChannelLinkProvider.class, ManagedItemChannelLinkProvider.class);
assertThat("Could not get ManagedItemChannelLinkProvider", itemChannelLinkProvider, is(notNullValue()));
itemRegistry = getService(ItemRegistry.class);
assertThat("Could not get ItemRegistry", itemRegistry, is(notNullValue()));
coreItemFactory = new CoreItemFactory();
// Clean slate for all tests
reset(mockedModbusManager);
stateSubscriber.stateUpdates.clear();
logger.debug("setUpAbstractModbusOSGiTest END");
}
@After
public void tearDownAbstractModbusOSGiTest() {
logger.debug("tearDownAbstractModbusOSGiTest BEGIN");
swapModbusManagerToReal();
for (Item item : addedItems) {
assertNotNull(itemProvider.remove(item.getName()));
}
for (Thing thing : addedThings) {
disposeThing(thing);
}
for (ItemChannelLink link : addedLinks) {
logger.debug("Unlinking {} <-> {}", link.getItemName(), link.getLinkedUID());
assertNotNull(itemChannelLinkProvider.remove(link.getUID()));
}
logger.debug("tearDownAbstractModbusOSGiTest END");
}
protected void addThing(Thing thing) {
assertThat(addedThings.contains(thing), not(equalTo(true)));
ThingHandler mockHandler = thing.getHandler();
if (mockHandler != null) {
// If there is a handler attached to fresh thing, it should be mocked (this pattern is used with some tests)
assertThat(Mockito.mockingDetails(thing.getHandler()).isMock(), is(equalTo(true)));
}
thingProvider.add(thing);
waitForAssert(() -> assertThat(thing.getHandler(), notNullValue()));
assertThat(thing.getConfiguration(), is(notNullValue()));
addedThings.add(thing);
if (mockHandler != null) {
// Re-attach mock handler
ThingHandler realHandlerInitedByCore = thing.getHandler();
assertNotNull(realHandlerInitedByCore);
assertNotSame(realHandlerInitedByCore, mockHandler);
realHandlerInitedByCore.dispose();
thing.setHandler(mockHandler);
}
}
protected void disposeThing(Thing thing) {
thingProvider.remove(thing.getUID());
}
protected void addItem(Item item) {
assertThat(addedItems.contains(item), not(equalTo(true)));
itemProvider.add(item);
addedItems.add(item);
}
protected void linkItem(String itemName, ChannelUID channelUID) {
logger.debug("Linking {} <-> {}", itemName, channelUID);
ItemChannelLink link = new ItemChannelLink(itemName, channelUID);
assertThat(addedLinks.contains(link), not(equalTo(true)));
itemChannelLinkProvider.add(link);
addedLinks.add(link);
}
protected List<State> getStateUpdates(String itemName) {
return stateSubscriber.stateUpdates.get(itemName);
}
protected void mockTransformation(String name, TransformationService service) {
Dictionary<String, Object> params = new Hashtable<>();
params.put("smarthome.transform", name);
registerService(service, params);
}
protected void mockCommsToModbusManager() {
assert comms != null;
doReturn(comms).when(mockedModbusManager).newModbusCommunicationInterface(any(), any());
}
protected void swapModbusManagerToMocked() {
assertNull(realModbusManager);
realModbusManager = getService(ModbusManager.class);
assertThat("Could not get ModbusManager", realModbusManager, is(notNullValue()));
assertThat("Could not get ModbusManagerImpl", realModbusManager.getClass().getSimpleName(),
is(equalTo("ModbusManagerImpl")));
assertNotNull(realModbusManager);
ModbusHandlerFactory modbusHandlerFactory = getService(ThingHandlerFactory.class, ModbusHandlerFactory.class);
assertThat("Could not get ModbusHandlerFactory", modbusHandlerFactory, is(notNullValue()));
assertNotNull(modbusHandlerFactory);
modbusHandlerFactory.unsetModbusManager(realModbusManager);
modbusHandlerFactory.setModbusManager(mockedModbusManager);
}
protected void swapModbusManagerToReal() {
assertNotNull(realModbusManager);
ModbusHandlerFactory modbusHandlerFactory = getService(ThingHandlerFactory.class, ModbusHandlerFactory.class);
assertThat("Could not get ModbusHandlerFactory", modbusHandlerFactory, is(notNullValue()));
assertNotNull(modbusHandlerFactory);
modbusHandlerFactory.unsetModbusManager(mockedModbusManager);
modbusHandlerFactory.setModbusManager(realModbusManager);
}
}

View File

@@ -0,0 +1,855 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.modbus.tests;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicReference;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.openhab.binding.modbus.handler.ModbusPollerThingHandler;
import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal;
import org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler;
import org.openhab.io.transport.modbus.AsyncModbusFailure;
import org.openhab.io.transport.modbus.AsyncModbusReadResult;
import org.openhab.io.transport.modbus.BitArray;
import org.openhab.io.transport.modbus.ModbusConstants;
import org.openhab.io.transport.modbus.ModbusFailureCallback;
import org.openhab.io.transport.modbus.ModbusReadCallback;
import org.openhab.io.transport.modbus.ModbusReadFunctionCode;
import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.ModbusRegisterArray;
import org.openhab.io.transport.modbus.PollTask;
import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
import org.openhab.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Sami Salonen - Initial contribution
*/
@RunWith(MockitoJUnitRunner.class)
public class ModbusPollerThingHandlerTest extends AbstractModbusOSGiTest {
private static final String HOST = "thisishost";
private static final int PORT = 44;
private final Logger logger = LoggerFactory.getLogger(ModbusPollerThingHandlerTest.class);
private Bridge endpoint;
private Bridge poller;
@Mock
private ThingHandlerCallback thingCallback;
public static BridgeBuilder createTcpThingBuilder(String id) {
return BridgeBuilder
.create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP,
new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP, id))
.withLabel("label for " + id);
}
public static BridgeBuilder createPollerThingBuilder(String id) {
return BridgeBuilder
.create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER,
new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER, id))
.withLabel("label for " + id);
}
/**
* Verify that basic poller <-> endpoint interaction has taken place (on poller init)
*/
private void verifyEndpointBasicInitInteraction() {
verify(mockedModbusManager).newModbusCommunicationInterface(any(), any());
}
public ModbusReadCallback getPollerCallback(ModbusPollerThingHandler handler)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
callbackField.setAccessible(true);
return (ModbusReadCallback) callbackField.get(handler);
}
public ModbusFailureCallback<ModbusReadRequestBlueprint> getPollerFailureCallback(ModbusPollerThingHandler handler)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
callbackField.setAccessible(true);
return (ModbusFailureCallback<ModbusReadRequestBlueprint>) callbackField.get(handler);
}
/**
* Before each test, setup TCP endpoint thing, configure mocked item registry
*/
@Before
public void setUp() {
mockCommsToModbusManager();
Configuration tcpConfig = new Configuration();
tcpConfig.put("host", HOST);
tcpConfig.put("port", PORT);
tcpConfig.put("id", 9);
endpoint = createTcpThingBuilder("tcpendpoint").withConfiguration(tcpConfig).build();
addThing(endpoint);
assertThat(endpoint.getStatus(), is(equalTo(ThingStatus.ONLINE)));
}
@After
public void tearDown() {
if (endpoint != null) {
thingProvider.remove(endpoint.getUID());
}
if (poller != null) {
thingProvider.remove(poller.getUID());
}
}
@Test
public void testInitializeNonPolling()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L); // 0 -> non polling
pollerConfig.put("start", 5);
pollerConfig.put("length", 9);
pollerConfig.put("type", ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
logger.info("Poller created, registering to registry...");
addThing(poller);
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
logger.info("Poller registered");
verifyEndpointBasicInitInteraction();
// polling is _not_ setup
verifyNoMoreInteractions(mockedModbusManager);
}
private void testPollerLengthCheck(String type, int length, boolean expectedOnline) {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", length);
pollerConfig.put("type", type);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
assertThat(poller.getStatus(), is(equalTo(expectedOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE)));
if (!expectedOnline) {
assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
}
verifyEndpointBasicInitInteraction();
verifyNoMoreInteractions(mockedModbusManager);
}
@Test
public void testPollerWithMaxRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER,
ModbusConstants.MAX_REGISTERS_READ_COUNT, true);
}
@Test
public void testPollerLengthOutOfBoundsWithRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER,
ModbusConstants.MAX_REGISTERS_READ_COUNT + 1, false);
}
@Test
public void testPollerWithMaxInputRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_INPUT_REGISTER,
ModbusConstants.MAX_REGISTERS_READ_COUNT, true);
}
@Test
public void testPollerLengthOutOfBoundsWithInputRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_INPUT_REGISTER,
ModbusConstants.MAX_REGISTERS_READ_COUNT + 1, false);
}
@Test
public void testPollerWithMaxCoils()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_COIL, ModbusConstants.MAX_BITS_READ_COUNT, true);
}
@Test
public void testPollerLengthOutOfBoundsWithCoils()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_COIL, ModbusConstants.MAX_BITS_READ_COUNT + 1,
false);
}
@Test
public void testPollerWithMaxDiscreteInput()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_DISCRETE_INPUT,
ModbusConstants.MAX_BITS_READ_COUNT, true);
}
@Test
public void testPollerLengthOutOfBoundsWithDiscreteInput()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_DISCRETE_INPUT,
ModbusConstants.MAX_BITS_READ_COUNT + 1, false);
}
public void testPollingGeneric(String type, ModbusReadFunctionCode expectedFunctionCode)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", type);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
assertThat(poller.getStatusInfo().toString(), poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verifyEndpointBasicInitInteraction();
verify(mockedModbusManager).newModbusCommunicationInterface(argThat(new TypeSafeMatcher<ModbusSlaveEndpoint>() {
@Override
public void describeTo(Description description) {
description.appendText("correct endpoint (");
}
@Override
protected boolean matchesSafely(ModbusSlaveEndpoint endpoint) {
return checkEndpoint(endpoint);
}
}), any());
verify(comms).registerRegularPoll(argThat(new TypeSafeMatcher<ModbusReadRequestBlueprint>() {
@Override
public void describeTo(Description description) {
description.appendText("correct request");
}
@Override
protected boolean matchesSafely(ModbusReadRequestBlueprint request) {
return checkRequest(request, expectedFunctionCode);
}
}), eq(150l), eq(0L), notNull(), notNull());
verifyNoMoreInteractions(mockedModbusManager);
}
@SuppressWarnings("null")
private boolean checkEndpoint(ModbusSlaveEndpoint endpointParam) {
return endpointParam.equals(new ModbusTCPSlaveEndpoint(HOST, PORT));
}
private boolean checkRequest(ModbusReadRequestBlueprint request, ModbusReadFunctionCode functionCode) {
return request.getDataLength() == 13 && request.getFunctionCode() == functionCode
&& request.getProtocolID() == 0 && request.getReference() == 5 && request.getUnitID() == 9;
}
@Test
public void testInitializePollingWithCoils()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollingGeneric("coil", ModbusReadFunctionCode.READ_COILS);
}
@Test
public void testInitializePollingWithDiscrete()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollingGeneric("discrete", ModbusReadFunctionCode.READ_INPUT_DISCRETES);
}
@Test
public void testInitializePollingWithInputRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollingGeneric("input", ModbusReadFunctionCode.READ_INPUT_REGISTERS);
}
@Test
public void testInitializePollingWithHoldingRegisters()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
testPollingGeneric("holding", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
}
@Test
public void testPollUnregistrationOnDispose()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
// verify registration
final AtomicReference<ModbusReadCallback> callbackRef = new AtomicReference<>();
verify(mockedModbusManager).newModbusCommunicationInterface(argThat(new TypeSafeMatcher<ModbusSlaveEndpoint>() {
@Override
public void describeTo(Description description) {
description.appendText("correct endpoint");
}
@Override
protected boolean matchesSafely(ModbusSlaveEndpoint endpoint) {
return checkEndpoint(endpoint);
}
}), any());
verify(comms).registerRegularPoll(argThat(new TypeSafeMatcher<ModbusReadRequestBlueprint>() {
@Override
public void describeTo(Description description) {
}
@Override
protected boolean matchesSafely(ModbusReadRequestBlueprint request) {
return checkRequest(request, ModbusReadFunctionCode.READ_COILS);
}
}), eq(150l), eq(0L), argThat(new TypeSafeMatcher<ModbusReadCallback>() {
@Override
public void describeTo(Description description) {
}
@Override
protected boolean matchesSafely(ModbusReadCallback callback) {
callbackRef.set(callback);
return true;
}
}), notNull());
verifyNoMoreInteractions(mockedModbusManager);
// reset call counts for easy assertions
reset(mockedModbusManager);
// remove the thing
disposeThing(poller);
// 1) should first unregister poll task
verify(comms).unregisterRegularPoll(eq(pollTask));
verifyNoMoreInteractions(mockedModbusManager);
}
@Test
public void testInitializeWithNoBridge()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.BRIDGE_OFFLINE)));
verifyNoMoreInteractions(mockedModbusManager);
}
@Test
public void testInitializeWithOfflineBridge()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
endpoint.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ""));
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.BRIDGE_OFFLINE)));
verifyNoMoreInteractions(mockedModbusManager);
}
@Test
public void testRegistersPassedToChildDataThings()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ArgumentCaptor<ModbusReadCallback> callbackCapturer = ArgumentCaptor.forClass(ModbusReadCallback.class);
verify(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), callbackCapturer.capture(), notNull());
ModbusReadCallback readCallback = callbackCapturer.getValue();
assertNotNull(readCallback);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
// has one data child
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
// two children (one child initialized)
thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
reset(child2);
// one child disposed
thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
}
@Test
public void testBitsPassedToChildDataThings()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ArgumentCaptor<ModbusReadCallback> callbackCapturer = ArgumentCaptor.forClass(ModbusReadCallback.class);
verify(comms).registerRegularPoll(any(), eq(150l), eq(0L), callbackCapturer.capture(), notNull());
ModbusReadCallback readCallback = callbackCapturer.getValue();
assertNotNull(readCallback);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
BitArray bits = Mockito.mock(BitArray.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, bits);
// has one data child
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
// two children (one child initialized)
thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
reset(child2);
// one child disposed
thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
}
@Test
public void testErrorPassedToChildDataThings()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
final ArgumentCaptor<ModbusFailureCallback<ModbusReadRequestBlueprint>> callbackCapturer = ArgumentCaptor
.forClass((Class) ModbusFailureCallback.class);
verify(comms).registerRegularPoll(any(), eq(150l), eq(0L), notNull(), callbackCapturer.capture());
ModbusFailureCallback<ModbusReadRequestBlueprint> readCallback = callbackCapturer.getValue();
assertNotNull(readCallback);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
Exception error = Mockito.mock(Exception.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
AsyncModbusFailure<ModbusReadRequestBlueprint> result = new AsyncModbusFailure<ModbusReadRequestBlueprint>(
request, error);
// has one data child
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).handleReadError(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
// two children (one child initialized)
thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).handleReadError(result);
verify(child2).handleReadError(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
reset(child2);
// one child disposed
thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child2).handleReadError(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
}
@Test
public void testRefresh()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
thingHandler.refresh();
verify(comms).submitOneTimePoll(any(), any(), any());
}
/**
* When there's no recently received data, refresh() will re-use that instead
*
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws NoSuchFieldException
* @throws SecurityException
*/
@Test
public void testRefreshWithPreviousData()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 10000L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
pollerReadCallback.handle(result);
// data child receives the data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
reset(child1);
// call refresh
// cache is still valid, we should not have real data poll this time
thingHandler.refresh();
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data child receives the cached data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
}
/**
* When there's no recently received data, refresh() will re-use that instead
*
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws NoSuchFieldException
* @throws SecurityException
*/
@Test
public void testRefreshWithPreviousDataCacheDisabled()
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 0L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
pollerReadCallback.handle(result);
// data child receives the data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
reset(child1);
// call refresh
// caching disabled, should poll from manager
thingHandler.refresh();
verify(comms).submitOneTimePoll(any(), any(), any());
verifyNoMoreInteractions(mockedModbusManager);
// data child receives the cached data
verifyNoMoreInteractions(child1);
}
/**
* Testing again caching, such that most recently received data is propagated to children
*
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws NoSuchFieldException
* @throws SecurityException
* @throws InterruptedException
*/
@Test
public void testRefreshWithPreviousData2() throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException, InterruptedException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 10000L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback = getPollerFailureCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusReadRequestBlueprint request2 = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
Exception error = Mockito.mock(Exception.class);
AsyncModbusReadResult registersResult = new AsyncModbusReadResult(request, registers);
AsyncModbusFailure<ModbusReadRequestBlueprint> errorResult = new AsyncModbusFailure<ModbusReadRequestBlueprint>(
request2, error);
pollerReadCallback.handle(registersResult);
// data child should receive the data
verify(child1).onReadResult(registersResult);
verifyNoMoreInteractions(child1);
reset(child1);
// Sleep to have time between the data
Thread.sleep(5L);
// error is received
failureCallback.handle(errorResult);
// data child should receive the error
verify(child1).handleReadError(errorResult);
verifyNoMoreInteractions(child1);
reset(child1);
// call refresh, should return latest data (that is, error)
// cache is still valid, we should not have real data poll this time
thingHandler.refresh();
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data child receives the cached error
verify(child1).handleReadError(errorResult);
verifyNoMoreInteractions(child1);
}
@Test
public void testRefreshWithOldPreviousData() throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException, InterruptedException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 10L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
.build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
pollerReadCallback.handle(result);
// data child should receive the data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
reset(child1);
// Sleep to ensure cache expiry
Thread.sleep(15L);
// call refresh. Since cache expired, will poll for more
verify(comms, never()).submitOneTimePoll(any(), any(), any());
thingHandler.refresh();
verify(comms).submitOneTimePoll(any(), any(), any());
}
}

View File

@@ -0,0 +1,185 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.modbus.tests;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Objects;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal;
import org.openhab.binding.modbus.internal.handler.ModbusTcpThingHandler;
import org.openhab.io.transport.modbus.endpoint.EndpointPoolConfiguration;
import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
import org.openhab.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint;
/**
* @author Sami Salonen - Initial contribution
*/
@RunWith(MockitoJUnitRunner.class)
public class ModbusTcpThingHandlerTest extends AbstractModbusOSGiTest {
private static BridgeBuilder createTcpThingBuilder(String id) {
return BridgeBuilder.create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP,
new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP, id));
}
@Test
public void testInitializeAndSlaveEndpoint() throws EndpointNotInitializedException {
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("id", 9);
thingConfig.put("timeBetweenTransactionsMillis", 1);
thingConfig.put("timeBetweenReconnectMillis", 2);
thingConfig.put("connectMaxTries", 3);
thingConfig.put("reconnectAfterMillis", 4);
thingConfig.put("connectTimeoutMillis", 5);
EndpointPoolConfiguration expectedPoolConfiguration = new EndpointPoolConfiguration();
expectedPoolConfiguration.setConnectMaxTries(3);
expectedPoolConfiguration.setConnectTimeoutMillis(5);
expectedPoolConfiguration.setInterConnectDelayMillis(2);
expectedPoolConfiguration.setInterTransactionDelayMillis(1);
expectedPoolConfiguration.setReconnectAfterMillis(4);
Bridge thing = createTcpThingBuilder("tcpendpoint").withConfiguration(thingConfig).build();
addThing(thing);
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler);
ModbusSlaveEndpoint slaveEndpoint = thingHandler.getEndpoint();
assertThat(slaveEndpoint, is(equalTo(new ModbusTCPSlaveEndpoint("thisishost", 44))));
assertThat(thingHandler.getSlaveId(), is(9));
InOrder orderedVerify = Mockito.inOrder(mockedModbusManager);
ModbusSlaveEndpoint endpoint = thingHandler.getEndpoint();
Objects.requireNonNull(endpoint);
orderedVerify.verify(mockedModbusManager).newModbusCommunicationInterface(endpoint, expectedPoolConfiguration);
}
@Test
public void testTwoDifferentEndpointWithDifferentParameters() {
// thing1
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 1);
final Bridge thing = createTcpThingBuilder("tcpendpoint").withConfiguration(thingConfig).build();
addThing(thing);
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler);
}
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 45);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 100);
final Bridge thing = createTcpThingBuilder("tcpendpoint2").withConfiguration(thingConfig).build();
addThing(thing);
// Different endpoint (port 45), so should be OK even though timeBetweenTransactionsMillis is different
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler);
}
}
@Test
public void testTwoIdenticalEndpointWithDifferentParameters() {
// Real implementation needed to validate this behaviour
swapModbusManagerToReal();
// thing1
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 1);
final Bridge thing = createTcpThingBuilder("tcpendpoint").withConfiguration(thingConfig).build();
addThing(thing);
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler);
}
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 100);
final Bridge thing = createTcpThingBuilder("tcpendpoint2").withConfiguration(thingConfig).build();
addThing(thing);
assertThat(thing.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
assertThat(thing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
}
}
@Test
public void testTwoIdenticalEndpointWithSameParameters() {
// Real implementation needed to validate this behaviour
swapModbusManagerToReal();
// thing1
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 1);
final Bridge thing = createTcpThingBuilder("tcpendpoint").withConfiguration(thingConfig).build();
addThing(thing);
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler);
}
{
Configuration thingConfig = new Configuration();
thingConfig.put("host", "thisishost");
thingConfig.put("port", 44);
thingConfig.put("connectMaxTries", 1);
thingConfig.put("timeBetweenTransactionsMillis", 1);
thingConfig.put("connectTimeoutMillis", 10000); // default
final Bridge thing = createTcpThingBuilder("tcpendpoint2").withConfiguration(thingConfig).build();
addThing(thing);
// Same endpoint and same parameters -> should not affect this thing
assertThat(thing.getStatus(), is(equalTo(ThingStatus.ONLINE)));
}
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.mqtt.homeassistant.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,74 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.mqtt.homeassistant
-runrequires: \
bnd.identity;id='org.openhab.binding.mqtt.homeassistant.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml',\
bnd.identity;id='org.openhab.io.mqttembeddedbroker'
#
# done
#
-runbundles: \
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.collections;version='[3.2.1,3.2.2)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\
org.eclipse.paho.client.mqttv3;version='[1.2.1,1.2.2)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.binding.mqtt;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.generic;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homeassistant;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homeassistant.tests;version='[2.5.2,2.5.3)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.io.transport.mqtt;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.transform;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
com.h2database.mvstore;version='[1.4.199,1.4.200)',\
io.netty.buffer;version='[4.1.42,4.1.43)',\
io.netty.codec;version='[4.1.42,4.1.43)',\
io.netty.codec-mqtt;version='[4.1.42,4.1.43)',\
io.netty.common;version='[4.1.42,4.1.43)',\
io.netty.handler;version='[4.1.42,4.1.43)',\
io.netty.resolver;version='[4.1.42,4.1.43)',\
io.netty.transport;version='[4.1.42,4.1.43)',\
org.openhab.io.mqttembeddedbroker;version='[2.5.0,2.5.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)'
-runvm: -Dio.netty.noUnsafe=true

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mqtt.homeassistant.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: MQTT HomeAssistant Tests</name>
<properties>
<netty.version>4.1.42.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.generic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.homeassistant</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.mqttembeddedbroker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.j-n-k</groupId>
<artifactId>moquette-broker</artifactId>
<version>0.13.0.OH2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2-mvstore</artifactId>
<version>1.4.199</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-mqtt</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
import org.openhab.binding.mqtt.homeassistant.internal.ChannelConfigurationTypeAdapterFactory;
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Tests the {@link DiscoverComponents} class.
*
* @author David Graeff - Initial contribution
*/
public class DiscoverComponentsTest extends JavaOSGiTest {
@Mock
MqttBrokerConnection connection;
@Mock
ComponentDiscovered discovered;
@Mock
TransformationServiceProvider transformationServiceProvider;
@Mock
ChannelStateUpdateListener channelStateUpdateListener;
@Mock
AvailabilityTracker availabilityTracker;
@Before
public void setUp() {
initMocks(this);
CompletableFuture<Void> voidFutureComplete = new CompletableFuture<>();
voidFutureComplete.complete(null);
doReturn(voidFutureComplete).when(connection).unsubscribeAll();
doReturn(CompletableFuture.completedFuture(true)).when(connection).subscribe(any(), any());
doReturn(CompletableFuture.completedFuture(true)).when(connection).unsubscribe(any(), any());
doReturn(CompletableFuture.completedFuture(true)).when(connection).publish(any(), any());
doReturn(CompletableFuture.completedFuture(true)).when(connection).publish(any(), any(), anyInt(),
anyBoolean());
doReturn(null).when(transformationServiceProvider).getTransformationService(any());
}
@Test
public void discoveryTimeTest() throws InterruptedException, ExecutionException, TimeoutException {
// Create a scheduler
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.testHomeAssistantThing,
scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider));
HandlerConfiguration config = new HandlerConfiguration("homeassistant",
Collections.singletonList("switch/object"));
Set<HaID> discoveryIds = new HashSet<>();
discoveryIds.addAll(HaID.fromConfig(config));
discover.startDiscovery(connection, 50, discoveryIds, discovered).get(100, TimeUnit.MILLISECONDS);
}
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.io.transport.mqtt.MqttServiceObserver;
import org.openhab.io.mqttembeddedbroker.Constants;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device
* tree.
*
* @author David Graeff - Initial contribution
*/
@NonNullByDefault
public class EmbeddedBrokerTools {
public @Nullable MqttBrokerConnection embeddedConnection = null;
/**
* Request the embedded broker connection from the {@link MqttService} and wait for a connection to be established.
*
* @throws InterruptedException
*/
public MqttBrokerConnection waitForConnection(MqttService mqttService) throws InterruptedException {
embeddedConnection = mqttService.getBrokerConnection(Constants.CLIENTID);
if (embeddedConnection == null) {
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttServiceObserver observer = new MqttServiceObserver() {
@Override
public void brokerAdded(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
if (brokerID.equals(Constants.CLIENTID)) {
embeddedConnection = broker;
semaphore.release();
}
}
@Override
public void brokerRemoved(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
}
};
mqttService.addBrokersListener(observer);
assertTrue("Wait for embedded connection client failed", semaphore.tryAcquire(1000, TimeUnit.MILLISECONDS));
}
MqttBrokerConnection embeddedConnection = this.embeddedConnection;
if (embeddedConnection == null) {
throw new IllegalStateException();
}
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttConnectionObserver mqttConnectionObserver = (state, error) -> {
if (state == MqttConnectionState.CONNECTED) {
semaphore.release();
}
};
embeddedConnection.addConnectionObserver(mqttConnectionObserver);
if (embeddedConnection.connectionState() == MqttConnectionState.CONNECTED) {
semaphore.release();
}
assertTrue("Connection " + embeddedConnection.getClientId() + " failed. State: "
+ embeddedConnection.connectionState(), semaphore.tryAcquire(500, TimeUnit.MILLISECONDS));
return embeddedConnection;
}
}

View File

@@ -0,0 +1,217 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.core.util.UIDUtils;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
import org.openhab.binding.mqtt.generic.TransformationServiceProvider;
import org.openhab.binding.mqtt.homeassistant.internal.AbstractComponent;
import org.openhab.binding.mqtt.homeassistant.internal.ChannelConfigurationTypeAdapterFactory;
import org.openhab.binding.mqtt.homeassistant.internal.ComponentSwitch;
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents;
import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered;
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device
* tree.
*
* @author David Graeff - Initial contribution
*/
public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest {
private MqttService mqttService;
private MqttBrokerConnection embeddedConnection;
private MqttBrokerConnection connection;
private int registeredTopics = 100;
private Throwable failure = null;
@Mock
ChannelStateUpdateListener channelStateUpdateListener;
@Mock
AvailabilityTracker availabilityTracker;
@Mock
TransformationServiceProvider transformationServiceProvider;
/**
* Create an observer that fails the test as soon as the broker client connection changes its connection state
* to something else then CONNECTED.
*/
private MqttConnectionObserver failIfChange = (state, error) -> assertThat(state,
is(MqttConnectionState.CONNECTED));
private String testObjectTopic;
@Before
public void setUp() throws InterruptedException, ExecutionException, TimeoutException, IOException {
registerVolatileStorageService();
initMocks(this);
mqttService = getService(MqttService.class);
// Wait for the EmbeddedBrokerService internal connection to be connected
embeddedConnection = new EmbeddedBrokerTools().waitForConnection(mqttService);
connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(),
embeddedConnection.isSecure(), "ha_mqtt");
connection.start().get(1000, TimeUnit.MILLISECONDS);
assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED));
// If the connection state changes in between -> fail
connection.addConnectionObserver(failIfChange);
// Create topic string and config for one example HA component (a Switch)
testObjectTopic = "homeassistant/switch/node/" + ThingChannelConstants.testHomeAssistantThing.getId();
final String config = "{'name':'testname','state_topic':'" + testObjectTopic + "/state','command_topic':'"
+ testObjectTopic + "/set'}";
// Publish component configurations and component states to MQTT
List<CompletableFuture<Boolean>> futures = new ArrayList<>();
futures.add(embeddedConnection.publish(testObjectTopic + "/config", config.getBytes()));
futures.add(embeddedConnection.publish(testObjectTopic + "/state", "true".getBytes()));
registeredTopics = futures.size();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(1000, TimeUnit.MILLISECONDS);
failure = null;
doReturn(null).when(transformationServiceProvider).getTransformationService(any());
}
@After
public void tearDown() throws InterruptedException, ExecutionException, TimeoutException {
if (connection != null) {
connection.removeConnectionObserver(failIfChange);
connection.stop().get(1000, TimeUnit.MILLISECONDS);
}
}
@Test
public void reconnectTest() throws InterruptedException, ExecutionException, TimeoutException {
connection.removeConnectionObserver(failIfChange);
connection.stop().get(2000, TimeUnit.MILLISECONDS);
connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(),
embeddedConnection.isSecure(), "ha_mqtt");
connection.start().get(2000, TimeUnit.MILLISECONDS);
}
@Test
public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException {
CountDownLatch c = new CountDownLatch(registeredTopics);
connection.subscribe("homeassistant/+/+/" + ThingChannelConstants.testHomeAssistantThing.getId() + "/#",
(topic, payload) -> c.countDown()).get(1000, TimeUnit.MILLISECONDS);
assertTrue("Connection " + connection.getClientId() + " not retrieving all topics",
c.await(1000, TimeUnit.MILLISECONDS));
}
@Test
public void parseHATree() throws InterruptedException, ExecutionException, TimeoutException {
MqttChannelTypeProvider channelTypeProvider = mock(MqttChannelTypeProvider.class);
final Map<String, AbstractComponent<?>> haComponents = new HashMap<>();
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.testHomeAssistantThing,
scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider));
// The DiscoverComponents object calls ComponentDiscovered callbacks.
// In the following implementation we add the found component to the `haComponents` map
// and add the types to the channelTypeProvider, like in the real Thing handler.
final CountDownLatch latch = new CountDownLatch(1);
ComponentDiscovered cd = (haID, c) -> {
haComponents.put(c.uid().getId(), c);
c.addChannelTypes(channelTypeProvider);
channelTypeProvider.setChannelGroupType(c.groupTypeUID(), c.type());
latch.countDown();
};
// Start the discovery for 500ms. Forced timeout after 1500ms.
HaID haID = new HaID(testObjectTopic + "/config");
CompletableFuture<Void> future = discover.startDiscovery(connection, 1000, Collections.singleton(haID), cd)
.thenRun(() -> {
}).exceptionally(e -> {
failure = e;
return null;
});
assertTrue(latch.await(1500, TimeUnit.MILLISECONDS));
future.get(800, TimeUnit.MILLISECONDS);
// No failure expected and one discovered result
assertNull(failure);
assertThat(haComponents.size(), is(1));
// For the switch component we should have one channel group type and one channel type
// setChannelGroupType is called once above
verify(channelTypeProvider, times(2)).setChannelGroupType(any(), any());
verify(channelTypeProvider, times(1)).setChannelType(any(), any());
String channelGroupId = UIDUtils
.encode("node_" + ThingChannelConstants.testHomeAssistantThing.getId() + "_switch");
State value = haComponents.get(channelGroupId).channelTypes().get(ComponentSwitch.switchChannelID).getState()
.getCache().getChannelState();
assertThat(value, is(UnDefType.UNDEF));
haComponents.values().stream().map(e -> e.start(connection, scheduler, 100))
.reduce(CompletableFuture.completedFuture(null), (a, v) -> a.thenCompose(b -> v)).exceptionally(e -> {
failure = e;
return null;
}).get();
// We should have received the retained value, while subscribing to the channels MQTT state topic.
verify(channelStateUpdateListener, timeout(1000).times(1)).updateChannelState(any(), any());
// Value should be ON now.
value = haComponents.get(channelGroupId).channelTypes().get(ComponentSwitch.switchChannelID).getState()
.getCache().getChannelState();
assertThat(value, is(OnOffType.ON));
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants.HOMEASSISTANT_MQTT_THING;
import org.openhab.core.thing.ThingUID;
/**
* Static test definitions, like thing, bridge and channel definitions
*
* @author David Graeff - Initial contribution
*/
public class ThingChannelConstants {
// Common ThingUID and ChannelUIDs
public static final ThingUID testHomeAssistantThing = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234");
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.mqtt.homie.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,73 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.mqtt.homie
-runrequires: \
bnd.identity;id='org.openhab.binding.mqtt.homie.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml',\
bnd.identity;id='org.openhab.io.mqttembeddedbroker'
#
# done
#
-runbundles: \
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.collections;version='[3.2.1,3.2.2)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\
org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\
org.eclipse.paho.client.mqttv3;version='[1.2.1,1.2.2)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.binding.mqtt;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.generic;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homie;version='[2.5.2,2.5.3)',\
org.openhab.binding.mqtt.homie.tests;version='[2.5.2,2.5.3)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.io.transport.mqtt;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.openhab.core.transform;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
com.h2database.mvstore;version='[1.4.199,1.4.200)',\
io.netty.buffer;version='[4.1.42,4.1.43)',\
io.netty.codec;version='[4.1.42,4.1.43)',\
io.netty.codec-mqtt;version='[4.1.42,4.1.43)',\
io.netty.common;version='[4.1.42,4.1.43)',\
io.netty.handler;version='[4.1.42,4.1.43)',\
io.netty.resolver;version='[4.1.42,4.1.43)',\
io.netty.transport;version='[4.1.42,4.1.43)',\
org.openhab.io.mqttembeddedbroker;version='[2.5.0,2.5.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)'
-runvm: -Dio.netty.noUnsafe=true

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.mqtt.homie.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: MQTT Homie Tests</name>
<properties>
<netty.version>4.1.42.Final</netty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.generic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mqtt.homie</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.io.mqttembeddedbroker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.j-n-k</groupId>
<artifactId>moquette-broker</artifactId>
<version>0.13.0.OH2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2-mvstore</artifactId>
<version>1.4.199</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-mqtt</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.io.transport.mqtt.MqttServiceObserver;
import org.openhab.io.mqttembeddedbroker.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device
* tree.
*
* @author David Graeff - Initial contribution
*/
@NonNullByDefault
public class EmbeddedBrokerTools {
private final Logger logger = LoggerFactory.getLogger(EmbeddedBrokerTools.class);
public @Nullable MqttBrokerConnection embeddedConnection = null;
/**
* Request the embedded broker connection from the {@link MqttService} and wait for a connection to be established.
*
* @throws InterruptedException
*/
public MqttBrokerConnection waitForConnection(MqttService mqttService) throws InterruptedException {
embeddedConnection = mqttService.getBrokerConnection(Constants.CLIENTID);
if (embeddedConnection == null) {
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttServiceObserver observer = new MqttServiceObserver() {
@Override
public void brokerAdded(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
if (brokerID.equals(Constants.CLIENTID)) {
embeddedConnection = broker;
semaphore.release();
}
}
@Override
public void brokerRemoved(@NonNull String brokerID, @NonNull MqttBrokerConnection broker) {
}
};
mqttService.addBrokersListener(observer);
assertTrue("Wait for embedded connection client failed", semaphore.tryAcquire(700, TimeUnit.MILLISECONDS));
}
MqttBrokerConnection embeddedConnection = this.embeddedConnection;
if (embeddedConnection == null) {
throw new IllegalStateException();
}
logger.warn("waitForConnection {}", embeddedConnection.connectionState());
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
MqttConnectionObserver mqttConnectionObserver = (state, error) -> {
if (state == MqttConnectionState.CONNECTED) {
semaphore.release();
}
};
embeddedConnection.addConnectionObserver(mqttConnectionObserver);
if (embeddedConnection.connectionState() == MqttConnectionState.CONNECTED) {
semaphore.release();
}
assertTrue("Connection " + embeddedConnection.getClientId() + " failed. State: "
+ embeddedConnection.connectionState(), semaphore.tryAcquire(500, TimeUnit.MILLISECONDS));
return embeddedConnection;
}
}

View File

@@ -0,0 +1,321 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.UnDefType;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttService;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.openhab.binding.mqtt.generic.ChannelState;
import org.openhab.binding.mqtt.generic.tools.ChildMap;
import org.openhab.binding.mqtt.generic.tools.WaitForTopicValue;
import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler;
import org.openhab.binding.mqtt.homie.internal.homie300.Device;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes.ReadyState;
import org.openhab.binding.mqtt.homie.internal.homie300.DeviceCallback;
import org.openhab.binding.mqtt.homie.internal.homie300.Node;
import org.openhab.binding.mqtt.homie.internal.homie300.NodeAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.Property;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum;
import org.openhab.binding.mqtt.homie.internal.homie300.PropertyHelper;
/**
* A full implementation test, that starts the embedded MQTT broker and publishes a homie device tree.
*
* @author David Graeff - Initial contribution
*/
public class HomieImplementationTest extends JavaOSGiTest {
private static final String BASE_TOPIC = "homie";
private static final String DEVICE_ID = ThingChannelConstants.testHomieThing.getId();
private static final String DEVICE_TOPIC = BASE_TOPIC + "/" + DEVICE_ID;
private MqttService mqttService;
private MqttBrokerConnection embeddedConnection;
private MqttBrokerConnection connection;
private int registeredTopics = 100;
// The handler is not tested here, so just mock the callback
@Mock
DeviceCallback callback;
// A handler mock is required to verify that channel value changes have been received
@Mock
HomieThingHandler handler;
private ScheduledExecutorService scheduler;
/**
* Create an observer that fails the test as soon as the broker client connection changes its connection state
* to something else then CONNECTED.
*/
private MqttConnectionObserver failIfChange = (state, error) -> assertThat(state,
is(MqttConnectionState.CONNECTED));
private String propertyTestTopic;
@Before
public void setUp() throws InterruptedException, ExecutionException, TimeoutException {
registerVolatileStorageService();
initMocks(this);
mqttService = getService(MqttService.class);
embeddedConnection = new EmbeddedBrokerTools().waitForConnection(mqttService);
embeddedConnection.setQos(1);
embeddedConnection.setRetain(true);
connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(),
embeddedConnection.isSecure(), "homie");
connection.setQos(1);
connection.setPersistencePath(Paths.get("subconn"));
connection.start().get(500, TimeUnit.MILLISECONDS);
assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED));
// If the connection state changes in between -> fail
connection.addConnectionObserver(failIfChange);
List<CompletableFuture<Boolean>> futures = new ArrayList<>();
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$homie", "3.0".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$name", "Name".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$state", "ready".getBytes()));
futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$nodes", "testnode".getBytes()));
// Add homie node topics
final String testNode = DEVICE_TOPIC + "/testnode";
futures.add(embeddedConnection.publish(testNode + "/$name", "Testnode".getBytes()));
futures.add(embeddedConnection.publish(testNode + "/$type", "Type".getBytes()));
futures.add(
embeddedConnection.publish(testNode + "/$properties", "temperature,doorbell,testRetain".getBytes()));
// Add homie property topics
final String property = testNode + "/temperature";
futures.add(embeddedConnection.publish(property, "10".getBytes()));
futures.add(embeddedConnection.publish(property + "/$name", "Testprop".getBytes()));
futures.add(embeddedConnection.publish(property + "/$settable", "true".getBytes()));
futures.add(embeddedConnection.publish(property + "/$unit", "°C".getBytes(StandardCharsets.UTF_8)));
futures.add(embeddedConnection.publish(property + "/$datatype", "float".getBytes()));
futures.add(embeddedConnection.publish(property + "/$format", "-100:100".getBytes()));
final String propertyBellTopic = testNode + "/doorbell";
futures.add(embeddedConnection.publish(propertyBellTopic + "/$name", "Doorbell".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$settable", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$retained", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyBellTopic + "/$datatype", "boolean".getBytes()));
this.propertyTestTopic = testNode + "/testRetain";
futures.add(embeddedConnection.publish(propertyTestTopic + "/$name", "Test".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$settable", "true".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$retained", "false".getBytes()));
futures.add(embeddedConnection.publish(propertyTestTopic + "/$datatype", "boolean".getBytes()));
registeredTopics = futures.size();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(1000, TimeUnit.MILLISECONDS);
scheduler = new ScheduledThreadPoolExecutor(6);
}
@After
public void tearDown() throws InterruptedException, ExecutionException, TimeoutException {
if (connection != null) {
connection.removeConnectionObserver(failIfChange);
connection.stop().get(500, TimeUnit.MILLISECONDS);
}
scheduler.shutdownNow();
}
@Test
public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException {
// four topics are not under /testnode !
CountDownLatch c = new CountDownLatch(registeredTopics - 4);
connection.subscribe(DEVICE_TOPIC + "/testnode/#", (topic, payload) -> c.countDown()).get(5000,
TimeUnit.MILLISECONDS);
assertTrue("Connection " + connection.getClientId() + " not retrieving all topics ",
c.await(5000, TimeUnit.MILLISECONDS));
}
@Test
public void retrieveOneAttribute() throws InterruptedException, ExecutionException {
WaitForTopicValue watcher = new WaitForTopicValue(connection, DEVICE_TOPIC + "/$homie");
assertThat(watcher.waitForTopicValue(1000), is("3.0"));
}
@SuppressWarnings("null")
@Test
public void retrieveAttributes() throws InterruptedException, ExecutionException {
assertThat(connection.hasSubscribers(), is(false));
Node node = new Node(DEVICE_TOPIC, "testnode", ThingChannelConstants.testHomieThing, callback,
new NodeAttributes());
Property property = spy(
new Property(DEVICE_TOPIC + "/testnode", node, "temperature", callback, new PropertyAttributes()));
// Create a scheduler
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
property.subscribe(connection, scheduler, 500).get();
assertThat(property.attributes.settable, is(true));
assertThat(property.attributes.retained, is(true));
assertThat(property.attributes.name, is("Testprop"));
assertThat(property.attributes.unit, is("°C"));
assertThat(property.attributes.datatype, is(DataTypeEnum.float_));
waitForAssert(() -> assertThat(property.attributes.format, is("-100:100")));
verify(property, timeout(500).atLeastOnce()).attributesReceived();
// Receive property value
ChannelState channelState = spy(property.getChannelState());
PropertyHelper.setChannelState(property, channelState);
property.startChannel(connection, scheduler, 500).get();
verify(channelState).start(any(), any(), anyInt());
verify(channelState, timeout(500)).processMessage(any(), any());
verify(callback).updateChannelState(any(), any());
assertThat(property.getChannelState().getCache().getChannelState(), is(new DecimalType(10)));
property.stop().get();
assertThat(connection.hasSubscribers(), is(false));
}
// Inject a spy'ed property
public Property createSpyProperty(InvocationOnMock invocation) {
final Node node = (Node) invocation.getMock();
final String id = (String) invocation.getArguments()[0];
return spy(node.createProperty(id, spy(new PropertyAttributes())));
}
// Inject a spy'ed node
public Node createSpyNode(InvocationOnMock invocation) {
final Device device = (Device) invocation.getMock();
final String id = (String) invocation.getArguments()[0];
// Create the node
Node node = spy(device.createNode(id, spy(new NodeAttributes())));
// Intercept creating a property in the next call and inject a spy'ed property.
doAnswer(this::createSpyProperty).when(node).createProperty(any());
return node;
}
@SuppressWarnings("null")
@Test
public void parseHomieTree() throws InterruptedException, ExecutionException, TimeoutException {
// Create a Homie Device object. Because spied Nodes are required for call verification,
// the full Device constructor need to be used and a ChildMap object need to be created manually.
ChildMap<Node> nodeMap = new ChildMap<>();
Device device = spy(
new Device(ThingChannelConstants.testHomieThing, callback, new DeviceAttributes(), nodeMap));
// Intercept creating a node in initialize()->start() and inject a spy'ed node.
doAnswer(this::createSpyNode).when(device).createNode(any());
// initialize the device, subscribe and wait.
device.initialize(BASE_TOPIC, DEVICE_ID, Collections.emptyList());
device.subscribe(connection, scheduler, 1500).get();
assertThat(device.isInitialized(), is(true));
// Check device attributes
assertThat(device.attributes.homie, is("3.0"));
assertThat(device.attributes.name, is("Name"));
assertThat(device.attributes.state, is(ReadyState.ready));
assertThat(device.attributes.nodes.length, is(1));
verify(device, times(4)).attributeChanged(any(), any(), any(), any(), anyBoolean());
verify(callback).readyStateChanged(eq(ReadyState.ready));
verify(device).attributesReceived(any(), any(), anyInt());
// Expect 1 node
assertThat(device.nodes.size(), is(1));
// Check node and node attributes
Node node = device.nodes.get("testnode");
verify(node).subscribe(any(), any(), anyInt());
verify(node).attributesReceived(any(), any(), anyInt());
verify(node.attributes).subscribeAndReceive(any(), any(), anyString(), any(), anyInt());
assertThat(node.attributes.type, is("Type"));
assertThat(node.attributes.name, is("Testnode"));
// Expect 2 property
assertThat(node.properties.size(), is(3));
// Check property and property attributes
Property property = node.properties.get("temperature");
assertThat(property.attributes.settable, is(true));
assertThat(property.attributes.retained, is(true));
assertThat(property.attributes.name, is("Testprop"));
assertThat(property.attributes.unit, is("°C"));
assertThat(property.attributes.datatype, is(DataTypeEnum.float_));
assertThat(property.attributes.format, is("-100:100"));
verify(property).attributesReceived();
assertNotNull(property.getChannelState());
assertThat(property.getType().getState().getMinimum().intValue(), is(-100));
assertThat(property.getType().getState().getMaximum().intValue(), is(100));
// Check property and property attributes
Property propertyBell = node.properties.get("doorbell");
verify(propertyBell).attributesReceived();
assertThat(propertyBell.attributes.settable, is(false));
assertThat(propertyBell.attributes.retained, is(false));
assertThat(propertyBell.attributes.name, is("Doorbell"));
assertThat(propertyBell.attributes.datatype, is(DataTypeEnum.boolean_));
// The device->node->property tree is ready. Now subscribe to property values.
device.startChannels(connection, scheduler, 50, handler).get();
assertThat(propertyBell.getChannelState().isStateful(), is(false));
assertThat(propertyBell.getChannelState().getCache().getChannelState(), is(UnDefType.UNDEF));
assertThat(property.getChannelState().getCache().getChannelState(), is(new DecimalType(10)));
property = node.properties.get("testRetain");
WaitForTopicValue watcher = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set");
// Watch the topic. Publish a retain=false value to MQTT
property.getChannelState().publishValue(OnOffType.OFF).get();
assertThat(watcher.waitForTopicValue(1000), is("false"));
// Publish a retain=false value to MQTT.
property.getChannelState().publishValue(OnOffType.ON).get();
// No value is expected to be retained on this MQTT topic
waitForAssert(() -> {
try {
WaitForTopicValue w = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set");
assertNull(w.waitForTopicValue(50));
} catch (InterruptedException | ExecutionException e) {
}
}, 500, 100);
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt;
import static org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants.HOMIE300_MQTT_THING;
import org.openhab.core.thing.ThingUID;
/**
* Static test definitions, like thing, bridge and channel definitions
*
* @author David Graeff - Initial contribution
*/
public class ThingChannelConstants {
// Common ThingUID and ChannelUIDs
public final static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123");
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.mqtt.homie.internal.homie300;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.ChannelState;
/**
* Helper to access {@link Property} internals.
*
* @author David Graeff - Initial contribution
*/
@NonNullByDefault
public class PropertyHelper {
public static void setChannelState(Property property, @Nullable ChannelState channelState) {
property.channelState = channelState;
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.ntp.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,57 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.ntp
-runrequires: \
bnd.identity;id='org.openhab.binding.ntp.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
#
# done
#
-runbundles: \
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.commons.net;version='[3.3.0,3.3.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.openhab.binding.ntp;version='[2.5.9,2.5.10)',\
org.openhab.binding.ntp.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.ntp.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: NTP Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ntp</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.ntp.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: NTP Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.ntp</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.ntp.server;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import org.apache.commons.net.ntp.NtpV3Impl;
import org.apache.commons.net.ntp.NtpV3Packet;
import org.apache.commons.net.ntp.TimeStamp;
import org.openhab.binding.ntp.test.NtpOSGiTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a simple NTP server which provides timestamps to the {@link NtpOSGiTest} tests.
* Its main purpose is to remove the dependence on a remote ntp server because it is hosted locally.
*
* @author Erdoan Hadzhiyusein - Initial Contribution
*
*/
public class SimpleNTPServer {
private DatagramSocket socket;
private int port;
private volatile boolean isRunning;
private byte[] array = new byte[48];
private final DatagramPacket request = new DatagramPacket(array, array.length);
private Logger logger = LoggerFactory.getLogger(SimpleNTPServer.class);
/**
* The server must use an available port to be able to start.
* According to RFC 793, the port is a 16 bit unsigned int.
*
* @param port
*/
public SimpleNTPServer(int port) {
if (port > 0 && port < 65535) {
this.port = port;
} else {
throw new IllegalArgumentException(
"Please choose an available port! This port cannot be used at the moment" + port);
}
}
/**
* This method opens a new socket and receives requests calling handleRequest() for each one.
*/
public void startServer() {
isRunning = true;
new Thread() {
@Override
public void run() {
try {
socket = new DatagramSocket(port);
} catch (SocketException e) {
logger.error("Occured an error {}. Couldn't open a socket on this port:", port, e);
}
while (isRunning) {
try {
socket.receive(request);
handleRequest(request);
} catch (IOException e) {
logger.error("There was an error {} while processing the request!", request, e);
}
}
}
}.start();
}
/**
* Stopping the server which causes closing the socket too
*/
public void stopServer() {
isRunning = false;
if (socket != null) {
socket.close(); // force closing of the socket
socket = null;
}
}
private void handleRequest(DatagramPacket requestPacket) throws IOException {
final long receivedTime = System.currentTimeMillis();
NtpV3Packet responsePacket = new NtpV3Impl();
responsePacket.setMode(NtpV3Packet.MODE_SERVER);
responsePacket.setTransmitTime(TimeStamp.getNtpTime(receivedTime));
DatagramPacket dataPacket = responsePacket.getDatagramPacket();
dataPacket.setPort(requestPacket.getPort());
dataPacket.setAddress(requestPacket.getAddress());
socket.send(dataPacket);
}
}

View File

@@ -0,0 +1,499 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.ntp.test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventSubscriber;
import org.openhab.core.items.GenericItem;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.events.ItemStateEvent;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.items.DateTimeItem;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ManagedThingProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingProvider;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.link.ItemChannelLink;
import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeBuilder;
import org.openhab.core.thing.type.ChannelTypeProvider;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.State;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.test.storage.VolatileStorageService;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.openhab.binding.ntp.internal.NtpBindingConstants;
import org.openhab.binding.ntp.internal.handler.NtpHandler;
import org.openhab.binding.ntp.server.SimpleNTPServer;
/**
* OSGi tests for the {@link NtpHandler}
*
* @author Petar Valchev - Initial Contribution
* @author Markus Rathgeb - Migrated tests from Groovy to pure Java
* @author Erdoan Hadzhiyusein - Migrated tests to Java 8 and integrated the new DateTimeType
*/
public class NtpOSGiTest extends JavaOSGiTest {
private static TimeZone systemTimeZone;
private static Locale locale;
private NtpHandler ntpHandler;
private Thing ntpThing;
private GenericItem testItem;
private ManagedThingProvider managedThingProvider;
private ThingRegistry thingRegistry;
private ItemRegistry itemRegistry;
private ChannelTypeProvider channelTypeProvider;
private static final ZoneId DEFAULT_TIME_ZONE_ID = ZoneId.of("Europe/Bucharest");
private static final String TEST_TIME_ZONE_ID = "America/Los_Angeles";
private static final String TEST_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss z";
private static final String TEST_ITEM_NAME = "testItem";
private static final String TEST_THING_ID = "testThingId";
// No bundle in ESH is exporting a package from which we can use item types
// as constants, so we will use String.
private static final String ACCEPTED_ITEM_TYPE_STRING = "String";
private static final String ACCEPTED_ITEM_TYPE_DATE_TIME = "DateTime";
private static final String TEST_HOSTNAME = "127.0.0.1";
private static final int TEST_PORT = 9002;
static SimpleNTPServer timeServer;
private ChannelTypeUID channelTypeUID;
enum UpdateEventType {
HANDLE_COMMAND("handleCommand"),
CHANNEL_LINKED("channelLinked");
private final String updateEventType;
private UpdateEventType(String updateEventType) {
this.updateEventType = updateEventType;
}
public String getUpdateEventType() {
return updateEventType;
}
}
@BeforeClass
public static void setUpClass() {
// Initializing a new local server on this port
timeServer = new SimpleNTPServer(TEST_PORT);
// Starting the local server
timeServer.startServer();
/*
* Store the initial system time zone and locale value, so that we can
* restore them at the test end.
*/
systemTimeZone = TimeZone.getDefault();
locale = Locale.getDefault();
/*
* Set new default time zone and locale, which will be used during the
* tests execution.
*/
TimeZone.setDefault(TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID));
Locale.setDefault(Locale.US);
}
@Before
public void setUp() {
VolatileStorageService volatileStorageService = new VolatileStorageService();
registerService(volatileStorageService);
managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
assertNotNull(managedThingProvider);
thingRegistry = getService(ThingRegistry.class);
assertNotNull(thingRegistry);
itemRegistry = getService(ItemRegistry.class);
assertNotNull(itemRegistry);
channelTypeUID = new ChannelTypeUID(NtpBindingConstants.BINDING_ID + ":channelType");
channelTypeProvider = mock(ChannelTypeProvider.class);
when(channelTypeProvider.getChannelType(any(ChannelTypeUID.class), any(Locale.class)))
.thenReturn(ChannelTypeBuilder.state(channelTypeUID, "label", CoreItemFactory.SWITCH).build());
registerService(channelTypeProvider);
}
@After
public void tearDown() {
if (ntpThing != null) {
Thing removedThing = thingRegistry.forceRemove(ntpThing.getUID());
assertNotNull(removedThing);
}
if (testItem != null) {
itemRegistry.remove(TEST_ITEM_NAME);
}
}
@AfterClass
public static void tearDownClass() {
// Stopping the local time server
timeServer.stopServer();
// Set the default time zone and locale to their initial value.
TimeZone.setDefault(systemTimeZone);
Locale.setDefault(locale);
}
@Test
public void testStringChannelTimeZoneUpdate() {
final String expectedTimeZonePDT = "PDT";
final String expectedTimeZonePST = "PST";
Configuration configuration = new Configuration();
configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID);
Configuration channelConfig = new Configuration();
/*
* Set the format of the date, so it is updated in the item registry in
* a format from which we can easily get the time zone.
*/
channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, TEST_DATE_TIME_FORMAT);
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null);
String timeZoneFromItemRegistry = getStringChannelTimeZoneFromItemRegistry();
assertThat(timeZoneFromItemRegistry, is(anyOf(equalTo(expectedTimeZonePDT), equalTo(expectedTimeZonePST))));
}
@Test
public void testDateTimeChannelTimeZoneUpdate() {
Configuration configuration = new Configuration();
configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID);
initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null);
String testItemState = getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME).toString();
assertFormat(testItemState, DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS);
ZonedDateTime timeZoneFromItemRegistry = ((DateTimeType) getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME))
.getZonedDateTime();
ZoneOffset expectedOffset = ZoneId.of(TEST_TIME_ZONE_ID).getRules()
.getOffset(timeZoneFromItemRegistry.toInstant());
assertEquals(expectedOffset, timeZoneFromItemRegistry.getOffset());
}
@Test
public void testDateTimeChannelCalendarTimeZoneUpdate() {
Configuration configuration = new Configuration();
configuration.put(NtpBindingConstants.PROPERTY_TIMEZONE, TEST_TIME_ZONE_ID);
initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null);
ZonedDateTime timeZoneIdFromItemRegistry = ((DateTimeType) getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME))
.getZonedDateTime();
ZoneOffset expectedOffset = ZoneId.of(TEST_TIME_ZONE_ID).getRules()
.getOffset(timeZoneIdFromItemRegistry.toInstant());
assertEquals(expectedOffset, timeZoneIdFromItemRegistry.getOffset());
}
@Test
public void testStringChannelDefaultTimeZoneUpdate() {
final String expectedTimeZoneEEST = "EEST";
final String expectedTimeZoneEET = "EET";
Configuration configuration = new Configuration();
Configuration channelConfig = new Configuration();
/*
* Set the format of the date, so it is updated in the item registry in
* a format from which we can easily get the time zone.
*/
channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, TEST_DATE_TIME_FORMAT);
// Initialize with configuration with no time zone property set.
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null, null);
String timeZoneFromItemRegistry = getStringChannelTimeZoneFromItemRegistry();
assertThat(timeZoneFromItemRegistry, is(anyOf(equalTo(expectedTimeZoneEEST), equalTo(expectedTimeZoneEET))));
}
@Test
public void testDateTimeChannelDefaultTimeZoneUpdate() {
ZonedDateTime zoned = ZonedDateTime.now();
ZoneOffset expectedTimeZone = zoned.getOffset();
Configuration configuration = new Configuration();
// Initialize with configuration with no time zone property set.
initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null);
String testItemState = getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME).toString();
assertFormat(testItemState, DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS);
ZoneOffset timeZoneFromItemRegistry = new DateTimeType(testItemState).getZonedDateTime().getOffset();
assertEquals(expectedTimeZone, timeZoneFromItemRegistry);
}
@Test
@Ignore("https://github.com/eclipse/smarthome/issues/5224")
public void testDateTimeChannelCalendarDefaultTimeZoneUpdate() {
Configuration configuration = new Configuration();
// Initialize with configuration with no time zone property set.
initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null, null);
ZonedDateTime timeZoneIdFromItemRegistry = ((DateTimeType) getItemState(ACCEPTED_ITEM_TYPE_DATE_TIME))
.getZonedDateTime();
ZoneOffset expectedOffset = ZoneId.systemDefault().getRules().getOffset(timeZoneIdFromItemRegistry.toInstant());
assertEquals(expectedOffset, timeZoneIdFromItemRegistry.getOffset());
}
@Test
public void testStringChannelFormatting() {
final String formatPattern = "EEE, d MMM yyyy HH:mm:ss z";
Configuration configuration = new Configuration();
Configuration channelConfig = new Configuration();
channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, formatPattern);
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null);
String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString();
assertFormat(dateFromItemRegistry, formatPattern);
}
@Test
public void testStringChannelDefaultFormatting() {
Configuration configuration = new Configuration();
// Initialize with configuration with no property for formatting set.
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null, null);
String dateFromItemRegistryString = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString();
assertFormat(dateFromItemRegistryString, NtpHandler.DATE_PATTERN_WITH_TZ);
}
@Test
public void testEmptyStringPropertyFormatting() {
Configuration configuration = new Configuration();
Configuration channelConfig = new Configuration();
// Empty string
channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, "");
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null);
String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString();
assertFormat(dateFromItemRegistry, NtpHandler.DATE_PATTERN_WITH_TZ);
}
@Test
public void testNullPropertyFormatting() {
Configuration configuration = new Configuration();
Configuration channelConfig = new Configuration();
channelConfig.put(NtpBindingConstants.PROPERTY_DATE_TIME_FORMAT, null);
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, channelConfig, null);
String dateFromItemRegistry = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString();
assertFormat(dateFromItemRegistry, NtpHandler.DATE_PATTERN_WITH_TZ);
}
@Test
public void testDateTimeChannelWithUnknownHost() {
assertCommunicationError(ACCEPTED_ITEM_TYPE_DATE_TIME);
}
@Test
public void testStringChannelWithUnknownHost() {
assertCommunicationError(ACCEPTED_ITEM_TYPE_STRING);
}
@Test
public void testStringChannelHandleCommand() {
assertEventIsReceived(UpdateEventType.HANDLE_COMMAND, NtpBindingConstants.CHANNEL_STRING,
ACCEPTED_ITEM_TYPE_STRING);
}
@Test
public void testDateTimeChannelHandleCommand() {
assertEventIsReceived(UpdateEventType.HANDLE_COMMAND, NtpBindingConstants.CHANNEL_DATE_TIME,
ACCEPTED_ITEM_TYPE_DATE_TIME);
}
@Test
public void testStringChannelLinking() {
assertEventIsReceived(UpdateEventType.CHANNEL_LINKED, NtpBindingConstants.CHANNEL_STRING,
ACCEPTED_ITEM_TYPE_STRING);
}
@Test
public void testDateTimeChannelLinking() {
assertEventIsReceived(UpdateEventType.CHANNEL_LINKED, NtpBindingConstants.CHANNEL_DATE_TIME,
ACCEPTED_ITEM_TYPE_DATE_TIME);
}
private void initialize(Configuration configuration, String channelID, String acceptedItemType,
Configuration channelConfiguration, String wrongHostname) {
// There are 2 tests which require wrong hostnames.
boolean isWrongHostNameTest = wrongHostname != null;
if (isWrongHostNameTest) {
configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_HOST, wrongHostname);
} else {
configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_HOST, TEST_HOSTNAME);
}
initialize(configuration, channelID, acceptedItemType, channelConfiguration);
}
private void initialize(Configuration configuration, String channelID, String acceptedItemType,
Configuration channelConfiguration) {
configuration.put(NtpBindingConstants.PROPERTY_NTP_SERVER_PORT, TEST_PORT);
ThingUID ntpUid = new ThingUID(NtpBindingConstants.THING_TYPE_NTP, TEST_THING_ID);
ChannelUID channelUID = new ChannelUID(ntpUid, channelID);
Channel channel = ChannelBuilder.create(channelUID, acceptedItemType).withType(channelTypeUID)
.withConfiguration(channelConfiguration).withLabel("label").withKind(ChannelKind.STATE).build();
ntpThing = ThingBuilder.create(NtpBindingConstants.THING_TYPE_NTP, ntpUid).withConfiguration(configuration)
.withChannel(channel).build();
managedThingProvider.add(ntpThing);
// Wait for the NTP thing to be added to the ManagedThingProvider.
ntpHandler = waitForAssert(() -> {
final ThingHandler thingHandler = ntpThing.getHandler();
assertThat(thingHandler, is(instanceOf(NtpHandler.class)));
return (NtpHandler) thingHandler;
}, DFL_TIMEOUT * 3, DFL_SLEEP_TIME);
if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_STRING)) {
testItem = new StringItem(TEST_ITEM_NAME);
} else if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_DATE_TIME)) {
testItem = new DateTimeItem(TEST_ITEM_NAME);
}
itemRegistry.add(testItem);
// Wait for the item , linked to the NTP thing to be added to the
// ManagedThingProvider.
final ManagedItemChannelLinkProvider itemChannelLinkProvider = waitForAssert(() -> {
final ManagedItemChannelLinkProvider tmp = getService(ManagedItemChannelLinkProvider.class);
assertNotNull(tmp);
return tmp;
});
itemChannelLinkProvider.add(new ItemChannelLink(TEST_ITEM_NAME, channelUID));
}
private State getItemState(String acceptedItemType) {
final Item testItem = waitForAssert(() -> {
Item tmp;
try {
tmp = itemRegistry.getItem(TEST_ITEM_NAME);
} catch (ItemNotFoundException e) {
tmp = null;
}
assertNotNull(tmp);
return tmp;
});
return waitForAssert(() -> {
final State testItemState = testItem.getState();
if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_STRING)) {
assertThat(testItemState, is(instanceOf(StringType.class)));
} else if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_DATE_TIME)) {
assertThat(testItemState, is(instanceOf(DateTimeType.class)));
}
return testItemState;
}, 3 * DFL_TIMEOUT, 2 * DFL_SLEEP_TIME);
}
private String getStringChannelTimeZoneFromItemRegistry() {
String itemState = getItemState(ACCEPTED_ITEM_TYPE_STRING).toString();
String timeZoneFromItemRegistry = StringUtils.substringAfterLast(itemState, " ");
return timeZoneFromItemRegistry;
}
private void assertFormat(String initialDate, String formatPattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern);
final ZonedDateTime date;
date = ZonedDateTime.parse(initialDate, formatter);
String formattedDate = formatter.format(date);
assertEquals(initialDate, formattedDate);
}
private void assertCommunicationError(String acceptedItemType) {
Configuration configuration = new Configuration();
final String WRONG_HOSTNAME = "wrong.hostname";
if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_DATE_TIME)) {
initialize(configuration, NtpBindingConstants.CHANNEL_DATE_TIME, ACCEPTED_ITEM_TYPE_DATE_TIME, null,
WRONG_HOSTNAME);
} else if (acceptedItemType.equals(ACCEPTED_ITEM_TYPE_STRING)) {
initialize(configuration, NtpBindingConstants.CHANNEL_STRING, ACCEPTED_ITEM_TYPE_STRING, null,
WRONG_HOSTNAME);
}
waitForAssert(() -> {
assertEquals(ThingStatusDetail.COMMUNICATION_ERROR, ntpThing.getStatusInfo().getStatusDetail());
}, 60000, DFL_SLEEP_TIME);
}
private void assertEventIsReceived(UpdateEventType updateEventType, String channelID, String acceptedItemType) {
Configuration configuration = new Configuration();
initialize(configuration, channelID, acceptedItemType, null, null);
EventSubscriber eventSubscriberMock = mock(EventSubscriber.class);
when(eventSubscriberMock.getSubscribedEventTypes()).thenReturn(Collections.singleton(ItemStateEvent.TYPE));
registerService(eventSubscriberMock);
if (updateEventType.equals(UpdateEventType.HANDLE_COMMAND)) {
ntpHandler.handleCommand(new ChannelUID("ntp:test:chan:1"), new StringType("test"));
} else if (updateEventType.equals(UpdateEventType.CHANNEL_LINKED)) {
ntpHandler.channelLinked(new ChannelUID("ntp:test:chan:1"));
}
waitForAssert(() -> {
verify(eventSubscriberMock, atLeastOnce()).receive(ArgumentMatchers.any(Event.class));
});
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.systeminfo.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,59 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.systeminfo
-runrequires: \
bnd.identity;id='org.openhab.binding.systeminfo.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
-runsystemcapabilities: ${native_capability}
#
# done
#
-runbundles: \
com.google.gson;version='[2.8.2,2.8.3)',\
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
slf4j.api;version='[1.7.25,1.7.26)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
com.sun.jna;version='[5.5.0,5.5.1)',\
com.sun.jna.platform;version='[5.5.0,5.5.1)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.openhab.binding.systeminfo;version='[2.5.9,2.5.10)',\
org.openhab.binding.systeminfo.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.systeminfo.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Systeminfo Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.systeminfo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>4.5.2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.systeminfo.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: Systeminfo Binding Tests</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.systeminfo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>4.5.2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.tradfri.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,62 @@
-include: ../itest-common.bndrun
Bundle-SymbolicName: ${project.artifactId}
Fragment-Host: org.openhab.binding.tradfri
-runrequires: \
bnd.identity;id='org.openhab.binding.tradfri.tests',\
bnd.identity;id='org.openhab.core.binding.xml',\
bnd.identity;id='org.openhab.core.thing.xml'
#
# done
#
-runbundles: \
javax.measure.unit-api;version='[1.0.0,1.0.1)',\
org.apache.commons.io;version='[2.2.0,2.2.1)',\
org.apache.commons.lang;version='[2.6.0,2.6.1)',\
org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.osgi.service.event;version='[1.4.0,1.4.1)',\
org.openhab.core;version='[2.5.0,2.5.1)',\
org.openhab.core.config.core;version='[2.5.0,2.5.1)',\
ch.qos.logback.core;version='[1.2.3,1.2.4)',\
org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\
org.openhab.core.io.console;version='[2.5.0,2.5.1)',\
org.openhab.core.thing;version='[2.5.0,2.5.1)',\
slf4j.api;version='[1.7.25,1.7.26)',\
org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\
com.google.gson;version='[2.8.2,2.8.3)',\
javax.jmdns;version='[3.5.5,3.5.6)',\
org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\
org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\
org.openhab.core.config.discovery.mdns;version='[2.5.0,2.5.1)',\
org.openhab.core.io.transport.mdns;version='[2.5.0,2.5.1)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.objenesis;version='[2.6.0,2.6.1)',\
org.openhab.core.test;version='[2.5.0,2.5.1)',\
osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\
osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\
org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\
org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\
org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\
tec.uom.lib.uom-lib-common;version='[1.0.3,1.0.4)',\
tec.uom.se;version='[1.0.10,1.0.11)',\
org.apache.servicemix.bundles.jaxb-impl;version='[2.2.11,2.2.12)',\
net.bytebuddy.byte-buddy;version='[1.9.10,1.9.11)',\
net.bytebuddy.byte-buddy-agent;version='[1.9.10,1.9.11)',\
org.mockito.mockito-core;version='[3.1.0,3.1.1)',\
org.eclipse.jetty.http;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.io;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.security;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.server;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.servlet;version='[9.4.20,9.4.21)',\
org.eclipse.jetty.util;version='[9.4.20,9.4.21)',\
org.eclipse.californium.core;version='[2.0.0,2.0.1)',\
org.eclipse.californium.element-connector;version='[2.0.0,2.0.1)',\
org.eclipse.californium.scandium;version='[2.0.0,2.0.1)',\
ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
org.openhab.binding.tradfri;version='[2.5.9,2.5.10)',\
org.openhab.binding.tradfri.tests;version='[2.5.9,2.5.10)'

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.tradfri.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: TRÅDFRI Binding</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.tradfri</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.itests</groupId>
<artifactId>org.openhab.addons.reactor.itests</artifactId>
<version>2.5.9-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.tradfri.tests</artifactId>
<name>openHAB Add-ons :: Integration Tests :: TRÅDFRI Binding</name>
<dependencies>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.tradfri</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.tradfri.internal.discovery;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.openhab.binding.tradfri.internal.TradfriBindingConstants.*;
import javax.jmdns.ServiceInfo;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultFlag;
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.test.java.JavaOSGiTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
/**
* Tests for {@link TradfriDiscoveryParticipant}.
*
* @author Kai Kreuzer - Initial contribution
*/
public class TradfriDiscoveryParticipantOSGITest extends JavaOSGiTest {
private MDNSDiscoveryParticipant discoveryParticipant;
@Mock
private ServiceInfo tradfriGateway;
@Mock
private ServiceInfo otherDevice;
@Before
public void setUp() {
initMocks(this);
discoveryParticipant = getService(MDNSDiscoveryParticipant.class, TradfriDiscoveryParticipant.class);
when(tradfriGateway.getType()).thenReturn("_coap._udp.local.");
when(tradfriGateway.getName()).thenReturn("gw:12-34-56-78-90-ab");
when(tradfriGateway.getHostAddresses()).thenReturn(new String[] { "192.168.0.5" });
when(tradfriGateway.getPort()).thenReturn(1234);
when(tradfriGateway.getPropertyString("version")).thenReturn("1.1");
when(otherDevice.getType()).thenReturn("_coap._udp.local.");
when(otherDevice.getName()).thenReturn("something");
when(otherDevice.getHostAddresses()).thenReturn(new String[] { "192.168.0.5" });
when(otherDevice.getPort()).thenReturn(1234);
when(otherDevice.getPropertyString("version")).thenReturn("1.1");
}
@Test
public void correctSupportedTypes() {
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().size(), is(1));
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().iterator().next(), is(GATEWAY_TYPE_UID));
}
@Test
public void correctThingUID() {
when(tradfriGateway.getName()).thenReturn("gw:12-34-56-78-90-ab");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
when(tradfriGateway.getName()).thenReturn("gw:1234567890ab");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
when(tradfriGateway.getName()).thenReturn("gw-12-34-56-78-90-ab");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
when(tradfriGateway.getName()).thenReturn("gw:1234567890ab");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
when(tradfriGateway.getName()).thenReturn("gw:1234567890abServiceInfo");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
when(tradfriGateway.getName()).thenReturn("gw:12-34-56-78-90-ab-service-info");
assertThat(discoveryParticipant.getThingUID(tradfriGateway),
is(new ThingUID("tradfri:gateway:gw1234567890ab")));
// restore original value
when(tradfriGateway.getName()).thenReturn("gw:12-34-56-78-90-ab");
}
@Test
public void validDiscoveryResult() {
DiscoveryResult result = discoveryParticipant.createResult(tradfriGateway);
assertNotNull(result);
assertThat(result.getProperties().get(Thing.PROPERTY_FIRMWARE_VERSION), is("1.1"));
assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
assertThat(result.getThingUID(), is(new ThingUID("tradfri:gateway:gw1234567890ab")));
assertThat(result.getThingTypeUID(), is(GATEWAY_TYPE_UID));
assertThat(result.getBridgeUID(), is(nullValue()));
assertThat(result.getProperties().get(Thing.PROPERTY_VENDOR), is("IKEA of Sweden"));
assertThat(result.getProperties().get(GATEWAY_CONFIG_HOST), is("192.168.0.5"));
assertThat(result.getProperties().get(GATEWAY_CONFIG_PORT), is(1234));
assertThat(result.getRepresentationProperty(), is(GATEWAY_CONFIG_HOST));
}
@Test
public void noThingUIDForUnknownDevice() {
when(otherDevice.getName()).thenReturn("something");
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
when(otherDevice.getName()).thenReturn("gw_1234567890ab");
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
when(otherDevice.getName()).thenReturn("gw:12-3456--7890-ab");
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
when(otherDevice.getName()).thenReturn("gw1234567890ab");
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
// restore original value
when(otherDevice.getName()).thenReturn("something");
}
@Test
public void noDiscoveryResultForUnknownDevice() {
assertThat(discoveryParticipant.createResult(otherDevice), is(nullValue()));
}
}

Some files were not shown because too many files have changed in this diff Show More