[aWATTar] push test coverage and improve code readability

Signed-off-by: Thomas Leber <thomas@tl-photography.at>
This commit is contained in:
Thomas Leber 2024-11-16 00:40:50 +01:00
parent 2cd8902017
commit 71b17c018e
5 changed files with 150 additions and 43 deletions

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.awattar.internal;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
@ -47,7 +49,18 @@ public abstract class AwattarBestPriceResult {
}
}
public abstract boolean isActive();
/**
* Returns true if the best price is active.
*
* @param now the current time
* @return true if the best price is active, false otherwise
*/
public abstract boolean isActive(Instant now);
/**
* Returns the hours of the best price.
*
* @return the hours of the best price as a string
*/
public abstract String getHours();
}

View File

@ -76,8 +76,8 @@ public class AwattarConsecutiveBestPriceResult extends AwattarBestPriceResult {
}
@Override
public boolean isActive() {
return contains(Instant.now().toEpochMilli());
public boolean isActive(Instant now) {
return contains(now.toEpochMilli());
}
public boolean contains(long timestamp) {

View File

@ -64,8 +64,8 @@ public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult
}
@Override
public boolean isActive() {
return members.stream().anyMatch(x -> x.timerange().contains(Instant.now().toEpochMilli()));
public boolean isActive(Instant now) {
return members.stream().anyMatch(x -> x.timerange().contains(now.toEpochMilli()));
}
@Override
@ -73,16 +73,9 @@ public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult
return String.format("NonConsecutiveBestpriceResult with %s", members.toString());
}
private void sort() {
if (!sorted) {
members.sort(Comparator.comparingLong(p -> p.timerange().start()));
}
}
@Override
public String getHours() {
boolean second = false;
sort();
StringBuilder res = new StringBuilder();
for (AwattarPrice price : members) {

View File

@ -23,7 +23,6 @@ import static org.openhab.binding.awattar.internal.AwattarUtil.getCalendarForHou
import static org.openhab.binding.awattar.internal.AwattarUtil.getDuration;
import static org.openhab.binding.awattar.internal.AwattarUtil.getMillisToNextMinute;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
@ -163,7 +162,7 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
long diff;
switch (channelId) {
case CHANNEL_ACTIVE:
state = OnOffType.from(result.isActive());
state = OnOffType.from(result.isActive(getNow(zoneId).toInstant()));
break;
case CHANNEL_START:
state = new DateTimeType(Instant.ofEpochMilli(result.getStart()));
@ -172,7 +171,7 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
state = new DateTimeType(Instant.ofEpochMilli(result.getEnd()));
break;
case CHANNEL_COUNTDOWN:
diff = result.getStart() - Instant.now().toEpochMilli();
diff = result.getStart() - getNow(zoneId).toInstant().toEpochMilli();
if (diff >= 0) {
state = getDuration(diff);
} else {
@ -180,8 +179,8 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
}
break;
case CHANNEL_REMAINING:
if (result.isActive()) {
diff = result.getEnd() - Instant.now().toEpochMilli();
if (result.isActive(getNow(zoneId).toInstant())) {
diff = result.getEnd() - getNow(zoneId).toInstant().toEpochMilli();
state = getDuration(diff);
} else {
state = QuantityType.valueOf(0, Units.MINUTE);
@ -216,20 +215,49 @@ public class AwattarBestPriceHandler extends BaseThingHandler {
return result;
}
/**
* Returns the time range for the given start hour and duration.
*
* @param start the start hour (0-23)
* @param duration the duration in hours
* @param zoneId the time zone to use
* @return the range
*/
protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
ZonedDateTime startCal = getCalendarForHour(start, zoneId);
ZonedDateTime endCal = startCal.plusHours(duration);
ZonedDateTime now = ZonedDateTime.now(zoneId);
ZonedDateTime startTime = getStarTime(start, zoneId);
ZonedDateTime endTime = startTime.plusHours(duration);
ZonedDateTime now = getNow(zoneId);
if (now.getHour() < start) {
// we are before the range, so we might be still within the last range
startCal = startCal.minusDays(1);
endCal = endCal.minusDays(1);
startTime = startTime.minusDays(1);
endTime = endTime.minusDays(1);
}
if (endCal.toInstant().toEpochMilli() < Instant.now().toEpochMilli()) {
if (endTime.toInstant().toEpochMilli() < now.toInstant().toEpochMilli()) {
// span is in the past, add one day
startCal = startCal.plusDays(1);
endCal = endCal.plusDays(1);
startTime = startTime.plusDays(1);
endTime = endTime.plusDays(1);
}
return new TimeRange(startCal.toInstant().toEpochMilli(), endCal.toInstant().toEpochMilli());
return new TimeRange(startTime.toInstant().toEpochMilli(), endTime.toInstant().toEpochMilli());
}
/**
* Returns the start time for the given hour.
*
* @param start the hour. Must be between 0 and 23.
* @param zoneId the time zone
* @return the start time
*/
protected ZonedDateTime getStarTime(int start, ZoneId zoneId) {
return getCalendarForHour(start, zoneId);
}
/**
* Returns the current time.
*
* @param zoneId the time zone
* @return the current time
*/
protected ZonedDateTime getNow(ZoneId zoneId) {
return ZonedDateTime.now(zoneId);
}
}

View File

@ -20,14 +20,18 @@ import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_ACTIVE;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_COUNTDOWN;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_END;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_HOURS;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_REMAINING;
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_START;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@ -58,6 +62,8 @@ import org.openhab.binding.awattar.internal.dto.AwattarApiData;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.thing.Bridge;
@ -166,31 +172,98 @@ public class AwattarBridgeHandlerTest extends JavaTest {
public static Stream<Arguments> testBestpriceHandler() {
return Stream.of( //
Arguments.of(1, true, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
Arguments.of(1, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(1, true, CHANNEL_HOURS, new StringType("14")),
Arguments.of(1, false, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
Arguments.of(1, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(1, false, CHANNEL_HOURS, new StringType("14")),
Arguments.of(2, true, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
Arguments.of(2, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(2, true, CHANNEL_HOURS, new StringType("13,14")),
Arguments.of(2, false, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
Arguments.of(2, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(2, false, CHANNEL_HOURS, new StringType("13,14")));
Arguments.of(24, 1, true, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
Arguments.of(24, 1, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(24, 1, true, CHANNEL_HOURS, new StringType("14")),
Arguments.of(24, 1, false, CHANNEL_START, new DateTimeType("2024-06-15T14:00:00.000+0200")),
Arguments.of(24, 1, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(24, 1, false, CHANNEL_HOURS, new StringType("14")),
Arguments.of(24, 2, true, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
Arguments.of(24, 2, true, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(24, 2, true, CHANNEL_HOURS, new StringType("13,14")),
Arguments.of(24, 2, false, CHANNEL_START, new DateTimeType("2024-06-15T13:00:00.000+0200")),
Arguments.of(24, 2, false, CHANNEL_END, new DateTimeType("2024-06-15T15:00:00.000+0200")),
Arguments.of(24, 2, false, CHANNEL_HOURS, new StringType("13,14")),
Arguments.of(34, 4, false, CHANNEL_START, new DateTimeType("2024-06-15T12:00:00.000+0200")),
Arguments.of(34, 4, false, CHANNEL_END, new DateTimeType("2024-06-15T16:00:00.000+0200")),
Arguments.of(34, 4, false, CHANNEL_HOURS, new StringType("12,13,14,15")),
Arguments.of(34, 8, false, CHANNEL_START, new DateTimeType("2024-06-15T12:00:00.000+0200")),
Arguments.of(34, 8, false, CHANNEL_END, new DateTimeType("2024-06-16T16:00:00.000+0200")),
Arguments.of(34, 8, false, CHANNEL_HOURS, new StringType("12,13,14,15,16,13,14,15")));
}
@ParameterizedTest
@MethodSource
void testBestpriceHandler(int length, boolean consecutive, String channelId, State expectedState) {
void testBestpriceHandler(int rangeDuration, int length, boolean consecutive, String channelId,
State expectedState) {
ThingUID bestPriceUid = new ThingUID(AwattarBindingConstants.THING_TYPE_BESTPRICE, "foo");
Map<String, Object> config = Map.of("length", length, "consecutive", consecutive);
Map<String, Object> config = Map.of("rangeDuration", rangeDuration, "length", length, "consecutive",
consecutive);
when(bestpriceMock.getConfiguration()).thenReturn(new Configuration(config));
AwattarBestPriceHandler handler = new AwattarBestPriceHandler(bestpriceMock, timeZoneProviderMock) {
@Override
protected TimeRange getRange(int start, int duration, ZoneId zoneId) {
return new TimeRange(1718402400000L, 1718488800000L);
protected ZonedDateTime getStarTime(int start, ZoneId zoneId) {
return ZonedDateTime.of(2024, 6, 15, 12, 0, 0, 0, zoneId);
}
protected ZonedDateTime getNow(ZoneId zoneId) {
return ZonedDateTime.of(2024, 6, 15, 12, 0, 0, 0, zoneId);
}
};
handler.setCallback(bestPriceCallbackMock);
ChannelUID channelUID = new ChannelUID(bestPriceUid, channelId);
handler.refreshChannel(channelUID);
verify(bestPriceCallbackMock).stateUpdated(channelUID, expectedState);
}
public static Stream<Arguments> testBestpriceHandler_channels() {
return Stream.of( //
Arguments.of(12, 0, 24, 1, true, CHANNEL_HOURS, new StringType("14")),
Arguments.of(12, 0, 24, 1, true, CHANNEL_ACTIVE, OnOffType.from(false)),
Arguments.of(12, 0, 24, 1, true, CHANNEL_COUNTDOWN, new QuantityType<>("120 min")),
Arguments.of(12, 0, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("0 min")),
Arguments.of(13, 59, 24, 1, true, CHANNEL_COUNTDOWN, new QuantityType<>("1 min")),
Arguments.of(13, 59, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("0 min")),
Arguments.of(13, 59, 24, 1, false, CHANNEL_ACTIVE, OnOffType.from(false)),
Arguments.of(13, 59, 24, 1, true, CHANNEL_ACTIVE, OnOffType.from(false)),
Arguments.of(14, 01, 24, 1, true, CHANNEL_COUNTDOWN, new QuantityType<>("0 min")),
Arguments.of(14, 01, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("59 min")),
Arguments.of(14, 01, 24, 1, false, CHANNEL_ACTIVE, OnOffType.from(true)),
Arguments.of(14, 01, 24, 1, true, CHANNEL_ACTIVE, OnOffType.from(true)),
Arguments.of(14, 59, 24, 1, true, CHANNEL_COUNTDOWN, new QuantityType<>("0 min")),
Arguments.of(14, 59, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("1 min")),
Arguments.of(14, 59, 24, 1, false, CHANNEL_ACTIVE, OnOffType.from(true)),
Arguments.of(14, 59, 24, 1, true, CHANNEL_ACTIVE, OnOffType.from(true)),
Arguments.of(15, 00, 24, 1, true, CHANNEL_COUNTDOWN, new QuantityType<>("0 min")),
Arguments.of(15, 00, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("0 min")),
Arguments.of(15, 00, 24, 1, false, CHANNEL_ACTIVE, OnOffType.from(false)),
Arguments.of(15, 00, 24, 1, true, CHANNEL_ACTIVE, OnOffType.from(false)),
Arguments.of(12, 0, 24, 1, true, CHANNEL_REMAINING, new QuantityType<>("0 min")));
}
@ParameterizedTest
@MethodSource
void testBestpriceHandler_channels(int currentHour, int currentMinute, int rangeDuration, int length,
boolean consecutive, String channelId, State expectedState) {
ThingUID bestPriceUid = new ThingUID(AwattarBindingConstants.THING_TYPE_BESTPRICE, "foo");
Map<String, Object> config = Map.of("rangeDuration", rangeDuration, "length", length, "consecutive",
consecutive);
when(bestpriceMock.getConfiguration()).thenReturn(new Configuration(config));
AwattarBestPriceHandler handler = new AwattarBestPriceHandler(bestpriceMock, timeZoneProviderMock) {
protected ZonedDateTime getStarTime(int start, ZoneId zoneId) {
return ZonedDateTime.of(2024, 6, 15, 0, 0, 0, 0, zoneId);
}
protected ZonedDateTime getNow(ZoneId zoneId) {
return ZonedDateTime.of(2024, 6, 15, currentHour, currentMinute, 0, 0, zoneId);
}
};