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