2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.modbus.tests;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.hamcrest.core.Is.is;
18 import static org.hamcrest.core.IsInstanceOf.instanceOf;
19 import static org.junit.jupiter.api.Assertions.*;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.*;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Dictionary;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Hashtable;
29 import java.util.List;
33 import org.eclipse.jdt.annotation.NonNull;
34 import org.eclipse.jdt.annotation.NonNullByDefault;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.junit.jupiter.api.AfterEach;
37 import org.junit.jupiter.api.BeforeEach;
38 import org.junit.jupiter.api.extension.ExtendWith;
39 import org.mockito.Mock;
40 import org.mockito.Mockito;
41 import org.mockito.junit.jupiter.MockitoExtension;
42 import org.mockito.junit.jupiter.MockitoSettings;
43 import org.mockito.quality.Strictness;
44 import org.openhab.binding.modbus.internal.ModbusHandlerFactory;
45 import org.openhab.core.events.Event;
46 import org.openhab.core.events.EventFilter;
47 import org.openhab.core.events.EventSubscriber;
48 import org.openhab.core.i18n.UnitProvider;
49 import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
50 import org.openhab.core.io.transport.modbus.ModbusManager;
51 import org.openhab.core.items.Item;
52 import org.openhab.core.items.ItemProvider;
53 import org.openhab.core.items.ItemRegistry;
54 import org.openhab.core.items.ManagedItemProvider;
55 import org.openhab.core.items.events.ItemStateEvent;
56 import org.openhab.core.library.CoreItemFactory;
57 import org.openhab.core.test.java.JavaOSGiTest;
58 import org.openhab.core.thing.ChannelUID;
59 import org.openhab.core.thing.ManagedThingProvider;
60 import org.openhab.core.thing.Thing;
61 import org.openhab.core.thing.ThingProvider;
62 import org.openhab.core.thing.binding.ThingHandler;
63 import org.openhab.core.thing.binding.ThingHandlerFactory;
64 import org.openhab.core.thing.link.AbstractLink;
65 import org.openhab.core.thing.link.ItemChannelLink;
66 import org.openhab.core.thing.link.ItemChannelLinkProvider;
67 import org.openhab.core.thing.link.ItemChannelLinkRegistry;
68 import org.openhab.core.thing.link.ManagedItemChannelLinkProvider;
69 import org.openhab.core.thing.type.ChannelTypeUID;
70 import org.openhab.core.transform.TransformationService;
71 import org.openhab.core.types.State;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
76 * @author Sami Salonen - Initial contribution
78 @ExtendWith(MockitoExtension.class)
79 @MockitoSettings(strictness = Strictness.LENIENT)
81 public abstract class AbstractModbusOSGiTest extends JavaOSGiTest {
84 * When Mockito is used for mocking {@link ThingHandler}s it has to be able to load the {@link ChannelTypeUID}
85 * class. Bnd will add the package to the generated manifest when the class is referenced here.
87 static void mockitoPackageImport() {
88 ChannelTypeUID.class.getClass();
91 private static class StateSubscriber implements EventSubscriber {
93 private final Logger logger = LoggerFactory.getLogger(StateSubscriber.class);
95 public Map<String, List<State>> stateUpdates = new HashMap<>();
98 public Set<@NonNull String> getSubscribedEventTypes() {
99 return Collections.singleton(ItemStateEvent.TYPE);
103 public @Nullable EventFilter getEventFilter() {
108 public void receive(Event event) {
109 // Expecting only state updates in the tests
110 assertThat(event, is(instanceOf(ItemStateEvent.class)));
111 ItemStateEvent stateEvent = (ItemStateEvent) event;
112 logger.trace("Captured event: {} of type {}. Payload: {}", event,
113 stateEvent.getItemState().getClass().getSimpleName(), event.getPayload());
114 stateUpdates.computeIfAbsent(stateEvent.getItemName(), item -> new ArrayList<>())
115 .add(stateEvent.getItemState());
119 private final Logger logger = LoggerFactory.getLogger(AbstractModbusOSGiTest.class);
121 protected @Mock @NonNullByDefault({}) ModbusManager mockedModbusManager;
122 protected @Mock @NonNullByDefault({}) UnitProvider mockedUnitProvider;
123 protected @NonNullByDefault({}) ModbusManager realModbusManager;
124 protected @NonNullByDefault({}) ManagedThingProvider thingProvider;
125 protected @NonNullByDefault({}) ManagedItemProvider itemProvider;
126 protected @NonNullByDefault({}) ManagedItemChannelLinkProvider itemChannelLinkProvider;
127 protected @NonNullByDefault({}) ItemRegistry itemRegistry;
128 protected @NonNullByDefault({}) ItemChannelLinkRegistry itemChannelLinkRegistry;
129 protected @NonNullByDefault({}) CoreItemFactory coreItemFactory;
131 private Set<Item> addedItems = new HashSet<>();
132 private Set<Thing> addedThings = new HashSet<>();
133 private Set<ItemChannelLink> addedLinks = new HashSet<>();
134 private StateSubscriber stateSubscriber = new StateSubscriber();
136 protected @Mock @NonNullByDefault({}) ModbusCommunicationInterface comms;
139 * Before each test, configure mocked services
142 public void setUpAbstractModbusOSGiTest() {
143 logger.debug("setUpAbstractModbusOSGiTest BEGIN");
144 registerVolatileStorageService();
145 registerService(mockedModbusManager);
146 registerService(stateSubscriber);
148 swapModbusManagerToMocked();
150 thingProvider = getService(ThingProvider.class, ManagedThingProvider.class);
151 assertThat("Could not get ManagedThingProvider", thingProvider, is(notNullValue()));
152 itemProvider = getService(ItemProvider.class, ManagedItemProvider.class);
153 assertThat("Could not get ManagedItemProvider", itemProvider, is(notNullValue()));
154 itemChannelLinkProvider = getService(ItemChannelLinkProvider.class, ManagedItemChannelLinkProvider.class);
155 assertThat("Could not get ManagedItemChannelLinkProvider", itemChannelLinkProvider, is(notNullValue()));
156 itemRegistry = getService(ItemRegistry.class);
157 assertThat("Could not get ItemRegistry", itemRegistry, is(notNullValue()));
158 itemChannelLinkRegistry = getService(ItemChannelLinkRegistry.class);
159 assertThat("Could not get ItemChannelLinkRegistry", itemChannelLinkRegistry, is(notNullValue()));
161 coreItemFactory = new CoreItemFactory(mockedUnitProvider);
163 // Clean slate for all tests
164 reset(mockedModbusManager);
166 stateSubscriber.stateUpdates.clear();
167 logger.debug("setUpAbstractModbusOSGiTest END");
171 public void tearDownAbstractModbusOSGiTest() throws Exception {
172 logger.debug("tearDownAbstractModbusOSGiTest BEGIN");
173 swapModbusManagerToReal();
174 for (Item item : addedItems) {
175 assertNotNull(itemProvider.remove(item.getName()));
177 for (Thing thing : addedThings) {
180 for (ItemChannelLink link : addedLinks) {
181 logger.debug("Unlinking {} <-> {}", link.getItemName(), link.getLinkedUID());
182 assertNotNull(itemChannelLinkProvider.remove(link.getUID()));
184 logger.debug("tearDownAbstractModbusOSGiTest END");
187 protected void addThing(Thing thing) {
188 assertThat(addedThings.contains(thing), not(equalTo(true)));
189 ThingHandler mockHandler = thing.getHandler();
190 if (mockHandler != null) {
191 // If there is a handler attached to fresh thing, it should be mocked (this pattern is used with some tests)
192 assertThat(Mockito.mockingDetails(thing.getHandler()).isMock(), is(equalTo(true)));
195 thingProvider.add(thing);
196 waitForAssert(() -> assertThat(thing.getHandler(), notNullValue()));
197 assertThat(thing.getConfiguration(), is(notNullValue()));
198 addedThings.add(thing);
199 if (mockHandler != null) {
200 // Re-attach mock handler
201 ThingHandler realHandlerInitedByCore = thing.getHandler();
202 assertNotNull(realHandlerInitedByCore);
203 assertNotSame(realHandlerInitedByCore, mockHandler);
204 realHandlerInitedByCore.dispose();
205 thing.setHandler(mockHandler);
209 protected void disposeThing(Thing thing) {
210 thingProvider.remove(thing.getUID());
213 protected void addItem(Item item) {
214 assertThat(addedItems.contains(item), not(equalTo(true)));
215 itemProvider.add(item);
216 addedItems.add(item);
219 protected void linkItem(String itemName, ChannelUID channelUID) {
220 logger.debug("Linking {} <-> {}", itemName, channelUID);
221 ItemChannelLink link = new ItemChannelLink(itemName, channelUID);
222 assertThat(addedLinks.contains(link), not(equalTo(true)));
223 itemChannelLinkProvider.add(link);
224 waitForAssert(() -> assertThat(itemChannelLinkRegistry.get(AbstractLink.getIDFor(itemName, channelUID)),
225 is(notNullValue())));
226 addedLinks.add(link);
229 protected @Nullable List<State> getStateUpdates(String itemName) {
230 return stateSubscriber.stateUpdates.get(itemName);
233 protected void mockTransformation(String name, TransformationService service) {
234 Dictionary<String, Object> params = new Hashtable<>();
235 params.put("openhab.transform", name);
236 registerService(service, params);
239 protected void mockCommsToModbusManager() {
240 assert comms != null;
241 doReturn(comms).when(mockedModbusManager).newModbusCommunicationInterface(any(), any());
244 protected void swapModbusManagerToMocked() {
245 assertNull(realModbusManager);
246 realModbusManager = getService(ModbusManager.class);
247 assertThat("Could not get ModbusManager", realModbusManager, is(notNullValue()));
248 assertThat("Could not get ModbusManagerImpl", realModbusManager.getClass().getSimpleName(),
249 is(equalTo("ModbusManagerImpl")));
250 assertNotNull(realModbusManager);
252 ModbusHandlerFactory modbusHandlerFactory = getService(ThingHandlerFactory.class, ModbusHandlerFactory.class);
253 assertThat("Could not get ModbusHandlerFactory", modbusHandlerFactory, is(notNullValue()));
254 assertNotNull(modbusHandlerFactory);
255 modbusHandlerFactory.unsetModbusManager(realModbusManager);
256 modbusHandlerFactory.setModbusManager(mockedModbusManager);
259 protected void swapModbusManagerToReal() {
260 assertNotNull(realModbusManager);
261 ModbusHandlerFactory modbusHandlerFactory = getService(ThingHandlerFactory.class, ModbusHandlerFactory.class);
262 assertThat("Could not get ModbusHandlerFactory", modbusHandlerFactory, is(notNullValue()));
263 assertNotNull(modbusHandlerFactory);
264 modbusHandlerFactory.unsetModbusManager(mockedModbusManager);
265 modbusHandlerFactory.setModbusManager(realModbusManager);