diff --git a/.github/scripts/maven-build b/.github/scripts/maven-build index 77722811027..6a3a2f7c0aa 100755 --- a/.github/scripts/maven-build +++ b/.github/scripts/maven-build @@ -66,7 +66,7 @@ function addon_projects() { local addon="$1" # include add-on projects - local projects=":$(find . -mindepth 2 -maxdepth 2 -type d -regextype egrep -regex "./(bundles|itests)/$addon(\..*)?$" | grep -Ev 'org.openhab.binding.mqtt.homeassistant.tests|org.openhab.binding.mqtt.homie.tests' | sort | sed -E 's#./(bundles|itests)/##g' | xargs | sed 's# #,:#g')" + local projects=":$(find . -mindepth 2 -maxdepth 2 -type d -regextype egrep -regex "./(bundles|itests)/$addon(\..*)?$" | sort | sed -E 's#./(bundles|itests)/##g' | xargs | sed 's# #,:#g')" # include BOMs projects="$projects,:org.openhab.addons.bom.openhab-core-index,:org.openhab.addons.bom.runtime-index,:org.openhab.addons.bom.test-index" diff --git a/itests/itest-common.bndrun b/itests/itest-common.bndrun index 6ff2541ba5d..babdeb6db75 100644 --- a/itests/itest-common.bndrun +++ b/itests/itest-common.bndrun @@ -11,9 +11,6 @@ # Run all integration tests which are named xyzTest Test-Cases: ${classes;CONCRETE;PUBLIC;NAMED;*Test} -# A temporary inclusion until an R7 framework is available -Import-Package: org.osgi.framework.*;version="[1.8,2)",* - # We would like to use the slf4j-api and implementation provided by pax-logging -runblacklist.itest-common: \ bnd.identity;id='slf4j.api' diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun b/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun index 0d60a1ca8a2..43ddec015c8 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/itest.bndrun @@ -3,92 +3,110 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.mqtt.homeassistant +Import-Package: \ + com.bugsnag.*;resolution:=optional,\ + com.librato.metrics.reporter.*;resolution:=optional,\ + * + +-includeresource: \ + moquette-broker-[0-9.]*.jar;lib:=true + -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' + bnd.identity;id='org.openhab.core.thing.xml' # We would like to use the "volatile" storage only -runblacklist: \ bnd.identity;id='org.openhab.core.storage.json' --runvm: \ +-runvm.mqtt: \ -Dio.netty.noUnsafe=true,\ - -Dmqttembeddedbroker.port=${mqttembeddedbroker.port} + -Dmqttbroker.port=${mqttbroker.port} # # 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.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.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ - org.objenesis;version='[2.6.0,2.6.1)',\ org.osgi.service.event;version='[1.4.0,1.4.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)',\ - 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)',\ - biz.aQute.tester.junit-platform;version='[5.1.2,5.1.3)',\ - com.google.dagger;version='[2.20.0,2.20.1)',\ - com.hivemq.client.mqtt;version='[1.1.2,1.1.3)',\ - io.netty.codec-http;version='[4.1.34,4.1.35)',\ - io.netty.transport-native-epoll;version='[4.1.34,4.1.35)',\ - io.netty.transport-native-unix-common;version='[4.1.34,4.1.35)',\ - io.reactivex.rxjava2.rxjava;version='[2.2.5,2.2.6)',\ - junit-jupiter-api;version='[5.6.2,5.6.3)',\ - junit-jupiter-engine;version='[5.6.2,5.6.3)',\ - junit-platform-commons;version='[1.6.2,1.6.3)',\ - junit-platform-engine;version='[1.6.2,1.6.3)',\ - junit-platform-launcher;version='[1.6.2,1.6.3)',\ - net.bytebuddy.byte-buddy;version='[1.10.13,1.10.14)',\ - net.bytebuddy.byte-buddy-agent;version='[1.10.13,1.10.14)',\ - org.apache.aries.javax.jax.rs-api;version='[1.0.0,1.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.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.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.jctools.core;version='[2.1.2,2.1.3)',\ - org.mockito.mockito-core;version='[3.4.6,3.4.7)',\ - org.openhab.binding.mqtt;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.generic;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.homeassistant;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.homeassistant.tests;version='[3.0.0,3.0.1)',\ - org.openhab.core;version='[3.0.0,3.0.1)',\ - org.openhab.core.binding.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.core;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.discovery;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.io.console;version='[3.0.0,3.0.1)',\ - org.openhab.core.io.transport.mqtt;version='[3.0.0,3.0.1)',\ - org.openhab.core.test;version='[3.0.0,3.0.1)',\ - org.openhab.core.thing;version='[3.0.0,3.0.1)',\ - org.openhab.core.thing.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.transform;version='[3.0.0,3.0.1)',\ - org.openhab.io.mqttembeddedbroker;version='[3.0.0,3.0.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\ com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ - org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ - org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)' + org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ + com.google.dagger;version='[2.27.0,2.27.1)',\ + com.google.gson;version='[2.8.9,2.8.10)',\ + com.hivemq.client.mqtt;version='[1.2.2,1.2.3)',\ + io.netty.buffer;version='[4.1.72,4.1.73)',\ + io.netty.codec;version='[4.1.72,4.1.73)',\ + io.netty.codec-http;version='[4.1.59,4.1.60)',\ + io.netty.codec-socks;version='[4.1.72,4.1.73)',\ + io.netty.common;version='[4.1.72,4.1.73)',\ + io.netty.handler;version='[4.1.72,4.1.73)',\ + io.netty.handler-proxy;version='[4.1.72,4.1.73)',\ + io.netty.resolver;version='[4.1.72,4.1.73)',\ + io.netty.tcnative-classes;version='[2.0.46,2.0.47)',\ + io.netty.transport;version='[4.1.72,4.1.73)',\ + io.netty.transport-native-epoll;version='[4.1.59,4.1.60)',\ + io.netty.transport-native-unix-common;version='[4.1.59,4.1.60)',\ + io.reactivex.rxjava2.rxjava;version='[2.2.19,2.2.20)',\ + jakarta.annotation-api;version='[2.0.0,2.0.1)',\ + jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ + javax.measure.unit-api;version='[2.1.2,2.1.3)',\ + junit-jupiter-api;version='[5.8.1,5.8.2)',\ + junit-jupiter-engine;version='[5.8.1,5.8.2)',\ + junit-platform-commons;version='[1.8.1,1.8.2)',\ + junit-platform-engine;version='[1.8.1,1.8.2)',\ + junit-platform-launcher;version='[1.8.1,1.8.2)',\ + net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\ + net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ + org.apache.aries.javax.jax.rs-api;version='[1.0.1,1.0.2)',\ + org.apache.felix.configadmin;version='[1.9.22,1.9.23)',\ + org.apache.felix.scr;version='[2.1.30,2.1.31)',\ + org.eclipse.jetty.http;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.io;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.security;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.server;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.servlet;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.util;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.util.ajax;version='[9.4.43,9.4.44)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ + org.jsr-305;version='[3.0.2,3.0.3)',\ + org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ + org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ + org.objenesis;version='[3.2.0,3.2.1)',\ + org.openhab.binding.mqtt;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.generic;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.homeassistant;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.homeassistant.tests;version='[3.3.0,3.3.1)',\ + org.openhab.core;version='[3.3.0,3.3.1)',\ + org.openhab.core.binding.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.core;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.discovery;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.io.console;version='[3.3.0,3.3.1)',\ + org.openhab.core.io.transport.mqtt;version='[3.3.0,3.3.1)',\ + org.openhab.core.test;version='[3.3.0,3.3.1)',\ + org.openhab.core.thing;version='[3.3.0,3.3.1)',\ + org.openhab.core.thing.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.transform;version='[3.3.0,3.3.1)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.0.14,2.0.15)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + org.osgi.util.function;version='[1.2.0,1.2.1)',\ + org.osgi.util.promise;version='[1.2.0,1.2.1)',\ + org.reactivestreams.reactive-streams;version='[1.0.3,1.0.4)',\ + si-units;version='[2.1.0,2.1.1)',\ + si.uom.si-quantity;version='[2.1.0,2.1.1)',\ + tech.units.indriya;version='[2.1.2,2.1.3)',\ + uom-lib-common;version='[2.1.0,2.1.1)',\ + xstream;version='[1.4.19,1.4.20)',\ + com.h2database.mvstore;version='[1.4.199,1.4.200)',\ + com.zaxxer.HikariCP;version='[2.4.7,2.4.8)',\ + io.dropwizard.metrics.core;version='[3.2.2,3.2.3)',\ + io.netty.codec-mqtt;version='[4.1.72,4.1.73)',\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ + biz.aQute.tester.junit-platform;version='[6.2.0,6.2.1)' diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/pom.xml b/itests/org.openhab.binding.mqtt.homeassistant.tests/pom.xml index b60da70c666..071b7b0a0ac 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/pom.xml +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/pom.xml @@ -7,13 +7,17 @@ org.openhab.addons.itests org.openhab.addons.reactor.itests - 3.1.0-SNAPSHOT + 3.3.0-SNAPSHOT org.openhab.binding.mqtt.homeassistant.tests openHAB Add-ons :: Integration Tests :: MQTT HomeAssistant Tests + + 1883 + + org.openhab.addons.bundles @@ -31,52 +35,43 @@ ${project.version} - com.github.j-n-k - moquette-broker - 0.13.0.OH2 - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - - - org.mockito - mockito-core - - + com.h2database + h2-mvstore + 1.4.199 - io.netty - netty-common - ${netty.version} + io.moquette + moquette-broker + 0.15 io.netty netty-buffer ${netty.version} - - io.netty - netty-transport - ${netty.version} - io.netty netty-codec ${netty.version} - com.h2database - h2-mvstore - 1.4.199 + io.netty + netty-codec-mqtt + ${netty.version} io.netty - netty-codec-mqtt + netty-common + ${netty.version} + + + io.netty + netty-handler + ${netty.version} + + + io.netty + netty-handler-proxy ${netty.version} @@ -86,7 +81,7 @@ io.netty - netty-handler + netty-transport ${netty.version} @@ -98,14 +93,14 @@ build-helper-maven-plugin - reserve-network-port + reserve-mqtt-broker-port reserve-network-port process-resources - mqttembeddedbroker.port + mqttbroker.port diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/Constants.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/Constants.java deleted file mode 100644 index f9f59254570..00000000000 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/Constants.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2010-2022 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; - -/** - * MQTT embedded broker constants - * - * @author David Graeff - Initial contribution - */ -public class Constants { - /** - * The broker connection client ID. You can request the embedded broker connection via the MqttService: - * - *
-     * MqttBrokerConnection c = mqttService.getBrokerConnection(Constants.CLIENTID);
-     * 
- */ - public static final String CLIENTID = "embedded-mqtt-broker"; - - /** - * The broker persistent identifier used for identifying configurations. - */ - public static final String PID = "org.openhab.core.mqttembeddedbroker"; - - /** - * The configuration key used for configuring the embedded broker port. - */ - public static final String PORT = "port"; -} diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java deleted file mode 100644 index f7e10c4681d..00000000000 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -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.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; - -/** - * A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device - * tree. - * - * @author David Graeff - Initial contribution - * @author Wouter Born - Support running MQTT itests in parallel by reconfiguring embedded broker port - */ -@NonNullByDefault -public class EmbeddedBrokerTools { - - private static final int BROKER_PORT = Integer.getInteger("mqttembeddedbroker.port", 1883); - - private final ConfigurationAdmin configurationAdmin; - private final MqttService mqttService; - - public @Nullable MqttBrokerConnection embeddedConnection; - - public EmbeddedBrokerTools(ConfigurationAdmin configurationAdmin, MqttService mqttService) { - this.configurationAdmin = configurationAdmin; - this.mqttService = mqttService; - } - - /** - * Request the embedded broker connection from the {@link MqttService} and wait for a connection to be established. - * - * @throws InterruptedException - * @throws IOException - */ - public MqttBrokerConnection waitForConnection() throws InterruptedException, IOException { - reconfigurePort(); - - embeddedConnection = mqttService.getBrokerConnection(Constants.CLIENTID); - if (embeddedConnection == null) { - Semaphore semaphore = new Semaphore(1); - semaphore.acquire(); - MqttServiceObserver observer = new MqttServiceObserver() { - - @Override - public void brokerAdded(String brokerID, MqttBrokerConnection broker) { - if (brokerID.equals(Constants.CLIENTID)) { - embeddedConnection = broker; - semaphore.release(); - } - } - - @Override - public void brokerRemoved(String brokerID, MqttBrokerConnection broker) { - } - }; - mqttService.addBrokersListener(observer); - assertTrue(semaphore.tryAcquire(5, TimeUnit.SECONDS), "Wait for embedded connection client failed"); - } - 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(semaphore.tryAcquire(5, TimeUnit.SECONDS), "Connection " + embeddedConnection.getClientId() - + " failed. State: " + embeddedConnection.connectionState()); - return embeddedConnection; - } - - public void reconfigurePort() throws IOException { - Configuration configuration = configurationAdmin.getConfiguration(Constants.PID, null); - - Dictionary properties = configuration.getProperties(); - if (properties == null) { - properties = new Hashtable<>(); - } - - Integer currentPort = (Integer) properties.get(Constants.PORT); - if (currentPort == null || currentPort.intValue() != BROKER_PORT) { - properties.put(Constants.PORT, BROKER_PORT); - configuration.update(properties); - // Remove the connection to make sure the test waits for the new connection to become available - mqttService.removeBrokerConnection(Constants.CLIENTID); - } - } -} diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTest.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/DiscoverComponentsTest.java similarity index 95% rename from itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTest.java rename to itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/DiscoverComponentsTest.java index e94f2358100..170f0e439ab 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTest.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/DiscoverComponentsTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.homeassistant; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -37,11 +37,11 @@ import org.mockito.quality.Strictness; 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 org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory; import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; import org.openhab.core.test.java.JavaOSGiTest; @@ -83,7 +83,7 @@ public class DiscoverComponentsTest extends JavaOSGiTest { Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create(); - DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.testHomeAssistantThing, + DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING, scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider)); HandlerConfiguration config = new HandlerConfiguration("homeassistant", diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTest.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/HomeAssistantMQTTImplementationTest.java similarity index 65% rename from itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTest.java rename to itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/HomeAssistantMQTTImplementationTest.java index 0cf38fb2f55..dde840756c0 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTest.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/HomeAssistantMQTTImplementationTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.homeassistant; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -25,11 +25,9 @@ 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.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -45,22 +43,19 @@ 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 org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent; +import org.openhab.binding.mqtt.homeassistant.internal.component.Switch; +import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory; 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.library.types.OnOffType; -import org.openhab.core.test.java.JavaOSGiTest; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.openhab.core.util.UIDUtils; -import org.osgi.service.cm.ConfigurationAdmin; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -74,11 +69,9 @@ import com.google.gson.GsonBuilder; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @NonNullByDefault -public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { - private @NonNullByDefault({}) ConfigurationAdmin configurationAdmin; - private @NonNullByDefault({}) MqttService mqttService; - private @NonNullByDefault({}) MqttBrokerConnection embeddedConnection; - private @NonNullByDefault({}) MqttBrokerConnection connection; +public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest { + + private @NonNullByDefault({}) MqttBrokerConnection haConnection; private int registeredTopics = 100; private @Nullable Throwable failure; @@ -93,24 +86,17 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { private final MqttConnectionObserver failIfChange = (state, error) -> assertThat(state, is(MqttConnectionState.CONNECTED)); private final String testObjectTopic = "homeassistant/switch/node/" - + ThingChannelConstants.testHomeAssistantThing.getId(); + + ThingChannelConstants.TEST_HOME_ASSISTANT_THING.getId(); + @Override @BeforeEach public void beforeEach() throws Exception { - registerVolatileStorageService(); - configurationAdmin = getService(ConfigurationAdmin.class); - mqttService = getService(MqttService.class); + super.beforeEach(); - // Wait for the EmbeddedBrokerService internal connection to be connected - embeddedConnection = new EmbeddedBrokerTools(configurationAdmin, mqttService).waitForConnection(); - - connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(), - embeddedConnection.isSecure(), "ha_mqtt"); - connection.start().get(2, TimeUnit.SECONDS); - assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED)); + haConnection = createBrokerConnection("ha_mqtt"); // If the connection state changes in between -> fail - connection.addConnectionObserver(failIfChange); + haConnection.addConnectionObserver(failIfChange); // Create topic string and config for one example HA component (a Switch) final String config = "{'name':'testname','state_topic':'" + testObjectTopic + "/state','command_topic':'" @@ -118,8 +104,8 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { // Publish component configurations and component states to MQTT List> futures = new ArrayList<>(); - futures.add(embeddedConnection.publish(testObjectTopic + "/config", config.getBytes(), 0, true)); - futures.add(embeddedConnection.publish(testObjectTopic + "/state", "ON".getBytes(), 0, true)); + futures.add(publish(testObjectTopic + "/config", config)); + futures.add(publish(testObjectTopic + "/state", "ON")); registeredTopics = futures.size(); CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(2, TimeUnit.SECONDS); @@ -129,41 +115,42 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { doReturn(null).when(transformationServiceProvider).getTransformationService(any()); } + @Override @AfterEach public void afterEach() throws Exception { - if (connection != null) { - connection.removeConnectionObserver(failIfChange); - connection.stop().get(2, TimeUnit.SECONDS); + if (haConnection != null) { + haConnection.removeConnectionObserver(failIfChange); + haConnection.stop().get(5, TimeUnit.SECONDS); } + + super.afterEach(); } @Test - public void reconnectTest() throws InterruptedException, ExecutionException, TimeoutException { - connection.removeConnectionObserver(failIfChange); - connection.stop().get(2, TimeUnit.SECONDS); - connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(), - embeddedConnection.isSecure(), "ha_mqtt"); - connection.start().get(2, TimeUnit.SECONDS); + public void reconnectTest() throws Exception { + haConnection.removeConnectionObserver(failIfChange); + haConnection.stop().get(5, TimeUnit.SECONDS); + haConnection = createBrokerConnection("ha_mqtt"); } @Test - public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException { + public void retrieveAllTopics() throws Exception { CountDownLatch c = new CountDownLatch(registeredTopics); - connection.subscribe("homeassistant/+/+/" + ThingChannelConstants.testHomeAssistantThing.getId() + "/#", - (topic, payload) -> c.countDown()).get(2, TimeUnit.SECONDS); + haConnection.subscribe("homeassistant/+/+/" + ThingChannelConstants.TEST_HOME_ASSISTANT_THING.getId() + "/#", + (topic, payload) -> c.countDown()).get(5, TimeUnit.SECONDS); assertTrue(c.await(2, TimeUnit.SECONDS), - "Connection " + connection.getClientId() + " not retrieving all topics"); + "Connection " + haConnection.getClientId() + " not retrieving all topics"); } @Test - public void parseHATree() throws InterruptedException, ExecutionException, TimeoutException { + public void parseHATree() throws Exception { MqttChannelTypeProvider channelTypeProvider = mock(MqttChannelTypeProvider.class); final Map> haComponents = new HashMap<>(); Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create(); ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4); - DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.testHomeAssistantThing, + DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING, scheduler, channelStateUpdateListener, availabilityTracker, gson, transformationServiceProvider)); // The DiscoverComponents object calls ComponentDiscovered callbacks. @@ -171,15 +158,15 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { // 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); + haComponents.put(c.getGroupUID().getId(), c); c.addChannelTypes(channelTypeProvider); - channelTypeProvider.setChannelGroupType(c.groupTypeUID(), c.type()); + channelTypeProvider.setChannelGroupType(c.getGroupTypeUID(), c.getType()); latch.countDown(); }; // Start the discovery for 2000ms. Forced timeout after 4000ms. HaID haID = new HaID(testObjectTopic + "/config"); - CompletableFuture future = discover.startDiscovery(connection, 2000, Collections.singleton(haID), cd) + CompletableFuture future = discover.startDiscovery(haConnection, 2000, Collections.singleton(haID), cd) .thenRun(() -> { }).exceptionally(e -> { failure = e; @@ -187,7 +174,7 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { }); assertTrue(latch.await(4, TimeUnit.SECONDS)); - future.get(2, TimeUnit.SECONDS); + future.get(5, TimeUnit.SECONDS); // No failure expected and one discovered result assertNull(failure); @@ -199,13 +186,13 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { verify(channelTypeProvider, times(1)).setChannelType(any(), any()); String channelGroupId = UIDUtils - .encode("node_" + ThingChannelConstants.testHomeAssistantThing.getId() + "_switch"); + .encode("node_" + ThingChannelConstants.TEST_HOME_ASSISTANT_THING.getId() + "_switch"); - State value = haComponents.get(channelGroupId).channelTypes().get(ComponentSwitch.switchChannelID).getState() - .getCache().getChannelState(); + State value = haComponents.get(channelGroupId).getChannel(Switch.SWITCH_CHANNEL_ID).getState().getCache() + .getChannelState(); assertThat(value, is(UnDefType.UNDEF)); - haComponents.values().stream().map(e -> e.start(connection, scheduler, 100)) + haComponents.values().stream().map(e -> e.start(haConnection, scheduler, 100)) .reduce(CompletableFuture.completedFuture(null), (a, v) -> a.thenCompose(b -> v)).exceptionally(e -> { failure = e; return null; @@ -215,8 +202,8 @@ public class HomeAssistantMQTTImplementationTest extends JavaOSGiTest { verify(channelStateUpdateListener, timeout(4000).times(1)).updateChannelState(any(), any()); // Value should be ON now. - value = haComponents.get(channelGroupId).channelTypes().get(ComponentSwitch.switchChannelID).getState() - .getCache().getChannelState(); + value = haComponents.get(channelGroupId).getChannel(Switch.SWITCH_CHANNEL_ID).getState().getCache() + .getChannelState(); assertThat(value, is(OnOffType.ON)); } } diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/MqttOSGiTest.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/MqttOSGiTest.java new file mode 100644 index 00000000000..79415f70aab --- /dev/null +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/MqttOSGiTest.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010-2022 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.homeassistant; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.core.io.transport.mqtt.MqttConnectionState; +import org.openhab.core.test.java.JavaOSGiTest; + +import io.moquette.BrokerConstants; +import io.moquette.broker.Server; + +/** + * Creates a Moquette MQTT broker instance and a {@link MqttBrokerConnection} for testing MQTT bindings. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class MqttOSGiTest extends JavaOSGiTest { + + private static final String BROKER_ID = "test-broker"; + private static final int BROKER_PORT = Integer.getInteger("mqttbroker.port", 1883); + + protected @NonNullByDefault({}) MqttBrokerConnection brokerConnection; + + private Server moquetteServer = new Server(); + + @BeforeEach + public void beforeEach() throws Exception { + registerVolatileStorageService(); + + moquetteServer = new Server(); + moquetteServer.startServer(brokerProperties()); + + brokerConnection = createBrokerConnection(BROKER_ID); + } + + @AfterEach + public void afterEach() throws Exception { + brokerConnection.stop().get(5, TimeUnit.SECONDS); + moquetteServer.stopServer(); + } + + private Properties brokerProperties() { + Properties properties = new Properties(); + properties.put(BrokerConstants.HOST_PROPERTY_NAME, BrokerConstants.HOST); + properties.put(BrokerConstants.PORT_PROPERTY_NAME, String.valueOf(BROKER_PORT)); + properties.put(BrokerConstants.SSL_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + properties.put(BrokerConstants.WEB_SOCKET_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + properties.put(BrokerConstants.WSS_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + return properties; + } + + protected MqttBrokerConnection createBrokerConnection(String clientId) throws Exception { + MqttBrokerConnection connection = new MqttBrokerConnection(BrokerConstants.HOST, BROKER_PORT, false, clientId); + connection.setQos(1); + connection.start().get(5, TimeUnit.SECONDS); + + waitForAssert(() -> assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED))); + + return connection; + } + + protected CompletableFuture publish(String topic, String message) { + return brokerConnection.publish(topic, message.getBytes(StandardCharsets.UTF_8), 1, true); + } +} diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/ThingChannelConstants.java similarity index 83% rename from itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java rename to itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/ThingChannelConstants.java index 2c9c856cb1c..5d69d00ee4c 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/homeassistant/ThingChannelConstants.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.homeassistant; import static org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants.HOMEASSISTANT_MQTT_THING; @@ -25,5 +25,5 @@ import org.openhab.core.thing.ThingUID; @NonNullByDefault public class ThingChannelConstants { // Common ThingUID and ChannelUIDs - public static final ThingUID testHomeAssistantThing = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234"); + public static final ThingUID TEST_HOME_ASSISTANT_THING = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234"); } diff --git a/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun b/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun index 2c862325e96..304906cf254 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun +++ b/itests/org.openhab.binding.mqtt.homie.tests/itest.bndrun @@ -3,93 +3,111 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.mqtt.homie +Import-Package: \ + com.bugsnag.*;resolution:=optional,\ + com.librato.metrics.reporter.*;resolution:=optional,\ + * + +-includeresource: \ + moquette-broker-[0-9.]*.jar;lib:=true + -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' + bnd.identity;id='org.openhab.core.thing.xml' # We would like to use the "volatile" storage only -runblacklist: \ bnd.identity;id='org.openhab.core.storage.json' --runvm: \ +-runvm.mqtt: \ -Dio.netty.noUnsafe=true,\ - -Dmqttembeddedbroker.port=${mqttembeddedbroker.port} + -Dmqttbroker.port=${mqttbroker.port} # # 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.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.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ - org.objenesis;version='[2.6.0,2.6.1)',\ org.osgi.service.event;version='[1.4.0,1.4.1)',\ - slf4j.api;version='[1.7.25,1.7.26)',\ - org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\ - 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)',\ - 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)',\ - biz.aQute.tester.junit-platform;version='[5.1.2,5.1.3)',\ - com.google.dagger;version='[2.20.0,2.20.1)',\ - com.hivemq.client.mqtt;version='[1.1.2,1.1.3)',\ - io.netty.codec-http;version='[4.1.34,4.1.35)',\ - io.netty.transport-native-epoll;version='[4.1.34,4.1.35)',\ - io.netty.transport-native-unix-common;version='[4.1.34,4.1.35)',\ - io.reactivex.rxjava2.rxjava;version='[2.2.5,2.2.6)',\ - junit-jupiter-api;version='[5.6.2,5.6.3)',\ - junit-jupiter-engine;version='[5.6.2,5.6.3)',\ - junit-platform-commons;version='[1.6.2,1.6.3)',\ - junit-platform-engine;version='[1.6.2,1.6.3)',\ - junit-platform-launcher;version='[1.6.2,1.6.3)',\ - net.bytebuddy.byte-buddy;version='[1.10.13,1.10.14)',\ - net.bytebuddy.byte-buddy-agent;version='[1.10.13,1.10.14)',\ - org.apache.aries.javax.jax.rs-api;version='[1.0.0,1.0.1)',\ - org.apache.commons.codec;version='[1.10.0,1.10.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.glassfish.hk2.external.javax.inject;version='[2.4.0,2.4.1)',\ org.hamcrest;version='[2.2.0,2.2.1)',\ org.jctools.core;version='[2.1.2,2.1.3)',\ - org.mockito.mockito-core;version='[3.4.6,3.4.7)',\ - org.openhab.binding.mqtt;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.generic;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.homie;version='[3.0.0,3.0.1)',\ - org.openhab.binding.mqtt.homie.tests;version='[3.0.0,3.0.1)',\ - org.openhab.core;version='[3.0.0,3.0.1)',\ - org.openhab.core.binding.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.core;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.discovery;version='[3.0.0,3.0.1)',\ - org.openhab.core.config.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.io.console;version='[3.0.0,3.0.1)',\ - org.openhab.core.io.transport.mqtt;version='[3.0.0,3.0.1)',\ - org.openhab.core.test;version='[3.0.0,3.0.1)',\ - org.openhab.core.thing;version='[3.0.0,3.0.1)',\ - org.openhab.core.thing.xml;version='[3.0.0,3.0.1)',\ - org.openhab.core.transform;version='[3.0.0,3.0.1)',\ - org.openhab.io.mqttembeddedbroker;version='[3.0.0,3.0.1)',\ org.opentest4j;version='[1.2.0,1.2.1)',\ - org.reactivestreams.reactive-streams;version='[1.0.2,1.0.3)',\ jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\ com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\ - org.glassfish.hk2.osgi-resource-locator;version='[1.0.1,1.0.2)',\ - org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)' + org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\ + com.google.dagger;version='[2.27.0,2.27.1)',\ + com.google.gson;version='[2.8.9,2.8.10)',\ + com.hivemq.client.mqtt;version='[1.2.2,1.2.3)',\ + io.netty.buffer;version='[4.1.72,4.1.73)',\ + io.netty.codec;version='[4.1.72,4.1.73)',\ + io.netty.codec-http;version='[4.1.59,4.1.60)',\ + io.netty.codec-socks;version='[4.1.72,4.1.73)',\ + io.netty.common;version='[4.1.72,4.1.73)',\ + io.netty.handler;version='[4.1.72,4.1.73)',\ + io.netty.handler-proxy;version='[4.1.72,4.1.73)',\ + io.netty.resolver;version='[4.1.72,4.1.73)',\ + io.netty.tcnative-classes;version='[2.0.46,2.0.47)',\ + io.netty.transport;version='[4.1.72,4.1.73)',\ + io.netty.transport-native-epoll;version='[4.1.59,4.1.60)',\ + io.netty.transport-native-unix-common;version='[4.1.59,4.1.60)',\ + io.reactivex.rxjava2.rxjava;version='[2.2.19,2.2.20)',\ + jakarta.annotation-api;version='[2.0.0,2.0.1)',\ + jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\ + javax.measure.unit-api;version='[2.1.2,2.1.3)',\ + junit-jupiter-api;version='[5.8.1,5.8.2)',\ + junit-jupiter-engine;version='[5.8.1,5.8.2)',\ + junit-platform-commons;version='[1.8.1,1.8.2)',\ + junit-platform-engine;version='[1.8.1,1.8.2)',\ + junit-platform-launcher;version='[1.8.1,1.8.2)',\ + net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\ + net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\ + org.apache.aries.javax.jax.rs-api;version='[1.0.1,1.0.2)',\ + org.apache.felix.configadmin;version='[1.9.22,1.9.23)',\ + org.apache.felix.scr;version='[2.1.30,2.1.31)',\ + org.eclipse.jetty.http;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.io;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.security;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.server;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.servlet;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.util;version='[9.4.43,9.4.44)',\ + org.eclipse.jetty.util.ajax;version='[9.4.43,9.4.44)',\ + org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\ + org.jsr-305;version='[3.0.2,3.0.3)',\ + org.mockito.junit-jupiter;version='[4.1.0,4.1.1)',\ + org.mockito.mockito-core;version='[4.1.0,4.1.1)',\ + org.objenesis;version='[3.2.0,3.2.1)',\ + org.openhab.binding.mqtt;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.generic;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.homie;version='[3.3.0,3.3.1)',\ + org.openhab.binding.mqtt.homie.tests;version='[3.3.0,3.3.1)',\ + org.openhab.core;version='[3.3.0,3.3.1)',\ + org.openhab.core.binding.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.core;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.discovery;version='[3.3.0,3.3.1)',\ + org.openhab.core.config.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.io.console;version='[3.3.0,3.3.1)',\ + org.openhab.core.io.transport.mqtt;version='[3.3.0,3.3.1)',\ + org.openhab.core.test;version='[3.3.0,3.3.1)',\ + org.openhab.core.thing;version='[3.3.0,3.3.1)',\ + org.openhab.core.thing.xml;version='[3.3.0,3.3.1)',\ + org.openhab.core.transform;version='[3.3.0,3.3.1)',\ + org.ops4j.pax.logging.pax-logging-api;version='[2.0.14,2.0.15)',\ + org.osgi.service.cm;version='[1.6.0,1.6.1)',\ + org.osgi.util.function;version='[1.2.0,1.2.1)',\ + org.osgi.util.promise;version='[1.2.0,1.2.1)',\ + org.reactivestreams.reactive-streams;version='[1.0.3,1.0.4)',\ + si-units;version='[2.1.0,2.1.1)',\ + si.uom.si-quantity;version='[2.1.0,2.1.1)',\ + tech.units.indriya;version='[2.1.2,2.1.3)',\ + uom-lib-common;version='[2.1.0,2.1.1)',\ + xstream;version='[1.4.19,1.4.20)',\ + com.h2database.mvstore;version='[1.4.199,1.4.200)',\ + com.zaxxer.HikariCP;version='[2.4.7,2.4.8)',\ + io.dropwizard.metrics.core;version='[3.2.2,3.2.3)',\ + io.netty.codec-mqtt;version='[4.1.72,4.1.73)',\ + org.apache.commons.codec;version='[1.10.0,1.10.1)',\ + biz.aQute.tester.junit-platform;version='[6.2.0,6.2.1)' diff --git a/itests/org.openhab.binding.mqtt.homie.tests/pom.xml b/itests/org.openhab.binding.mqtt.homie.tests/pom.xml index a77c9bc7667..a6aa0a365ce 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/pom.xml +++ b/itests/org.openhab.binding.mqtt.homie.tests/pom.xml @@ -7,13 +7,17 @@ org.openhab.addons.itests org.openhab.addons.reactor.itests - 3.1.0-SNAPSHOT + 3.3.0-SNAPSHOT org.openhab.binding.mqtt.homie.tests openHAB Add-ons :: Integration Tests :: MQTT Homie Tests + + 1883 + + org.openhab.addons.bundles @@ -31,52 +35,43 @@ ${project.version} - com.github.j-n-k - moquette-broker - 0.13.0.OH2 - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - - - org.mockito - mockito-core - - + com.h2database + h2-mvstore + 1.4.199 - io.netty - netty-common - ${netty.version} + io.moquette + moquette-broker + 0.15 io.netty netty-buffer ${netty.version} - - io.netty - netty-transport - ${netty.version} - io.netty netty-codec ${netty.version} - com.h2database - h2-mvstore - 1.4.199 + io.netty + netty-codec-mqtt + ${netty.version} io.netty - netty-codec-mqtt + netty-common + ${netty.version} + + + io.netty + netty-handler + ${netty.version} + + + io.netty + netty-handler-proxy ${netty.version} @@ -86,7 +81,7 @@ io.netty - netty-handler + netty-transport ${netty.version} @@ -98,14 +93,14 @@ build-helper-maven-plugin - reserve-network-port + reserve-mqtt-broker-port reserve-network-port process-resources - mqttembeddedbroker.port + mqttbroker.port diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/Constants.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/Constants.java deleted file mode 100644 index f9f59254570..00000000000 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/Constants.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2010-2022 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; - -/** - * MQTT embedded broker constants - * - * @author David Graeff - Initial contribution - */ -public class Constants { - /** - * The broker connection client ID. You can request the embedded broker connection via the MqttService: - * - *
-     * MqttBrokerConnection c = mqttService.getBrokerConnection(Constants.CLIENTID);
-     * 
- */ - public static final String CLIENTID = "embedded-mqtt-broker"; - - /** - * The broker persistent identifier used for identifying configurations. - */ - public static final String PID = "org.openhab.core.mqttembeddedbroker"; - - /** - * The configuration key used for configuring the embedded broker port. - */ - public static final String PORT = "port"; -} diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java deleted file mode 100644 index f7e10c4681d..00000000000 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/EmbeddedBrokerTools.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -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.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; - -/** - * A full implementation test, that starts the embedded MQTT broker and publishes a homeassistant MQTT discovery device - * tree. - * - * @author David Graeff - Initial contribution - * @author Wouter Born - Support running MQTT itests in parallel by reconfiguring embedded broker port - */ -@NonNullByDefault -public class EmbeddedBrokerTools { - - private static final int BROKER_PORT = Integer.getInteger("mqttembeddedbroker.port", 1883); - - private final ConfigurationAdmin configurationAdmin; - private final MqttService mqttService; - - public @Nullable MqttBrokerConnection embeddedConnection; - - public EmbeddedBrokerTools(ConfigurationAdmin configurationAdmin, MqttService mqttService) { - this.configurationAdmin = configurationAdmin; - this.mqttService = mqttService; - } - - /** - * Request the embedded broker connection from the {@link MqttService} and wait for a connection to be established. - * - * @throws InterruptedException - * @throws IOException - */ - public MqttBrokerConnection waitForConnection() throws InterruptedException, IOException { - reconfigurePort(); - - embeddedConnection = mqttService.getBrokerConnection(Constants.CLIENTID); - if (embeddedConnection == null) { - Semaphore semaphore = new Semaphore(1); - semaphore.acquire(); - MqttServiceObserver observer = new MqttServiceObserver() { - - @Override - public void brokerAdded(String brokerID, MqttBrokerConnection broker) { - if (brokerID.equals(Constants.CLIENTID)) { - embeddedConnection = broker; - semaphore.release(); - } - } - - @Override - public void brokerRemoved(String brokerID, MqttBrokerConnection broker) { - } - }; - mqttService.addBrokersListener(observer); - assertTrue(semaphore.tryAcquire(5, TimeUnit.SECONDS), "Wait for embedded connection client failed"); - } - 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(semaphore.tryAcquire(5, TimeUnit.SECONDS), "Connection " + embeddedConnection.getClientId() - + " failed. State: " + embeddedConnection.connectionState()); - return embeddedConnection; - } - - public void reconfigurePort() throws IOException { - Configuration configuration = configurationAdmin.getConfiguration(Constants.PID, null); - - Dictionary properties = configuration.getProperties(); - if (properties == null) { - properties = new Hashtable<>(); - } - - Integer currentPort = (Integer) properties.get(Constants.PORT); - if (currentPort == null || currentPort.intValue() != BROKER_PORT) { - properties.put(Constants.PORT, BROKER_PORT); - configuration.update(properties); - // Remove the connection to make sure the test waits for the new connection to become available - mqttService.removeBrokerConnection(Constants.CLIENTID); - } - } -} diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/HomieImplementationTest.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/HomieImplementationTest.java similarity index 78% rename from itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/HomieImplementationTest.java rename to itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/HomieImplementationTest.java index be6873179bc..2ff5f27db90 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/HomieImplementationTest.java +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/HomieImplementationTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.homie; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -18,17 +18,14 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.nio.charset.StandardCharsets; 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.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.AfterEach; @@ -57,12 +54,10 @@ import org.openhab.binding.mqtt.homie.internal.homie300.PropertyHelper; 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.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; -import org.openhab.core.test.java.JavaOSGiTest; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; import org.openhab.core.types.UnDefType; -import org.osgi.service.cm.ConfigurationAdmin; /** * A full implementation test, that starts the embedded MQTT broker and publishes a homie device tree. @@ -72,15 +67,12 @@ import org.osgi.service.cm.ConfigurationAdmin; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @NonNullByDefault -public class HomieImplementationTest extends JavaOSGiTest { +public class HomieImplementationTest extends MqttOSGiTest { private static final String BASE_TOPIC = "homie"; - private static final String DEVICE_ID = ThingChannelConstants.testHomieThing.getId(); + private static final String DEVICE_ID = ThingChannelConstants.TEST_HOME_THING.getId(); private static final String DEVICE_TOPIC = BASE_TOPIC + "/" + DEVICE_ID; - private @NonNullByDefault({}) ConfigurationAdmin configurationAdmin; - private @NonNullByDefault({}) MqttService mqttService; - private @NonNullByDefault({}) MqttBrokerConnection embeddedConnection; - private @NonNullByDefault({}) MqttBrokerConnection connection; + private @NonNullByDefault({}) MqttBrokerConnection homieConnection; private int registeredTopics = 100; // The handler is not tested here, so just mock the callback @@ -100,23 +92,15 @@ public class HomieImplementationTest extends JavaOSGiTest { private String propertyTestTopic = ""; + @Override @BeforeEach public void beforeEach() throws Exception { - registerVolatileStorageService(); - configurationAdmin = getService(ConfigurationAdmin.class); - mqttService = getService(MqttService.class); + super.beforeEach(); - // Wait for the EmbeddedBrokerService internal connection to be connected - embeddedConnection = new EmbeddedBrokerTools(configurationAdmin, mqttService).waitForConnection(); - embeddedConnection.setQos(1); + homieConnection = createBrokerConnection("homie"); - connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(), - embeddedConnection.isSecure(), "homie"); - connection.setQos(1); - connection.start().get(5, TimeUnit.SECONDS); - assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED)); // If the connection state changes in between -> fail - connection.addConnectionObserver(failIfChange); + homieConnection.addConnectionObserver(failIfChange); List> futures = new ArrayList<>(); futures.add(publish(DEVICE_TOPIC + "/$homie", "3.0")); @@ -152,47 +136,46 @@ public class HomieImplementationTest extends JavaOSGiTest { futures.add(publish(propertyTestTopic + "/$datatype", "boolean")); registeredTopics = futures.size(); - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(2, TimeUnit.SECONDS); + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(5, TimeUnit.SECONDS); scheduler = new ScheduledThreadPoolExecutor(6); } - private CompletableFuture publish(String topic, String message) { - return embeddedConnection.publish(topic, message.getBytes(StandardCharsets.UTF_8), 0, true); - } - + @Override @AfterEach public void afterEach() throws Exception { - if (connection != null) { - connection.removeConnectionObserver(failIfChange); - connection.stop().get(2, TimeUnit.SECONDS); + if (homieConnection != null) { + homieConnection.removeConnectionObserver(failIfChange); + homieConnection.stop().get(5, TimeUnit.SECONDS); } if (scheduler != null) { scheduler.shutdownNow(); } + super.afterEach(); } @Test - public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException { + public void retrieveAllTopics() throws Exception { // four topics are not under /testnode ! CountDownLatch c = new CountDownLatch(registeredTopics - 4); - connection.subscribe(DEVICE_TOPIC + "/testnode/#", (topic, payload) -> c.countDown()).get(5, TimeUnit.SECONDS); + homieConnection.subscribe(DEVICE_TOPIC + "/testnode/#", (topic, payload) -> c.countDown()).get(5, + TimeUnit.SECONDS); assertTrue(c.await(5, TimeUnit.SECONDS), - "Connection " + connection.getClientId() + " not retrieving all topics "); + "Connection " + homieConnection.getClientId() + " not retrieving all topics "); } @Test - public void retrieveOneAttribute() throws InterruptedException, ExecutionException { - WaitForTopicValue watcher = new WaitForTopicValue(connection, DEVICE_TOPIC + "/$homie"); + public void retrieveOneAttribute() throws Exception { + WaitForTopicValue watcher = new WaitForTopicValue(homieConnection, DEVICE_TOPIC + "/$homie"); assertThat(watcher.waitForTopicValue(1000), is("3.0")); } @SuppressWarnings("null") @Test - public void retrieveAttributes() throws InterruptedException, ExecutionException { - assertThat(connection.hasSubscribers(), is(false)); + public void retrieveAttributes() throws Exception { + assertThat(homieConnection.hasSubscribers(), is(false)); - Node node = new Node(DEVICE_TOPIC, "testnode", ThingChannelConstants.testHomieThing, callback, + Node node = new Node(DEVICE_TOPIC, "testnode", ThingChannelConstants.TEST_HOME_THING, callback, new NodeAttributes()); Property property = spy( new Property(DEVICE_TOPIC + "/testnode", node, "temperature", callback, new PropertyAttributes())); @@ -200,7 +183,7 @@ public class HomieImplementationTest extends JavaOSGiTest { // Create a scheduler ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4); - property.subscribe(connection, scheduler, 500).get(); + property.subscribe(homieConnection, scheduler, 500).get(); assertThat(property.attributes.settable, is(true)); assertThat(property.attributes.retained, is(true)); @@ -214,15 +197,16 @@ public class HomieImplementationTest extends JavaOSGiTest { ChannelState channelState = spy(property.getChannelState()); PropertyHelper.setChannelState(property, channelState); - property.startChannel(connection, scheduler, 500).get(); + property.startChannel(homieConnection, 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))); + assertThat(property.getChannelState().getCache().getChannelState(), + is(new QuantityType<>(10, SIUnits.CELSIUS))); property.stop().get(); - assertThat(connection.hasSubscribers(), is(false)); + assertThat(homieConnection.hasSubscribers(), is(false)); } // Inject a spy'ed property @@ -245,19 +229,19 @@ public class HomieImplementationTest extends JavaOSGiTest { @SuppressWarnings("null") @Test - public void parseHomieTree() throws InterruptedException, ExecutionException, TimeoutException { + public void parseHomieTree() throws Exception { // 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 nodeMap = new ChildMap<>(); Device device = spy( - new Device(ThingChannelConstants.testHomieThing, callback, new DeviceAttributes(), nodeMap)); + new Device(ThingChannelConstants.TEST_HOME_THING, 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(); + device.subscribe(homieConnection, scheduler, 1500).get(); assertThat(device.isInitialized(), is(true)); @@ -306,22 +290,23 @@ public class HomieImplementationTest extends JavaOSGiTest { 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(); + device.startChannels(homieConnection, 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))); + assertThat(property.getChannelState().getCache().getChannelState(), + is(new QuantityType<>(10, SIUnits.CELSIUS))); property = node.properties.get("testRetain"); - WaitForTopicValue watcher = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set"); + WaitForTopicValue watcher = new WaitForTopicValue(brokerConnection, propertyTestTopic + "/set"); // Watch the topic. Publish a retain=false value to MQTT property.getChannelState().publishValue(OnOffType.OFF).get(); - assertThat(watcher.waitForTopicValue(1000), is("false")); + assertThat(watcher.waitForTopicValue(10000), 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(() -> { - WaitForTopicValue w = new WaitForTopicValue(embeddedConnection, propertyTestTopic + "/set"); + WaitForTopicValue w = new WaitForTopicValue(brokerConnection, propertyTestTopic + "/set"); assertNull(w.waitForTopicValue(50)); }, 500, 100); } diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/MqttOSGiTest.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/MqttOSGiTest.java new file mode 100644 index 00000000000..64ae708ad15 --- /dev/null +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/MqttOSGiTest.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010-2022 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; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.core.io.transport.mqtt.MqttConnectionState; +import org.openhab.core.test.java.JavaOSGiTest; + +import io.moquette.BrokerConstants; +import io.moquette.broker.Server; + +/** + * Creates a Moquette MQTT broker instance and a {@link MqttBrokerConnection} for testing MQTT bindings. + * + * @author Wouter Born - Initial contribution + */ +@NonNullByDefault +public class MqttOSGiTest extends JavaOSGiTest { + + private static final String BROKER_ID = "test-broker"; + private static final int BROKER_PORT = Integer.getInteger("mqttbroker.port", 1883); + + protected @NonNullByDefault({}) MqttBrokerConnection brokerConnection; + + private Server moquetteServer = new Server(); + + @BeforeEach + public void beforeEach() throws Exception { + registerVolatileStorageService(); + + moquetteServer = new Server(); + moquetteServer.startServer(brokerProperties()); + + brokerConnection = createBrokerConnection(BROKER_ID); + } + + @AfterEach + public void afterEach() throws Exception { + brokerConnection.stop().get(5, TimeUnit.SECONDS); + moquetteServer.stopServer(); + } + + private Properties brokerProperties() { + Properties properties = new Properties(); + properties.put(BrokerConstants.HOST_PROPERTY_NAME, BrokerConstants.HOST); + properties.put(BrokerConstants.PORT_PROPERTY_NAME, String.valueOf(BROKER_PORT)); + properties.put(BrokerConstants.SSL_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + properties.put(BrokerConstants.WEB_SOCKET_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + properties.put(BrokerConstants.WSS_PORT_PROPERTY_NAME, BrokerConstants.DISABLED_PORT_BIND); + return properties; + } + + protected MqttBrokerConnection createBrokerConnection(String clientId) throws Exception { + MqttBrokerConnection connection = new MqttBrokerConnection(BrokerConstants.HOST, BROKER_PORT, false, clientId); + connection.setQos(1); + connection.start().get(5, TimeUnit.SECONDS); + + waitForAssert(() -> assertThat(connection.connectionState(), is(MqttConnectionState.CONNECTED))); + + return connection; + } + + protected CompletableFuture publish(String topic, String message) { + return brokerConnection.publish(topic, message.getBytes(StandardCharsets.UTF_8), 1, true); + } +} diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/ThingChannelConstants.java similarity index 85% rename from itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java rename to itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/ThingChannelConstants.java index b254837c48f..60646acba24 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/homie/ThingChannelConstants.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.homie; import static org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants.HOMIE300_MQTT_THING; @@ -25,5 +25,5 @@ import org.openhab.core.thing.ThingUID; @NonNullByDefault public class ThingChannelConstants { // Common ThingUID and ChannelUIDs - public final static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123"); + public static final ThingUID TEST_HOME_THING = new ThingUID(HOMIE300_MQTT_THING, "device123"); } diff --git a/itests/pom.xml b/itests/pom.xml index d5cb682fa9c..f58c4d9a7dc 100644 --- a/itests/pom.xml +++ b/itests/pom.xml @@ -24,10 +24,8 @@ org.openhab.binding.max.tests org.openhab.binding.mielecloud.tests org.openhab.binding.modbus.tests - + org.openhab.binding.mqtt.homeassistant.tests + org.openhab.binding.mqtt.homie.tests org.openhab.binding.nest.tests org.openhab.binding.ntp.tests org.openhab.binding.systeminfo.tests