2 * Copyright (c) 2010-2020 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.Assert.*;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
18 import static org.mockito.MockitoAnnotations.initMocks;
20 import java.io.ByteArrayInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.TooManyListenersException;
24 import java.util.concurrent.ScheduledExecutorService;
25 import java.util.concurrent.atomic.AtomicReference;
26 import java.util.stream.Stream;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.mockito.Mock;
31 import org.openhab.binding.dsmr.internal.DSMRBindingConstants;
32 import org.openhab.binding.dsmr.internal.TelegramReaderUtil;
33 import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice.DeviceState;
34 import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent;
35 import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram;
36 import org.openhab.core.io.transport.serial.PortInUseException;
37 import org.openhab.core.io.transport.serial.SerialPort;
38 import org.openhab.core.io.transport.serial.SerialPortEvent;
39 import org.openhab.core.io.transport.serial.SerialPortEventListener;
40 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
41 import org.openhab.core.io.transport.serial.SerialPortManager;
44 * Test class for {@link DSMRSerialAutoDevice}.
46 * @author Hilbrand Bouwkamp - Initial contribution
48 public class DSMRSerialAutoDeviceTest {
50 private static final String DUMMY_PORTNAME = "/dev/dummy-serial";
51 private static final String TELEGRAM_NAME = "dsmr_50";
54 private SerialPortIdentifier mockIdentifier;
56 private ScheduledExecutorService scheduler;
58 private SerialPort mockSerialPort;
60 private SerialPortManager serialPortManager = new SerialPortManager() {
62 public SerialPortIdentifier getIdentifier(String name) {
63 assertEquals("Expect the passed serial port name", DUMMY_PORTNAME, name);
64 return mockIdentifier;
68 public Stream<SerialPortIdentifier> getIdentifiers() {
69 return Stream.empty();
72 private SerialPortEventListener serialPortEventListener;
75 public void setUp() throws PortInUseException, TooManyListenersException {
78 serialPortEventListener = a.getArgument(0);
80 }).when(mockSerialPort).addEventListener(any());
84 public void testHandlingDataAndRestart() throws IOException, PortInUseException {
85 mockValidSerialPort();
86 AtomicReference<P1Telegram> telegramRef = new AtomicReference<>(null);
87 DSMREventListener listener = new DSMREventListener() {
89 public void handleTelegramReceived(P1Telegram telegram) {
90 telegramRef.set(telegram);
94 public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
95 fail("No handleErrorEvent Expected" + connectorErrorEvent);
98 try (InputStream inputStream = new ByteArrayInputStream(TelegramReaderUtil.readRawTelegram(TELEGRAM_NAME))) {
99 when(mockSerialPort.getInputStream()).thenReturn(inputStream);
100 DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener,
101 new DSMRTelegramListener(), scheduler, 1);
103 assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState());
104 serialPortEventListener
105 .serialEvent(new MockSerialPortEvent(mockSerialPort, SerialPortEvent.BI, false, true));
106 assertSame("Expect to be still in discovery state", DeviceState.DISCOVER_SETTINGS, device.getState());
107 serialPortEventListener
108 .serialEvent(new MockSerialPortEvent(mockSerialPort, SerialPortEvent.DATA_AVAILABLE, false, true));
109 assertSame("Expect to be in normal state", DeviceState.NORMAL, device.getState());
111 assertSame("Expect not to start rediscovery when in normal state", DeviceState.NORMAL, device.getState());
114 assertNotNull("Expected to have read a telegram", telegramRef.get());
118 public void testHandleError() throws IOException, PortInUseException {
119 AtomicReference<DSMRConnectorErrorEvent> eventRef = new AtomicReference<>(null);
120 DSMREventListener listener = new DSMREventListener() {
122 public void handleTelegramReceived(P1Telegram telegram) {
123 fail("No telegram expected:" + telegram);
127 public void handleErrorEvent(DSMRConnectorErrorEvent connectorErrorEvent) {
128 eventRef.set(connectorErrorEvent);
131 try (InputStream inputStream = new ByteArrayInputStream(new byte[] {})) {
132 when(mockSerialPort.getInputStream()).thenReturn(inputStream);
133 // Trigger device to go into error stage with port in use.
135 throw new PortInUseException();
136 }).when(mockIdentifier).open(eq(DSMRBindingConstants.DSMR_PORT_NAME), anyInt());
137 DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener,
138 new DSMRTelegramListener(), scheduler, 1);
140 assertSame("Expected an error", DSMRConnectorErrorEvent.IN_USE, eventRef.get());
141 assertSame("Expect to be in error state", DeviceState.ERROR, device.getState());
142 // Trigger device to restart
143 mockValidSerialPort();
145 assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState());
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(),
151 assertSame("Expected an error", DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get());
152 assertSame("Expect to be in error state", DeviceState.ERROR, device.getState());
156 private void mockValidSerialPort() throws PortInUseException {
157 doReturn(mockSerialPort).when(mockIdentifier).open(anyString(), anyInt());
161 * Mock class implementing {@link SerialPortEvent}.
163 private static class MockSerialPortEvent implements SerialPortEvent {
164 private final int eventType;
165 private final boolean newValue;
167 public MockSerialPortEvent(SerialPort mockSerialPort, int eventType, boolean oldValue, boolean newValue) {
168 this.eventType = eventType;
169 this.newValue = newValue;
173 public int getEventType() {
178 public boolean getNewValue() {