Skip to content

Commit

Permalink
[boschshc] Boost unit test coverage (openhab#16500)
Browse files Browse the repository at this point in the history
Boosts the unit test coverage for the `boschshc` binding in `src/main/java` to 94%.

Signed-off-by: David Pace <dev@davidpace.de>
  • Loading branch information
david-pace authored and joni1993 committed Oct 15, 2024
1 parent ff611d6 commit 76399d1
Show file tree
Hide file tree
Showing 19 changed files with 918 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ private String getServiceId(BoschSHCServiceState serviceData) {
*
* @param e error during long polling
*/
private void handleLongPollFailure(Throwable e) {
void handleLongPollFailure(Throwable e) {
logger.warn("Long polling failed, will try to reconnect", e);
@Nullable
BoschHttpClient localHttpClient = this.httpClient;
Expand Down Expand Up @@ -722,7 +722,7 @@ public UserDefinedState getUserStateInfo(String stateId)
return new BoschSHCException("@text/offline.conf-error.invalid-state-id");
} else {
return new BoschSHCException(String.format(
"Request for info of user-defines state %s failed with status code %d and error code %s",
"Request for info of user-defined state %s failed with status code %d and error code %s",
stateId, errorResponse.statusCode, errorResponse.errorCode));
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private void sendPOSTRequest(final String url, final BoschHttpClient httpClient)
}
}

private String prettyLogScenarios(final Scenario[] scenarios) {
String prettyLogScenarios(final Scenario[] scenarios) {
final StringBuilder builder = new StringBuilder();
builder.append("[");
for (Scenario scenario : scenarios) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.nio.file.Files;
Expand All @@ -35,6 +40,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
Expand Down Expand Up @@ -89,6 +96,24 @@ void execute() {
verify(consoleMock, atLeastOnce()).print(any());
}

@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutExceptionArguments()")
void executeHandleExceptions(Exception exception)
throws InterruptedException, BoschSHCException, ExecutionException, TimeoutException {
Console console = mock(Console.class);
Bridge bridge = mock(Bridge.class);
BridgeHandler bridgeHandler = mock(BridgeHandler.class);
when(bridgeHandler.getThing()).thenReturn(bridge);
when(bridgeHandler.getPublicInformation()).thenThrow(exception);
when(bridge.getHandler()).thenReturn(bridgeHandler);
List<Thing> things = List.of(bridge);
when(thingRegistry.getAll()).thenReturn(things);

fixture.execute(new String[] { BoschShcCommandExtension.GET_BRIDGEINFO }, console);

verify(console).print(anyString());
}

@Test
void getCompleter() {
assertThat(fixture.getCompleter(), is(fixture));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ public abstract class AbstractBatteryPoweredDeviceHandlerTest<T extends Abstract
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
super.beforeEach();

DeviceServiceData deviceServiceData = new DeviceServiceData();
deviceServiceData.path = "/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel";
deviceServiceData.id = "BatteryLevel";
deviceServiceData.deviceId = "hdm:ZigBee:000d6f0004b93361";
lenient().when(bridgeHandler.getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);
when(getBridgeHandler().getServiceData(anyString(), anyString())).thenReturn(deviceServiceData);

super.beforeEach();
}

@Test
Expand Down Expand Up @@ -137,10 +137,11 @@ public void testProcessUpdateBatteryLevelOK() {
"deviceId":"hdm:ZigBee:000d6f0004b93361" }\
""");
getFixture().processUpdate("BatteryLevel", deviceServiceData);
verify(getCallback()).stateUpdated(
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
verify(getCallback()).stateUpdated(
verify(getCallback(), times(2)).stateUpdated(
new ChannelUID(getThing().getUID(), BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
}

Expand All @@ -165,19 +166,24 @@ public void testProcessUpdateBatteryLevelNotAvailable() {
getFixture().processUpdate("BatteryLevel", deviceServiceData);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
UnDefType.UNDEF);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
OnOffType.OFF);
}

@Test
public void testHandleCommandRefreshBatteryLevelChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_BATTERY_LEVEL),
new DecimalType(100));
}

@Test
public void testHandleCommandRefreshLowBatteryChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY), OnOffType.OFF);
// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_LOW_BATTERY),
OnOffType.OFF);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@
*/
package org.openhab.binding.boschshc.internal.devices;

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;

/**
* Abstract unit test implementation for device handlers.
Expand Down Expand Up @@ -42,4 +56,27 @@ protected Configuration getConfiguration() {
}

protected abstract String getDeviceID();

@Test
void initializeInvalidDeviceId() {
getFixture().getThing().getConfiguration().remove("id");
getFixture().initialize();

verify(getCallback()).statusUpdated(eq(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}

@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionExceptionAndInterruptedExceptionArguments()")
void initializeHandleExceptionDuringDeviceInfoRestCall(Exception exception)
throws BoschSHCException, InterruptedException, TimeoutException, ExecutionException {
when(getBridgeHandler().getDeviceInfo(getDeviceID())).thenThrow(exception);

getFixture().initialize();

verify(getCallback()).statusUpdated(eq(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/
package org.openhab.binding.boschshc.internal.devices;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -60,7 +63,7 @@ public abstract class AbstractBoschSHCHandlerTest<T extends BoschSHCHandler> {

private @Mock @NonNullByDefault({}) Bridge bridge;

protected @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;
private @Mock @NonNullByDefault({}) BridgeHandler bridgeHandler;

private @Mock @NonNullByDefault({}) ThingHandlerCallback callback;

Expand Down Expand Up @@ -128,8 +131,25 @@ protected void configureDevice(Device device) {
}

@Test
public void testInitialize() {
void testInitialize() {
ThingStatusInfo expectedStatusInfo = new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
verify(callback).statusUpdated(same(thing), eq(expectedStatusInfo));
}

@Test
void testGetBridgeHandler() throws BoschSHCException {
assertThat(fixture.getBridgeHandler(), sameInstance(bridgeHandler));
}

@Test
void testGetBridgeHandlerThrowExceptionIfBridgeIsNull() throws BoschSHCException {
when(callback.getBridge(any())).thenReturn(null);
assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
}

@Test
void testGetBridgeHandlerThrowExceptionIfBridgeHandlerIsNull() throws BoschSHCException {
when(bridge.getHandler()).thenReturn(null);
assertThrows(BoschSHCException.class, () -> fixture.getBridgeHandler());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,31 @@
package org.openhab.binding.boschshc.internal.devices;

import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.services.powerswitch.PowerSwitchState;
import org.openhab.binding.boschshc.internal.services.powerswitch.dto.PowerSwitchServiceState;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.RefreshType;

import com.google.gson.JsonElement;
Expand All @@ -53,12 +59,12 @@ public abstract class AbstractPowerSwitchHandlerTest<T extends AbstractPowerSwit
@BeforeEach
@Override
public void beforeEach() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
super.beforeEach();

PowerSwitchServiceState powerSwitchServiceState = new PowerSwitchServiceState();
powerSwitchServiceState.switchState = PowerSwitchState.ON;
lenient().when(bridgeHandler.getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
.thenReturn(powerSwitchServiceState);

super.beforeEach();
}

@Test
Expand All @@ -76,22 +82,58 @@ public void testHandleCommandPowerSwitchChannel()
assertSame(PowerSwitchState.OFF, state.switchState);
}

@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getExecutionAndTimeoutAndInterruptedExceptionArguments()")
public void testHandleCommandPowerSwitchChannelHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(getBridgeHandler().putState(any(), any(), any())).thenThrow(e);

getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);

verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}

@Test
public void testUpdateChannelPowerSwitchState() {
JsonElement jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"ON\"\n" + "}");

getFixture().processUpdate("PowerSwitch", jsonObject);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);

// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
OnOffType.ON);

jsonObject = JsonParser
.parseString("{\n" + " \"@type\": \"powerSwitchState\",\n" + " \"switchState\": \"OFF\"\n" + "}");

getFixture().processUpdate("PowerSwitch", jsonObject);

verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.OFF);
}

@Test
public void testHandleCommandRefreshPowerSwitchChannel() {
getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);
verify(getCallback()).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), OnOffType.ON);

// state is updated twice: via short poll in initialize() and via long poll result in this test
verify(getCallback(), times(2)).stateUpdated(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH),
OnOffType.ON);
}

@ParameterizedTest
@MethodSource("org.openhab.binding.boschshc.internal.tests.common.CommonTestUtils#getBoschShcAndExecutionAndTimeoutAndInterruptedExceptionArguments()")
public void testHandleCommandRefreshPowerSwitchChannelHandleExceptions(Exception e)
throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
when(getBridgeHandler().getState(anyString(), eq("PowerSwitch"), same(PowerSwitchServiceState.class)))
.thenThrow(e);

getFixture().handleCommand(getChannelUID(BoschSHCBindingConstants.CHANNEL_POWER_SWITCH), RefreshType.REFRESH);

verify(getCallback()).statusUpdated(same(getThing()),
argThat(status -> status.getStatus().equals(ThingStatus.OFFLINE)
&& status.getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)));
}
}
Loading

0 comments on commit 76399d1

Please sign in to comment.