2 * Copyright (c) 2010-2021 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.dsmr.internal.device;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
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;
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;
45 * Test class for {@link DSMRSerialAutoDevice}.
47 * @author Hilbrand Bouwkamp - Initial contribution
49 @ExtendWith(MockitoExtension.class)
50 public class DSMRSerialAutoDeviceTest {
52 private static final String DUMMY_PORTNAME = "/dev/dummy-serial";
53 private static final String TELEGRAM_NAME = "dsmr_50";
55 private @Mock SerialPortIdentifier mockIdentifier;
56 private @Mock ScheduledExecutorService scheduler;
57 private @Mock SerialPort mockSerialPort;
59 private SerialPortManager serialPortManager = new SerialPortManager() {
61 public SerialPortIdentifier getIdentifier(String name) {
62 assertEquals(DUMMY_PORTNAME, name, "Expect the passed serial port name");
63 return mockIdentifier;
67 public Stream<SerialPortIdentifier> getIdentifiers() {
68 return Stream.empty();
71 private SerialPortEventListener serialPortEventListener;
74 public void setUp() throws PortInUseException, TooManyListenersException {
76 serialPortEventListener = a.getArgument(0);
78 }).when(mockSerialPort).addEventListener(any());
82 public void testHandlingDataAndRestart() throws IOException, PortInUseException {
83 mockValidSerialPort();
84 AtomicReference<P1Telegram> telegramRef = new AtomicReference<>(null);
85 DSMREventListener listener = new DSMREventListener() {
87 public void handleTelegramReceived(P1Telegram telegram) {
88 telegramRef.set(telegram);
92 public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
93 fail("No handleErrorEvent Expected" + connectorErrorEvent);
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);
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");
109 assertSame(DeviceState.NORMAL, device.getState(), "Expect not to start rediscovery when in normal state");
112 assertNotNull(telegramRef.get(), "Expected to have read a telegram");
116 public void testHandleError() throws IOException, PortInUseException {
117 AtomicReference<DSMRConnectorErrorEvent> eventRef = new AtomicReference<>(null);
118 DSMREventListener listener = new DSMREventListener() {
120 public void handleTelegramReceived(P1Telegram telegram) {
121 fail("No telegram expected:" + telegram);
125 public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
126 eventRef.set(connectorErrorEvent);
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.
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);
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();
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(),
149 assertSame(DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get(), "Expected an error");
150 assertSame(DeviceState.ERROR, device.getState(), "Expect to be in error state");
154 private void mockValidSerialPort() throws PortInUseException {
155 doReturn(mockSerialPort).when(mockIdentifier).open(anyString(), anyInt());
159 * Mock class implementing {@link SerialPortEvent}.
161 private static class MockSerialPortEvent implements SerialPortEvent {
162 private final int eventType;
163 private final boolean newValue;
165 public MockSerialPortEvent(SerialPort mockSerialPort, int eventType, boolean oldValue, boolean newValue) {
166 this.eventType = eventType;
167 this.newValue = newValue;
171 public int getEventType() {
176 public boolean getNewValue() {