]> git.basschouten.com Git - openhab-addons.git/blob
87b975f1fea0a9be648b36421bd77b47043a0599
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.any;
16 import static org.mockito.ArgumentMatchers.argThat;
17 import static org.mockito.ArgumentMatchers.eq;
18 import static org.mockito.Mockito.doAnswer;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.Mockito.when;
22
23 import java.util.ArrayList;
24 import java.util.Calendar;
25 import java.util.GregorianCalendar;
26 import java.util.List;
27 import java.util.concurrent.ScheduledExecutorService;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.junit.jupiter.api.Test;
31 import org.mockito.Mockito;
32 import org.mockito.invocation.InvocationOnMock;
33 import org.openhab.binding.deutschebahn.internal.timetable.TimeproviderStub;
34 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiFactory;
35 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ApiStub;
36 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1Impl.HttpCallable;
37 import org.openhab.binding.deutschebahn.internal.timetable.TimetablesV1ImplTestHelper;
38 import org.openhab.binding.deutschebahn.internal.timetable.dto.Event;
39 import org.openhab.binding.deutschebahn.internal.timetable.dto.Timetable;
40 import org.openhab.binding.deutschebahn.internal.timetable.dto.TimetableStop;
41 import org.openhab.core.config.core.Configuration;
42 import org.openhab.core.thing.Bridge;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.Thing;
45 import org.openhab.core.thing.ThingStatus;
46 import org.openhab.core.thing.ThingUID;
47 import org.openhab.core.thing.binding.ThingHandlerCallback;
48 import org.openhab.core.types.UnDefType;
49
50 /**
51  * Tests for {@link DeutscheBahnTimetableHandler}.
52  *
53  * @author Sönke Küper - initial contribution.
54  */
55 @NonNullByDefault
56 public class DeutscheBahnTimetableHandlerTest implements TimetablesV1ImplTestHelper {
57
58     private static Configuration createConfig(String trainFilter) {
59         final Configuration config = new Configuration();
60         config.put("accessToken", "letMeIn");
61         config.put("evaNo", "8000226");
62         config.put("trainFilter", trainFilter);
63         return config;
64     }
65
66     private static Bridge mockBridge(String trainFilter) {
67         final Bridge bridge = mock(Bridge.class);
68         when(bridge.getUID()).thenReturn(new ThingUID(DeutscheBahnBindingConstants.TIMETABLE_TYPE, "timetable"));
69         when(bridge.getConfiguration()).thenReturn(createConfig(trainFilter));
70
71         final List<Thing> things = new ArrayList<>();
72         things.add(DeutscheBahnTrainHandlerTest.mockThing(1));
73         things.add(DeutscheBahnTrainHandlerTest.mockThing(2));
74         things.add(DeutscheBahnTrainHandlerTest.mockThing(3));
75         when(things.get(0).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
76         when(things.get(1).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
77         when(things.get(2).getHandler()).thenReturn(mock(DeutscheBahnTrainHandler.class));
78
79         when(bridge.getThings()).thenReturn(things);
80
81         return bridge;
82     }
83
84     private DeutscheBahnTimetableHandler createAndInitHandler(final ThingHandlerCallback callback, final Bridge bridge,
85             String dataDirectory) throws Exception {
86         return createAndInitHandler(callback, bridge, createApiWithTestdata(dataDirectory).getApiFactory());
87     }
88
89     private DeutscheBahnTimetableHandler createAndInitHandler( //
90             final ThingHandlerCallback callback, //
91             final Bridge bridge, //
92             final TimetablesV1ApiFactory apiFactory) { //
93         final TimeproviderStub timeProvider = new TimeproviderStub();
94         timeProvider.time = new GregorianCalendar(2021, Calendar.AUGUST, 16, 9, 30);
95
96         final ScheduledExecutorService executorStub = Mockito.mock(ScheduledExecutorService.class);
97         doAnswer((InvocationOnMock invocation) -> {
98             ((Runnable) invocation.getArguments()[0]).run();
99             return null;
100         }).when(executorStub).execute(any(Runnable.class));
101
102         final DeutscheBahnTimetableHandler handler = new DeutscheBahnTimetableHandler(bridge, apiFactory, timeProvider,
103                 executorStub);
104         handler.setCallback(callback);
105         handler.initialize();
106         return handler;
107     }
108
109     @Test
110     public void testUpdateChannels() throws Exception {
111         final Bridge bridge = mockBridge("all");
112         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
113
114         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge, "/timetablesData");
115
116         try {
117             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
118             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
119
120             verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
121             verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
122             verifyThingUpdated(bridge, 2, "-2949440726131702047-2108160858-10");
123         } finally {
124             handler.dispose();
125         }
126     }
127
128     @Test
129     public void testStopsAreOrderedByDeparture() throws Exception {
130         final Bridge bridge = mockBridge("departures");
131         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
132
133         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
134                 "/timetablesDataDifferentOrder");
135
136         try {
137             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
138             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
139
140             verifyThingUpdated(bridge, 0, "-5296516961807204721-2108160906-5");
141             verifyThingUpdated(bridge, 1, "-8364795265993682073-2108160911-6");
142         } finally {
143             handler.dispose();
144         }
145     }
146
147     @Test
148     public void testStopsAreOrderedByArrival() throws Exception {
149         final Bridge bridge = mockBridge("arrivals");
150         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
151
152         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
153                 "/timetablesDataDifferentOrder");
154
155         try {
156             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
157             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
158
159             verifyThingUpdated(bridge, 0, "-8364795265993682073-2108160911-6");
160             verifyThingUpdated(bridge, 1, "-5296516961807204721-2108160906-5");
161         } finally {
162             handler.dispose();
163         }
164     }
165
166     private void verifyThingUpdated(final Bridge bridge, int offset, String stopId) {
167         final Thing train = bridge.getThings().get(offset);
168         final DeutscheBahnTrainHandler childHandler = (DeutscheBahnTrainHandler) train.getHandler();
169         verify(childHandler).updateChannels(argThat((TimetableStop stop) -> stop.getId().equals(stopId)));
170     }
171
172     @Test
173     public void testUpdateTrainsToUndefinedIfNoDataWasProvided() {
174         final Bridge bridge = mockBridge("all");
175         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
176
177         final TimetablesV1ApiStub stubWithError = TimetablesV1ApiStub.createWithException();
178
179         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
180                 (String clientId, String clientSecret, HttpCallable httpCallable) -> stubWithError);
181
182         try {
183             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
184             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)));
185
186             verifyChannelsUpdatedToUndef(bridge, 0, callback);
187             verifyChannelsUpdatedToUndef(bridge, 1, callback);
188             verifyChannelsUpdatedToUndef(bridge, 2, callback);
189         } finally {
190             handler.dispose();
191         }
192     }
193
194     private static void verifyChannelsUpdatedToUndef(Bridge bridge, int offset, ThingHandlerCallback callback) {
195         final Thing thing = bridge.getThings().get(offset);
196         for (Channel channel : thing.getChannels()) {
197             verify(callback).stateUpdated(eq(channel.getUID()), eq(UnDefType.UNDEF));
198         }
199     }
200
201     @Test
202     public void testUpdateTrainsToUndefinedIfNotEnoughDataWasProvided() {
203         final Bridge bridge = mockBridge("all");
204         final ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
205
206         // Bridge contains 3 trains, but Timetable contains only 1 items, so two trains has to be updated to undef
207         // value.
208         final Timetable timetable = new Timetable();
209         TimetableStop stop01 = new TimetableStop();
210         stop01.setId("stop01id");
211         Event dp = new Event();
212         dp.setPt("2108161000");
213         stop01.setDp(dp);
214         timetable.getS().add(stop01);
215
216         final TimetablesV1ApiStub stubWithData = TimetablesV1ApiStub.createWithResult(timetable);
217
218         final DeutscheBahnTimetableHandler handler = createAndInitHandler(callback, bridge,
219                 (String clientId, String clientSecret, HttpCallable httpCallable) -> stubWithData);
220
221         try {
222             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
223             verify(callback).statusUpdated(eq(bridge), argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
224
225             verifyThingUpdated(bridge, 0, stop01.getId());
226             verifyChannelsUpdatedToUndef(bridge, 1, callback);
227             verifyChannelsUpdatedToUndef(bridge, 2, callback);
228         } finally {
229             handler.dispose();
230         }
231     }
232 }