2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.deutschebahn.internal;
15 import static org.mockito.ArgumentMatchers.*;
16 import static org.mockito.Mockito.*;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.util.Arrays;
21 import java.util.Date;
22 import java.util.GregorianCalendar;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.junit.jupiter.api.Test;
26 import org.mockito.Mockito;
27 import org.openhab.binding.deutschebahn.internal.timetable.dto.Event;
28 import org.openhab.binding.deutschebahn.internal.timetable.dto.TimetableStop;
29 import org.openhab.binding.deutschebahn.internal.timetable.dto.TripLabel;
30 import org.openhab.core.config.core.Configuration;
31 import org.openhab.core.library.types.DateTimeType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.thing.Channel;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.ThingHandlerCallback;
39 import org.openhab.core.thing.internal.BridgeImpl;
40 import org.openhab.core.types.State;
41 import org.openhab.core.types.UnDefType;
44 * Tests for {@link DeutscheBahnTrainHandler}.
46 * @author Sönke Küper - initial contribution.
49 public class DeutscheBahnTrainHandlerTest {
51 private static final String SAMPLE_PATH = "Bielefeld Hbf|Herford|Löhne(Westf)|Bad Oeynhausen|Porta Westfalica|Minden(Westf)|Bückeburg|Stadthagen|Haste|Wunstorf|Hannover Hbf|Lehrte";
53 private static Configuration createConfig(int position) {
54 final Configuration config = new Configuration();
55 config.put("position", String.valueOf(position));
59 static Thing mockThing(int id) {
60 final Thing thing = mock(Thing.class);
61 when(thing.getUID()).thenReturn(new ThingUID(DeutscheBahnBindingConstants.TRAIN_TYPE, "train-" + id));
62 when(thing.getThingTypeUID()).thenReturn(DeutscheBahnBindingConstants.TRAIN_TYPE);
63 when(thing.getConfiguration()).thenReturn(createConfig(id));
64 ThingUID bridgeId = new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable");
65 when(thing.getBridgeUID()).thenReturn(bridgeId);
67 final Channel tripLabelCategory = mockChannel(thing.getUID(), "trip#category");
69 final Channel arrivalPlannedTime = mockChannel(thing.getUID(), "arrival#planned-time");
70 final Channel arrivalLine = mockChannel(thing.getUID(), "arrival#line");
71 final Channel arrivalChangedTime = mockChannel(thing.getUID(), "arrival#changed-time");
73 final Channel departurePlannedTime = mockChannel(thing.getUID(), "departure#planned-time");
74 final Channel departurePlannedPlatform = mockChannel(thing.getUID(), "departure#planned-platform");
75 final Channel departureTargetStation = mockChannel(thing.getUID(), "departure#planned-final-station");
77 when(thing.getChannelsOfGroup("trip")).thenReturn(Arrays.asList(tripLabelCategory));
78 when(thing.getChannelsOfGroup("arrival"))
79 .thenReturn(Arrays.asList(arrivalPlannedTime, arrivalLine, arrivalChangedTime));
80 when(thing.getChannelsOfGroup("departure"))
81 .thenReturn(Arrays.asList(departurePlannedTime, departurePlannedPlatform, departureTargetStation));
82 when(thing.getChannels()).thenReturn(Arrays.asList( //
84 arrivalPlannedTime, arrivalLine, arrivalChangedTime, //
85 departurePlannedTime, departurePlannedPlatform, departureTargetStation));
90 private static Channel mockChannel(final ThingUID thingId, final String channelId) {
91 final Channel channel = Mockito.mock(Channel.class);
92 when(channel.getUID()).thenReturn(new ChannelUID(thingId, channelId));
96 private static DeutscheBahnTrainHandler createAndInitHandler(final ThingHandlerCallback callback,
98 final DeutscheBahnTrainHandler handler = new DeutscheBahnTrainHandler(thing);
99 handler.setCallback(callback);
100 handler.initialize();
104 private static State getDateTime(final Date day) {
105 final ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(day.toInstant(), ZoneId.systemDefault());
106 return new DateTimeType(zonedDateTime);
110 public void testUpdateChannels() {
111 final Thing thing = mockThing(1);
112 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
113 ThingUID bridgeId = new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable");
114 when(callback.getBridge(bridgeId))
115 .thenReturn(new BridgeImpl(DeutscheBahnBindingConstants.TIMETABLE_TYPE, bridgeId));
116 final DeutscheBahnTrainHandler handler = createAndInitHandler(callback, thing);
119 verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
120 verify(callback, timeout(1000)).statusUpdated(eq(thing),
121 argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
123 // Provide data that will update the channels
124 TimetableStop stop = new TimetableStop();
126 TripLabel label = new TripLabel();
130 Event arrival = new Event();
131 arrival.setPt("2108161434");
132 arrival.setL("RE60");
134 Event departure = new Event();
135 departure.setPt("2108161435");
136 departure.setPp("2");
137 departure.setPpth(SAMPLE_PATH);
138 stop.setDp(departure);
140 handler.updateChannels(stop);
142 final Date arrivalTime = new GregorianCalendar(2021, 7, 16, 14, 34).getTime();
143 final Date departureTime = new GregorianCalendar(2021, 7, 16, 14, 35).getTime();
145 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "trip#category"),
146 new StringType("WFB"));
147 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#planned-time"),
148 getDateTime(arrivalTime));
149 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#line"),
150 new StringType("RE60"));
151 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#changed-time"),
153 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "departure#planned-time"),
154 getDateTime(departureTime));
155 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "departure#planned-platform"),
156 new StringType("2"));
157 verify(callback, timeout(1000)).stateUpdated(
158 new ChannelUID(thing.getUID(), "departure#planned-final-station"), new StringType("Lehrte"));
165 public void testUpdateChannelsWithEventNotPresent() {
166 final Thing thing = mockThing(1);
167 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
168 ThingUID bridgeId = new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable");
169 when(callback.getBridge(bridgeId))
170 .thenReturn(new BridgeImpl(DeutscheBahnBindingConstants.TIMETABLE_TYPE, bridgeId));
171 final DeutscheBahnTrainHandler handler = createAndInitHandler(callback, thing);
174 verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
175 verify(callback, timeout(1000)).statusUpdated(eq(thing),
176 argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
178 // Provide data that will update the channels
179 TimetableStop stop = new TimetableStop();
181 Event arrival = new Event();
182 arrival.setPt("2108161434");
183 arrival.setL("RE60");
186 handler.updateChannels(stop);
188 final Date arrivalTime = new GregorianCalendar(2021, 7, 16, 14, 34).getTime();
190 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "trip#category"),
193 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#planned-time"),
194 getDateTime(arrivalTime));
195 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#line"),
196 new StringType("RE60"));
197 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "arrival#changed-time"),
200 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "departure#planned-time"),
202 verify(callback, timeout(1000)).stateUpdated(new ChannelUID(thing.getUID(), "departure#planned-platform"),
204 verify(callback, timeout(1000))
205 .stateUpdated(new ChannelUID(thing.getUID(), "departure#planned-final-station"), UnDefType.UNDEF);
212 public void testWithoutBridgeStateUpdatesToOffline() {
213 final Thing thing = mockThing(1);
214 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
215 final DeutscheBahnTrainHandler handler = createAndInitHandler(callback, thing);
218 verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
219 verify(callback, timeout(1000)).statusUpdated(eq(thing),
220 argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)));