]> git.basschouten.com Git - openhab-addons.git/blob
83f1a629a2b1c2d311493103299282c703083917
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.io.transport.modbus.test;
14
15 import static org.junit.jupiter.api.Assertions.*;
16
17 import java.util.Arrays;
18 import java.util.Collection;
19 import java.util.Optional;
20 import java.util.function.Function;
21 import java.util.function.Supplier;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
24 import java.util.stream.Stream.Builder;
25
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.junit.jupiter.params.ParameterizedTest;
28 import org.junit.jupiter.params.provider.MethodSource;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.io.transport.modbus.ModbusBitUtilities;
31 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
32 import org.openhab.io.transport.modbus.ModbusRegisterArray;
33
34 /**
35  * @author Sami Salonen - Initial contribution
36  */
37 public class BitUtilitiesExtractIndividualMethodsTest {
38
39     public static Collection<Object[]> data() {
40         // We use test data from BitUtilitiesExtractStateFromRegistersTest
41         // In BitUtilitiesExtractStateFromRegistersTest the data is aligned to registers
42         //
43         // Here (in registerVariations) we generate offsetted variations of the byte data
44         // to test extractXX which can operate on data aligned on byte-level, not just data aligned on-register level
45         Collection<Object[]> data = BitUtilitiesExtractStateFromRegistersTest.data();
46         return data.stream().flatMap(values -> {
47             Object expectedResult = values[0];
48             ValueType type = (ValueType) values[1];
49             ModbusRegisterArray registers = (ModbusRegisterArray) values[2];
50             int index = (int) values[3];
51             return registerVariations(expectedResult, type, registers, index);
52         }).collect(Collectors.toList());
53     }
54
55     public static Stream<Object[]> filteredTestData(ValueType type) {
56         return data().stream().filter(values -> (ValueType) values[1] == type);
57     }
58
59     /**
60      * Generate register variations for extractXX functions
61      *
62      *
63      * @return entries of (byte[], byteIndex)
64      */
65     private static Stream<Object[]> registerVariations(Object expectedResult, ValueType type,
66             ModbusRegisterArray registers, int index) {
67         byte[] origBytes = registers.getBytes();
68         int origRegisterIndex = index;
69         int origByteIndex = origRegisterIndex * 2;
70
71         Builder<Object[]> streamBuilder = Stream.builder();
72         for (int offset = 0; offset < 5; offset++) {
73             int byteIndex = origByteIndex + offset;
74             byte[] bytesOffsetted = new byte[origBytes.length + offset];
75             for (int i = 0; i < bytesOffsetted.length; i++) {
76                 bytesOffsetted[i] = 99;
77             }
78             System.arraycopy(origBytes, 0, bytesOffsetted, offset, origBytes.length);
79             // offsetted:
80             streamBuilder.add(new Object[] { expectedResult, type, bytesOffsetted, byteIndex });
81
82             // offsetted, with no extra bytes following
83             // (this is only done for successfull cases to avoid copyOfRange padding with zeros
84             if (!(expectedResult instanceof Class)) {
85                 byte[] bytesOffsettedCutExtra = Arrays.copyOfRange(bytesOffsetted, 0, byteIndex + type.getBits() / 8);
86                 if (bytesOffsettedCutExtra.length != bytesOffsetted.length) {
87                     streamBuilder.add(new Object[] { expectedResult, type, bytesOffsettedCutExtra, byteIndex });
88                 }
89             }
90         }
91         return streamBuilder.build();
92     }
93
94     private void testIndividual(Object expectedResult, ValueType type, byte[] bytes, int byteIndex,
95             Supplier<Number> methodUnderTest, Function<DecimalType, Number> expectedPrimitive) {
96         testIndividual(expectedResult, type, bytes, byteIndex, methodUnderTest, expectedPrimitive, null);
97     }
98
99     @SuppressWarnings("unchecked")
100     private void testIndividual(Object expectedResult, ValueType type, byte[] bytes, int byteIndex,
101             Supplier<Number> methodUnderTest, Function<DecimalType, Number> expectedPrimitive,
102             @Nullable Number defaultWhenEmptyOptional) {
103         String testExplanation = String.format("bytes=%s, byteIndex=%d, type=%s", Arrays.toString(bytes), byteIndex,
104                 type);
105         final Object expectedNumber;
106         if (expectedResult instanceof Class && Exception.class.isAssignableFrom((Class<?>) expectedResult)) {
107             assertThrows((Class<? extends Throwable>) expectedResult, () -> methodUnderTest.get());
108         } else if (expectedResult instanceof Optional<?>) {
109             assertTrue(!((Optional<?>) expectedResult).isPresent());
110             if (defaultWhenEmptyOptional == null) {
111                 fail("Should provide defaultWhenEmptyOptional");
112             }
113             return;
114         } else {
115             DecimalType expectedDecimal = (DecimalType) expectedResult;
116             expectedNumber = expectedPrimitive.apply(expectedDecimal);
117             assertEquals(expectedNumber, methodUnderTest.get(), testExplanation);
118         }
119     }
120
121     public static Stream<Object[]> filteredTestDataSInt16() {
122         return filteredTestData(ValueType.INT16);
123     }
124
125     @ParameterizedTest
126     @MethodSource("filteredTestDataSInt16")
127     public void testExtractIndividualSInt16(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
128             throws InstantiationException, IllegalAccessException {
129         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractSInt16(bytes, byteIndex),
130                 decimal -> decimal.shortValue());
131     }
132
133     public static Stream<Object[]> filteredTestDataUInt16() {
134         return filteredTestData(ValueType.UINT16);
135     }
136
137     @ParameterizedTest
138     @MethodSource("filteredTestDataUInt16")
139     public void testExtractIndividualUInt16(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
140             throws InstantiationException, IllegalAccessException {
141         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractUInt16(bytes, byteIndex),
142                 decimal -> decimal.intValue());
143     }
144
145     public static Stream<Object[]> filteredTestDataSInt32() {
146         return filteredTestData(ValueType.INT32);
147     }
148
149     @ParameterizedTest
150     @MethodSource("filteredTestDataSInt32")
151     public void testExtractIndividualSInt32(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
152             throws InstantiationException, IllegalAccessException {
153         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractSInt32(bytes, byteIndex),
154                 decimal -> decimal.intValue());
155     }
156
157     public static Stream<Object[]> filteredTestDataUInt32() {
158         return filteredTestData(ValueType.UINT32);
159     }
160
161     @ParameterizedTest
162     @MethodSource("filteredTestDataUInt32")
163     public void testExtractIndividualUInt32(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
164             throws InstantiationException, IllegalAccessException {
165         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractUInt32(bytes, byteIndex),
166                 decimal -> decimal.longValue());
167     }
168
169     public static Stream<Object[]> filteredTestDataSInt32Swap() {
170         return filteredTestData(ValueType.INT32_SWAP);
171     }
172
173     @ParameterizedTest
174     @MethodSource("filteredTestDataSInt32Swap")
175     public void testExtractIndividualSInt32Swap(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
176             throws InstantiationException, IllegalAccessException {
177         testIndividual(expectedResult, type, bytes, byteIndex,
178                 () -> ModbusBitUtilities.extractSInt32Swap(bytes, byteIndex), decimal -> decimal.intValue());
179     }
180
181     public static Stream<Object[]> filteredTestDataUInt32Swap() {
182         return filteredTestData(ValueType.UINT32_SWAP);
183     }
184
185     @ParameterizedTest
186     @MethodSource("filteredTestDataUInt32Swap")
187     public void testExtractIndividualUInt32Swap(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
188             throws InstantiationException, IllegalAccessException {
189         testIndividual(expectedResult, type, bytes, byteIndex,
190                 () -> ModbusBitUtilities.extractUInt32Swap(bytes, byteIndex), decimal -> decimal.longValue());
191     }
192
193     public static Stream<Object[]> filteredTestDataSInt64() {
194         return filteredTestData(ValueType.INT64);
195     }
196
197     @ParameterizedTest
198     @MethodSource("filteredTestDataSInt64")
199     public void testExtractIndividualSInt64(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
200             throws InstantiationException, IllegalAccessException {
201         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractSInt64(bytes, byteIndex),
202                 decimal -> decimal.longValue());
203     }
204
205     public static Stream<Object[]> filteredTestDataUInt64() {
206         return filteredTestData(ValueType.UINT64);
207     }
208
209     @ParameterizedTest
210     @MethodSource("filteredTestDataUInt64")
211     public void testExtractIndividualUInt64(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
212             throws InstantiationException, IllegalAccessException {
213         testIndividual(expectedResult, type, bytes, byteIndex, () -> ModbusBitUtilities.extractUInt64(bytes, byteIndex),
214                 decimal -> decimal.toBigDecimal().toBigIntegerExact());
215     }
216
217     public static Stream<Object[]> filteredTestDataSInt64Swap() {
218         return filteredTestData(ValueType.INT64_SWAP);
219     }
220
221     @ParameterizedTest
222     @MethodSource("filteredTestDataSInt64Swap")
223     public void testExtractIndividualSInt64Swap(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
224             throws InstantiationException, IllegalAccessException {
225         testIndividual(expectedResult, type, bytes, byteIndex,
226                 () -> ModbusBitUtilities.extractSInt64Swap(bytes, byteIndex), decimal -> decimal.longValue());
227     }
228
229     public static Stream<Object[]> filteredTestDataUInt64Swap() {
230         return filteredTestData(ValueType.UINT64_SWAP);
231     }
232
233     @ParameterizedTest
234     @MethodSource("filteredTestDataUInt64Swap")
235     public void testExtractIndividualUInt64Swap(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
236             throws InstantiationException, IllegalAccessException {
237         testIndividual(expectedResult, type, bytes, byteIndex,
238                 () -> ModbusBitUtilities.extractUInt64Swap(bytes, byteIndex),
239                 decimal -> decimal.toBigDecimal().toBigIntegerExact());
240     }
241
242     public static Stream<Object[]> filteredTestDataFloat32() {
243         return filteredTestData(ValueType.FLOAT32);
244     }
245
246     @ParameterizedTest
247     @MethodSource("filteredTestDataFloat32")
248     public void testExtractIndividualFloat32(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
249             throws InstantiationException, IllegalAccessException {
250         testIndividual(expectedResult, type, bytes, byteIndex,
251                 () -> ModbusBitUtilities.extractFloat32(bytes, byteIndex), decimal -> decimal.floatValue(), Float.NaN);
252     }
253
254     public static Stream<Object[]> filteredTestDataFloat32Swap() {
255         return filteredTestData(ValueType.FLOAT32_SWAP);
256     }
257
258     @ParameterizedTest
259     @MethodSource("filteredTestDataFloat32Swap")
260     public void testExtractIndividualFloat32Swap(Object expectedResult, ValueType type, byte[] bytes, int byteIndex)
261             throws InstantiationException, IllegalAccessException {
262         testIndividual(expectedResult, type, bytes, byteIndex,
263                 () -> ModbusBitUtilities.extractFloat32Swap(bytes, byteIndex), decimal -> decimal.floatValue(),
264                 Float.NaN);
265     }
266 }