[fmiweather] Fix compiler warnings and SAT issues (#17621)

* Reduce number of warnings and SAT issues

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Jacob Laursen 2024-10-25 19:15:27 +02:00 committed by Ciprian Pascu
parent c9351b3dcb
commit 2c7c1c586f
24 changed files with 174 additions and 232 deletions

View File

@ -18,6 +18,7 @@ import java.math.BigDecimal;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -81,13 +82,11 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
}
@Override
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public void handleCommand(ChannelUID channelUID, Command command) {
if (RefreshType.REFRESH == command) {
ScheduledFuture<?> prevFuture = updateChannelsFutureRef.get();
ScheduledFuture<?> newFuture = updateChannelsFutureRef
.updateAndGet(fut -> fut == null || fut.isDone() ? submitUpdateChannelsThrottled() : fut);
assert newFuture != null; // invariant
ScheduledFuture<?> newFuture = Objects.requireNonNull(updateChannelsFutureRef
.updateAndGet(fut -> fut == null || fut.isDone() ? submitUpdateChannelsThrottled() : fut));
if (logger.isTraceEnabled()) {
long delayRemainingMillis = newFuture.getDelay(TimeUnit.MILLISECONDS);
if (delayRemainingMillis <= 0) {
@ -97,7 +96,7 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
delayRemainingMillis);
}
// Compare by reference to check if the future changed
if (prevFuture == newFuture) {
if (isSameFuture(prevFuture, newFuture)) {
logger.trace("REFRESH received. Previous refresh ongoing, will wait for it to complete in {} ms",
lastRefreshMillis + REFRESH_THROTTLE_MILLIS - System.currentTimeMillis());
}
@ -105,6 +104,11 @@ public abstract class AbstractWeatherHandler extends BaseThingHandler {
}
}
@SuppressWarnings("PMD.CompareObjectsWithEquals")
private boolean isSameFuture(@Nullable ScheduledFuture<?> future1, @Nullable ScheduledFuture<?> future2) {
return future1 == future2;
}
@Override
public void initialize() {
client = new Client();

View File

@ -232,7 +232,6 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
return (int) (TimeUnit.HOURS.toMinutes(hours) / QUERY_RESOLUTION_MINUTES);
}
@SuppressWarnings({ "unused", "null" })
private static @Nullable String getDataField(ChannelUID channelUID) {
Entry<String, @Nullable Unit<?>> entry = CHANNEL_TO_FORECAST_FIELD_NAME_AND_UNIT
.get(channelUID.getIdWithoutGroup());
@ -242,7 +241,6 @@ public class ForecastWeatherHandler extends AbstractWeatherHandler {
return entry.getKey();
}
@SuppressWarnings({ "unused", "null" })
private static @Nullable Unit<?> getUnit(ChannelUID channelUID) {
Entry<String, @Nullable Unit<?>> entry = CHANNEL_TO_FORECAST_FIELD_NAME_AND_UNIT
.get(channelUID.getIdWithoutGroup());

View File

@ -62,10 +62,17 @@ public class ObservationWeatherHandler extends AbstractWeatherHandler {
private static final long OBSERVATION_LOOK_BACK_SECONDS = TimeUnit.MINUTES.toSeconds(30);
private static final int STEP_MINUTES = 10;
private static final int POLL_INTERVAL_SECONDS = 600;
private static BigDecimal HUNDRED = BigDecimal.valueOf(100);
private static BigDecimal NA_CLOUD_MAX = BigDecimal.valueOf(8); // API value when having full clouds (overcast)
private static BigDecimal NA_CLOUD_COVERAGE = BigDecimal.valueOf(9); // API value when cloud coverage could not be
// determined.
private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
/**
* API value when having full clouds (overcast)
*/
private static final BigDecimal NA_CLOUD_MAX = BigDecimal.valueOf(8);
/**
* API value when cloud coverage could not be determined.
*/
private static final BigDecimal NA_CLOUD_COVERAGE = BigDecimal.valueOf(9);
public static final Unit<Length> MILLIMETRE = MetricPrefix.MILLI(METRE);
public static final Unit<Length> CENTIMETRE = MetricPrefix.CENTI(METRE);
@ -175,7 +182,6 @@ public class ObservationWeatherHandler extends AbstractWeatherHandler {
}
}
@SuppressWarnings({ "null", "unused" })
private static @Nullable String getDataField(ChannelUID channelUID) {
Entry<String, @Nullable Unit<?>> entry = CHANNEL_TO_OBSERVATION_FIELD_NAME_AND_UNIT
.get(channelUID.getIdWithoutGroup());
@ -185,7 +191,6 @@ public class ObservationWeatherHandler extends AbstractWeatherHandler {
return entry.getKey();
}
@SuppressWarnings({ "null", "unused" })
private static @Nullable Unit<?> getUnit(ChannelUID channelUID) {
Entry<String, @Nullable Unit<?>> entry = CHANNEL_TO_OBSERVATION_FIELD_NAME_AND_UNIT
.get(channelUID.getIdWithoutGroup());

View File

@ -15,7 +15,6 @@ package org.openhab.binding.fmiweather.internal.client;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@ -67,22 +66,20 @@ import org.xml.sax.SAXException;
@NonNullByDefault
public class Client {
private static final String WEATHER_STATIONS_URL = "https://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::ef::stations&networkid=121&";
private static final Map<String, String> NAMESPACES = Map.of( //
"target", "http://xml.fmi.fi/namespace/om/atmosphericfeatures/1.1", //
"gml", "http://www.opengis.net/gml/3.2", //
"xlink", "http://www.w3.org/1999/xlink", //
"ows", "http://www.opengis.net/ows/1.1", //
"gmlcov", "http://www.opengis.net/gmlcov/1.0", //
"swe", "http://www.opengis.net/swe/2.0", //
"wfs", "http://www.opengis.net/wfs/2.0", //
"ef", "http://inspire.ec.europa.eu/schemas/ef/4.0");
private final Logger logger = LoggerFactory.getLogger(Client.class);
public static final String WEATHER_STATIONS_URL = "https://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::ef::stations&networkid=121&";
private static final Map<String, String> NAMESPACES = new HashMap<>();
static {
NAMESPACES.put("target", "http://xml.fmi.fi/namespace/om/atmosphericfeatures/1.1");
NAMESPACES.put("gml", "http://www.opengis.net/gml/3.2");
NAMESPACES.put("xlink", "http://www.w3.org/1999/xlink");
NAMESPACES.put("ows", "http://www.opengis.net/ows/1.1");
NAMESPACES.put("gmlcov", "http://www.opengis.net/gmlcov/1.0");
NAMESPACES.put("swe", "http://www.opengis.net/swe/2.0");
NAMESPACES.put("wfs", "http://www.opengis.net/wfs/2.0");
NAMESPACES.put("ef", "http://inspire.ec.europa.eu/schemas/ef/4.0");
}
private static final NamespaceContext NAMESPACE_CONTEXT = new NamespaceContext() {
@Override
public @Nullable String getNamespaceURI(@Nullable String prefix) {
@ -91,12 +88,12 @@ public class Client {
@SuppressWarnings("rawtypes")
@Override
public @Nullable Iterator getPrefixes(@Nullable String val) {
public @Nullable Iterator getPrefixes(@Nullable String namespaceURI) {
return null;
}
@Override
public @Nullable String getPrefix(@Nullable String uri) {
public @Nullable String getPrefix(@Nullable String namespaceURI) {
return null;
}
};
@ -173,7 +170,7 @@ public class Client {
}
}
private Set<Location> parseStations(String response) throws FMIExceptionReportException,
protected Set<Location> parseStations(String response) throws FMIExceptionReportException,
FMIUnexpectedResponseException, SAXException, IOException, XPathExpressionException {
Document document = documentBuilder.parse(new InputSource(new StringReader(response)));
@ -218,7 +215,7 @@ public class Client {
* Parse FMI multipointcoverage formatted xml response
*
*/
private FMIResponse parseMultiPointCoverageXml(String response) throws FMIUnexpectedResponseException,
protected FMIResponse parseMultiPointCoverageXml(String response) throws FMIUnexpectedResponseException,
FMIExceptionReportException, SAXException, IOException, XPathExpressionException {
Document document = documentBuilder.parse(new InputSource(new StringReader(response)));

View File

@ -18,6 +18,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@ -54,11 +55,15 @@ public class FMIResponse {
public Builder appendLocationData(Location location, @Nullable Integer capacityHintForValues, String parameter,
long epochSecond, @Nullable BigDecimal val) {
timestampsByLocationByParameter.computeIfAbsent(location, k -> new HashMap<>()).computeIfAbsent(parameter,
k -> capacityHintForValues == null ? new ArrayList<>() : new ArrayList<>(capacityHintForValues))
Objects.requireNonNull(Objects
.requireNonNull(timestampsByLocationByParameter.computeIfAbsent(location, k -> new HashMap<>()))
.computeIfAbsent(parameter, k -> capacityHintForValues == null ? new ArrayList<>()
: new ArrayList<>(capacityHintForValues)))
.add(epochSecond);
valuesByLocationByParameter.computeIfAbsent(location, k -> new HashMap<>()).computeIfAbsent(parameter,
k -> capacityHintForValues == null ? new ArrayList<>() : new ArrayList<>(capacityHintForValues))
Objects.requireNonNull(
Objects.requireNonNull(valuesByLocationByParameter.computeIfAbsent(location, k -> new HashMap<>()))
.computeIfAbsent(parameter, k -> capacityHintForValues == null ? new ArrayList<>()
: new ArrayList<>(capacityHintForValues)))
.add(val);
return this;
}
@ -85,10 +90,11 @@ public class FMIResponse {
Entry<String, List<Long>> parameterEntry) {
String parameter = parameterEntry.getKey();
long[] timestamps = parameterEntry.getValue().stream().mapToLong(Long::longValue).toArray();
BigDecimal[] values = valuesByLocationByParameter.get(location).get(parameter)
BigDecimal[] values = Objects
.requireNonNull(Objects.requireNonNull(valuesByLocationByParameter.get(location)).get(parameter))
.toArray(new @Nullable BigDecimal[0]);
Data dataValues = new Data(timestamps, values);
out.get(location).put(parameter, dataValues);
Objects.requireNonNull(out.get(location)).put(parameter, dataValues);
}
}

View File

@ -37,8 +37,8 @@ public class Request {
public final String storedQueryId;
public final String[] parameters;
private static ZoneId UTC = ZoneId.of("Z");
private static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
private static final ZoneId UTC = ZoneId.of("Z");
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
public Request(String storedQueryId, QueryParameter location, long startEpoch, long endEpoch,
long timestepMinutes) {

View File

@ -10,23 +10,16 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.hamcrest.Description;
@ -37,6 +30,9 @@ import org.openhab.binding.fmiweather.internal.client.Client;
import org.openhab.binding.fmiweather.internal.client.Data;
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
import org.openhab.binding.fmiweather.internal.client.Location;
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
import org.xml.sax.SAXException;
/**
* Base class for response parsing tests
@ -47,41 +43,23 @@ import org.openhab.binding.fmiweather.internal.client.Location;
public class AbstractFMIResponseParsingTest {
@NonNullByDefault({})
protected Client client;
protected ClientExposed client;
@BeforeEach
public void setUpClient() {
client = new Client();
client = new ClientExposed();
}
protected Path getTestResource(String filename) {
try {
return Paths.get(getClass().getResource(filename).toURI());
} catch (URISyntaxException e) {
fail(e.getMessage());
// Make the compiler happy by throwing here, fails already above
throw new IllegalStateException();
}
}
protected String readTestResourceUtf8(String filename) {
return readTestResourceUtf8(getTestResource(filename));
}
protected String readTestResourceUtf8(Path path) {
try {
BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
StringBuilder content = new StringBuilder();
char[] buffer = new char[1024];
int read = -1;
while ((read = reader.read(buffer)) != -1) {
content.append(buffer, 0, read);
protected String readTestResourceUtf8(String filename) throws IOException {
try (InputStream inputStream = AbstractFMIResponseParsingTest.class.getResourceAsStream(filename)) {
if (inputStream == null) {
throw new IOException("Input stream is null");
}
return content.toString();
} catch (IOException e) {
fail(e.getMessage());
// Make the compiler happy by throwing here, fails already above
throw new IllegalStateException();
byte[] bytes = inputStream.readAllBytes();
if (bytes == null) {
throw new IOException("Resulting byte-array empty");
}
return new String(bytes, StandardCharsets.UTF_8);
}
}
@ -143,42 +121,15 @@ public class AbstractFMIResponseParsingTest {
};
}
/**
*
* @param content
* @return
* @throws Throwable exception raised by parseMultiPointCoverageXml
* @throws AssertionError exception raised when parseMultiPointCoverageXml method signature does not match excepted
* (test & implementation is out-of-sync)
*/
protected FMIResponse parseMultiPointCoverageXml(String content) throws Throwable {
try {
Method parseMethod = Client.class.getDeclaredMethod("parseMultiPointCoverageXml", String.class);
parseMethod.setAccessible(true);
return Objects.requireNonNull((FMIResponse) parseMethod.invoke(client, content));
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
fail(String.format("Unexpected reflection error (code changed?) %s: %s", e.getClass().getName(),
e.getMessage()));
// Make the compiler happy by throwing here, fails already above
throw new IllegalStateException();
protected class ClientExposed extends Client {
public FMIResponse parseMultiPointCoverageXml(String response) throws FMIUnexpectedResponseException,
FMIExceptionReportException, SAXException, IOException, XPathExpressionException {
return super.parseMultiPointCoverageXml(response);
}
}
@SuppressWarnings("unchecked")
protected Set<Location> parseStations(String content) {
try {
Method parseMethod = Objects.requireNonNull(Client.class.getDeclaredMethod("parseStations", String.class));
parseMethod.setAccessible(true);
return Objects.requireNonNull((Set<Location>) parseMethod.invoke(client, content));
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
} catch (Exception e) {
fail(String.format("Unexpected reflection error (code changed?) %s: %s", e.getClass().getName(),
e.getMessage()));
// Make the compiler happy by throwing here, fails already above
throw new IllegalStateException();
public Set<Location> parseStations(String response) throws FMIExceptionReportException,
FMIUnexpectedResponseException, SAXException, IOException, XPathExpressionException {
return super.parseStations(response);
}
}
}

View File

@ -10,12 +10,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
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;
@ -24,7 +22,6 @@ 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;
/**
@ -41,33 +38,15 @@ public class AbstractWeatherHandlerTest {
}
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("", epochSeconds, roundMinutes);
assertNotNull(res);
return (long) res;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
fail(e);
throw new IllegalStateException(); // to make compiler happy
}
Object res = AbstractWeatherHandler.floorToEvenMinutes(epochSeconds, roundMinutes);
assertNotNull(res);
return (long) res;
}
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("", epochSeconds, roundMinutes);
assertNotNull(res);
return (long) res;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
fail(e);
throw new IllegalStateException(); // to make compiler happy
}
Object res = AbstractWeatherHandler.ceilToEvenMinutes(epochSeconds, roundMinutes);
assertNotNull(res);
return (long) res;
}
public static List<Object[]> parametersForFloorToEvenMinutes() {
@ -81,18 +60,9 @@ public class AbstractWeatherHandlerTest {
}
protected static int lastValidIndex(Data data) {
final Method method;
try {
method = AbstractWeatherHandler.class.getDeclaredMethod("lastValidIndex", Data.class);
method.setAccessible(true);
Object res = method.invoke("", data);
assertNotNull(res);
return (int) res;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
fail(e);
throw new IllegalStateException(); // to make compiler happy
}
Object res = AbstractWeatherHandler.lastValidIndex(data);
assertNotNull(res);
return (int) res;
}
@ParameterizedTest

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

View File

@ -10,43 +10,48 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.nio.file.Path;
import java.io.IOException;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.Client;
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
import org.xml.sax.SAXException;
/**
* Test cases for {@link Client.parseMultiPointCoverageXml} with an "empty" (no data) XML response
* Test cases for {@link org.openhab.binding.fmiweather.internal.client.Client#parseMultiPointCoverageXml}
* with an "empty" (no data) XML response
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class FMIResponseParsingEmptyTest extends AbstractFMIResponseParsingTest {
private Path observations = getTestResource("observations_empty.xml");
private static final String OBSERVATIONS = "observations_empty.xml";
@NonNullByDefault({})
private FMIResponse observationsResponse;
@BeforeEach
public void setUp() {
client = new Client();
try {
observationsResponse = parseMultiPointCoverageXml(readTestResourceUtf8(observations));
} catch (Throwable e) {
throw new RuntimeException("Test data malformed", e);
}
public void setUp() throws FMIUnexpectedResponseException, FMIExceptionReportException, XPathExpressionException,
SAXException, IOException {
client = new ClientExposed();
observationsResponse = client.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS));
assertNotNull(observationsResponse);
}
@Test
public void testLocationsSinglePlace() throws Throwable {
public void testLocationsSinglePlace() {
assertThat(observationsResponse.getLocations().size(), is(0));
}
}

View File

@ -10,38 +10,37 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import java.nio.file.Path;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.exception.FMIResponseException;
/**
* Test cases for AbstractWeatherHandler. The tests provide mocks for supporting entities using Mockito.
* Test cases for {@link AbstractWeatherHandler}.
* The tests provide mocks for supporting entities using Mockito.
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class FMIResponseParsingExceptionReportTest extends AbstractFMIResponseParsingTest {
private Path error1 = getTestResource("error1.xml");
private static final String ERROR1 = "error1.xml";
@Test
public void testErrorResponse() {
try {
parseMultiPointCoverageXml(readTestResourceUtf8(error1));
client.parseMultiPointCoverageXml(readTestResourceUtf8(ERROR1));
} catch (FMIResponseException e) {
// OK
assertThat(e.getMessage(), is(
"Exception report (OperationParsingFailed): [Invalid time interval!, The start time is later than the end time., URI: /wfs?endtime=1900-03-10T20%3A10%3A00Z&fmisid=101023&parameters=t2m%2Crh%2Cwd_10min%2Cws_10min%2Cwg_10min%2Cp_sea&request=getFeature&service=WFS&starttime=2019-03-10T10%3A10%3A00Z&storedquery_id=fmi%3A%3Aobservations%3A%3Aweather%3A%3Amultipointcoverage&timestep=60&version=2.0.0]"));
return;
} catch (Throwable e) {
} catch (Exception e) {
fail("Wrong exception, was " + e.getClass().getName());
}
fail("FMIResponseException expected");

View File

@ -10,36 +10,35 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.nio.file.Path;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.exception.FMIResponseException;
import org.xml.sax.SAXParseException;
/**
* Test cases for AbstractWeatherHandler. The tests provide mocks for supporting entities using Mockito.
* Test cases for {@link AbstractWeatherHandler}.
* The tests provide mocks for supporting entities using Mockito.
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class FMIResponseParsingInvalidOrUnexpectedXmlTest extends AbstractFMIResponseParsingTest {
private Path observations1 = getTestResource("observations_single_place.xml");
private static final String OBSERVATIONS1 = "observations_single_place.xml";
@Test
public void testInvalidXml() {
assertThrows(SAXParseException.class,
() -> parseMultiPointCoverageXml(readTestResourceUtf8(observations1).replace("276.0", "<<")));
() -> client.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS1).replace("276.0", "<<")));
}
@Test
public void testUnexpectedXml() {
assertThrows(FMIResponseException.class,
() -> parseMultiPointCoverageXml(readTestResourceUtf8(observations1).replace("276.0", "<foo>4</foo>")));
assertThrows(FMIResponseException.class, () -> client
.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS1).replace("276.0", "<foo>4</foo>")));
}
}

View File

@ -10,34 +10,39 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.Data;
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
import org.openhab.binding.fmiweather.internal.client.Location;
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
import org.xml.sax.SAXException;
/**
* Test cases for Client.parseMultiPointCoverageXml with a xml response having multiple places, parameters
* and timestamps
* Test cases for {@link org.openhab.binding.fmiweather.internal.client.Client#parseMultiPointCoverageXml}
* with a xml response having multiple places, parameters and timestamps
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class FMIResponseParsingMultiplePlacesTest extends AbstractFMIResponseParsingTest {
private Path observationsMultiplePlaces = getTestResource("observations_multiple_places.xml");
private Path forecastsMultiplePlaces = getTestResource("forecast_multiple_places.xml");
private static final String OBSERVATIONS_MULTIPLE_PLACES = "observations_multiple_places.xml";
private static final String FORECAST_MULTIPLE_PLACES = "forecast_multiple_places.xml";
@NonNullByDefault({})
private FMIResponse observationsMultiplePlacesResponse;
@ -61,16 +66,14 @@ public class FMIResponseParsingMultiplePlacesTest extends AbstractFMIResponsePar
new BigDecimal("19.90000"));
@BeforeEach
public void setUp() {
try {
observationsMultiplePlacesResponse = parseMultiPointCoverageXml(
readTestResourceUtf8(observationsMultiplePlaces));
observationsMultiplePlacesNaNResponse = parseMultiPointCoverageXml(
readTestResourceUtf8(observationsMultiplePlaces).replace("276.0", "NaN"));
forecastsMultiplePlacesResponse = parseMultiPointCoverageXml(readTestResourceUtf8(forecastsMultiplePlaces));
} catch (Throwable e) {
throw new RuntimeException("Test data malformed", e);
}
public void setUp() throws FMIUnexpectedResponseException, FMIExceptionReportException, XPathExpressionException,
SAXException, IOException {
observationsMultiplePlacesResponse = client
.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS_MULTIPLE_PLACES));
observationsMultiplePlacesNaNResponse = client
.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS_MULTIPLE_PLACES).replace("276.0", "NaN"));
forecastsMultiplePlacesResponse = client
.parseMultiPointCoverageXml(readTestResourceUtf8(FORECAST_MULTIPLE_PLACES));
}
@Test
@ -111,8 +114,8 @@ public class FMIResponseParsingMultiplePlacesTest extends AbstractFMIResponsePar
@Test
public void testParseObservationsMultipleData() {
Data wd_10min = observationsMultiplePlacesResponse.getData(emasalo, "wd_10min").get();
assertThat(wd_10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
Data wd10min = observationsMultiplePlacesResponse.getData(emasalo, "wd_10min").get();
assertThat(wd10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
"243.0", "252.0", "262.0", "276.0")));
Data rh = observationsMultiplePlacesResponse.getData(kilpilahti, "rh").get();
assertThat(rh, is(deeplyEqualTo(1552215600L, 60, "73.0", "65.0", "60.0", "59.0", "57.0", "64.0", "66.0", "65.0",
@ -140,8 +143,8 @@ public class FMIResponseParsingMultiplePlacesTest extends AbstractFMIResponsePar
@Test
public void testParseObservations1NaN() {
// last value is null, due to NaN measurement value
Data wd_10min = observationsMultiplePlacesNaNResponse.getData(emasalo, "wd_10min").get();
assertThat(wd_10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
Data wd10min = observationsMultiplePlacesNaNResponse.getData(emasalo, "wd_10min").get();
assertThat(wd10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
"243.0", "252.0", "262.0", null)));
}
}

View File

@ -10,35 +10,39 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.Data;
import org.openhab.binding.fmiweather.internal.client.FMIResponse;
import org.openhab.binding.fmiweather.internal.client.Location;
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
import org.xml.sax.SAXException;
/**
* Test cases for Client.parseMultiPointCoverageXml with a xml response having single place and multiple
* parameters
* and timestamps
* Test cases for {@link {@link org.openhab.binding.fmiweather.internal.client.Client#parseMultiPointCoverageXml}
* with an xml response having single place and multiple parameters and timestamps
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class FMIResponseParsingSinglePlaceTest extends AbstractFMIResponseParsingTest {
private Path observations1 = getTestResource("observations_single_place.xml");
private static final String OBSERVATIONS1 = "observations_single_place.xml";
@NonNullByDefault({})
private FMIResponse observationsResponse1;
@ -48,14 +52,11 @@ public class FMIResponseParsingSinglePlaceTest extends AbstractFMIResponseParsin
new BigDecimal("25.62546"));
@BeforeEach
public void setUp() {
try {
observationsResponse1 = parseMultiPointCoverageXml(readTestResourceUtf8(observations1));
observationsResponse1NaN = parseMultiPointCoverageXml(
readTestResourceUtf8(observations1).replace("276.0", "NaN"));
} catch (Throwable e) {
throw new RuntimeException("Test data malformed", e);
}
public void setUp() throws FMIUnexpectedResponseException, FMIExceptionReportException, XPathExpressionException,
SAXException, IOException {
observationsResponse1 = client.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS1));
observationsResponse1NaN = client
.parseMultiPointCoverageXml(readTestResourceUtf8(OBSERVATIONS1).replace("276.0", "NaN"));
assertNotNull(observationsResponse1);
}
@ -86,16 +87,16 @@ public class FMIResponseParsingSinglePlaceTest extends AbstractFMIResponseParsin
@Test
public void testParseObservations1Data() {
Data wd_10min = observationsResponse1.getData(emasalo, "wd_10min").get();
assertThat(wd_10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
Data wd10min = observationsResponse1.getData(emasalo, "wd_10min").get();
assertThat(wd10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
"243.0", "252.0", "262.0", "276.0")));
}
@Test
public void testParseObservations1NaN() {
// last value is null, due to NaN measurement value
Data wd_10min = observationsResponse1NaN.getData(emasalo, "wd_10min").get();
assertThat(wd_10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
Data wd10min = observationsResponse1NaN.getData(emasalo, "wd_10min").get();
assertThat(wd10min, is(deeplyEqualTo(1552215600L, 60, "312.0", "286.0", "295.0", "282.0", "271.0", "262.0",
"243.0", "252.0", "262.0", null)));
}
}

View File

@ -10,34 +10,39 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.fmiweather.internal.client.Location;
import org.openhab.binding.fmiweather.internal.client.exception.FMIExceptionReportException;
import org.openhab.binding.fmiweather.internal.client.exception.FMIUnexpectedResponseException;
import org.xml.sax.SAXException;
/**
* Test cases for Client.parseStations
* Test cases for {@link org.openhab.binding.fmiweather.internal.client.Client#parseStations}
*
* @author Sami Salonen - Initial contribution
*/
@NonNullByDefault
public class ParsingStationsTest extends AbstractFMIResponseParsingTest {
private Path stations_xml = getTestResource("stations.xml");
private static final String STATIONS_XML = "stations.xml";
@SuppressWarnings("unchecked")
@Test
public void testParseStations() {
Set<Location> stations = parseStations(readTestResourceUtf8(stations_xml));
public void testParseStations() throws FMIExceptionReportException, FMIUnexpectedResponseException, SAXException,
IOException, XPathExpressionException {
Set<Location> stations = client.parseStations(readTestResourceUtf8(STATIONS_XML));
assertNotNull(stations);
assertThat(stations.size(), is(3));
assertThat(stations,

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import java.math.BigDecimal;
import java.util.Objects;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.fmiweather;
package org.openhab.binding.fmiweather.internal;
import java.math.BigDecimal;
import java.util.Arrays;
@ -40,7 +40,6 @@ public class ValuesMatcher extends TypeSafeMatcher<@Nullable BigDecimal[]> {
return s == null ? null : new BigDecimal(s);
}
@SuppressWarnings("null")
@Override
public void describeTo(@Nullable Description description) {
if (description == null) {