diff --git a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java index c819a90bcde13..e67e0cc06340d 100644 --- a/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java +++ b/bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java @@ -165,18 +165,13 @@ protected static int lastValidIndex(Data data) { if (data.values.length < 2) { throw new IllegalStateException("Excepted at least two data items"); } - if (data.values[0] == null) { - return -1; - } - for (int i = 1; i < data.values.length; i++) { - if (data.values[i] == null) { - return i - 1; + for (int i = data.values.length - 1; i >= 0; i--) { + if (data.values[i] != null) { + return i; } } - if (data.values[data.values.length - 1] == null) { - return -1; - } - return data.values.length - 1; + // if we have reached here, it means that array was full of nulls + return -1; } protected static long floorToEvenMinutes(long epochSeconds, int roundMinutes) { diff --git a/bundles/org.openhab.binding.fmiweather/src/test/java/org/openhab/binding/fmiweather/AbstractWeatherHandlerTest.java b/bundles/org.openhab.binding.fmiweather/src/test/java/org/openhab/binding/fmiweather/AbstractWeatherHandlerTest.java new file mode 100644 index 0000000000000..38f50d646a480 --- /dev/null +++ b/bundles/org.openhab.binding.fmiweather/src/test/java/org/openhab/binding/fmiweather/AbstractWeatherHandlerTest.java @@ -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 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 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 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); + } +}