2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mqtt.generic.mapping;
15 import static java.lang.annotation.ElementType.FIELD;
16 import static org.hamcrest.CoreMatchers.is;
17 import static org.hamcrest.MatcherAssert.assertThat;
18 import static org.junit.jupiter.api.Assertions.*;
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.Mockito.doReturn;
22 import java.lang.annotation.Retention;
23 import java.lang.annotation.RetentionPolicy;
24 import java.lang.annotation.Target;
25 import java.lang.reflect.Field;
26 import java.math.BigDecimal;
27 import java.util.concurrent.CompletableFuture;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ScheduledExecutorService;
30 import java.util.concurrent.ScheduledThreadPoolExecutor;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.TimeoutException;
34 import org.eclipse.jdt.annotation.NonNullByDefault;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.junit.jupiter.api.BeforeEach;
37 import org.junit.jupiter.api.Test;
38 import org.junit.jupiter.api.extension.ExtendWith;
39 import org.mockito.Mock;
40 import org.mockito.junit.jupiter.MockitoExtension;
41 import org.mockito.junit.jupiter.MockitoSettings;
42 import org.mockito.quality.Strictness;
43 import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic.FieldChanged;
44 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
47 * Tests cases for {@link org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic}.
49 * @author David Graeff - Initial contribution
51 @ExtendWith(MockitoExtension.class)
52 @MockitoSettings(strictness = Strictness.LENIENT)
54 public class SubscribeFieldToMQTTtopicTests {
55 @Retention(RetentionPolicy.RUNTIME)
57 private @interface TestValue {
58 String value() default "";
62 public static class Attributes extends AbstractMqttAttributeClass {
63 @SuppressWarnings("unused")
64 public transient String ignoreTransient = "";
65 @SuppressWarnings("unused")
66 public final String ignoreFinal = "";
68 public @TestValue("string") @Nullable String aString;
69 public @TestValue("false") @Nullable Boolean aBoolean;
70 public @TestValue("10") @Nullable Long aLong;
71 public @TestValue("10") @Nullable Integer aInteger;
72 public @TestValue("10") @Nullable BigDecimal aDecimal;
74 public @TestValue("10") @TopicPrefix("a") int aInt = 24;
75 public @TestValue("false") boolean aBool = true;
76 public @TestValue("abc,def") @MQTTvalueTransform(splitCharacter = ",") String @Nullable [] properties;
78 public enum ReadyState {
84 public @TestValue("init") ReadyState state = ReadyState.unknown;
86 public enum DataTypeEnum {
92 public @TestValue("integer") @MQTTvalueTransform(suffix = "_") DataTypeEnum datatype = DataTypeEnum.unknown;
95 public Object getFieldsOf() {
100 Attributes attributes = new Attributes();
102 private @Mock @NonNullByDefault({}) MqttBrokerConnection connectionMock;
103 private @Mock @NonNullByDefault({}) FieldChanged fieldChangedMock;
106 public void setUp() {
107 doReturn(CompletableFuture.completedFuture(true)).when(connectionMock).subscribe(any(), any());
111 public void timeoutIfNoMessageReceive() throws Exception {
112 final Field field = Attributes.class.getField("aInt");
113 ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
115 SubscribeFieldToMQTTtopic subscriber = new SubscribeFieldToMQTTtopic(scheduler, field, fieldChangedMock,
116 "homie/device123", false);
117 assertThrows(TimeoutException.class,
118 () -> subscriber.subscribeAndReceive(connectionMock, 1000).get(50, TimeUnit.MILLISECONDS));
122 public void mandatoryMissing() throws Exception {
123 final Field field = Attributes.class.getField("aInt");
124 ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
126 SubscribeFieldToMQTTtopic subscriber = new SubscribeFieldToMQTTtopic(scheduler, field, fieldChangedMock,
127 "homie/device123", true);
128 assertThrows(ExecutionException.class, () -> subscriber.subscribeAndReceive(connectionMock, 50).get());
132 public void messageReceive() throws Exception {
133 final FieldChanged changed = (field, value) -> {
135 field.set(attributes.getFieldsOf(), value);
136 } catch (IllegalArgumentException | IllegalAccessException e) {
137 fail(e.getMessage());
140 final Field field = Attributes.class.getField("aInt");
141 ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
143 SubscribeFieldToMQTTtopic subscriber = new SubscribeFieldToMQTTtopic(scheduler, field, changed,
144 "homie/device123", false);
145 CompletableFuture<@Nullable Void> future = subscriber.subscribeAndReceive(connectionMock, 1000);
147 // Simulate a received MQTT message
148 subscriber.processMessage("ignored", "10".getBytes());
149 // No timeout should happen
150 future.get(50, TimeUnit.MILLISECONDS);
151 assertThat(attributes.aInt, is(10));