Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
catchJsonArrayConversion.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Test vpArray2D and children JSON parse / save.
32 */
33
39
40#include <visp3/core/vpConfig.h>
41
42#if defined(VISP_HAVE_NLOHMANN_JSON) && defined(VISP_HAVE_CATCH2)
43
44#include <random>
45#include <visp3/core/vpArray2D.h>
46#include <visp3/core/vpIoTools.h>
47
48#include VISP_NLOHMANN_JSON(json.hpp)
49using json = nlohmann::json;
50
51#include <catch_amalgamated.hpp>
52
53#ifdef ENABLE_VISP_NAMESPACE
54using namespace VISP_NAMESPACE_NAME;
55#endif
56
57namespace
58{
59using StringMatcherBase = Catch::Matchers::StringMatcherBase;
60class vpExceptionMatcher : public Catch::Matchers::MatcherBase<vpException>
61{
63 const StringMatcherBase &m_messageMatcher;
64
65public:
66 vpExceptionMatcher(vpException::generalExceptionEnum type, const StringMatcherBase &messageMatcher)
67 : m_type(type), m_messageMatcher(messageMatcher)
68 { }
69
70 bool match(vpException const &in) const VP_OVERRIDE
71 {
72 return m_type == in.getCode() && m_messageMatcher.match(in.getStringMessage());
73 }
74
75 std::string describe() const VP_OVERRIDE
76 {
77 std::ostringstream ss;
78 ss << "vpException has type " << m_type << " and message " << m_messageMatcher.describe();
79 return ss.str();
80 }
81};
82
83class vpRandomArray2DGenerator : public Catch::Generators::IGenerator<vpArray2D<double> >
84{
85private:
86 std::minstd_rand m_rand;
87 std::uniform_real_distribution<> m_val_dist;
88 std::uniform_int_distribution<> m_dim_dist;
89
90 vpArray2D<double> current;
91
92public:
93 vpRandomArray2DGenerator(double valueRange, int minSize, int maxSize)
94 : m_rand(std::random_device {}()), m_val_dist(-valueRange, valueRange), m_dim_dist(minSize, maxSize)
95
96 {
97 static_cast<void>(next());
98 }
99
100 vpArray2D<double> const &get() const VP_OVERRIDE { return current; }
101 bool next() VP_OVERRIDE
102 {
103 const unsigned nCols = m_dim_dist(m_rand);
104 const unsigned nRows = m_dim_dist(m_rand);
105 current.resize(nRows, nCols);
106 for (unsigned i = 0; i < nRows; ++i) {
107 for (unsigned j = 0; j < nCols; ++j) {
108 current[i][j] = m_val_dist(m_rand);
109 }
110 }
111 return true;
112 }
113};
114class vpRandomColVectorGenerator : public Catch::Generators::IGenerator<vpColVector>
115{
116private:
117 std::minstd_rand m_rand;
118 std::uniform_real_distribution<> m_val_dist;
119 std::uniform_int_distribution<> m_dim_dist;
120
121 vpColVector current;
122
123public:
124 vpRandomColVectorGenerator(double valueRange, int minSize, int maxSize)
125 : m_rand(std::random_device {}()), m_val_dist(-valueRange, valueRange), m_dim_dist(minSize, maxSize)
126
127 {
128 static_cast<void>(next());
129 }
130
131 const vpColVector &get() const VP_OVERRIDE { return current; }
132 bool next() VP_OVERRIDE
133 {
134 const unsigned nRows = m_dim_dist(m_rand);
135 current.resize(nRows);
136 for (unsigned i = 0; i < nRows; ++i) {
137 current[i] = m_val_dist(m_rand);
138 }
139 return true;
140 }
141};
142Catch::Generators::GeneratorWrapper<vpArray2D<double> > randomArray(double v, int minSize, int maxSize)
143{
144 return Catch::Generators::GeneratorWrapper<vpArray2D<double> >(Catch::Detail::make_unique<vpRandomArray2DGenerator>(v, minSize, maxSize));
145}
146Catch::Generators::GeneratorWrapper<vpColVector> randomColVector(double v, int minSize, int maxSize)
147{
148 return Catch::Generators::GeneratorWrapper<vpColVector>(Catch::Detail::make_unique<vpRandomColVectorGenerator>(v, minSize, maxSize));
149}
150vpExceptionMatcher matchVpException(vpException::generalExceptionEnum type, const StringMatcherBase &matcher)
151{
152 return vpExceptionMatcher(type, matcher);
153}
154} // namespace
155SCENARIO("Serializing a vpArray2D", "[json]")
156{
157 GIVEN("A random vpArray2D<double>")
158 {
159 const vpArray2D<double> array = GENERATE(take(10, randomArray(50.0, 1, 10)));
160 WHEN("Serializing to a JSON object")
161 {
162 const json j = array;
163 THEN("JSON object is a dictionary") { REQUIRE(j.is_object()); }
164 THEN("JSON object contains correct number of columns")
165 {
166 REQUIRE(j.at("cols").get<unsigned int>() == array.getCols());
167 }
168 THEN("JSON object contains correct number of rows")
169 {
170 REQUIRE(j.at("rows").get<unsigned int>() == array.getRows());
171 }
172 THEN("JSON object contains the array values")
173 {
174 const json jData = j.at("data");
175 THEN("The data field is an array") { REQUIRE(jData.is_array()); }
176 THEN("The data field contains the correct number of values") { REQUIRE(jData.size() == array.size()); }
177 THEN("The data field contains the correct number of values")
178 {
179 const double *const start = array[0];
180 const std::vector<double> vec(start, start + array.size());
181 REQUIRE(vec == jData.get<std::vector<double> >());
182 }
183 }
184 }
185 }
186}
187
188SCENARIO("Trying to instantiate a vpArray with a wrong type of object", "[json]")
189{
190 GIVEN("A random scalar converted to a JSON representation")
191 {
192 vpArray2D<double> array;
193 std::uniform_real_distribution<> dist;
194 const json j = GENERATE(take(50, random(-200.0, 200.0)));
195 THEN("An exception is thrown")
196 {
197 const auto matcher = Catch::Matchers::ContainsSubstring("is not an array or object");
198 REQUIRE_THROWS_MATCHES(from_json(j, array), vpException, matchVpException(vpException::badValue, matcher));
199 }
200 }
201}
202
203SCENARIO("Recovering a vpArray2D from a JSON array", "[json]")
204{
205 GIVEN("An empty array")
206 {
207 const json j = json::array_t();
208 WHEN("Converting to a vpArray2D")
209 {
210 vpArray2D<double> array = j;
211 THEN("The resulting array is empty") { REQUIRE(array.size() == 0); }
212 }
213 }
214 GIVEN("A 1D array")
215 {
216 const json j = { 10.0, 20.0, 30.0 };
217 WHEN("Converting to a vpArray2D")
218 {
219 THEN("An exception is thrown, since this is an ambiguous array")
220 {
221 vpArray2D<double> array;
222 const auto matcher = Catch::Matchers::ContainsSubstring("is not an array of array");
223 REQUIRE_THROWS_MATCHES(from_json(j, array), vpException, matchVpException(vpException::badValue, matcher));
224 }
225 }
226 }
227 GIVEN("A vpArray2D converted to a json 2D array")
228 {
229 vpArray2D<double> array = GENERATE(take(10, randomArray(50.0, 2, 10)));
230 json j;
231 for (unsigned i = 0; i < array.getRows(); ++i) {
232 json jRow;
233 for (unsigned j = 0; j < array.getCols(); ++j) {
234 jRow.push_back(array[i][j]);
235 }
236 j.push_back(jRow);
237 }
238 WHEN("Converting back to a vpArray2D")
239 {
240 vpArray2D<double> array2 = j;
241 THEN("The values are correct") { REQUIRE(array == array2); }
242 }
243 WHEN("Removing elements from rows so that they do not have the same size")
244 {
245 j[0].erase(0); // Generating at least a 2x2 array
246 THEN("An exception is thrown")
247 {
248 const auto matcher = Catch::Matchers::ContainsSubstring("row arrays that are not of the same size");
249 REQUIRE_THROWS_MATCHES(from_json(j, array), vpException, matchVpException(vpException::badValue, matcher));
250 }
251 }
252 }
253}
254
255SCENARIO("Recovering a vpArray2D from a JSON object as serialized by ViSP", "[json]")
256{
257 GIVEN("A vpArray2D converted to JSON format")
258 {
259 const vpArray2D<double> array = GENERATE(take(10, randomArray(50.0, 1, 10)));
260 json j = array;
261 WHEN("Converting back to a vpArray2D")
262 {
263 const vpArray2D<double> array2 = j;
264 THEN("The 2 arrays are equal") { REQUIRE(array == array2); }
265 }
266 WHEN("Removing or adding some values from the data field so that its size does not match the expected array size")
267 {
268 j.at("data").erase(0);
269 THEN("An exception is thrown")
270 {
271 vpArray2D<double> array2;
272 const auto matcher = Catch::Matchers::ContainsSubstring("must be an array of size");
273 REQUIRE_THROWS_MATCHES(from_json(j, array2), vpException, matchVpException(vpException::badValue, matcher));
274 }
275 }
276 }
277}
278
279SCENARIO("Serializing and deserializing a vpColVector", "[json]")
280{
281 GIVEN("A random vpColVector")
282 {
283 const vpColVector v = GENERATE(take(100, randomColVector(100.0, 1, 50)));
284 WHEN("Serializing to JSON")
285 {
286 const json j = v;
287 THEN("There is only one column") { REQUIRE(j.at("cols") == 1); }
288 THEN("The type is vpColVector") { REQUIRE(j.at("type") == "vpColVector"); }
289 WHEN("Deserializing back to a vpColVector")
290 {
291 const vpColVector v2 = j;
292 THEN("The 2 vectors are the same") { REQUIRE(v == v2); }
293 }
294 }
295 }
296 GIVEN("A random 2D array with number of cols > 1")
297 {
298 const vpArray2D<double> array = GENERATE(take(10, randomArray(50.0, 2, 5)));
299 WHEN("Serializing this array to JSON")
300 {
301 const json j = array;
302 THEN("Serializing back to a vpColVector throws an exception")
303 {
305 const auto matcher = Catch::Matchers::ContainsSubstring("tried to read a 2D array into a vpColVector");
306 REQUIRE_THROWS_MATCHES(from_json(j, v), vpException, matchVpException(vpException::badValue, matcher));
307 }
308 }
309 }
310 GIVEN("A random 2D array with number of cols = 1")
311 {
312 const vpColVector v = GENERATE(take(10, randomColVector(100.0, 1, 50)));
313 const vpArray2D<double> array = (vpArray2D<double>)v;
314 WHEN("Serializing this array to JSON")
315 {
316 const json j = array;
317 THEN("Serializing back to a vpColVector is ok and gives the same vector")
318 {
319 const vpColVector v2 = j;
320 REQUIRE(v == v2);
321 }
322 }
323 }
324}
325
326int main(int argc, char *argv[])
327{
328 Catch::Session session;
329 session.applyCommandLine(argc, argv);
330 int numFailed = session.run();
331 return numFailed;
332}
333
334#else
335
336int main() { return EXIT_SUCCESS; }
337
338#endif
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition vpArray2D.h:146
unsigned int getCols() const
Definition vpArray2D.h:423
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:448
unsigned int size() const
Return the number of elements of the 2D array.
Definition vpArray2D.h:435
unsigned int getRows() const
Definition vpArray2D.h:433
Implementation of column vector and the associated operations.
void resize(unsigned int i, bool flagNullify=true)
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73