2 * Copyright (c) 2010-2021 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;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.junit.jupiter.api.Test;
25 import org.openhab.binding.deutschebahn.internal.timetable.TimeproviderStub;
26 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiFactory;
27 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiStub;
28 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1Impl.HttpCallable;
29 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ImplTestHelper;
30 import org.openhab.binding.deutschebahn.internal.timetable.dto.Event;
31 import org.openhab.binding.deutschebahn.internal.timetable.dto.Timetable;
32 import org.openhab.binding.deutschebahn.internal.timetable.dto.TimetableStop;
33 import org.openhab.core.config.core.Configuration;
34 import org.openhab.core.thing.Bridge;
35 import org.openhab.core.thing.Channel;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.ThingHandlerCallback;
40 import org.openhab.core.types.State;
41 import org.openhab.core.types.UnDefType;
44 * Tests for {@link DeutscheBahnTimetableHandler}.
46 * @author Sönke Küper - initial contribution.
49 public class DeutscheBahnTimetableHandlerTest implements TimetablesV1ImplTestHelper {
51 private static Configuration createConfig() {
52 final Configuration config = new Configuration();
53 config.put("accessToken", "letMeIn");
54 config.put("evaNo", "8000226");
55 config.put("trainFilter", "all");
59 private static Bridge mockBridge() {
60 final Bridge bridge = mock(Bridge.class);
61 when(bridge.getUID()).thenReturn(new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable"));
62 when(bridge.getConfiguration()).thenReturn(createConfig());
64 final List<Thing> things = new ArrayList<>();
65 things.add(DeutscheBahnTrainHandlerTest.mockThing(1));
66 things.add(DeutscheBahnTrainHandlerTest.mockThing(2));
67 things.add(DeutscheBahnTrainHandlerTest.mockThing(3));
68 when(things.get(0).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
69 when(things.get(1).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
70 when(things.get(2).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
72 when(bridge.getThings()).thenReturn(things);
77 private DeutscheBahnTimetableHandler createAndInitHandler(final ThingHandlerCallback callback, final Bridge bridge)
79 return createAndInitHandler(callback, bridge, createApiWithTestdata().getApiFactory());
82 private DeutscheBahnTimetableHandler createAndInitHandler( //
83 final ThingHandlerCallback callback, //
84 final Bridge bridge, //
85 final TimetablesV1ApiFactory apiFactory) throws Exception { //
86 final TimeproviderStub timeProvider = new TimeproviderStub();
87 timeProvider.time = new GregorianCalendar(2021, Calendar.AUGUST, 16, 9, 30);
89 final DeutscheBahnTimetableHandler handler = new DeutscheBahnTimetableHandler(bridge, apiFactory, timeProvider);
90 handler.setCallback(callback);
96 public void testUpdateChannels() throws Exception {
97 final Bridge bridge = mockBridge();
98 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
100 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge);
103 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
104 verify(callback, timeout(1000)).statusUpdated(eq(bridge),
105 argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
107 verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
108 verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
109 verifyThingUpdated(bridge, 2, "-2949440726131702047-2108160858-10");
115 private void verifyThingUpdated(final Bridge bridge, int offset, String stopId) {
116 final Thing train = bridge.getThings().get(offset);
117 final DeutscheBahnTrainHandler childHandler = (DeutscheBahnTrainHandler) train.getHandler();
118 verify(childHandler, timeout(1000))
119 .updateChannels(argThat((TimetableStop stop) -> stop.getId().equals(stopId)));
123 public void testUpdateTrainsToUndefinedIfNoDataWasProvided() throws Exception {
124 final Bridge bridge = mockBridge();
125 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
127 final TimetablesV1ApiStub stubWithError = TimetablesV1ApiStub.createWithException();
129 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
130 (String authToken, HttpCallable httpCallable) -> stubWithError);
133 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
134 verify(callback, timeout(1000)).statusUpdated(eq(bridge),
135 argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)));
137 verifyChannelsUpdatedToUndef(bridge, 0, callback, UnDefType.UNDEF);
138 verifyChannelsUpdatedToUndef(bridge, 1, callback, UnDefType.UNDEF);
139 verifyChannelsUpdatedToUndef(bridge, 2, callback, UnDefType.UNDEF);
146 private static void verifyChannelsUpdatedToUndef(Bridge bridge, int offset, ThingHandlerCallback callback,
147 State expectedState) {
148 final Thing thing = bridge.getThings().get(offset);
149 for (Channel channel : thing.getChannels()) {
150 verify(callback).stateUpdated(eq(channel.getUID()), eq(expectedState));
155 public void testUpdateTrainsToUndefinedIfNotEnoughDataWasProvided() throws Exception {
156 final Bridge bridge = mockBridge();
157 final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
159 // Bridge contains 3 trains, but Timetable contains only 1 items, so two trains has to be updated to undef
161 final Timetable timetable = new Timetable();
162 TimetableStop stop01 = new TimetableStop();
163 stop01.setId("stop01id");
164 Event dp = new Event();
165 dp.setPt("2108161000");
167 timetable.getS().add(stop01);
169 final TimetablesV1ApiStub stubWithData = TimetablesV1ApiStub.createWithResult(timetable);
171 final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
172 (String authToken, HttpCallable httpCallable) -> stubWithData);
175 verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
176 verify(callback, timeout(1000)).statusUpdated(eq(bridge),
177 argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
179 verifyThingUpdated(bridge, 0, stop01.getId());
180 verifyChannelsUpdatedToUndef(bridge, 1, callback, UnDefType.UNDEF);
181 verifyChannelsUpdatedToUndef(bridge, 2, callback, UnDefType.UNDEF);