Allow sending TimeSeries for items (#3597)

Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
J-N-K
2023-11-07 20:08:41 -08:00
committed by GitHub
parent fb6f1923e8
commit cdbca4dd0a
47 changed files with 1622 additions and 267 deletions
@@ -14,11 +14,13 @@ package org.openhab.core.internal.items;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Instant;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.events.Event;
@@ -28,9 +30,13 @@ import org.openhab.core.items.Item;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.items.events.ItemEventFactory;
import org.openhab.core.items.events.ItemStateChangedEvent;
import org.openhab.core.items.events.ItemStateUpdatedEvent;
import org.openhab.core.items.events.ItemTimeSeriesUpdatedEvent;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.test.java.JavaOSGiTest;
import org.openhab.core.types.TimeSeries;
import org.openhab.core.types.UnDefType;
/**
* The {@link ItemUpdaterOSGiTest} runs inside an OSGi container and tests the {@link ItemRegistry}.
@@ -44,6 +50,8 @@ public class ItemUpdaterOSGiTest extends JavaOSGiTest {
private @NonNullByDefault({}) EventPublisher eventPublisher;
private @NonNullByDefault({}) ItemRegistry itemRegistry;
private @NonNullByDefault({}) SwitchItem switchItem;
private final Queue<Event> receivedEvents = new ConcurrentLinkedQueue<>();
@BeforeEach
@@ -55,7 +63,8 @@ public class ItemUpdaterOSGiTest extends JavaOSGiTest {
itemRegistry = getService(ItemRegistry.class);
assertNotNull(itemRegistry);
itemRegistry.add(new SwitchItem("switch"));
switchItem = new SwitchItem("switch");
itemRegistry.add(switchItem);
EventSubscriber eventSubscriber = new EventSubscriber() {
@Override
@@ -65,12 +74,18 @@ public class ItemUpdaterOSGiTest extends JavaOSGiTest {
@Override
public Set<String> getSubscribedEventTypes() {
return Set.of(ItemStateChangedEvent.TYPE);
return Set.of(ItemStateChangedEvent.TYPE, ItemStateUpdatedEvent.TYPE, ItemTimeSeriesUpdatedEvent.TYPE);
}
};
registerService(eventSubscriber);
}
@AfterEach
public void tearDown() {
receivedEvents.clear();
itemRegistry.remove(switchItem.getName());
}
@Test
public void testItemUpdaterSetsItemState() {
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.ON));
@@ -79,32 +94,91 @@ public class ItemUpdaterOSGiTest extends JavaOSGiTest {
}
@Test
public void testItemUpdaterSendsStateChangedEvent() throws Exception {
public void testItemUpdaterSendsStateUpdatedEvent() throws Exception {
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.ON));
Item switchItem = itemRegistry.get("switch");
waitForAssert(() -> assertEquals(OnOffType.ON, switchItem.getState()));
// wait for the initial events (updated and changed, because it was NULL before)
waitForAssert(() -> {
assertEquals(2, receivedEvents.size());
ItemStateUpdatedEvent updatedEvent = (ItemStateUpdatedEvent) receivedEvents.poll();
assertNotNull(updatedEvent);
assertEquals(OnOffType.ON, updatedEvent.getItemState());
ItemStateChangedEvent changedEvent = (ItemStateChangedEvent) receivedEvents.poll();
assertNotNull(changedEvent);
assertEquals(UnDefType.NULL, changedEvent.getOldItemState());
assertEquals(OnOffType.ON, changedEvent.getItemState());
});
// update with same value
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.ON));
// wait for the updated event
waitForAssert(() -> {
assertEquals(1, receivedEvents.size());
ItemStateUpdatedEvent updatedEvent = (ItemStateUpdatedEvent) receivedEvents.poll();
assertNotNull(updatedEvent);
assertEquals(OnOffType.ON, updatedEvent.getItemState());
});
// ensure no other events send
Thread.sleep(1000);
assertTrue(receivedEvents.isEmpty());
}
@Test
public void testItemUpdaterSendsStateChangedEvent() throws Exception {
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.ON));
// wait for the initial events (updated and changed, because it was NULL before)
waitForAssert(() -> {
assertEquals(2, receivedEvents.size());
ItemStateUpdatedEvent updatedEvent = (ItemStateUpdatedEvent) receivedEvents.poll();
assertNotNull(updatedEvent);
assertEquals(OnOffType.ON, updatedEvent.getItemState());
ItemStateChangedEvent changedEvent = (ItemStateChangedEvent) receivedEvents.poll();
assertNotNull(changedEvent);
assertEquals(UnDefType.NULL, changedEvent.getOldItemState());
assertEquals(OnOffType.ON, changedEvent.getItemState());
});
// change state
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.OFF));
// wait for an event that change the state from OFF to ON
// there could be one remaining event from the 'ItemUpdater sets item state' test
// wait for two events: the updated event and the changed event
waitForAssert(() -> {
assertFalse(receivedEvents.isEmpty());
assertEquals(2, receivedEvents.size());
ItemStateUpdatedEvent updatedEvent = (ItemStateUpdatedEvent) receivedEvents.poll();
assertNotNull(updatedEvent);
assertEquals(OnOffType.OFF, updatedEvent.getItemState());
ItemStateChangedEvent changedEvent = (ItemStateChangedEvent) receivedEvents.poll();
assertNotNull(changedEvent);
assertEquals(OnOffType.ON, changedEvent.getOldItemState());
assertEquals(OnOffType.OFF, changedEvent.getItemState());
});
// send update for same state
eventPublisher.post(ItemEventFactory.createStateEvent("switch", OnOffType.OFF));
// wait a second and make sure no other events have been sent
Thread.sleep(1000);
assertTrue(receivedEvents.isEmpty());
}
// wait a few milliseconds
Thread.sleep(100);
@Test
public void testItemUpdaterSetsTimeSeries() throws InterruptedException {
TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.ADD);
timeSeries.add(Instant.now(), OnOffType.ON);
eventPublisher.post(ItemEventFactory.createTimeSeriesEvent("switch", timeSeries, null));
// make sure no state changed event has been sent
// wait for the event
waitForAssert(() -> {
assertEquals(1, receivedEvents.size());
ItemTimeSeriesUpdatedEvent updatedEvent = (ItemTimeSeriesUpdatedEvent) receivedEvents.poll();
assertNotNull(updatedEvent);
assertEquals(timeSeries, updatedEvent.getTimeSeries());
});
Thread.sleep(1000);
assertTrue(receivedEvents.isEmpty());
}
}
@@ -72,12 +72,13 @@ import org.openhab.core.thing.profiles.ProfileContext;
import org.openhab.core.thing.profiles.ProfileFactory;
import org.openhab.core.thing.profiles.ProfileTypeProvider;
import org.openhab.core.thing.profiles.ProfileTypeUID;
import org.openhab.core.thing.profiles.StateProfile;
import org.openhab.core.thing.profiles.TimeSeriesProfile;
import org.openhab.core.thing.profiles.TriggerProfile;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.TimeSeries;
/**
*
@@ -148,7 +149,7 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
private @Mock @NonNullByDefault({}) ItemStateConverter itemStateConverterMock;
private @Mock @NonNullByDefault({}) ProfileAdvisor profileAdvisorMock;
private @Mock @NonNullByDefault({}) ProfileFactory profileFactoryMock;
private @Mock @NonNullByDefault({}) StateProfile stateProfileMock;
private @Mock @NonNullByDefault({}) TimeSeriesProfile stateProfileMock;
private @Mock @NonNullByDefault({}) ThingHandler thingHandlerMock;
private @Mock @NonNullByDefault({}) ThingRegistry thingRegistryMock;
private @Mock @NonNullByDefault({}) TriggerProfile triggerProfileMock;
@@ -272,6 +273,32 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
verifyNoMoreInteractions(triggerProfileMock);
}
@Test
public void testTimeSeriesSingleLink() {
TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
manager.sendTimeSeries(STATE_CHANNEL_UID_1, timeSeries);
waitForAssert(() -> {
verify(stateProfileMock).onTimeSeriesFromHandler(eq(timeSeries));
});
verifyNoMoreInteractions(stateProfileMock);
verifyNoMoreInteractions(triggerProfileMock);
}
@Test
public void testTimeSeriesMultiLink() {
TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
manager.sendTimeSeries(STATE_CHANNEL_UID_2, timeSeries);
waitForAssert(() -> {
verify(stateProfileMock, times(2)).onTimeSeriesFromHandler(eq(timeSeries));
});
verifyNoMoreInteractions(stateProfileMock);
verifyNoMoreInteractions(triggerProfileMock);
}
@Test
public void testItemCommandEventSingleLink() {
manager.receive(ItemEventFactory.createCommandEvent(ITEM_NAME_2, OnOffType.ON));