[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,19 +165,14 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
|
|||
if (data.values.length < 2) {
|
||||
throw new IllegalStateException("Excepted at least two data items");
|
||||
}
|
||||
if (data.values[0] == null) {
|
||||
for (int i = data.values.length - 1; i >= 0; i--) {
|
||||
if (data.values[i] != null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// if we have reached here, it means that array was full of nulls
|
||||
return -1;
|
||||
}
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
return data.values.length - 1;
|
||||
}
|
||||
|
||||
protected static long floorToEvenMinutes(long epochSeconds, int roundMinutes) {
|
||||
long roundSecs = roundMinutes * 60;
|
||||
|
|
|
@ -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