]> git.basschouten.com Git - openhab-addons.git/commitdiff
[fmiweather] Fix UNDEF observations in corner case situations. (#11025)
authorSami Salonen <ssalonen@gmail.com>
Sun, 18 Jul 2021 17:10:34 +0000 (20:10 +0300)
committerGitHub <noreply@github.com>
Sun, 18 Jul 2021 17:10:34 +0000 (19:10 +0200)
* [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>
bundles/org.openhab.binding.fmiweather/src/main/java/org/openhab/binding/fmiweather/internal/AbstractWeatherHandler.java
bundles/org.openhab.binding.fmiweather/src/test/java/org/openhab/binding/fmiweather/AbstractWeatherHandlerTest.java [new file with mode: 0644]

index c819a90bcde13f9598de67ca31923367a2a252ae..e67e0cc06340d443292d7a0d4daba671f6211504 100644 (file)
@@ -165,18 +165,13 @@ 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) {
-            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 (file)
index 0000000..38f50d6
--- /dev/null
@@ -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);
+    }
+}