]> git.basschouten.com Git - openhab-addons.git/blob
bfbca2990b7c1973fb4850f4de88eb0a355180a1
[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.tplinksmarthome.internal.device;
14
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.lenient;
18
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;
25
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;
37
38 /**
39  * Base class for tests that test classes extending {@link SmartHomeDevice} class.
40  *
41  * @author Hilbrand Bouwkamp - Initial contribution
42  */
43 @ExtendWith(MockitoExtension.class)
44 @MockitoSettings(strictness = Strictness.LENIENT)
45 @NonNullByDefault
46 public class DeviceTestBase<T extends SmartHomeDevice> {
47
48     protected final T device;
49     protected final Connection connection;
50     protected final TPLinkSmartHomeConfiguration configuration = new TPLinkSmartHomeConfiguration();
51     protected @NonNullByDefault({}) DeviceState deviceState;
52
53     private final String deviceStateFilename;
54
55     private @Mock @NonNullByDefault({}) Socket socket;
56     private @Mock @NonNullByDefault({}) OutputStream outputStream;
57
58     /**
59      * Constructor.
60      *
61      * @param device Device under test
62      * @param deviceStateFilename name of the file to read the device state json from to use in tests
63      *
64      * @throws IOException exception in case device not reachable
65      */
66     protected DeviceTestBase(final T device, final String deviceStateFilename) throws IOException {
67         this.device = device;
68         this.deviceStateFilename = deviceStateFilename;
69         configuration.ipAddress = "localhost";
70         configuration.refresh = 30;
71         configuration.transitionPeriod = 10;
72         connection = new Connection(configuration.ipAddress) {
73             @Override
74             protected Socket createSocket() throws IOException {
75                 return socket;
76             }
77         };
78         device.initialize(connection, configuration);
79     }
80
81     @BeforeEach
82     public void setUp() throws IOException {
83         lenient().when(socket.getOutputStream()).thenReturn(outputStream);
84         deviceState = new DeviceState(ModelTestUtil.readJson(deviceStateFilename));
85     }
86
87     /**
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.
90      *
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
93      */
94     protected void setSocketReturnAssert(final String... responseFilenames) throws IOException {
95         final AtomicInteger index = new AtomicInteger();
96
97         lenient().doAnswer(i -> {
98             final String stateResponse = ModelTestUtil.readJson(responseFilenames[index.getAndIncrement()]);
99
100             return new ByteArrayInputStream(CryptUtil.encryptWithLength(stateResponse));
101         }).when(socket).getInputStream();
102     }
103
104     /**
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.
108      *
109      * @param filenames names of the files containing the reference json
110      * @throws IOException exception in case device not reachable
111      */
112     protected void assertInput(final String... filenames) throws IOException {
113         assertInput(Function.identity(), Function.identity(), filenames);
114     }
115
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();
119
120         lenient().doAnswer(arg -> {
121             final String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()]));
122
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()]);
127             }
128             index.incrementAndGet();
129             return null;
130         }).when(outputStream).write(any());
131     }
132 }