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.tplinksmarthome.internal.device;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.lenient;
19 import java.io.ByteArrayInputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.net.Socket;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import java.util.function.Function;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.extension.ExtendWith;
29 import org.mockito.Mock;
30 import org.mockito.junit.jupiter.MockitoExtension;
31 import org.mockito.junit.jupiter.MockitoSettings;
32 import org.mockito.quality.Strictness;
33 import org.openhab.binding.tplinksmarthome.internal.Connection;
34 import org.openhab.binding.tplinksmarthome.internal.CryptUtil;
35 import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration;
36 import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil;
39 * Base class for tests that test classes extending {@link SmartHomeDevice} class.
41 * @author Hilbrand Bouwkamp - Initial contribution
43 @ExtendWith(MockitoExtension.class)
44 @MockitoSettings(strictness = Strictness.LENIENT)
46 public class DeviceTestBase<T extends SmartHomeDevice> {
48 protected final T device;
49 protected final Connection connection;
50 protected final TPLinkSmartHomeConfiguration configuration = new TPLinkSmartHomeConfiguration();
51 protected @NonNullByDefault({}) DeviceState deviceState;
53 private final String deviceStateFilename;
55 private @Mock @NonNullByDefault({}) Socket socket;
56 private @Mock @NonNullByDefault({}) OutputStream outputStream;
61 * @param device Device under test
62 * @param deviceStateFilename name of the file to read the device state json from to use in tests
64 * @throws IOException exception in case device not reachable
66 protected DeviceTestBase(final T device, final String deviceStateFilename) throws IOException {
68 this.deviceStateFilename = deviceStateFilename;
69 configuration.ipAddress = "localhost";
70 configuration.refresh = 30;
71 configuration.transitionPeriod = 10;
72 connection = new Connection(configuration.ipAddress) {
74 protected Socket createSocket() throws IOException {
78 device.initialize(connection, configuration);
82 public void setUp() throws IOException {
83 lenient().when(socket.getOutputStream()).thenReturn(outputStream);
84 deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
88 * Sets the answer to return when the socket.getInputStream() is requested. If multiple files are given they will
89 * returned in order each time a call to socket.getInputStream() is done.
91 * @param responseFilenames names of the files to read that contains the answer. It's the unencrypted json string
92 * @throws IOException exception in case device not reachable
94 protected void setSocketReturnAssert(final String... responseFilenames) throws IOException {
95 final AtomicInteger index = new AtomicInteger();
97 lenient().doAnswer(i -> {
98 final String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
100 return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
101 }).when(socket).getInputStream();
105 * Asserts the value passed to outputstream.write, which is the call that would be made to the actual device. This
106 * checks if the value sent to the device is what is expected to be sent to the device. If multiple files are given
107 * they will be used to check in order each time a call outputstream.write is done.
109 * @param filenames names of the files containing the reference json
110 * @throws IOException exception in case device not reachable
112 protected void assertInput(final String... filenames) throws IOException {
113 assertInput(Function.identity(), Function.identity(), filenames);
116 protected void assertInput(final Function<String, String> jsonProcessor,
117 final Function<String, String> expectedProcessor, final String... filenames) throws IOException {
118 final AtomicInteger index = new AtomicInteger();
120 lenient().doAnswer(arg -> {
121 final String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
123 final byte[] input = (byte[]) arg.getArguments()[0];
124 try (ByteArrayInputStream inputStream = new ByteArrayInputStream(input)) {
125 final String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream));
126 assertEquals(json, expectedString, filenames[index.get()]);
128 index.incrementAndGet();
130 }).when(outputStream).write(any());