]> git.basschouten.com Git - openhab-addons.git/blob
ff800ed45c818cc3e6679ca7d9d5e89be0aecb62
[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.mybmw.internal.handler;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.Mockito.*;
17
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.junit.jupiter.api.Test;
25 import org.mockito.ArgumentCaptor;
26 import org.openhab.binding.mybmw.internal.MyBMWConstants.VehicleType;
27 import org.openhab.binding.mybmw.internal.VehicleConfiguration;
28 import org.openhab.binding.mybmw.internal.dto.StatusWrapper;
29 import org.openhab.binding.mybmw.internal.util.FileReader;
30 import org.openhab.binding.mybmw.internal.utils.Constants;
31 import org.openhab.core.i18n.LocationProvider;
32 import org.openhab.core.library.types.PointType;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingUID;
36 import org.openhab.core.thing.binding.ThingHandlerCallback;
37 import org.openhab.core.types.State;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * The {@link VehicleTests} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
45  * @author Bernd Weymann - Initial contribution
46  */
47 @NonNullByDefault
48 @SuppressWarnings("null")
49 public class VehicleTests {
50     private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
51
52     private static final int STATUS_ELECTRIC = 12;
53     private static final int STATUS_CONV = 9;
54     private static final int RANGE_HYBRID = 9;
55     private static final int RANGE_CONV = 4;
56     private static final int RANGE_ELECTRIC = 4;
57     private static final int DOORS = 11;
58     private static final int CHECK_EMPTY = 3;
59     private static final int CHECK_AVAILABLE = 3;
60     private static final int SERVICE_AVAILABLE = 3;
61     private static final int SERVICE_EMPTY = 3;
62     private static final int LOCATION = 4;
63     private static final int CHARGE_PROFILE = 44;
64     private static final int TIRES = 8;
65     public static final PointType HOME_LOCATION = new PointType("54.321,9.876");
66     @Nullable
67     ArgumentCaptor<ChannelUID> channelCaptor;
68     @Nullable
69     ArgumentCaptor<State> stateCaptor;
70     @Nullable
71     ThingHandlerCallback tc;
72     @Nullable
73     VehicleHandler cch;
74     @Nullable
75     List<ChannelUID> allChannels;
76     @Nullable
77     List<State> allStates;
78     String driveTrain = Constants.EMPTY;
79
80     /**
81      * Prepare environment for Vehicle Status Updates
82      */
83     public void setup(String type, String vin) {
84         driveTrain = type;
85         Thing thing = mock(Thing.class);
86         when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
87         MyBMWCommandOptionProvider cop = mock(MyBMWCommandOptionProvider.class);
88         LocationProvider locationProvider = mock(LocationProvider.class);
89         when(locationProvider.getLocation()).thenReturn(HOME_LOCATION);
90         cch = new VehicleHandler(thing, cop, locationProvider, type);
91         VehicleConfiguration vc = new VehicleConfiguration();
92         vc.vin = vin;
93         Optional<VehicleConfiguration> ovc = Optional.of(vc);
94         cch.configuration = ovc;
95         tc = mock(ThingHandlerCallback.class);
96         cch.setCallback(tc);
97         channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
98         stateCaptor = ArgumentCaptor.forClass(State.class);
99     }
100
101     private boolean testVehicle(String statusContent, int callbacksExpected,
102             Optional<Map<String, State>> concreteChecks) {
103         assertNotNull(statusContent);
104         cch.vehicleStatusCallback.onResponse(statusContent);
105         verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
106         allChannels = channelCaptor.getAllValues();
107         allStates = stateCaptor.getAllValues();
108
109         assertNotNull(driveTrain);
110         StatusWrapper checker = new StatusWrapper(driveTrain, statusContent);
111         trace();
112         if (concreteChecks.isPresent()) {
113             return checker.append(concreteChecks.get()).checkResults(allChannels, allStates);
114         } else {
115             return checker.checkResults(allChannels, allStates);
116         }
117     }
118
119     private void trace() {
120         for (int i = 0; i < allChannels.size(); i++) {
121             // change to info for debugging channel updates
122             logger.debug("Channel {} {}", allChannels.get(i), allStates.get(i));
123         }
124     }
125
126     /**
127      * Test various Vehicles from users which delivered their fingerprint.
128      * The tests are checking the chain from "JSON to Channel update".
129      * Checks are done in an automated way cross checking the data from JSON and data delivered via Channel.
130      * Also important the updates are counted in order to check if code changes are affecting Channel Updates.
131      *
132      * With the given output the updated Channels are visible.
133      * Example:
134      *
135      * testi3Rex
136      * Channel testbinding::test:status#lock Locked
137      * Channel testbinding::test:status#service-date 2023-11-01T00:00:00.000+0100
138      * Channel testbinding::test:status#check-control No Issues
139      * Channel testbinding::test:status#last-update 2021-12-21T16:46:02.000+0100
140      * Channel testbinding::test:status#doors Closed
141      * Channel testbinding::test:status#windows Closed
142      * Channel testbinding::test:status#plug-connection Not connected
143      * Channel testbinding::test:status#charge Not Charging
144      * Channel testbinding::test:status#charge-type Not Available
145      * Channel testbinding::test:range#electric 76 km
146      * Channel testbinding::test:range#radius-electric 60.800000000000004 km
147      * Channel testbinding::test:range#fuel 31 km
148      * Channel testbinding::test:range#radius-fuel 24.8 km
149      * Channel testbinding::test:range#hybrid 31 km
150      * Channel testbinding::test:range#radius-hybrid 24.8 km
151      * Channel testbinding::test:range#mileage 31537 km
152      * Channel testbinding::test:range#soc 74 %
153      * Channel testbinding::test:range#remaining-fuel 4 l
154      * Channel testbinding::test:doors#driver-front Closed
155      * Channel testbinding::test:doors#driver-rear Closed
156      * Channel testbinding::test:doors#passenger-front Closed
157      * Channel testbinding::test:doors#passenger-rear Closed
158      * Channel testbinding::test:doors#trunk Closed
159      * Channel testbinding::test:doors#hood Closed
160      * Channel testbinding::test:doors#win-driver-front Closed
161      * Channel testbinding::test:doors#win-driver-rear Undef
162      * Channel testbinding::test:doors#win-passenger-front Closed
163      * Channel testbinding::test:doors#win-passenger-rear Undef
164      * Channel testbinding::test:doors#sunroof Closed
165      * Channel testbinding::test:location#gps 1.2345,6.789
166      * Channel testbinding::test:location#heading 222 °
167      * Channel testbinding::test:service#name Brake Fluid
168      * Channel testbinding::test:service#date 2023-11-01T00:00:00.000+0100
169      * Channel testbinding::test:profile#prefs Chargingwindow
170      * Channel testbinding::test:profile#mode Immediatecharging
171      * Channel testbinding::test:profile#control Weeklyplanner
172      * Channel testbinding::test:profile#target 100
173      * Channel testbinding::test:profile#limit OFF
174      * Channel testbinding::test:profile#climate OFF
175      * Channel testbinding::test:profile#window-start 1970-01-01T11:00:00.000+0100
176      * Channel testbinding::test:profile#window-end 1970-01-01T14:30:00.000+0100
177      * Channel testbinding::test:profile#timer1-departure 1970-01-01T16:00:00.000+0100
178      * Channel testbinding::test:profile#timer1-enabled OFF
179      * Channel testbinding::test:profile#timer1-day-mon ON
180      * Channel testbinding::test:profile#timer1-day-tue ON
181      * Channel testbinding::test:profile#timer1-day-wed ON
182      * Channel testbinding::test:profile#timer1-day-thu ON
183      * Channel testbinding::test:profile#timer1-day-fri ON
184      * Channel testbinding::test:profile#timer1-day-sat ON
185      * Channel testbinding::test:profile#timer1-day-sun ON
186      * Channel testbinding::test:profile#timer2-departure 1970-01-01T12:02:00.000+0100
187      * Channel testbinding::test:profile#timer2-enabled ON
188      * Channel testbinding::test:profile#timer2-day-mon OFF
189      * Channel testbinding::test:profile#timer2-day-tue OFF
190      * Channel testbinding::test:profile#timer2-day-wed OFF
191      * Channel testbinding::test:profile#timer2-day-thu OFF
192      * Channel testbinding::test:profile#timer2-day-fri OFF
193      * Channel testbinding::test:profile#timer2-day-sat OFF
194      * Channel testbinding::test:profile#timer2-day-sun ON
195      * Channel testbinding::test:profile#timer3-departure 1970-01-01T13:03:00.000+0100
196      * Channel testbinding::test:profile#timer3-enabled OFF
197      * Channel testbinding::test:profile#timer3-day-mon OFF
198      * Channel testbinding::test:profile#timer3-day-tue OFF
199      * Channel testbinding::test:profile#timer3-day-wed OFF
200      * Channel testbinding::test:profile#timer3-day-thu OFF
201      * Channel testbinding::test:profile#timer3-day-fri OFF
202      * Channel testbinding::test:profile#timer3-day-sat ON
203      * Channel testbinding::test:profile#timer3-day-sun OFF
204      * Channel testbinding::test:profile#timer4-departure 1970-01-01T12:02:00.000+0100
205      * Channel testbinding::test:profile#timer4-enabled OFF
206      * Channel testbinding::test:profile#timer4-day-mon OFF
207      * Channel testbinding::test:profile#timer4-day-tue OFF
208      * Channel testbinding::test:profile#timer4-day-wed OFF
209      * Channel testbinding::test:profile#timer4-day-thu OFF
210      * Channel testbinding::test:profile#timer4-day-fri OFF
211      * Channel testbinding::test:profile#timer4-day-sat OFF
212      * Channel testbinding::test:profile#timer4-day-sun ON
213      */
214     @Test
215     public void testI01Rex() {
216         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
217         setup(VehicleType.ELECTRIC_REX.toString(), Constants.ANONYMOUS);
218         String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/vehicles.json");
219         assertTrue(testVehicle(content, STATUS_ELECTRIC + RANGE_HYBRID + DOORS + LOCATION + SERVICE_AVAILABLE
220                 + CHECK_EMPTY + CHARGE_PROFILE + TIRES, Optional.empty()));
221     }
222
223     @Test
224     public void testF11() {
225         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
226         setup(VehicleType.CONVENTIONAL.toString(), "some_vin_F11");
227         String content = FileReader.readFileInString("src/test/resources/responses/F11/vehicles_v2_bmw_0.json");
228         assertTrue(testVehicle(content,
229                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + LOCATION + TIRES,
230                 Optional.empty()));
231     }
232
233     @Test
234     public void testF31() {
235         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
236         setup(VehicleType.CONVENTIONAL.toString(), "some_vin_F31");
237         String content = FileReader.readFileInString("src/test/resources/responses/F31/vehicles_v2_bmw_0.json");
238         assertTrue(testVehicle(content,
239                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + LOCATION + TIRES,
240                 Optional.empty()));
241     }
242
243     @Test
244     public void testF44() {
245         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
246         setup(VehicleType.CONVENTIONAL.toString(), "some_vin_F44");
247         String content = FileReader.readFileInString("src/test/resources/responses/F44/vehicles_v2_bmw_0.json");
248         assertTrue(testVehicle(content,
249                 STATUS_CONV + DOORS + RANGE_CONV + LOCATION + SERVICE_EMPTY + CHECK_EMPTY + TIRES, Optional.empty()));
250     }
251
252     @Test
253     public void testF45() {
254         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
255         setup(VehicleType.PLUGIN_HYBRID.toString(), "some_vin_F45");
256         String content = FileReader.readFileInString("src/test/resources/responses/F45/vehicles_v2_bmw_0.json");
257         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
258                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
259     }
260
261     @Test
262     public void testF48() {
263         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
264         setup(VehicleType.CONVENTIONAL.toString(), "some_vin_F48");
265         String content = FileReader.readFileInString("src/test/resources/responses/F48/vehicles_v2_bmw_0.json");
266         assertTrue(testVehicle(content,
267                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_AVAILABLE + LOCATION + TIRES,
268                 Optional.empty()));
269     }
270
271     @Test
272     public void testG01() {
273         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
274         setup(VehicleType.PLUGIN_HYBRID.toString(), "some_vin_G01");
275         String content = FileReader.readFileInString("src/test/resources/responses/G01/vehicles_v2_bmw_0.json");
276         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
277                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
278     }
279
280     @Test
281     public void testG05() {
282         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
283         setup(VehicleType.PLUGIN_HYBRID.toString(), "some_vin_G05");
284         String content = FileReader.readFileInString("src/test/resources/responses/G05/vehicles_v2_bmw_0.json");
285         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
286                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
287     }
288
289     @Test
290     public void testG08() {
291         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
292         setup(VehicleType.ELECTRIC.toString(), "some_vin_G08");
293         String content = FileReader.readFileInString("src/test/resources/responses/G08/vehicles_v2_bmw_0.json");
294         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY
295                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
296     }
297
298     @Test
299     public void testG21() {
300         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
301         setup(VehicleType.PLUGIN_HYBRID.toString(), "some_vin_G21");
302         String content = FileReader.readFileInString("src/test/resources/responses/G21/vehicles_v2_bmw_0.json");
303         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
304                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
305     }
306
307     @Test
308     public void testG30() {
309         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
310         setup(VehicleType.PLUGIN_HYBRID.toString(), "some_vin_G30");
311         String content = FileReader.readFileInString("src/test/resources/responses/G30/vehicles_v2_bmw_0.json");
312         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
313                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
314     }
315
316     @Test
317     public void testI01NoRex() {
318         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
319         setup(VehicleType.ELECTRIC.toString(), "some_vin_I01_NOREX");
320         String content = FileReader.readFileInString("src/test/resources/responses/I01_NOREX/vehicles_v2_bmw_0.json");
321         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY
322                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
323     }
324
325     @Test
326     public void test530e() {
327         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
328         setup(VehicleType.PLUGIN_HYBRID.toString(), "anonymous");
329         String content = FileReader.readFileInString("src/test/resources/responses/530e/vehicles.json");
330         assertTrue(testVehicle(content, STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY
331                 + LOCATION + CHARGE_PROFILE + TIRES, Optional.empty()));
332     }
333
334     @Test
335     public void test340i() {
336         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
337         setup(VehicleType.MILD_HYBRID.toString(), "anonymous");
338         String content = FileReader.readFileInString("src/test/resources/responses/G21/340i.json");
339         assertTrue(testVehicle(content,
340                 STATUS_CONV + DOORS + RANGE_CONV + LOCATION + SERVICE_EMPTY + CHECK_EMPTY + TIRES, Optional.empty()));
341     }
342 }