2 * Copyright (c) 2010-2022 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.util.ArrayList;
19 import java.util.Calendar;
20 import java.util.GregorianCalendar;
21 import java.util.List;
22 import java.util.concurrent.ScheduledExecutorService;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.junit.jupiter.api.Test;
26 import org.mockito.Mockito;
27 import org.mockito.invocation.InvocationOnMock;
28 import org.openhab.binding.deutschebahn.internal.timetable.TimeproviderStub;
29 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiFactory;
30 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiStub;
31 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1Impl.HttpCallable;
32 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ImplTestHelper;
33 import org.openhab.binding.deutschebahn.internal.timetable.dto.Event;
34 import org.openhab.binding.deutschebahn.internal.timetable.dto.Timetable;
35 import org.openhab.binding.deutschebahn.internal.timetable.dto.TimetableStop;
36 import org.openhab.core.config.core.Configuration;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingUID;
42 import org.openhab.core.thing.binding.ThingHandlerCallback;
43 import org.openhab.core.types.UnDefType;
46 * Tests for {@link DeutscheBahnTimetableHandler}.
48 * @author Sönke Küper - initial contribution.
51 public class DeutscheBahnTimetableHandlerTest implements TimetablesV1ImplTestHelper {
53 private static Configuration createConfig(String trainFilter) {
54 final Configuration config = new Configuration();
55 config.put("accessToken", "letMeIn");
56 config.put("evaNo", "8000226");
57 config.put("trainFilter", trainFilter);
61 private static Bridge mockBridge(String trainFilter) {
62 final Bridge bridge = mock(Bridge.class);
63 when(bridge.getUID()).thenReturn(new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable"));
64 when(bridge.getConfiguration()).thenReturn(createConfig(trainFilter));
66 final List<Thing> things = new ArrayList<>();
67 things.add(DeutscheBahnTrainHandlerTest.mockThing(1));
68 things.add(DeutscheBahnTrainHandlerTest.mockThing(2));
69 things.add(DeutscheBahnTrainHandlerTest.mockThing(3));
70 when(things.get(0).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
71 when(things.get(1).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
72 when(things.get(2).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
74 when(bridge.getThings()).thenReturn(things);
79 private DeutscheBahnTimetableHandler createAndInitHandler(final ThingHandlerCallback callback, final Bridge bridge,
80 String dataDirectory) throws Exception {
81 return createAndInitHandler(callback, bridge, createApiWithTestdata(dataDirectory).getApiFactory());
84 private DeutscheBahnTimetableHandler createAndInitHandler( //
85 final ThingHandlerCallback callback, //
86 final Bridge bridge, //
87 final TimetablesV1ApiFactory apiFactory) { //
88 final TimeproviderStub timeProvider = new TimeproviderStub();
89 timeProvider.time = new GregorianCalendar(2021, Calendar.AUGUST, 16, 9, 30);
91 final ScheduledExecutorService executorStub = Mockito.mock(ScheduledExecutorService.class);
92 doAnswer((InvocationOnMock invocation) -> {
93 ((Runnable) invocation.getArguments()[0]).run();
95 }).when(executorStub).execute(any(Runnable.class));
97 final DeutscheBahnTimetableHandler handler = new DeutscheBahnTimetableHandler(bridge, apiFactory, timeProvider,
99 handler.setCallback(callback);
100 handler.initialize();
105 public void testUpdateChannels() throws Exception {
106 final Bridge bridge = mockBridge("all");
107 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
109 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge, "/timetablesData");
112 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
113 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
115 verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
116 verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
117 verifyThingUpdated(bridge, 2, "-2949440726131702047-2108160858-10");
124 public void testStopsAreOrderedByDeparture() throws Exception {
125 final Bridge bridge = mockBridge("departures");
126 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
128 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
129 "/timetablesDataDifferentOrder");
132 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
133 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
135 verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
136 verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
143 public void testStopsAreOrderedByArrival() throws Exception {
144 final Bridge bridge = mockBridge("arrivals");
145 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
147 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
148 "/timetablesDataDifferentOrder");
151 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
152 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
154 verifyThingUpdated(bridge, 0, "-8364795265993682073-2108160911-6");
155 verifyThingUpdated(bridge, 1, "-5296516961807204721-2108160906-5");
161 private void verifyThingUpdated(final Bridge bridge, int offset, String stopId) {
162 final Thing train = bridge.getThings().get(offset);
163 final DeutscheBahnTrainHandler childHandler = (DeutscheBahnTrainHandler) train.getHandler();
164 verify(childHandler).updateChannels(argThat((TimetableStop stop) -> stop.getId().equals(stopId)));
168 public void testUpdateTrainsToUndefinedIfNoDataWasProvided() {
169 final Bridge bridge = mockBridge("all");
170 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
172 final TimetablesV1ApiStub stubWithError = TimetablesV1ApiStub.createWithException();
174 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
175 (String authToken, HttpCallable httpCallable) -> stubWithError);
178 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
179 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)));
181 verifyChannelsUpdatedToUndef(bridge, 0, callback);
182 verifyChannelsUpdatedToUndef(bridge, 1, callback);
183 verifyChannelsUpdatedToUndef(bridge, 2, callback);
190 private static void verifyChannelsUpdatedToUndef(Bridge bridge, int offset, ThingHandlerCallback callback) {
191 final Thing thing = bridge.getThings().get(offset);
192 for (Channel channel : thing.getChannels()) {
193 verify(callback).stateUpdated(eq(channel.getUID()), eq(UnDefType.UNDEF));
198 public void testUpdateTrainsToUndefinedIfNotEnoughDataWasProvided() {
199 final Bridge bridge = mockBridge("all");
200 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
202 // Bridge contains 3 trains, but Timetable contains only 1 items, so two trains has to be updated to undef
204 final Timetable timetable = new Timetable();
205 TimetableStop stop01 = new TimetableStop();
206 stop01.setId("stop01id");
207 Event dp = new Event();
208 dp.setPt("2108161000");
210 timetable.getS().add(stop01);
212 final TimetablesV1ApiStub stubWithData = TimetablesV1ApiStub.createWithResult(timetable);
214 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
215 (String authToken, HttpCallable httpCallable) -> stubWithData);
218 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
219 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
221 verifyThingUpdated(bridge, 0, stop01.getId());
222 verifyChannelsUpdatedToUndef(bridge, 1, callback);
223 verifyChannelsUpdatedToUndef(bridge, 2, callback);