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