]> git.basschouten.com Git - openhab-addons.git/blob
ae05dc37bc165bddd965b2225950637f2fca9ffd
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.deutschebahn.internal;
14
15 import static org.mockito.ArgumentMatchers.*;
16 import static org.mockito.Mockito.*;
17
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;
23
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;
44
45 /**
46  * Tests for {@link DeutscheBahnTimetableHandler}.
47  *
48  * @author Sönke Küper - initial contribution.
49  */
50 @NonNullByDefault
51 public class DeutscheBahnTimetableHandlerTest implements TimetablesV1ImplTestHelper {
52
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);
58         return config;
59     }
60
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));
65
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));
73
74         when(bridge.getThings()).thenReturn(things);
75
76         return bridge;
77     }
78
79     private DeutscheBahnTimetableHandler createAndInitHandler(final ThingHandlerCallback callback, final Bridge bridge,
80             String dataDirectory) throws Exception {
81         return createAndInitHandler(callback, bridge, createApiWithTestdata(dataDirectory).getApiFactory());
82     }
83
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);
90
91         final ScheduledExecutorService executorStub = Mockito.mock(ScheduledExecutorService.class);
92         doAnswer((InvocationOnMock invocation) -> {
93             ((Runnable) invocation.getArguments()[0]).run();
94             return null;
95         }).when(executorStub).execute(any(Runnable.class));
96
97         final DeutscheBahnTimetableHandler handler = new DeutscheBahnTimetableHandler(bridge, apiFactory, timeProvider,
98                 executorStub);
99         handler.setCallback(callback);
100         handler.initialize();
101         return handler;
102     }
103
104     @Test
105     public void testUpdateChannels() throws Exception {
106         final Bridge bridge = mockBridge("all");
107         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
108
109         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge, "/timetablesData");
110
111         try {
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)));
114
115             verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
116             verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
117             verifyThingUpdated(bridge, 2, "-2949440726131702047-2108160858-10");
118         } finally {
119             handler.dispose();
120         }
121     }
122
123     @Test
124     public void testStopsAreOrderedByDeparture() throws Exception {
125         final Bridge bridge = mockBridge("departures");
126         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
127
128         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
129                 "/timetablesDataDifferentOrder");
130
131         try {
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)));
134
135             verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
136             verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
137         } finally {
138             handler.dispose();
139         }
140     }
141
142     @Test
143     public void testStopsAreOrderedByArrival() throws Exception {
144         final Bridge bridge = mockBridge("arrivals");
145         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
146
147         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
148                 "/timetablesDataDifferentOrder");
149
150         try {
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)));
153
154             verifyThingUpdated(bridge, 0, "-8364795265993682073-2108160911-6");
155             verifyThingUpdated(bridge, 1, "-5296516961807204721-2108160906-5");
156         } finally {
157             handler.dispose();
158         }
159     }
160
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)));
165     }
166
167     @Test
168     public void testUpdateTrainsToUndefinedIfNoDataWasProvided() {
169         final Bridge bridge = mockBridge("all");
170         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
171
172         final TimetablesV1ApiStub stubWithError = TimetablesV1ApiStub.createWithException();
173
174         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
175                 (String authToken, HttpCallable httpCallable) -> stubWithError);
176
177         try {
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)));
180
181             verifyChannelsUpdatedToUndef(bridge, 0, callback);
182             verifyChannelsUpdatedToUndef(bridge, 1, callback);
183             verifyChannelsUpdatedToUndef(bridge, 2, callback);
184
185         } finally {
186             handler.dispose();
187         }
188     }
189
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));
194         }
195     }
196
197     @Test
198     public void testUpdateTrainsToUndefinedIfNotEnoughDataWasProvided() {
199         final Bridge bridge = mockBridge("all");
200         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
201
202         // Bridge contains 3 trains, but Timetable contains only 1 items, so two trains has to be updated to undef
203         // value.
204         final Timetable timetable = new Timetable();
205         TimetableStop stop01 = new TimetableStop();
206         stop01.setId("stop01id");
207         Event dp = new Event();
208         dp.setPt("2108161000");
209         stop01.setDp(dp);
210         timetable.getS().add(stop01);
211
212         final TimetablesV1ApiStub stubWithData = TimetablesV1ApiStub.createWithResult(timetable);
213
214         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
215                 (String authToken, HttpCallable httpCallable) -> stubWithData);
216
217         try {
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)));
220
221             verifyThingUpdated(bridge, 0, stop01.getId());
222             verifyChannelsUpdatedToUndef(bridge, 1, callback);
223             verifyChannelsUpdatedToUndef(bridge, 2, callback);
224
225         } finally {
226             handler.dispose();
227         }
228     }
229 }