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.equalTo;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.hamcrest.core.Is.is;
18 import static org.junit.jupiter.api.Assertions.assertNotNull;
19 import static org.mockito.ArgumentMatchers.*;
20 import static org.mockito.Mockito.*;
21 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
23 import java.lang.reflect.Field;
24 import java.util.concurrent.atomic.AtomicReference;
26 import org.hamcrest.Description;
27 import org.hamcrest.TypeSafeMatcher;
28 import org.junit.jupiter.api.AfterEach;
29 import org.junit.jupiter.api.BeforeEach;
30 import org.junit.jupiter.api.Test;
31 import org.mockito.ArgumentCaptor;
32 import org.mockito.Mock;
33 import org.mockito.Mockito;
34 import org.openhab.binding.modbus.handler.ModbusPollerThingHandler;
35 import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal;
36 import org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler;
37 import org.openhab.core.config.core.Configuration;
38 import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
39 import org.openhab.core.io.transport.modbus.AsyncModbusReadResult;
40 import org.openhab.core.io.transport.modbus.BitArray;
41 import org.openhab.core.io.transport.modbus.ModbusConstants;
42 import org.openhab.core.io.transport.modbus.ModbusFailureCallback;
43 import org.openhab.core.io.transport.modbus.ModbusReadCallback;
44 import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
45 import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
46 import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
47 import org.openhab.core.io.transport.modbus.PollTask;
48 import org.openhab.core.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
49 import org.openhab.core.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint;
50 import org.openhab.core.thing.Bridge;
51 import org.openhab.core.thing.Thing;
52 import org.openhab.core.thing.ThingStatus;
53 import org.openhab.core.thing.ThingStatusDetail;
54 import org.openhab.core.thing.ThingStatusInfo;
55 import org.openhab.core.thing.ThingUID;
56 import org.openhab.core.thing.binding.ThingHandlerCallback;
57 import org.openhab.core.thing.binding.builder.BridgeBuilder;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * @author Sami Salonen - Initial contribution
64 public class ModbusPollerThingHandlerTest extends AbstractModbusOSGiTest {
66 private static final String HOST = "thisishost";
67 private static final int PORT = 44;
69 private final Logger logger = LoggerFactory.getLogger(ModbusPollerThingHandlerTest.class);
71 private Bridge endpoint;
72 private Bridge poller;
74 private @Mock ThingHandlerCallback thingCallback;
76 public static BridgeBuilder createTcpThingBuilder(String id) {
78 .create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP,
79 new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP, id))
80 .withLabel("label for " + id);
83 public static BridgeBuilder createPollerThingBuilder(String id) {
85 .create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER,
86 new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER, id))
87 .withLabel("label for " + id);
91 * Verify that basic poller <-> endpoint interaction has taken place (on poller init)
93 private void verifyEndpointBasicInitInteraction() {
94 verify(mockedModbusManager).newModbusCommunicationInterface(any(), any());
97 public ModbusReadCallback getPollerCallback(ModbusPollerThingHandler handler)
98 throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
99 Field callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
100 callbackField.setAccessible(true);
101 return (ModbusReadCallback) callbackField.get(handler);
104 public ModbusFailureCallback<ModbusReadRequestBlueprint> getPollerFailureCallback(ModbusPollerThingHandler handler)
105 throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
106 Field callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
107 callbackField.setAccessible(true);
108 return (ModbusFailureCallback<ModbusReadRequestBlueprint>) callbackField.get(handler);
112 * Before each test, setup TCP endpoint thing, configure mocked item registry
115 public void setUp() {
116 mockCommsToModbusManager();
117 Configuration tcpConfig = new Configuration();
118 tcpConfig.put("host", HOST);
119 tcpConfig.put("port", PORT);
120 tcpConfig.put("id", 9);
121 endpoint = createTcpThingBuilder("tcpendpoint").withConfiguration(tcpConfig).build();
124 assertThat(endpoint.getStatus(), is(equalTo(ThingStatus.ONLINE)));
128 public void tearDown() {
129 if (endpoint != null) {
130 thingProvider.remove(endpoint.getUID());
132 if (poller != null) {
133 thingProvider.remove(poller.getUID());
138 public void testInitializeNonPolling()
139 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
140 Configuration pollerConfig = new Configuration();
141 pollerConfig.put("refresh", 0L); // 0 -> non polling
142 pollerConfig.put("start", 5);
143 pollerConfig.put("length", 9);
144 pollerConfig.put("type", ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER);
145 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
148 logger.info("Poller created, registering to registry...");
150 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
151 logger.info("Poller registered");
153 verifyEndpointBasicInitInteraction();
154 // polling is _not_ setup
155 verifyNoMoreInteractions(mockedModbusManager);
158 private void testPollerLengthCheck(String type, int length, boolean expectedOnline) {
159 Configuration pollerConfig = new Configuration();
160 pollerConfig.put("refresh", 0L);
161 pollerConfig.put("start", 5);
162 pollerConfig.put("length", length);
163 pollerConfig.put("type", type);
164 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
168 assertThat(poller.getStatus(), is(equalTo(expectedOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE)));
169 if (!expectedOnline) {
170 assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
173 verifyEndpointBasicInitInteraction();
174 verifyNoMoreInteractions(mockedModbusManager);
178 public void testPollerWithMaxRegisters()
179 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
180 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER,
181 ModbusConstants.MAX_REGISTERS_READ_COUNT, true);
185 public void testPollerLengthOutOfBoundsWithRegisters()
186 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
187 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER,
188 ModbusConstants.MAX_REGISTERS_READ_COUNT + 1, false);
192 public void testPollerWithMaxInputRegisters()
193 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
194 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_INPUT_REGISTER,
195 ModbusConstants.MAX_REGISTERS_READ_COUNT, true);
199 public void testPollerLengthOutOfBoundsWithInputRegisters()
200 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
201 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_INPUT_REGISTER,
202 ModbusConstants.MAX_REGISTERS_READ_COUNT + 1, false);
206 public void testPollerWithMaxCoils()
207 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
208 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_COIL, ModbusConstants.MAX_BITS_READ_COUNT, true);
212 public void testPollerLengthOutOfBoundsWithCoils()
213 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
214 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_COIL, ModbusConstants.MAX_BITS_READ_COUNT + 1,
219 public void testPollerWithMaxDiscreteInput()
220 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
221 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_DISCRETE_INPUT,
222 ModbusConstants.MAX_BITS_READ_COUNT, true);
226 public void testPollerLengthOutOfBoundsWithDiscreteInput()
227 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
228 testPollerLengthCheck(ModbusBindingConstantsInternal.READ_TYPE_DISCRETE_INPUT,
229 ModbusConstants.MAX_BITS_READ_COUNT + 1, false);
232 public void testPollingGeneric(String type, ModbusReadFunctionCode expectedFunctionCode)
233 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
234 PollTask pollTask = Mockito.mock(PollTask.class);
235 doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
237 Configuration pollerConfig = new Configuration();
238 pollerConfig.put("refresh", 150L);
239 pollerConfig.put("start", 5);
240 pollerConfig.put("length", 13);
241 pollerConfig.put("type", type);
242 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
246 assertThat(poller.getStatusInfo().toString(), poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
248 verifyEndpointBasicInitInteraction();
249 verify(mockedModbusManager).newModbusCommunicationInterface(argThat(new TypeSafeMatcher<ModbusSlaveEndpoint>() {
252 public void describeTo(Description description) {
253 description.appendText("correct endpoint (");
257 protected boolean matchesSafely(ModbusSlaveEndpoint endpoint) {
258 return checkEndpoint(endpoint);
262 verify(comms).registerRegularPoll(argThat(new TypeSafeMatcher<ModbusReadRequestBlueprint>() {
265 public void describeTo(Description description) {
266 description.appendText("correct request");
270 protected boolean matchesSafely(ModbusReadRequestBlueprint request) {
271 return checkRequest(request, expectedFunctionCode);
273 }), eq(150l), eq(0L), notNull(), notNull());
274 verifyNoMoreInteractions(mockedModbusManager);
277 @SuppressWarnings("null")
278 private boolean checkEndpoint(ModbusSlaveEndpoint endpointParam) {
279 return endpointParam.equals(new ModbusTCPSlaveEndpoint(HOST, PORT, false));
282 private boolean checkRequest(ModbusReadRequestBlueprint request, ModbusReadFunctionCode functionCode) {
283 return request.getDataLength() == 13 && request.getFunctionCode() == functionCode
284 && request.getProtocolID() == 0 && request.getReference() == 5 && request.getUnitID() == 9;
288 public void testInitializePollingWithCoils()
289 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
290 testPollingGeneric("coil", ModbusReadFunctionCode.READ_COILS);
294 public void testInitializePollingWithDiscrete()
295 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
296 testPollingGeneric("discrete", ModbusReadFunctionCode.READ_INPUT_DISCRETES);
300 public void testInitializePollingWithInputRegisters()
301 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
302 testPollingGeneric("input", ModbusReadFunctionCode.READ_INPUT_REGISTERS);
306 public void testInitializePollingWithHoldingRegisters()
307 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
308 testPollingGeneric("holding", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
312 public void testPollUnregistrationOnDispose()
313 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
314 PollTask pollTask = Mockito.mock(PollTask.class);
315 doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
317 Configuration pollerConfig = new Configuration();
318 pollerConfig.put("refresh", 150L);
319 pollerConfig.put("start", 5);
320 pollerConfig.put("length", 13);
321 pollerConfig.put("type", "coil");
322 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
325 verifyEndpointBasicInitInteraction();
327 // verify registration
328 final AtomicReference<ModbusReadCallback> callbackRef = new AtomicReference<>();
329 verify(mockedModbusManager).newModbusCommunicationInterface(argThat(new TypeSafeMatcher<ModbusSlaveEndpoint>() {
332 public void describeTo(Description description) {
333 description.appendText("correct endpoint");
337 protected boolean matchesSafely(ModbusSlaveEndpoint endpoint) {
338 return checkEndpoint(endpoint);
341 verify(comms).registerRegularPoll(argThat(new TypeSafeMatcher<ModbusReadRequestBlueprint>() {
344 public void describeTo(Description description) {
348 protected boolean matchesSafely(ModbusReadRequestBlueprint request) {
349 return checkRequest(request, ModbusReadFunctionCode.READ_COILS);
351 }), eq(150l), eq(0L), argThat(new TypeSafeMatcher<ModbusReadCallback>() {
354 public void describeTo(Description description) {
358 protected boolean matchesSafely(ModbusReadCallback callback) {
359 callbackRef.set(callback);
363 verifyNoMoreInteractions(mockedModbusManager);
365 // reset call counts for easy assertions
366 reset(mockedModbusManager);
369 disposeThing(poller);
371 // 1) should first unregister poll task
372 verify(comms).unregisterRegularPoll(eq(pollTask));
374 verifyNoMoreInteractions(mockedModbusManager);
378 public void testInitializeWithNoBridge()
379 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
380 Configuration pollerConfig = new Configuration();
381 pollerConfig.put("refresh", 150L);
382 pollerConfig.put("start", 5);
383 pollerConfig.put("length", 13);
384 pollerConfig.put("type", "coil");
385 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).build();
387 verifyEndpointBasicInitInteraction();
389 assertThat(poller.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
390 assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.BRIDGE_OFFLINE)));
392 verifyNoMoreInteractions(mockedModbusManager);
396 public void testInitializeWithOfflineBridge()
397 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
398 Configuration pollerConfig = new Configuration();
399 pollerConfig.put("refresh", 150L);
400 pollerConfig.put("start", 5);
401 pollerConfig.put("length", 13);
402 pollerConfig.put("type", "coil");
404 endpoint.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ""));
405 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
408 verifyEndpointBasicInitInteraction();
410 assertThat(poller.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
411 assertThat(poller.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.BRIDGE_OFFLINE)));
413 verifyNoMoreInteractions(mockedModbusManager);
417 public void testRegistersPassedToChildDataThings()
418 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
419 PollTask pollTask = Mockito.mock(PollTask.class);
420 doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
422 Configuration pollerConfig = new Configuration();
423 pollerConfig.put("refresh", 150L);
424 pollerConfig.put("start", 5);
425 pollerConfig.put("length", 13);
426 pollerConfig.put("type", "coil");
427 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
430 verifyEndpointBasicInitInteraction();
432 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
434 ArgumentCaptor<ModbusReadCallback> callbackCapturer = ArgumentCaptor.forClass(ModbusReadCallback.class);
435 verify(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), callbackCapturer.capture(), notNull());
436 ModbusReadCallback readCallback = callbackCapturer.getValue();
438 assertNotNull(readCallback);
440 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
441 ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
443 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
444 assertNotNull(thingHandler);
446 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
447 ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
449 AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
451 // has one data child
452 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
453 readCallback.handle(result);
454 verify(child1).onReadResult(result);
455 verifyNoMoreInteractions(child1);
456 verifyNoMoreInteractions(child2);
460 // two children (one child initialized)
461 thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
462 readCallback.handle(result);
463 verify(child1).onReadResult(result);
464 verify(child2).onReadResult(result);
465 verifyNoMoreInteractions(child1);
466 verifyNoMoreInteractions(child2);
471 // one child disposed
472 thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
473 readCallback.handle(result);
474 verify(child2).onReadResult(result);
475 verifyNoMoreInteractions(child1);
476 verifyNoMoreInteractions(child2);
480 public void testBitsPassedToChildDataThings()
481 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
482 PollTask pollTask = Mockito.mock(PollTask.class);
483 doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
485 Configuration pollerConfig = new Configuration();
486 pollerConfig.put("refresh", 150L);
487 pollerConfig.put("start", 5);
488 pollerConfig.put("length", 13);
489 pollerConfig.put("type", "coil");
490 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
493 verifyEndpointBasicInitInteraction();
495 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
497 ArgumentCaptor<ModbusReadCallback> callbackCapturer = ArgumentCaptor.forClass(ModbusReadCallback.class);
498 verify(comms).registerRegularPoll(any(), eq(150l), eq(0L), callbackCapturer.capture(), notNull());
499 ModbusReadCallback readCallback = callbackCapturer.getValue();
501 assertNotNull(readCallback);
503 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
504 BitArray bits = Mockito.mock(BitArray.class);
506 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
507 assertNotNull(thingHandler);
509 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
510 ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
512 AsyncModbusReadResult result = new AsyncModbusReadResult(request, bits);
514 // has one data child
515 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
516 readCallback.handle(result);
517 verify(child1).onReadResult(result);
518 verifyNoMoreInteractions(child1);
519 verifyNoMoreInteractions(child2);
523 // two children (one child initialized)
524 thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
525 readCallback.handle(result);
526 verify(child1).onReadResult(result);
527 verify(child2).onReadResult(result);
528 verifyNoMoreInteractions(child1);
529 verifyNoMoreInteractions(child2);
534 // one child disposed
535 thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
536 readCallback.handle(result);
537 verify(child2).onReadResult(result);
538 verifyNoMoreInteractions(child1);
539 verifyNoMoreInteractions(child2);
543 public void testErrorPassedToChildDataThings()
544 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
545 PollTask pollTask = Mockito.mock(PollTask.class);
546 doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
548 Configuration pollerConfig = new Configuration();
549 pollerConfig.put("refresh", 150L);
550 pollerConfig.put("start", 5);
551 pollerConfig.put("length", 13);
552 pollerConfig.put("type", "coil");
553 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
556 verifyEndpointBasicInitInteraction();
558 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
560 final ArgumentCaptor<ModbusFailureCallback<ModbusReadRequestBlueprint>> callbackCapturer = ArgumentCaptor
561 .forClass((Class) ModbusFailureCallback.class);
562 verify(comms).registerRegularPoll(any(), eq(150l), eq(0L), notNull(), callbackCapturer.capture());
563 ModbusFailureCallback<ModbusReadRequestBlueprint> readCallback = callbackCapturer.getValue();
565 assertNotNull(readCallback);
567 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
568 Exception error = Mockito.mock(Exception.class);
570 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
571 assertNotNull(thingHandler);
573 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
574 ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
576 AsyncModbusFailure<ModbusReadRequestBlueprint> result = new AsyncModbusFailure<ModbusReadRequestBlueprint>(
579 // has one data child
580 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
581 readCallback.handle(result);
582 verify(child1).handleReadError(result);
583 verifyNoMoreInteractions(child1);
584 verifyNoMoreInteractions(child2);
588 // two children (one child initialized)
589 thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
590 readCallback.handle(result);
591 verify(child1).handleReadError(result);
592 verify(child2).handleReadError(result);
593 verifyNoMoreInteractions(child1);
594 verifyNoMoreInteractions(child2);
599 // one child disposed
600 thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
601 readCallback.handle(result);
602 verify(child2).handleReadError(result);
603 verifyNoMoreInteractions(child1);
604 verifyNoMoreInteractions(child2);
608 public void testRefresh()
609 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
610 Configuration pollerConfig = new Configuration();
611 pollerConfig.put("refresh", 0L);
612 pollerConfig.put("start", 5);
613 pollerConfig.put("length", 13);
614 pollerConfig.put("type", "coil");
615 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
618 verifyEndpointBasicInitInteraction();
620 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
622 verify(comms, never()).submitOneTimePoll(any(), any(), any());
623 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
624 assertNotNull(thingHandler);
625 thingHandler.refresh();
626 verify(comms).submitOneTimePoll(any(), any(), any());
630 * When there's no recently received data, refresh() will re-use that instead
632 * @throws IllegalArgumentException
633 * @throws IllegalAccessException
634 * @throws NoSuchFieldException
635 * @throws SecurityException
638 public void testRefreshWithPreviousData()
639 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
640 Configuration pollerConfig = new Configuration();
641 pollerConfig.put("refresh", 0L);
642 pollerConfig.put("start", 5);
643 pollerConfig.put("length", 13);
644 pollerConfig.put("type", "coil");
645 pollerConfig.put("cacheMillis", 10000L);
646 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
649 verifyEndpointBasicInitInteraction();
651 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
652 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
653 assertNotNull(thingHandler);
654 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
656 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
658 verify(comms, never()).submitOneTimePoll(any(), any(), any());
661 ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
662 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
663 ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
664 AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
665 pollerReadCallback.handle(result);
667 // data child receives the data
668 verify(child1).onReadResult(result);
669 verifyNoMoreInteractions(child1);
673 // cache is still valid, we should not have real data poll this time
674 thingHandler.refresh();
675 verify(comms, never()).submitOneTimePoll(any(), any(), any());
677 // data child receives the cached data
678 verify(child1).onReadResult(result);
679 verifyNoMoreInteractions(child1);
683 * When there's no recently received data, refresh() will re-use that instead
685 * @throws IllegalArgumentException
686 * @throws IllegalAccessException
687 * @throws NoSuchFieldException
688 * @throws SecurityException
691 public void testRefreshWithPreviousDataCacheDisabled()
692 throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
693 Configuration pollerConfig = new Configuration();
694 pollerConfig.put("refresh", 0L);
695 pollerConfig.put("start", 5);
696 pollerConfig.put("length", 13);
697 pollerConfig.put("type", "coil");
698 pollerConfig.put("cacheMillis", 0L);
699 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
702 verifyEndpointBasicInitInteraction();
704 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
705 assertNotNull(thingHandler);
706 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
707 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
709 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
711 verify(comms, never()).submitOneTimePoll(any(), any(), any());
714 ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
715 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
716 ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
717 AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
719 pollerReadCallback.handle(result);
721 // data child receives the data
722 verify(child1).onReadResult(result);
723 verifyNoMoreInteractions(child1);
727 // caching disabled, should poll from manager
728 thingHandler.refresh();
729 verify(comms).submitOneTimePoll(any(), any(), any());
730 verifyNoMoreInteractions(mockedModbusManager);
732 // data child receives the cached data
733 verifyNoMoreInteractions(child1);
737 * Testing again caching, such that most recently received data is propagated to children
739 * @throws IllegalArgumentException
740 * @throws IllegalAccessException
741 * @throws NoSuchFieldException
742 * @throws SecurityException
743 * @throws InterruptedException
746 public void testRefreshWithPreviousData2() throws IllegalArgumentException, IllegalAccessException,
747 NoSuchFieldException, SecurityException, InterruptedException {
748 Configuration pollerConfig = new Configuration();
749 pollerConfig.put("refresh", 0L);
750 pollerConfig.put("start", 5);
751 pollerConfig.put("length", 13);
752 pollerConfig.put("type", "coil");
753 pollerConfig.put("cacheMillis", 10000L);
754 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
757 verifyEndpointBasicInitInteraction();
759 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
760 assertNotNull(thingHandler);
761 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
762 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
764 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
766 verify(comms, never()).submitOneTimePoll(any(), any(), any());
769 ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
770 ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback = getPollerFailureCallback(thingHandler);
771 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
772 ModbusReadRequestBlueprint request2 = Mockito.mock(ModbusReadRequestBlueprint.class);
773 ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
774 Exception error = Mockito.mock(Exception.class);
775 AsyncModbusReadResult registersResult = new AsyncModbusReadResult(request, registers);
776 AsyncModbusFailure<ModbusReadRequestBlueprint> errorResult = new AsyncModbusFailure<ModbusReadRequestBlueprint>(
779 pollerReadCallback.handle(registersResult);
781 // data child should receive the data
782 verify(child1).onReadResult(registersResult);
783 verifyNoMoreInteractions(child1);
786 // Sleep to have time between the data
790 failureCallback.handle(errorResult);
792 // data child should receive the error
793 verify(child1).handleReadError(errorResult);
794 verifyNoMoreInteractions(child1);
797 // call refresh, should return latest data (that is, error)
798 // cache is still valid, we should not have real data poll this time
799 thingHandler.refresh();
800 verify(comms, never()).submitOneTimePoll(any(), any(), any());
802 // data child receives the cached error
803 verify(child1).handleReadError(errorResult);
804 verifyNoMoreInteractions(child1);
808 public void testRefreshWithOldPreviousData() throws IllegalArgumentException, IllegalAccessException,
809 NoSuchFieldException, SecurityException, InterruptedException {
811 Configuration pollerConfig = new Configuration();
812 pollerConfig.put("refresh", 0L);
813 pollerConfig.put("start", 5);
814 pollerConfig.put("length", 13);
815 pollerConfig.put("type", "coil");
816 pollerConfig.put("cacheMillis", 10L);
817 poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID())
820 verifyEndpointBasicInitInteraction();
822 ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
823 assertNotNull(thingHandler);
824 ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
825 thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
827 assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
829 verify(comms, never()).submitOneTimePoll(any(), any(), any());
832 ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
833 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
834 ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
835 AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
837 pollerReadCallback.handle(result);
839 // data child should receive the data
840 verify(child1).onReadResult(result);
841 verifyNoMoreInteractions(child1);
844 // Sleep to ensure cache expiry
847 // call refresh. Since cache expired, will poll for more
848 verify(comms, never()).submitOneTimePoll(any(), any(), any());
849 thingHandler.refresh();
850 verify(comms).submitOneTimePoll(any(), any(), any());