]> git.basschouten.com Git - openhab-addons.git/blob
5f3acee95886e7d24cbc84e01337851e3b6219d7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.smartmeter;
14
15 import static org.mockito.ArgumentMatchers.any;
16 import static org.mockito.Mockito.*;
17
18 import java.io.IOException;
19 import java.time.Duration;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.TimeoutException;
22 import java.util.function.Supplier;
23
24 import javax.measure.Quantity;
25
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.junit.jupiter.api.Test;
28 import org.mockito.ArgumentMatchers;
29 import org.mockito.Mockito;
30 import org.openhab.binding.smartmeter.connectors.ConnectorBase;
31 import org.openhab.binding.smartmeter.connectors.IMeterReaderConnector;
32 import org.openhab.binding.smartmeter.internal.MeterDevice;
33 import org.openhab.binding.smartmeter.internal.MeterValue;
34 import org.openhab.binding.smartmeter.internal.MeterValueListener;
35 import org.openhab.binding.smartmeter.internal.helper.ProtocolMode;
36 import org.openhab.core.io.transport.serial.SerialPortManager;
37
38 import io.reactivex.disposables.Disposable;
39 import io.reactivex.functions.Consumer;
40 import io.reactivex.plugins.RxJavaPlugins;
41
42 /**
43  *
44  * @author Matthias Steigenberger - Initial contribution
45  *
46  */
47 public class TestMeterReading {
48
49     @Test
50     public void testContinousReading() throws Exception {
51         final Duration period = Duration.ofSeconds(1);
52         final int executionCount = 5;
53         MockMeterReaderConnector connector = getMockedConnector(false, () -> new Object());
54         MeterDevice<Object> meter = getMeterDevice(connector);
55         MeterValueListener changeListener = Mockito.mock(MeterValueListener.class);
56         meter.addValueChangeListener(changeListener);
57         long executionTime = period.toMillis() * executionCount;
58         Disposable disposable = meter.readValues(executionTime, Executors.newScheduledThreadPool(1), period);
59         try {
60             verify(changeListener, after(executionTime + period.toMillis() / 2 + 50).never()).errorOccurred(any());
61             verify(changeListener, times(executionCount)).valueChanged(any());
62         } finally {
63             disposable.dispose();
64         }
65     }
66
67     @Test
68     public void testRetryHandling() {
69         final Duration period = Duration.ofSeconds(1);
70         MockMeterReaderConnector connector = spy(getMockedConnector(true, () -> {
71             throw new IllegalArgumentException();
72         }));
73         MeterDevice<Object> meter = getMeterDevice(connector);
74         MeterValueListener changeListener = Mockito.mock(MeterValueListener.class);
75         meter.addValueChangeListener(changeListener);
76         Disposable disposable = meter.readValues(5000, Executors.newScheduledThreadPool(1), period);
77         try {
78             verify(changeListener, after(
79                     period.toMillis() + 2 * period.toMillis() * ConnectorBase.NUMBER_OF_RETRIES + period.toMillis() / 2)
80                     .times(1)).errorOccurred(any());
81             verify(connector, times(ConnectorBase.NUMBER_OF_RETRIES)).retryHook(ArgumentMatchers.anyInt());
82         } finally {
83             disposable.dispose();
84         }
85     }
86
87     @Test
88     public void testTimeoutHandling() {
89         final Duration period = Duration.ofSeconds(2);
90         final int timeout = 5000;
91         MockMeterReaderConnector connector = spy(getMockedConnector(true, () -> {
92             try {
93                 Thread.sleep(timeout);
94             } catch (InterruptedException e) {
95             }
96             return new Object();
97         }));
98         MeterDevice<Object> meter = getMeterDevice(connector);
99         MeterValueListener changeListener = Mockito.mock(MeterValueListener.class);
100         meter.addValueChangeListener(changeListener);
101         Disposable disposable = meter.readValues(timeout / 2, Executors.newScheduledThreadPool(2), period);
102         try {
103             verify(changeListener, timeout(timeout)).errorOccurred(any(TimeoutException.class));
104         } finally {
105             disposable.dispose();
106         }
107     }
108
109     @Test
110     public void shouldNotReportToFallbackException() {
111         final Duration period = Duration.ofSeconds(2);
112         final int timeout = 5000;
113         MockMeterReaderConnector connector = spy(getMockedConnector(true, () -> {
114             try {
115                 Thread.sleep(timeout);
116             } catch (InterruptedException e) {
117             }
118             throw new RuntimeException(new IOException("fucked up"));
119         }));
120         MeterDevice<Object> meter = getMeterDevice(connector);
121         Consumer<Throwable> errorHandler = mock(Consumer.class);
122         RxJavaPlugins.setErrorHandler(errorHandler);
123         MeterValueListener changeListener = Mockito.mock(MeterValueListener.class);
124         meter.addValueChangeListener(changeListener);
125         Disposable disposable = meter.readValues(timeout / 2, Executors.newScheduledThreadPool(2), period);
126         try {
127             verify(changeListener, timeout(timeout)).errorOccurred(any(TimeoutException.class));
128             verifyNoMoreInteractions(errorHandler);
129         } finally {
130             disposable.dispose();
131         }
132     }
133
134     MockMeterReaderConnector getMockedConnector(boolean applyRetry, Supplier<Object> readNextSupplier) {
135         return new MockMeterReaderConnector("Test port", applyRetry, readNextSupplier);
136     }
137
138     MeterDevice<Object> getMeterDevice(ConnectorBase<Object> connector) {
139         return new MeterDevice<>(() -> mock(SerialPortManager.class), "id", "port", null, 9600, 0, ProtocolMode.SML) {
140
141             @Override
142             protected @NonNull IMeterReaderConnector<Object> createConnector(
143                     @NonNull Supplier<@NonNull SerialPortManager> serialPortManagerSupplier, @NonNull String serialPort,
144                     int baudrate, int baudrateChangeDelay, @NonNull ProtocolMode protocolMode) {
145                 return connector;
146             }
147
148             @Override
149             protected <Q extends @NonNull Quantity<Q>> void populateValueCache(Object smlFile) {
150                 addObisCache(new MeterValue("123", "333", null));
151             }
152         };
153     }
154 }