#include <visp3/core/vpConfig.h>
#if defined(VISP_HAVE_NLOHMANN_JSON) && defined(VISP_HAVE_CATCH2)
#include <random>
#include <visp3/core/vpArray2D.h>
#include <visp3/core/vpIoTools.h>
#include VISP_NLOHMANN_JSON(json.hpp)
using json = nlohmann::json;
#include <catch_amalgamated.hpp>
#ifdef ENABLE_VISP_NAMESPACE
#endif
namespace
{
using StringMatcherBase = Catch::Matchers::StringMatcherBase;
class vpExceptionMatcher : public Catch::Matchers::MatcherBase<vpException>
{
const StringMatcherBase &m_messageMatcher;
public:
: m_type(type), m_messageMatcher(messageMatcher)
{ }
{
}
std::string describe() const VP_OVERRIDE
{
std::ostringstream ss;
ss << "vpException has type " << m_type << " and message " << m_messageMatcher.describe();
return ss.str();
}
};
class vpRandomArray2DGenerator : public Catch::Generators::IGenerator<vpArray2D<double> >
{
private:
std::minstd_rand m_rand;
std::uniform_real_distribution<> m_val_dist;
std::uniform_int_distribution<> m_dim_dist;
public:
vpRandomArray2DGenerator(double valueRange, int minSize, int maxSize)
: m_rand(std::random_device {}()), m_val_dist(-valueRange, valueRange), m_dim_dist(minSize, maxSize)
{
static_cast<void>(next());
}
bool next() VP_OVERRIDE
{
const unsigned nCols = m_dim_dist(m_rand);
const unsigned nRows = m_dim_dist(m_rand);
for (unsigned i = 0; i < nRows; ++i) {
for (unsigned j = 0; j < nCols; ++j) {
current[i][j] = m_val_dist(m_rand);
}
}
return true;
}
};
class vpRandomColVectorGenerator : public Catch::Generators::IGenerator<vpColVector>
{
private:
std::minstd_rand m_rand;
std::uniform_real_distribution<> m_val_dist;
std::uniform_int_distribution<> m_dim_dist;
public:
vpRandomColVectorGenerator(double valueRange, int minSize, int maxSize)
: m_rand(std::random_device {}()), m_val_dist(-valueRange, valueRange), m_dim_dist(minSize, maxSize)
{
static_cast<void>(next());
}
const vpColVector &get()
const VP_OVERRIDE {
return current; }
bool next() VP_OVERRIDE
{
const unsigned nRows = m_dim_dist(m_rand);
for (unsigned i = 0; i < nRows; ++i) {
current[i] = m_val_dist(m_rand);
}
return true;
}
};
Catch::Generators::GeneratorWrapper<vpArray2D<double> > randomArray(double v, int minSize, int maxSize)
{
return Catch::Generators::GeneratorWrapper<vpArray2D<double> >(Catch::Detail::make_unique<vpRandomArray2DGenerator>(v, minSize, maxSize));
}
Catch::Generators::GeneratorWrapper<vpColVector> randomColVector(double v, int minSize, int maxSize)
{
return Catch::Generators::GeneratorWrapper<vpColVector>(Catch::Detail::make_unique<vpRandomColVectorGenerator>(v, minSize, maxSize));
}
{
return vpExceptionMatcher(type, matcher);
}
}
SCENARIO("Serializing a vpArray2D", "[json]")
{
GIVEN("A random vpArray2D<double>")
{
WHEN("Serializing to a JSON object")
{
const json j = array;
THEN("JSON object is a dictionary") { REQUIRE(j.is_object()); }
THEN("JSON object contains correct number of columns")
{
REQUIRE(j.at(
"cols").get<
unsigned int>() == array.
getCols());
}
THEN("JSON object contains correct number of rows")
{
REQUIRE(j.at(
"rows").get<
unsigned int>() == array.
getRows());
}
THEN("JSON object contains the array values")
{
const json jData = j.at("data");
THEN("The data field is an array") { REQUIRE(jData.is_array()); }
THEN(
"The data field contains the correct number of values") { REQUIRE(jData.size() == array.
size()); }
THEN("The data field contains the correct number of values")
{
const double *const start = array[0];
const std::vector<double> vec(start, start + array.
size());
REQUIRE(vec == jData.get<std::vector<double> >());
}
}
}
}
}
SCENARIO("Trying to instantiate a vpArray with a wrong type of object", "[json]")
{
GIVEN("A random scalar converted to a JSON representation")
{
std::uniform_real_distribution<> dist;
const json j = GENERATE(take(50, random(-200.0, 200.0)));
THEN("An exception is thrown")
{
const auto matcher = Catch::Matchers::ContainsSubstring("is not an array or object");
}
}
}
SCENARIO("Recovering a vpArray2D from a JSON array", "[json]")
{
GIVEN("An empty array")
{
const json j = json::array_t();
WHEN("Converting to a vpArray2D")
{
THEN(
"The resulting array is empty") { REQUIRE(array.
size() == 0); }
}
}
GIVEN("A 1D array")
{
const json j = { 10.0, 20.0, 30.0 };
WHEN("Converting to a vpArray2D")
{
THEN("An exception is thrown, since this is an ambiguous array")
{
const auto matcher = Catch::Matchers::ContainsSubstring("is not an array of array");
}
}
}
GIVEN("A vpArray2D converted to a json 2D array")
{
json j;
for (
unsigned i = 0; i < array.
getRows(); ++i) {
json jRow;
for (
unsigned j = 0; j < array.
getCols(); ++j) {
jRow.push_back(array[i][j]);
}
j.push_back(jRow);
}
WHEN("Converting back to a vpArray2D")
{
THEN("The values are correct") { REQUIRE(array == array2); }
}
WHEN("Removing elements from rows so that they do not have the same size")
{
j[0].erase(0);
THEN("An exception is thrown")
{
const auto matcher = Catch::Matchers::ContainsSubstring("row arrays that are not of the same size");
}
}
}
}
SCENARIO("Recovering a vpArray2D from a JSON object as serialized by ViSP", "[json]")
{
GIVEN("A vpArray2D converted to JSON format")
{
json j = array;
WHEN("Converting back to a vpArray2D")
{
THEN("The 2 arrays are equal") { REQUIRE(array == array2); }
}
WHEN("Removing or adding some values from the data field so that its size does not match the expected array size")
{
j.at("data").erase(0);
THEN("An exception is thrown")
{
const auto matcher = Catch::Matchers::ContainsSubstring("must be an array of size");
}
}
}
}
SCENARIO("Serializing and deserializing a vpColVector", "[json]")
{
GIVEN("A random vpColVector")
{
const vpColVector v = GENERATE(take(100, randomColVector(100.0, 1, 50)));
WHEN("Serializing to JSON")
{
const json j = v;
THEN("There is only one column") { REQUIRE(j.at("cols") == 1); }
THEN("The type is vpColVector") { REQUIRE(j.at("type") == "vpColVector"); }
WHEN("Deserializing back to a vpColVector")
{
THEN("The 2 vectors are the same") { REQUIRE(v == v2); }
}
}
}
GIVEN("A random 2D array with number of cols > 1")
{
WHEN("Serializing this array to JSON")
{
const json j = array;
THEN("Serializing back to a vpColVector throws an exception")
{
const auto matcher = Catch::Matchers::ContainsSubstring("tried to read a 2D array into a vpColVector");
}
}
}
GIVEN("A random 2D array with number of cols = 1")
{
const vpColVector v = GENERATE(take(10, randomColVector(100.0, 1, 50)));
WHEN("Serializing this array to JSON")
{
const json j = array;
THEN("Serializing back to a vpColVector is ok and gives the same vector")
{
REQUIRE(v == v2);
}
}
}
}
int main(int argc, char *argv[])
{
Catch::Session session;
session.applyCommandLine(argc, argv);
int numFailed = session.run();
return numFailed;
}
#else
int main() { return EXIT_SUCCESS; }
#endif
unsigned int getCols() const
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
unsigned int size() const
Return the number of elements of the 2D array.
unsigned int getRows() const
Implementation of column vector and the associated operations.
void resize(unsigned int i, bool flagNullify=true)
error that can be emitted by ViSP classes.
const std::string & getStringMessage() const
@ badValue
Used to indicate that a value is not in the allowed range.