]> git.basschouten.com Git - openhab-addons.git/blob
78b2d14a6b83ff99bbf3a70014a4a6bcb044da9f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.bmwconnecteddrive.internal.handler;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.Mockito.*;
17
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Optional;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.junit.jupiter.api.Test;
26 import org.mockito.ArgumentCaptor;
27 import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants;
28 import org.openhab.binding.bmwconnecteddrive.internal.ConnectedDriveConstants.VehicleType;
29 import org.openhab.binding.bmwconnecteddrive.internal.dto.StatusWrapper;
30 import org.openhab.binding.bmwconnecteddrive.internal.dto.compat.VehicleAttributesContainer;
31 import org.openhab.binding.bmwconnecteddrive.internal.util.FileReader;
32 import org.openhab.binding.bmwconnecteddrive.internal.utils.Constants;
33 import org.openhab.binding.bmwconnecteddrive.internal.utils.Converter;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.ThingHandlerCallback;
39 import org.openhab.core.types.State;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link VehicleTests} is responsible for handling commands, which are
45  * sent to one of the channels.
46  *
47  * @author Bernd Weymann - Initial contribution
48  */
49 @NonNullByDefault
50 @SuppressWarnings("null")
51 public class VehicleTests {
52     private final Logger logger = LoggerFactory.getLogger(VehicleHandler.class);
53
54     private static final int STATUS_ELECTRIC = 12;
55     private static final int STATUS_CONV = 8;
56     private static final int RANGE_HYBRID = 12;
57     private static final int RANGE_CONV = 4;
58     private static final int RANGE_ELECTRIC = 5;
59     private static final int DOORS = 12;
60     private static final int CHECK_EMPTY = 3;
61     private static final int CHECK_AVAILABLE = 3;
62     private static final int SERVICE_AVAILABLE = 4;
63     private static final int SERVICE_EMPTY = 4;
64     private static final int POSITION = 2;
65
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     boolean imperial;
80
81     /**
82      * Prepare environment for Vehicle Status Updates
83      */
84     public void setup(String type, boolean imperial) {
85         driveTrain = type;
86         this.imperial = imperial;
87         Thing thing = mock(Thing.class);
88         when(thing.getUID()).thenReturn(new ThingUID("testbinding", "test"));
89         BMWConnectedDriveOptionProvider op = mock(BMWConnectedDriveOptionProvider.class);
90         cch = new VehicleHandler(thing, op, type, imperial);
91         tc = mock(ThingHandlerCallback.class);
92         cch.setCallback(tc);
93         channelCaptor = ArgumentCaptor.forClass(ChannelUID.class);
94         stateCaptor = ArgumentCaptor.forClass(State.class);
95     }
96
97     private boolean testVehicle(String statusContent, int callbacksExpected,
98             Optional<Map<String, State>> concreteChecks) {
99         assertNotNull(statusContent);
100         cch.vehicleStatusCallback.onResponse(statusContent);
101         verify(tc, times(callbacksExpected)).stateUpdated(channelCaptor.capture(), stateCaptor.capture());
102         allChannels = channelCaptor.getAllValues();
103         allStates = stateCaptor.getAllValues();
104
105         assertNotNull(driveTrain);
106         StatusWrapper checker = new StatusWrapper(driveTrain, imperial, statusContent);
107         trace();
108         if (concreteChecks.isPresent()) {
109             return checker.append(concreteChecks.get()).checkResults(allChannels, allStates);
110         } else {
111             return checker.checkResults(allChannels, allStates);
112         }
113     }
114
115     private void trace() {
116         for (int i = 0; i < allChannels.size(); i++) {
117             logger.info("Channel {} {}", allChannels.get(i), allStates.get(i));
118         }
119     }
120
121     /**
122      * Test various Vehicles from users which delivered their fingerprint.
123      * The tests are checking the chain from "JSON to Channel update".
124      * Checks are done in an automated way cross checking the data from JSON and data delivered via Channel.
125      * Also important the updates are counted in order to check if code changes are affecting Channel Updates.
126      *
127      * With the given output the updated Channels are visible.
128      * Example:
129      *
130      * testi3Rex
131      * [main] INFO org.eclipse.jetty.util.log - Logging initialized @1731ms
132      * Channel testbinding::test:status#lock Secured
133      * Channel testbinding::test:status#service-date 2021-11-01T13:00:00.000+0100
134      * Channel testbinding::test:status#service-mileage -1.0 km
135      * Channel testbinding::test:status#check-control Not Active
136      * Channel testbinding::test:status#last-update 2020-08-24T17:55:32.000+0200
137      * Channel testbinding::test:status#doors CLOSED
138      * Channel testbinding::test:status#windows CLOSED
139      * Channel testbinding::test:doors#driver-front CLOSED
140      * Channel testbinding::test:doors#driver-rear CLOSED
141      * Channel testbinding::test:doors#passenger-front CLOSED
142      * Channel testbinding::test:doors#passenger-rear CLOSED
143      * Channel testbinding::test:doors#trunk CLOSED
144      * Channel testbinding::test:doors#hood CLOSED
145      * Channel testbinding::test:doors#window-driver-front CLOSED
146      * Channel testbinding::test:doors#window-driver-rear CLOSED
147      * Channel testbinding::test:doors#window-passenger-front CLOSED
148      * Channel testbinding::test:doors#window-passenger-rear CLOSED
149      * Channel testbinding::test:doors#window-rear INVALID
150      * Channel testbinding::test:doors#sunroof CLOSED
151      * Channel testbinding::test:range#mileage 17273.0 km
152      * Channel testbinding::test:range#electric 148.0 km
153      * Channel testbinding::test:range#radius-electric 118.4 km
154      * Channel testbinding::test:range#fuel 70.0 km
155      * Channel testbinding::test:range#radius-fuel 56.0 km
156      * Channel testbinding::test:range#hybrid 218.0 km
157      * Channel testbinding::test:range#radius-hybrid 174.4 km
158      * Channel testbinding::test:range#soc 71.0 %
159      * Channel testbinding::test:range#remaining-fuel 4.0 l
160      * Channel testbinding::test:status#charge Charging Goal Reached
161      * Channel testbinding::test:check#size 0
162      * Channel testbinding::test:check#name INVALID
163      * Channel testbinding::test:check#mileage -1.0 km
164      * Channel testbinding::test:check#index -1
165      * Channel testbinding::test:service#size 4
166      * Channel testbinding::test:service#name Brake Fluid
167      * Channel testbinding::test:service#date 2021-11-01T13:00:00.000+0100
168      * Channel testbinding::test:service#mileage 15345.0 km
169      * Channel testbinding::test:service#index 0
170      * Channel testbinding::test:location#latitude 50.55604934692383
171      * Channel testbinding::test:location#longitude 8.4956693649292
172      * Channel testbinding::test:location#heading 219.0 °
173      *
174      */
175
176     @Test
177     public void testi3Rex() {
178         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
179         setup(VehicleType.ELECTRIC_REX.toString(), false);
180         String content = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
181         assertTrue(testVehicle(content,
182                 STATUS_ELECTRIC + RANGE_HYBRID + DOORS + CHECK_EMPTY + SERVICE_AVAILABLE + POSITION, Optional.empty()));
183     }
184
185     @Test
186     public void testi3RexMiles() {
187         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
188         setup(VehicleType.ELECTRIC_REX.toString(), true);
189         String content = FileReader.readFileInString("src/test/resources/webapi/vehicle-status.json");
190         // assertTrue(testVehicle(content, HYBRID_CALL_TIMES + LIST_UPDATES, Optional.empty()));
191         assertTrue(testVehicle(content,
192                 STATUS_ELECTRIC + RANGE_HYBRID + DOORS + CHECK_EMPTY + SERVICE_AVAILABLE + POSITION, Optional.empty()));
193     }
194
195     @Test
196     public void testF15() {
197         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
198         setup(VehicleType.CONVENTIONAL.toString(), false);
199         String content = FileReader.readFileInString("src/test/resources/responses/F15/status.json");
200         // Check earliest Service by hard
201         Map<String, State> m = new HashMap<String, State>();
202         // Don>'t test on concrete timestamp - it's is different on each machine
203         // Check for cbsType which is "Oil" instead
204         // m.put(ConnectedDriveConstants.SERVICE_DATE, DateTimeType.valueOf("2018-06-01T14:00:00.000+0200"));
205         m.put(ConnectedDriveConstants.NAME, StringType.valueOf("Oil"));
206         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
207                 Optional.of(m)));
208     }
209
210     @Test
211     public void testF15Miles() {
212         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
213         setup(VehicleType.CONVENTIONAL.toString(), true);
214         String content = FileReader.readFileInString("src/test/resources/responses/F15/status.json");
215         // Check earliest Service by hard
216         Map<String, State> m = new HashMap<String, State>();
217         // Don>'t test on concrete timestamp - it's idfferent on each machine
218         // Check for cbsType which is "Oil" instead
219         // m.put(ConnectedDriveConstants.SERVICE_DATE, DateTimeType.valueOf("2018-06-01T14:00:00.000+0200"));
220         m.put(ConnectedDriveConstants.NAME, StringType.valueOf("Oil"));
221         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
222                 Optional.of(m)));
223     }
224
225     @Test
226     public void testF31() {
227         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
228         setup(VehicleType.CONVENTIONAL.toString(), false);
229         String content = FileReader.readFileInString("src/test/resources/responses/F31/status.json");
230         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
231                 Optional.empty()));
232     }
233
234     @Test
235     public void testF31Miles() {
236         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
237         setup(VehicleType.CONVENTIONAL.toString(), true);
238         String content = FileReader.readFileInString("src/test/resources/responses/F31/status.json");
239         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_AVAILABLE + CHECK_EMPTY,
240                 Optional.empty()));
241     }
242
243     @Test
244     public void testF35() {
245         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
246         setup(VehicleType.CONVENTIONAL.toString(), false);
247         String content = FileReader.readFileInString("src/test/resources/responses/F35/status.json");
248         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_EMPTY + CHECK_EMPTY,
249                 Optional.empty()));
250     }
251
252     @Test
253     public void testF35Miles() {
254         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
255         setup(VehicleType.CONVENTIONAL.toString(), true);
256         String content = FileReader.readFileInString("src/test/resources/responses/F35/status.json");
257         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + POSITION + SERVICE_EMPTY + CHECK_EMPTY,
258                 Optional.empty()));
259     }
260
261     @Test
262     public void testF45() {
263         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
264         setup(VehicleType.CONVENTIONAL.toString(), false);
265         String content = FileReader.readFileInString("src/test/resources/responses/F45/status.json");
266         // assertTrue(testVehicle(content, 27, Optional.empty()));
267         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_EMPTY + CHECK_EMPTY + POSITION,
268                 Optional.empty()));
269     }
270
271     @Test
272     public void testF45Miles() {
273         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
274         setup(VehicleType.CONVENTIONAL.toString(), true);
275         String content = FileReader.readFileInString("src/test/resources/responses/F45/status.json");
276         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_EMPTY + CHECK_EMPTY + POSITION,
277                 Optional.empty()));
278     }
279
280     @Test
281     public void testF48() {
282         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
283         setup(VehicleType.CONVENTIONAL.toString(), false);
284         String content = FileReader.readFileInString("src/test/resources/responses/F48/status.json");
285         assertTrue(testVehicle(content,
286                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION, Optional.empty()));
287     }
288
289     @Test
290     public void testF48Miles() {
291         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
292         setup(VehicleType.CONVENTIONAL.toString(), true);
293         String content = FileReader.readFileInString("src/test/resources/responses/F48/status.json");
294         assertTrue(testVehicle(content,
295                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION, Optional.empty()));
296     }
297
298     @Test
299     public void testG31NBTEvo() {
300         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
301         setup(VehicleType.CONVENTIONAL.toString(), false);
302         String content = FileReader.readFileInString("src/test/resources/responses/G31_NBTevo/status.json");
303         // assertTrue(testVehicle(content, 27, Optional.empty()));
304         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
305                 Optional.empty()));
306     }
307
308     @Test
309     public void testG31NBTEvoMiles() {
310         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
311         setup(VehicleType.CONVENTIONAL.toString(), true);
312         String content = FileReader.readFileInString("src/test/resources/responses/G31_NBTevo/status.json");
313         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
314                 Optional.empty()));
315     }
316
317     @Test
318     public void testI01NoRex() {
319         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
320         setup(VehicleType.ELECTRIC.toString(), false);
321         String content = FileReader.readFileInString("src/test/resources/responses/I01_NOREX/status.json");
322         assertTrue(testVehicle(content,
323                 STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
324                 Optional.empty()));
325     }
326
327     @Test
328     public void testI01NoRexMiles() {
329         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
330         setup(VehicleType.ELECTRIC.toString(), true);
331         String content = FileReader.readFileInString("src/test/resources/responses/I01_NOREX/status.json");
332         assertTrue(testVehicle(content,
333                 STATUS_ELECTRIC + DOORS + RANGE_ELECTRIC + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
334                 Optional.empty()));
335     }
336
337     @Test
338     public void testI01Rex() {
339         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
340         setup(VehicleType.ELECTRIC_REX.toString(), false);
341         String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/status.json");
342         assertTrue(testVehicle(content,
343                 STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
344     }
345
346     @Test
347     public void testI01RexMiles() {
348         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
349         setup(VehicleType.ELECTRIC_REX.toString(), true);
350         String content = FileReader.readFileInString("src/test/resources/responses/I01_REX/status.json");
351         assertTrue(testVehicle(content,
352                 STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
353     }
354
355     @Test
356     public void test318iF31() {
357         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
358         setup(VehicleType.CONVENTIONAL.toString(), false);
359         String content = FileReader.readFileInString("src/test/resources/responses/F31/status-318i.json");
360         Map<String, State> m = new HashMap<String, State>();
361         m.put(ConnectedDriveConstants.WINDOWS, StringType.valueOf(Constants.INTERMEDIATE));
362         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
363                 Optional.empty()));
364     }
365
366     @Test
367     public void test318iF31Miles() {
368         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
369         setup(VehicleType.CONVENTIONAL.toString(), true);
370         String content = FileReader.readFileInString("src/test/resources/responses/F31/status-318i.json");
371         Map<String, State> m = new HashMap<String, State>();
372         m.put(ConnectedDriveConstants.WINDOWS, StringType.valueOf(Constants.INTERMEDIATE));
373         assertTrue(testVehicle(content, STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION,
374                 Optional.empty()));
375     }
376
377     @Test
378     public void testI01RexCompat() {
379         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
380         setup(VehicleType.ELECTRIC_REX.toString(), false);
381         String content = FileReader.readFileInString("src/test/resources/api/vehicle/vehicle-ccm.json");
382         VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
383         assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
384                 STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION,
385                 Optional.empty()));
386     }
387
388     @Test
389     public void testI01RexMilesCompat() {
390         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
391         setup(VehicleType.ELECTRIC_REX.toString(), true);
392         String content = FileReader.readFileInString("src/test/resources/api/vehicle/vehicle-ccm.json");
393         VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
394         assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
395                 STATUS_ELECTRIC + DOORS + RANGE_HYBRID + SERVICE_AVAILABLE + CHECK_AVAILABLE + POSITION,
396                 Optional.empty()));
397     }
398
399     @Test
400     public void testF11Compat() {
401         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
402         setup(VehicleType.CONVENTIONAL.toString(), false);
403         String content = FileReader.readFileInString("src/test/resources/responses/F11/vehicle-status.json");
404         VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
405         assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
406                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
407     }
408
409     @Test
410     public void testF11MilesCompat() {
411         logger.info("{}", Thread.currentThread().getStackTrace()[1].getMethodName());
412         setup(VehicleType.CONVENTIONAL.toString(), true);
413         String content = FileReader.readFileInString("src/test/resources/responses/F11/vehicle-status.json");
414         VehicleAttributesContainer vac = Converter.getGson().fromJson(content, VehicleAttributesContainer.class);
415         assertTrue(testVehicle(Converter.transformLegacyStatus(vac),
416                 STATUS_CONV + DOORS + RANGE_CONV + SERVICE_AVAILABLE + CHECK_EMPTY + POSITION, Optional.empty()));
417     }
418 }