]> git.basschouten.com Git - openhab-addons.git/blob
fc721a3de4b13dfd2a2188859b46571def83e3f6
[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.fmiweather;
14
15 import static org.junit.jupiter.api.Assertions.fail;
16
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.net.URISyntaxException;
22 import java.nio.charset.StandardCharsets;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.nio.file.Paths;
26 import java.util.HashSet;
27 import java.util.Objects;
28 import java.util.Set;
29
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.hamcrest.Description;
33 import org.hamcrest.Matcher;
34 import org.hamcrest.TypeSafeMatcher;
35 import org.junit.jupiter.api.BeforeEach;
36 import org.openhab.binding.fmiweather.internal.client.Client;
37 import org.openhab.binding.fmiweather.internal.client.Data;
38 import org.openhab.binding.fmiweather.internal.client.FMIResponse;
39 import org.openhab.binding.fmiweather.internal.client.Location;
40
41 /**
42  * Base class for response parsing tests
43  *
44  * @author Sami Salonen - Initial contribution
45  */
46 @NonNullByDefault
47 public class AbstractFMIResponseParsingTest {
48
49     @NonNullByDefault({})
50     protected Client client;
51
52     @BeforeEach
53     public void setUpClient() {
54         client = new Client();
55     }
56
57     protected Path getTestResource(String filename) {
58         try {
59             return Paths.get(getClass().getResource(filename).toURI());
60         } catch (URISyntaxException e) {
61             fail(e.getMessage());
62             // Make the compiler happy by throwing here, fails already above
63             throw new IllegalStateException();
64         }
65     }
66
67     protected String readTestResourceUtf8(String filename) {
68         return readTestResourceUtf8(getTestResource(filename));
69     }
70
71     protected String readTestResourceUtf8(Path path) {
72         try {
73             BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
74             StringBuilder content = new StringBuilder();
75             char[] buffer = new char[1024];
76             int read = -1;
77             while ((read = reader.read(buffer)) != -1) {
78                 content.append(buffer, 0, read);
79             }
80             return content.toString();
81         } catch (IOException e) {
82             fail(e.getMessage());
83             // Make the compiler happy by throwing here, fails already above
84             throw new IllegalStateException();
85         }
86     }
87
88     protected static TypeSafeMatcher<Location> deeplyEqualTo(Location location) {
89         return new ResponseLocationMatcher(location);
90     }
91
92     protected static Matcher<Data> deeplyEqualTo(long start, int intervalMinutes, String... values) {
93         return new TypeSafeMatcher<Data>() {
94
95             private TimestampMatcher timestampMatcher = new TimestampMatcher(start, intervalMinutes, values.length);
96             private ValuesMatcher valuesMatcher = new ValuesMatcher(values);
97
98             @Override
99             public void describeTo(@Nullable Description description) {
100                 if (description == null) {
101                     return;
102                 }
103                 description.appendDescriptionOf(timestampMatcher);
104                 description.appendText(" and ");
105                 description.appendDescriptionOf(valuesMatcher);
106             }
107
108             @Override
109             protected boolean matchesSafely(Data dataValues) {
110                 return timestampMatcher.matches(dataValues.timestampsEpochSecs)
111                         && valuesMatcher.matches(dataValues.values);
112             }
113
114             @Override
115             protected void describeMismatchSafely(Data dataValues, @Nullable Description mismatchDescription) {
116                 if (mismatchDescription == null) {
117                     super.describeMismatchSafely(dataValues, mismatchDescription);
118                     return;
119                 }
120                 if (!timestampMatcher.matches(dataValues.timestampsEpochSecs)) {
121                     mismatchDescription.appendText("timestamps mismatch: ");
122                     if (dataValues.timestampsEpochSecs[0] != start) {
123                         mismatchDescription.appendText("start mismatch (was ");
124                         mismatchDescription.appendValue(dataValues.timestampsEpochSecs[0]);
125                         mismatchDescription.appendText(")");
126                     } else if (dataValues.timestampsEpochSecs.length != values.length) {
127                         mismatchDescription.appendText("length mismatch (was ");
128                         mismatchDescription.appendValue(dataValues.timestampsEpochSecs.length);
129                         mismatchDescription.appendText(")");
130                     } else {
131                         mismatchDescription.appendText("interval mismatch (was ");
132                         Set<Long> intervals = new HashSet<>();
133                         for (int i = 1; i < values.length; i++) {
134                             long interval = dataValues.timestampsEpochSecs[i] - dataValues.timestampsEpochSecs[i - 1];
135                             intervals.add(interval);
136                         }
137                         mismatchDescription.appendValue(intervals.toArray());
138                         mismatchDescription.appendText(")");
139                     }
140                 }
141                 mismatchDescription.appendText(", valuesMatch=").appendValue(valuesMatcher.matches(dataValues.values));
142             }
143         };
144     }
145
146     /**
147      *
148      * @param content
149      * @return
150      * @throws Throwable exception raised by parseMultiPointCoverageXml
151      * @throws AssertionError exception raised when parseMultiPointCoverageXml method signature does not match excepted
152      *             (test & implementation is out-of-sync)
153      */
154     protected FMIResponse parseMultiPointCoverageXml(String content) throws Throwable {
155         try {
156             Method parseMethod = Client.class.getDeclaredMethod("parseMultiPointCoverageXml", String.class);
157             parseMethod.setAccessible(true);
158             return Objects.requireNonNull((FMIResponse) parseMethod.invoke(client, content));
159         } catch (InvocationTargetException e) {
160             throw e.getTargetException();
161         } catch (Exception e) {
162             fail(String.format("Unexpected reflection error (code changed?) %s: %s", e.getClass().getName(),
163                     e.getMessage()));
164             // Make the compiler happy by throwing here, fails already above
165             throw new IllegalStateException();
166         }
167     }
168
169     @SuppressWarnings("unchecked")
170     protected Set<Location> parseStations(String content) {
171         try {
172             Method parseMethod = Objects.requireNonNull(Client.class.getDeclaredMethod("parseStations", String.class));
173             parseMethod.setAccessible(true);
174             return Objects.requireNonNull((Set<Location>) parseMethod.invoke(client, content));
175         } catch (InvocationTargetException e) {
176             throw new RuntimeException(e.getTargetException());
177         } catch (Exception e) {
178             fail(String.format("Unexpected reflection error (code changed?) %s: %s", e.getClass().getName(),
179                     e.getMessage()));
180             // Make the compiler happy by throwing here, fails already above
181             throw new IllegalStateException();
182         }
183     }
184 }