]> git.basschouten.com Git - openhab-addons.git/blob
0d9d1a06c8a4bb2ad61225e49dea4f27ff8ccd13
[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.deconz.internal.handler;
14
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.Matchers.is;
17 import static org.hamcrest.Matchers.notNullValue;
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.ArgumentMatchers.eq;
20 import static org.mockito.Mockito.atLeast;
21 import static org.mockito.Mockito.doAnswer;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 import static org.openhab.binding.deconz.internal.BindingConstants.BINDING_ID;
26 import static org.openhab.binding.deconz.internal.BindingConstants.BRIDGE_TYPE;
27 import static org.openhab.binding.deconz.internal.BindingConstants.CONFIG_ID;
28 import static org.openhab.binding.deconz.internal.BindingConstants.THING_TYPE_THERMOSTAT;
29
30 import java.io.IOException;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.concurrent.CompletableFuture;
36 import java.util.function.BiFunction;
37 import java.util.stream.Collectors;
38
39 import org.eclipse.jdt.annotation.NonNullByDefault;
40 import org.eclipse.jdt.annotation.Nullable;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.mockito.ArgumentCaptor;
43 import org.mockito.Mock;
44 import org.openhab.binding.deconz.DeconzTest;
45 import org.openhab.binding.deconz.internal.dto.BridgeFullState;
46 import org.openhab.binding.deconz.internal.dto.DeconzBaseMessage;
47 import org.openhab.binding.deconz.internal.dto.SensorMessage;
48 import org.openhab.binding.deconz.internal.netutils.WebSocketConnection;
49 import org.openhab.binding.deconz.internal.types.LightType;
50 import org.openhab.binding.deconz.internal.types.LightTypeDeserializer;
51 import org.openhab.binding.deconz.internal.types.ResourceType;
52 import org.openhab.binding.deconz.internal.types.ThermostatMode;
53 import org.openhab.binding.deconz.internal.types.ThermostatModeGsonTypeAdapter;
54 import org.openhab.core.config.core.Configuration;
55 import org.openhab.core.test.java.JavaTest;
56 import org.openhab.core.thing.Bridge;
57 import org.openhab.core.thing.Channel;
58 import org.openhab.core.thing.ChannelUID;
59 import org.openhab.core.thing.Thing;
60 import org.openhab.core.thing.ThingStatus;
61 import org.openhab.core.thing.ThingStatusDetail;
62 import org.openhab.core.thing.ThingStatusInfo;
63 import org.openhab.core.thing.ThingTypeUID;
64 import org.openhab.core.thing.ThingUID;
65 import org.openhab.core.thing.binding.ThingHandlerCallback;
66 import org.openhab.core.thing.binding.builder.ChannelBuilder;
67 import org.openhab.core.thing.binding.builder.ThingBuilder;
68 import org.openhab.core.thing.type.ChannelTypeUID;
69 import org.openhab.core.types.State;
70
71 import com.google.gson.Gson;
72 import com.google.gson.GsonBuilder;
73
74 /**
75  * The {@link BaseDeconzThingHandlerTest} is the base class for test classes that are used to test subclasses of
76  * {@link DeconzBaseThingHandler}
77  *
78  * @author Jan N. Klug - Initial contribution
79  */
80 @NonNullByDefault
81 public class BaseDeconzThingHandlerTest extends JavaTest {
82     private static final ThingUID BRIDGE_UID = new ThingUID(BRIDGE_TYPE, "bridge");
83     private static final ThingUID THING_UID = new ThingUID(THING_TYPE_THERMOSTAT, "thing");
84     protected @NonNullByDefault({}) DeconzBaseMessage deconzMessage;
85     private @Mock @NonNullByDefault({}) Bridge bridge;
86     private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;
87     private @Mock @NonNullByDefault({}) DeconzBridgeHandler bridgeHandler;
88     private @Mock @NonNullByDefault({}) WebSocketConnection webSocketConnection;
89     private @Mock @NonNullByDefault({}) BridgeFullState bridgeFullState;
90     private @NonNullByDefault({}) Gson gson;
91     private @NonNullByDefault({}) Thing thing;
92     private @NonNullByDefault({}) DeconzBaseThingHandler thingHandler;
93
94     @BeforeEach
95     public void setupMocks() {
96         GsonBuilder gsonBuilder = new GsonBuilder();
97         gsonBuilder.registerTypeAdapter(LightType.class, new LightTypeDeserializer());
98         gsonBuilder.registerTypeAdapter(ThermostatMode.class, new ThermostatModeGsonTypeAdapter());
99         gson = gsonBuilder.create();
100
101         when(callback.getBridge(BRIDGE_UID)).thenReturn(bridge);
102         when(callback.createChannelBuilder(any(ChannelUID.class), any(ChannelTypeUID.class)))
103                 .thenAnswer(i -> ChannelBuilder.create((ChannelUID) i.getArgument(0)).withType(i.getArgument(1)));
104         doAnswer(i -> {
105             thing = i.getArgument(0);
106             thingHandler.thingUpdated(thing);
107             return null;
108         }).when(callback).thingUpdated(any(Thing.class));
109
110         when(bridge.getStatusInfo()).thenReturn(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""));
111         when(bridge.getHandler()).thenReturn(bridgeHandler);
112
113         when(bridgeHandler.getWebSocketConnection()).thenReturn(webSocketConnection);
114         when(bridgeHandler.getBridgeFullState())
115                 .thenReturn(CompletableFuture.completedFuture(Optional.of(bridgeFullState)));
116
117         when(bridgeFullState.getMessage(ResourceType.SENSORS, "1")).thenAnswer(i -> deconzMessage);
118     }
119
120     protected void createThing(ThingTypeUID thingTypeUID, List<String> channels,
121             BiFunction<Thing, Gson, DeconzBaseThingHandler> handlerSupplier) {
122         ThingBuilder thingBuilder = ThingBuilder.create(thingTypeUID, THING_UID);
123         thingBuilder.withBridge(BRIDGE_UID);
124         for (String channelId : channels) {
125             Channel channel = ChannelBuilder.create(new ChannelUID(THING_UID, channelId))
126                     .withType(new ChannelTypeUID(BINDING_ID, channelId)).build();
127             thingBuilder.withChannel(channel);
128         }
129         thingBuilder.withConfiguration(new Configuration(Map.of(CONFIG_ID, "1")));
130         thing = thingBuilder.build();
131
132         thingHandler = handlerSupplier.apply(thing, gson);
133         thingHandler.setCallback(callback);
134     }
135
136     protected void assertThing(String fileName, Set<TestParam> expected) throws IOException {
137         deconzMessage = DeconzTest.getObjectFromJson(fileName, SensorMessage.class, gson);
138
139         thingHandler.initialize();
140
141         ArgumentCaptor<ThingStatusInfo> captor = ArgumentCaptor.forClass(ThingStatusInfo.class);
142         verify(callback, atLeast(2).description("assertQuantityOfStatusUpdates")).statusUpdated(eq(thing),
143                 captor.capture());
144
145         List<ThingStatusInfo> statusInfoList = captor.getAllValues();
146         assertThat("assertFirstThingStatus", statusInfoList.get(0).getStatus(), is(ThingStatus.UNKNOWN));
147         assertThat("assertLastThingStatus", statusInfoList.get(statusInfoList.size() - 1).getStatus(),
148                 is(ThingStatus.ONLINE));
149
150         assertThat("assertChannelCount:" + getAllChannels(thing), thing.getChannels().size(), is(expected.size()));
151         for (TestParam testParam : expected) {
152             Channel channel = thing.getChannel(testParam.channelId());
153             assertThat("assertNonNullChannel:" + testParam.channelId, channel, is(notNullValue()));
154
155             State state = testParam.state;
156             if (channel != null && state != null) {
157                 verify(callback, times(3).description(channel + " did not receive an update"))
158                         .stateUpdated(eq(channel.getUID()), eq(state));
159             }
160         }
161     }
162
163     private String getAllChannels(Thing thing) {
164         return thing.getChannels().stream().map(Channel::getUID).map(ChannelUID::getId)
165                 .collect(Collectors.joining(","));
166     }
167
168     protected record TestParam(String channelId, @Nullable State state) {
169     }
170 }