]> git.basschouten.com Git - openhab-addons.git/blob
4a49fcae7e7c0b3bfd9f259bf3590d962fb74e3f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.dsmr.internal.device;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.TooManyListenersException;
23 import java.util.concurrent.ScheduledExecutorService;
24 import java.util.concurrent.atomic.AtomicReference;
25 import java.util.stream.Stream;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.junit.jupiter.api.BeforeEach;
30 import org.junit.jupiter.api.Test;
31 import org.junit.jupiter.api.extension.ExtendWith;
32 import org.mockito.Mock;
33 import org.mockito.junit.jupiter.MockitoExtension;
34 import org.openhab.binding.dsmr.internal.DSMRBindingConstants;
35 import org.openhab.binding.dsmr.internal.TelegramReaderUtil;
36 import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice.DeviceState;
37 import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent;
38 import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram;
39 import org.openhab.core.io.transport.serial.PortInUseException;
40 import org.openhab.core.io.transport.serial.SerialPort;
41 import org.openhab.core.io.transport.serial.SerialPortEvent;
42 import org.openhab.core.io.transport.serial.SerialPortEventListener;
43 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
44 import org.openhab.core.io.transport.serial.SerialPortManager;
45
46 /**
47  * Test class for {@link DSMRSerialAutoDevice}.
48  *
49  * @author Hilbrand Bouwkamp - Initial contribution
50  */
51 @ExtendWith(MockitoExtension.class)
52 @NonNullByDefault
53 public class DSMRSerialAutoDeviceTest {
54
55     private static final String DUMMY_PORTNAME = "/dev/dummy-serial";
56     private static final String TELEGRAM_NAME = "dsmr_50";
57
58     private @NonNullByDefault({}) @Mock SerialPortIdentifier mockIdentifier;
59     private @NonNullByDefault({}) @Mock ScheduledExecutorService scheduler;
60     private @NonNullByDefault({}) @Mock SerialPort mockSerialPort;
61
62     private final SerialPortManager serialPortManager = new SerialPortManager() {
63         @Override
64         public @Nullable SerialPortIdentifier getIdentifier(String name) {
65             assertEquals(DUMMY_PORTNAME, name, "Expect the passed serial port name");
66             return mockIdentifier;
67         }
68
69         @Override
70         public Stream<SerialPortIdentifier> getIdentifiers() {
71             return Stream.empty();
72         }
73     };
74     private @NonNullByDefault({}) SerialPortEventListener serialPortEventListener;
75
76     @BeforeEach
77     public void setUp() throws PortInUseException, TooManyListenersException {
78         doAnswer(a -> {
79             serialPortEventListener = a.getArgument(0);
80             return null;
81         }).when(mockSerialPort).addEventListener(any());
82     }
83
84     @Test
85     public void testHandlingDataAndRestart() throws IOException, PortInUseException {
86         mockValidSerialPort();
87         AtomicReference<@Nullable P1Telegram> telegramRef = new AtomicReference<>(null);
88         DSMREventListener listener = new DSMREventListener() {
89             @Override
90             public void handleTelegramReceived(P1Telegram telegram) {
91                 telegramRef.set(telegram);
92             }
93
94             @Override
95             public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
96                 fail("No handleErrorEvent Expected" + connectorErrorEvent);
97             }
98         };
99         try (InputStream inputStream = new ByteArrayInputStream(TelegramReaderUtil.readRawTelegram(TELEGRAM_NAME))) {
100             when(mockSerialPort.getInputStream()).thenReturn(inputStream);
101             DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener,
102                     new DSMRTelegramListener(), scheduler, 1);
103             device.start();
104             assertSame(DeviceState.DISCOVER_SETTINGS, device.getState(), "Expect to be starting discovery state");
105             serialPortEventListener
106                     .serialEvent(new MockSerialPortEvent(mockSerialPort, SerialPortEvent.BI, false, true));
107             assertSame(DeviceState.DISCOVER_SETTINGS, device.getState(), "Expect to be still in discovery state");
108             serialPortEventListener
109                     .serialEvent(new MockSerialPortEvent(mockSerialPort, SerialPortEvent.DATA_AVAILABLE, false, true));
110             assertSame(DeviceState.NORMAL, device.getState(), "Expect to be in normal state");
111             device.restart();
112             assertSame(DeviceState.NORMAL, device.getState(), "Expect not to start rediscovery when in normal state");
113             device.stop();
114         }
115         assertNotNull(telegramRef.get(), "Expected to have read a telegram");
116     }
117
118     @Test
119     public void testHandleError() throws IOException, PortInUseException {
120         AtomicReference<@Nullable DSMRConnectorErrorEvent> eventRef = new AtomicReference<>(null);
121         DSMREventListener listener = new DSMREventListener() {
122             @Override
123             public void handleTelegramReceived(P1Telegram telegram) {
124                 fail("No telegram expected:" + telegram);
125             }
126
127             @Override
128             public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
129                 eventRef.set(connectorErrorEvent);
130             }
131         };
132         try (InputStream inputStream = new ByteArrayInputStream(new byte[] {})) {
133             when(mockSerialPort.getInputStream()).thenReturn(inputStream);
134             // Trigger device to go into error stage with port in use.
135             doThrow(new PortInUseException(new Exception())).when(mockIdentifier)
136                     .open(eq(DSMRBindingConstants.DSMR_PORT_NAME), anyInt());
137             DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener,
138                     new DSMRTelegramListener(), scheduler, 1);
139             device.start();
140             assertSame(DSMRConnectorErrorEvent.IN_USE, eventRef.get(), "Expected an error");
141             assertSame(DeviceState.ERROR, device.getState(), "Expect to be in error state");
142             // Trigger device to restart
143             mockValidSerialPort();
144             device.restart();
145             assertSame(DeviceState.DISCOVER_SETTINGS, device.getState(), "Expect to be starting discovery state");
146             // Trigger device to go into error stage with port doesn't exist.
147             mockIdentifier = null;
148             device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(),
149                     scheduler, 1);
150             device.start();
151             assertSame(DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get(), "Expected an error");
152             assertSame(DeviceState.ERROR, device.getState(), "Expect to be in error state");
153         }
154     }
155
156     private void mockValidSerialPort() throws PortInUseException {
157         doReturn(mockSerialPort).when(mockIdentifier).open(anyString(), anyInt());
158     }
159
160     /**
161      * Mock class implementing {@link SerialPortEvent}.
162      */
163     private static class MockSerialPortEvent implements SerialPortEvent {
164         private final int eventType;
165         private final boolean newValue;
166
167         public MockSerialPortEvent(SerialPort mockSerialPort, int eventType, boolean oldValue, boolean newValue) {
168             this.eventType = eventType;
169             this.newValue = newValue;
170         }
171
172         @Override
173         public int getEventType() {
174             return eventType;
175         }
176
177         @Override
178         public boolean getNewValue() {
179             return newValue;
180         }
181     }
182 }