]> git.basschouten.com Git - openhab-addons.git/blob
71de8c7b5cdffc3564068fca4c9000214645a876
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.generic;
14
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.junit.Assert.*;
17 import static org.mockito.ArgumentMatchers.*;
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.*;
20 import static org.mockito.MockitoAnnotations.initMocks;
21
22 import java.math.BigDecimal;
23 import java.time.ZonedDateTime;
24 import java.time.format.DateTimeFormatter;
25 import java.util.Arrays;
26 import java.util.concurrent.CompletableFuture;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.ScheduledExecutorService;
29 import java.util.concurrent.ScheduledThreadPoolExecutor;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
32
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.mockito.Mock;
38 import org.mockito.Spy;
39 import org.openhab.binding.mqtt.generic.mapping.ColorMode;
40 import org.openhab.binding.mqtt.generic.values.ColorValue;
41 import org.openhab.binding.mqtt.generic.values.DateTimeValue;
42 import org.openhab.binding.mqtt.generic.values.ImageValue;
43 import org.openhab.binding.mqtt.generic.values.LocationValue;
44 import org.openhab.binding.mqtt.generic.values.NumberValue;
45 import org.openhab.binding.mqtt.generic.values.PercentageValue;
46 import org.openhab.binding.mqtt.generic.values.TextValue;
47 import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
48 import org.openhab.core.library.types.HSBType;
49 import org.openhab.core.library.types.RawType;
50 import org.openhab.core.library.types.StringType;
51 import org.openhab.core.thing.ChannelUID;
52
53 /**
54  * Tests the {@link ChannelState} class.
55  *
56  * @author David Graeff - Initial contribution
57  */
58 public class ChannelStateTests {
59     @Mock
60     private MqttBrokerConnection connection;
61
62     @Mock
63     private ChannelStateUpdateListener channelStateUpdateListener;
64
65     @Mock
66     private ChannelUID channelUID;
67
68     @Spy
69     private TextValue textValue;
70
71     private ScheduledExecutorService scheduler;
72
73     private ChannelConfig config = ChannelConfigBuilder.create("state", "command").build();
74
75     @Before
76     public void setUp() {
77         initMocks(this);
78         CompletableFuture<Void> voidFutureComplete = new CompletableFuture<>();
79         voidFutureComplete.complete(null);
80         doReturn(voidFutureComplete).when(connection).unsubscribeAll();
81         doReturn(CompletableFuture.completedFuture(true)).when(connection).subscribe(any(), any());
82         doReturn(CompletableFuture.completedFuture(true)).when(connection).unsubscribe(any(), any());
83         doReturn(CompletableFuture.completedFuture(true)).when(connection).publish(any(), any());
84         doReturn(CompletableFuture.completedFuture(true)).when(connection).publish(any(), any(), anyInt(),
85                 anyBoolean());
86
87         scheduler = new ScheduledThreadPoolExecutor(1);
88     }
89
90     @After
91     public void tearDown() {
92         scheduler.shutdownNow();
93     }
94
95     @Test
96     public void noInteractionTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException {
97         ChannelState c = spy(new ChannelState(config, channelUID, textValue, channelStateUpdateListener));
98         c.start(connection, scheduler, 50).get(100, TimeUnit.MILLISECONDS);
99         verify(connection).subscribe(eq("state"), eq(c));
100         c.stop().get();
101         verify(connection).unsubscribe(eq("state"), eq(c));
102     }
103
104     @Test
105     public void publishFormatTest() throws InterruptedException, ExecutionException, TimeoutException {
106         ChannelState c = spy(new ChannelState(config, channelUID, textValue, channelStateUpdateListener));
107
108         c.start(connection, scheduler, 0).get(50, TimeUnit.MILLISECONDS);
109         verify(connection).subscribe(eq("state"), eq(c));
110
111         c.publishValue(new StringType("UPDATE")).get();
112         verify(connection).publish(eq("command"), argThat(p -> Arrays.equals(p, "UPDATE".getBytes())), anyInt(),
113                 eq(false));
114
115         c.config.formatBeforePublish = "prefix%s";
116         c.publishValue(new StringType("UPDATE")).get();
117         verify(connection).publish(eq("command"), argThat(p -> Arrays.equals(p, "prefixUPDATE".getBytes())), anyInt(),
118                 eq(false));
119
120         c.config.formatBeforePublish = "%1$s-%1$s";
121         c.publishValue(new StringType("UPDATE")).get();
122         verify(connection).publish(eq("command"), argThat(p -> Arrays.equals(p, "UPDATE-UPDATE".getBytes())), anyInt(),
123                 eq(false));
124
125         c.config.formatBeforePublish = "%s";
126         c.config.retained = true;
127         c.publishValue(new StringType("UPDATE")).get();
128         verify(connection).publish(eq("command"), any(), anyInt(), eq(true));
129
130         c.stop().get();
131         verify(connection).unsubscribe(eq("state"), eq(c));
132     }
133
134     @Test
135     public void receiveWildcardTest() throws InterruptedException, ExecutionException, TimeoutException {
136         ChannelState c = spy(new ChannelState(ChannelConfigBuilder.create("state/+/topic", "command").build(),
137                 channelUID, textValue, channelStateUpdateListener));
138
139         CompletableFuture<@Nullable Void> future = c.start(connection, scheduler, 100);
140         c.processMessage("state/bla/topic", "A TEST".getBytes());
141         future.get(300, TimeUnit.MILLISECONDS);
142
143         assertThat(textValue.getChannelState().toString(), is("A TEST"));
144         verify(channelStateUpdateListener).updateChannelState(eq(channelUID), any());
145     }
146
147     @Test
148     public void receiveStringTest() throws InterruptedException, ExecutionException, TimeoutException {
149         ChannelState c = spy(new ChannelState(config, channelUID, textValue, channelStateUpdateListener));
150
151         CompletableFuture<@Nullable Void> future = c.start(connection, scheduler, 100);
152         c.processMessage("state", "A TEST".getBytes());
153         future.get(300, TimeUnit.MILLISECONDS);
154
155         assertThat(textValue.getChannelState().toString(), is("A TEST"));
156         verify(channelStateUpdateListener).updateChannelState(eq(channelUID), any());
157     }
158
159     @Test
160     public void receiveDecimalTest() {
161         NumberValue value = new NumberValue(null, null, new BigDecimal(10), null);
162         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
163         c.start(connection, mock(ScheduledExecutorService.class), 100);
164
165         c.processMessage("state", "15".getBytes());
166         assertThat(value.getChannelState().toString(), is("15"));
167
168         c.processMessage("state", "INCREASE".getBytes());
169         assertThat(value.getChannelState().toString(), is("25"));
170
171         c.processMessage("state", "DECREASE".getBytes());
172         assertThat(value.getChannelState().toString(), is("15"));
173
174         verify(channelStateUpdateListener, times(3)).updateChannelState(eq(channelUID), any());
175     }
176
177     @Test
178     public void receiveDecimalFractionalTest() {
179         NumberValue value = new NumberValue(null, null, new BigDecimal(10.5), null);
180         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
181         c.start(connection, mock(ScheduledExecutorService.class), 100);
182
183         c.processMessage("state", "5.5".getBytes());
184         assertThat(value.getChannelState().toString(), is("5.5"));
185
186         c.processMessage("state", "INCREASE".getBytes());
187         assertThat(value.getChannelState().toString(), is("16.0"));
188     }
189
190     @Test
191     public void receivePercentageTest() {
192         PercentageValue value = new PercentageValue(new BigDecimal(-100), new BigDecimal(100), new BigDecimal(10), null,
193                 null);
194         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
195         c.start(connection, mock(ScheduledExecutorService.class), 100);
196
197         c.processMessage("state", "-100".getBytes()); // 0%
198         assertThat(value.getChannelState().toString(), is("0"));
199
200         c.processMessage("state", "100".getBytes()); // 100%
201         assertThat(value.getChannelState().toString(), is("100"));
202
203         c.processMessage("state", "0".getBytes()); // 50%
204         assertThat(value.getChannelState().toString(), is("50"));
205
206         c.processMessage("state", "INCREASE".getBytes());
207         assertThat(value.getChannelState().toString(), is("55"));
208         assertThat(value.getMQTTpublishValue(null), is("10"));
209         assertThat(value.getMQTTpublishValue("%03.0f"), is("010"));
210     }
211
212     @Test
213     public void receiveRGBColorTest() {
214         ColorValue value = new ColorValue(ColorMode.RGB, "FON", "FOFF", 10);
215         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
216         c.start(connection, mock(ScheduledExecutorService.class), 100);
217
218         c.processMessage("state", "ON".getBytes()); // Normal on state
219         assertThat(value.getChannelState().toString(), is("0,0,10"));
220         assertThat(value.getMQTTpublishValue(null), is("25,25,25"));
221
222         c.processMessage("state", "FOFF".getBytes()); // Custom off state
223         assertThat(value.getChannelState().toString(), is("0,0,0"));
224         assertThat(value.getMQTTpublishValue(null), is("0,0,0"));
225
226         c.processMessage("state", "10".getBytes()); // Brightness only
227         assertThat(value.getChannelState().toString(), is("0,0,10"));
228         assertThat(value.getMQTTpublishValue(null), is("25,25,25"));
229
230         HSBType t = HSBType.fromRGB(12, 18, 231);
231
232         c.processMessage("state", "12,18,231".getBytes());
233         assertThat(value.getChannelState(), is(t)); // HSB
234         // rgb -> hsv -> rgb is quite lossy
235         assertThat(value.getMQTTpublishValue(null), is("13,20,225"));
236         assertThat(value.getMQTTpublishValue("%3$d,%2$d,%1$d"), is("225,20,13"));
237     }
238
239     @Test
240     public void receiveHSBColorTest() {
241         ColorValue value = new ColorValue(ColorMode.HSB, "FON", "FOFF", 10);
242         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
243         c.start(connection, mock(ScheduledExecutorService.class), 100);
244
245         c.processMessage("state", "ON".getBytes()); // Normal on state
246         assertThat(value.getChannelState().toString(), is("0,0,10"));
247         assertThat(value.getMQTTpublishValue(null), is("0,0,10"));
248
249         c.processMessage("state", "FOFF".getBytes()); // Custom off state
250         assertThat(value.getChannelState().toString(), is("0,0,0"));
251         assertThat(value.getMQTTpublishValue(null), is("0,0,0"));
252
253         c.processMessage("state", "10".getBytes()); // Brightness only
254         assertThat(value.getChannelState().toString(), is("0,0,10"));
255         assertThat(value.getMQTTpublishValue(null), is("0,0,10"));
256
257         c.processMessage("state", "12,18,100".getBytes());
258         assertThat(value.getChannelState().toString(), is("12,18,100"));
259         assertThat(value.getMQTTpublishValue(null), is("12,18,100"));
260     }
261
262     @Test
263     public void receiveXYYColorTest() {
264         ColorValue value = new ColorValue(ColorMode.XYY, "FON", "FOFF", 10);
265         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
266         c.start(connection, mock(ScheduledExecutorService.class), 100);
267
268         c.processMessage("state", "ON".getBytes()); // Normal on state
269         assertThat(value.getChannelState().toString(), is("0,0,10"));
270         assertThat(value.getMQTTpublishValue(null), is("0.312716,0.329002,10.00"));
271
272         c.processMessage("state", "FOFF".getBytes()); // Custom off state
273         assertThat(value.getChannelState().toString(), is("0,0,0"));
274         assertThat(value.getMQTTpublishValue(null), is("0.312716,0.329002,0.00"));
275
276         c.processMessage("state", "10".getBytes()); // Brightness only
277         assertThat(value.getChannelState().toString(), is("0,0,10"));
278         assertThat(value.getMQTTpublishValue(null), is("0.312716,0.329002,10.00"));
279
280         HSBType t = HSBType.fromXY(0.3f, 0.6f);
281
282         c.processMessage("state", "0.3,0.6,100".getBytes());
283         assertThat(value.getChannelState(), is(t)); // HSB
284         assertThat(value.getMQTTpublishValue(null), is("0.300000,0.600000,100.00"));
285         assertThat(value.getMQTTpublishValue("%3$.1f,%2$.4f,%1$.4f"), is("100.0,0.6000,0.3000"));
286     }
287
288     @Test
289     public void receiveLocationTest() {
290         LocationValue value = new LocationValue();
291         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
292         c.start(connection, mock(ScheduledExecutorService.class), 100);
293
294         c.processMessage("state", "46.833974, 7.108433".getBytes());
295         assertThat(value.getChannelState().toString(), is("46.833974,7.108433"));
296         assertThat(value.getMQTTpublishValue(null), is("46.833974,7.108433"));
297     }
298
299     @Test
300     public void receiveDateTimeTest() {
301         DateTimeValue value = new DateTimeValue();
302         ChannelState subject = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
303         subject.start(connection, mock(ScheduledExecutorService.class), 100);
304
305         ZonedDateTime zd = ZonedDateTime.now();
306         String datetime = zd.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
307
308         subject.processMessage("state", datetime.getBytes());
309
310         String channelState = value.getChannelState().toString();
311         assertTrue("Expected '" + channelState + "' to start with '" + datetime + "'",
312                 channelState.startsWith(datetime));
313         assertThat(value.getMQTTpublishValue(null), is(datetime));
314     }
315
316     @Test
317     public void receiveImageTest() {
318         ImageValue value = new ImageValue();
319         ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener));
320         c.start(connection, mock(ScheduledExecutorService.class), 100);
321
322         byte[] payload = new byte[] { (byte) 0xFF, (byte) 0xD8, 0x01, 0x02, (byte) 0xFF, (byte) 0xD9 };
323         c.processMessage("state", payload);
324         assertThat(value.getChannelState(), is(instanceOf(RawType.class)));
325         assertThat(((RawType) value.getChannelState()).getMimeType(), is("image/jpeg"));
326     }
327 }