[fmiweather] Fix UNDEF observations in corner case situations. (#11025)
* [fmiweather] Fix UNDEF observations in corner case situations. In addition, tests added for static utility functions in AbstractWeatherHandler. Resolves #11024. Signed-off-by: Sami Salonen <ssalonen@gmail.com>
This commit is contained in:
parent
7cdb41a459
commit
4cf8b1a5ab
|
@ -165,18 +165,13 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
||||||
if (data.values.length < 2) {
|
if (data.values.length < 2) {
|
||||||
throw new IllegalStateException("Excepted at least two data items");
|
throw new IllegalStateException("Excepted at least two data items");
|
||||||
}
|
}
|
||||||
if (data.values[0] == null) {
|
for (int i = data.values.length - 1; i >= 0; i--) {
|
||||||
return -1;
|
if (data.values[i] != null) {
|
||||||
}
|
return i;
|
||||||
for (int i = 1; i < data.values.length; i++) {
|
|
||||||
if (data.values[i] == null) {
|
|
||||||
return i - 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.values[data.values.length - 1] == null) {
|
// if we have reached here, it means that array was full of nulls
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
return data.values.length - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static long floorToEvenMinutes(long epochSeconds, int roundMinutes) {
|
protected static long floorToEvenMinutes(long epochSeconds, int roundMinutes) {
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.fmiweather;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.openhab.binding.fmiweather.internal.AbstractWeatherHandler;
|
||||||
|
import org.openhab.binding.fmiweather.internal.client.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for response parsing tests
|
||||||
|
*
|
||||||
|
* @author Sami Salonen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
|
||||||
|
public class AbstractWeatherHandlerTest {
|
||||||
|
|
||||||
|
private static BigDecimal bd(Number x) {
|
||||||
|
return new BigDecimal(x.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static long floorToEvenMinutes(long epochSeconds, int roundMinutes) {
|
||||||
|
final Method method;
|
||||||
|
try {
|
||||||
|
method = AbstractWeatherHandler.class.getDeclaredMethod("floorToEvenMinutes", long.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object res = method.invoke(null, epochSeconds, roundMinutes);
|
||||||
|
assertNotNull(res);
|
||||||
|
return (long) res;
|
||||||
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
|
||||||
|
| InvocationTargetException e) {
|
||||||
|
fail(e);
|
||||||
|
throw new IllegalStateException(); // to make compiler happy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static long ceilToEvenMinutes(long epochSeconds, int roundMinutes) {
|
||||||
|
final Method method;
|
||||||
|
try {
|
||||||
|
method = AbstractWeatherHandler.class.getDeclaredMethod("ceilToEvenMinutes", long.class, int.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object res = method.invoke(null, epochSeconds, roundMinutes);
|
||||||
|
assertNotNull(res);
|
||||||
|
return (long) res;
|
||||||
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
|
||||||
|
| InvocationTargetException e) {
|
||||||
|
fail(e);
|
||||||
|
throw new IllegalStateException(); // to make compiler happy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final List<Object[]> parametersForFloorToEvenMinutes() {
|
||||||
|
return Arrays.asList(new Object[][] { //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 1, 1626605100 /* 10:45 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 5, 1626605100 /* 10:45 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 10, 1626604800 /* 10:40 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 30, 1626604200 /* 10:30 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 60, 1626602400 /* 10:00 */ }, //
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int lastValidIndex(Data data) {
|
||||||
|
final Method method;
|
||||||
|
try {
|
||||||
|
method = AbstractWeatherHandler.class.getDeclaredMethod("lastValidIndex", Data.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object res = method.invoke(null, data);
|
||||||
|
assertNotNull(res);
|
||||||
|
return (int) res;
|
||||||
|
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
|
||||||
|
| InvocationTargetException e) {
|
||||||
|
fail(e);
|
||||||
|
throw new IllegalStateException(); // to make compiler happy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersForFloorToEvenMinutes")
|
||||||
|
public void testFloorToEvenMinutes(long epochSeconds, int roundMinutes, long expected) {
|
||||||
|
assertEquals(expected, floorToEvenMinutes(epochSeconds, roundMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final List<Object[]> parametersForCeilToEvenMinutes() {
|
||||||
|
return Arrays.asList(new Object[][] { //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 1, 1626605160 /* 10:46 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 5, 1626605400 /* 10:50 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 10, 1626605400 /* 10:50 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 30, 1626606000 /* 11:00 */ }, //
|
||||||
|
{ 1626605128L /* 2021-07-18 10:45:28 */, 60, 1626606000 /* 11:00 */ }, //
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersForCeilToEvenMinutes")
|
||||||
|
public void testCeilToEvenMinutes(long epochSeconds, int roundMinutes, long expected) {
|
||||||
|
assertEquals(expected, ceilToEvenMinutes(epochSeconds, roundMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final List<Object[]> parametersForLastValidIndex() {
|
||||||
|
return Arrays.asList(new Object[][] { //
|
||||||
|
{ "no nulls", 1, new BigDecimal[] { bd(1), bd(2) } }, //
|
||||||
|
{ "one null in beginning", 1, new BigDecimal[] { null, bd(2) } }, //
|
||||||
|
{ "two nulls", 2, new BigDecimal[] { null, null, bd(2) } }, //
|
||||||
|
{ "three nulls", 3, new BigDecimal[] { null, null, null, bd(2) } }, //
|
||||||
|
{ "null at end", 1, new BigDecimal[] { bd(1), bd(2), null } }, //
|
||||||
|
{ "null at end #2", 2, new BigDecimal[] { bd(1), bd(2), bd(2), null } }, //
|
||||||
|
{ "null in middle", 3, new BigDecimal[] { bd(1), bd(2), null, bd(3) } }, //
|
||||||
|
{ "null in beginning and middle", 3, new BigDecimal[] { null, bd(2), null, bd(3) } }, //
|
||||||
|
{ "all null #1", -1, new BigDecimal[] { null, null, } }, //
|
||||||
|
{ "all null #2", -1, new BigDecimal[] { null, null, null, } }, //
|
||||||
|
{ "all null #3", -1, new BigDecimal[] { null, null, null, null } }, //
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parametersForLastValidIndex")
|
||||||
|
public void testLastValidIndex(String msg, int expected, @Nullable BigDecimal... values) {
|
||||||
|
long[] dummyTimestamps = new long[values.length];
|
||||||
|
assertEquals(expected, lastValidIndex(new Data(dummyTimestamps, values)), msg);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue