42#include <visp3/core/vpIoTools.h>
43#include <visp3/io/vpJsonArgumentParser.h>
45#if defined(VISP_HAVE_NLOHMANN_JSON) && defined(VISP_HAVE_CATCH2)
46#include <nlohmann/json.hpp>
47using json = nlohmann::json;
49#define CATCH_CONFIG_RUNNER
52std::pair<int, std::vector<char *>> convertToArgcAndArgv(
const std::vector<std::string> &args)
54 std::vector<char *> argvs;
55 argvs.reserve(args.size());
56 int argc =
static_cast<int>(args.size());
57 for (
unsigned i = 0; i < args.size(); ++i) {
58 argvs.push_back(
const_cast<char *
>(args[i].c_str()));
60 return std::make_pair(argc, argvs);
63json loadJson(
const std::string &path)
65 std::ifstream json_file(path);
66 if (!json_file.good()) {
69 json j = json::parse(json_file);
74void saveJson(
const json &j,
const std::string &path)
76 std::ofstream json_file(path);
77 if (!json_file.good()) {
80 json_file << j.dump();
84SCENARIO(
"Parsing arguments from JSON file",
"[json]")
90 const std::string jsonPath = tmp_dir +
"/" +
"arguments.json";
92 const auto modifyJson = [&jsonPath](std::function<void(json &)> modify) ->
void {
93 json j = loadJson(jsonPath);
95 saveJson(j, jsonPath);
98 GIVEN(
"Some specific arguments")
100 const std::string s =
"hello";
101 WHEN(
"Converting a string to a json rep")
103 const json js = convertCommandLineArgument<std::string>(s);
104 const json truejs = s;
105 THEN(
"Conversion is correct")
107 REQUIRE(js == truejs);
112 GIVEN(
"Some JSON parameters saved in a file, and some C++ variables")
123 saveJson(j, jsonPath);
130 WHEN(
"Declaring a parser with all parameters required")
133 parser.addArgument(
"a", a,
true)
134 .addArgument(
"b", b,
true)
135 .addArgument(
"c", c,
true)
136 .addArgument(
"d", d,
true)
137 .addArgument(
"e/a", ea,
true);
139 THEN(
"Calling the parser without any argument fails")
142 const char *argv [] = {
146 REQUIRE_THROWS(parser.parse(argc, argv));
149 THEN(
"Calling the parser with only the JSON file works")
152 const char *argv [] = {
157 REQUIRE_NOTHROW(parser.parse(argc, argv));
158 REQUIRE(a == j[
"a"]);
159 REQUIRE(b == j[
"b"]);
160 REQUIRE(c == j[
"c"]);
161 REQUIRE(d == j[
"d"]);
162 REQUIRE(ea == j[
"e"][
"a"]);
165 THEN(
"Calling the parser by specifying the json argument but leaving the file path empty throws an error")
168 const char *argv [] = {
172 REQUIRE_THROWS(parser.parse(argc, argv));
174 THEN(
"Calling the parser with only the json file but deleting a random field throws an error")
177 for (
const auto &jsonElem : j.items()) {
178 modifyJson([&jsonElem](json &j) { j.erase(jsonElem.key()); });
179 const char *argv [] = {
184 REQUIRE_THROWS(parser.parse(argc, argv));
187 THEN(
"Calling the parser with only the json file but setting a random field to null throws an error")
190 for (
const auto &jsonElem : j.items()) {
191 modifyJson([&jsonElem](json &j) { j[jsonElem.key()] =
nullptr; });
192 const char *argv [] = {
197 REQUIRE_THROWS(parser.parse(argc, argv));
200 THEN(
"Calling the parser with an invalid json file path throws an error")
203 const char *argv [] = {
206 "some_invalid_json/file/path.json"
208 REQUIRE_THROWS(parser.parse(argc, argv));
210 THEN(
"Calling the parser with only the command line arguments works")
212 const int newa = a + 1, newea = ea + 6;
213 const double newb = b + 2.0;
214 const std::string newc = c +
"hello";
215 const bool newd = !d;
217 const std::string newdstr(newd ?
"true" :
"false");
218 std::vector<std::string> args = {
220 "a", std::to_string(newa),
221 "b", std::to_string(newb),
224 "e/a", std::to_string(newea)
227 std::vector<char *> argv;
228 std::tie(argc, argv) = convertToArgcAndArgv(args);
229 REQUIRE_NOTHROW(parser.parse(argc, (
const char **)(&argv[0])));
234 REQUIRE(ea == newea);
237 THEN(
"Calling the parser with JSON and command line argument works")
239 const int newa = a + 1;
240 const double newb = b + 2.0;
241 std::vector<std::string> args = {
243 "--config", jsonPath,
244 "a", std::to_string(newa),
245 "b", std::to_string(newb)
248 std::vector<char *> argv;
249 std::tie(argc, argv) = convertToArgcAndArgv(args);
250 REQUIRE_NOTHROW(parser.parse(argc, (
const char **)(&argv[0])));
253 REQUIRE(c == j[
"c"]);
254 REQUIRE(d == j[
"d"]);
255 REQUIRE(ea == j[
"e"][
"a"]);
258 THEN(
"Calling the parser with a missing argument value throws an error")
261 std::vector<std::string> args = {
263 "--config", jsonPath,
267 std::vector<char *> argv;
268 std::tie(argc, argv) = convertToArgcAndArgv(args);
269 REQUIRE_THROWS(parser.parse(argc, (
const char **)(&argv[0])));
273 THEN(
"Declaring a parser with an undefined nesting delimiter fails")
277 THEN(
"Declaring a parser with an invalid JSON file argument fails")
282 WHEN(
"Instanciating a parser with some optional fields")
286 parser.addArgument(
"b", b,
false);
288 THEN(
"Calling the parser without any argument works and does not modify the default value")
292 const char *argv [] = {
296 REQUIRE_NOTHROW(parser.parse(argc, argv));
301 WHEN(
"Instanciating a parser with nested parameters")
305 parser.addArgument(
"b", b,
false);
307 THEN(
"Calling the parser without any argument works and does not modify the default value")
311 const char *argv [] = {
315 REQUIRE_NOTHROW(parser.parse(argc, argv));
322 WHEN(
"Instanciating a parser with some documentation")
324 const std::string programString =
"ProgramString";
325 const std::string firstArg =
"FirstArgName", firstArgDescription =
"FirstArgDescription";
326 const std::string secondArg =
"secondArgName", secondArgDescription =
"secondArgDescription";
328 std::string a =
"DefaultFirstArg", b =
"DefaultSecondArg";
329 parser.addArgument(firstArg, a,
true, firstArgDescription);
330 parser.addArgument(secondArg, b,
false, secondArgDescription);
331 WHEN(
"Getting the help string")
333 const std::string help = parser.help();
334 THEN(
"Output should contain the basic program description")
336 REQUIRE(help.find(programString) < help.size());
338 THEN(
"Output should contain the json argument")
340 REQUIRE(help.find(
"--config") < help.size());
342 THEN(
"Output should contain the arguments, their description and their default value")
344 const std::vector<std::string> requireds = { firstArg, secondArg, firstArgDescription, secondArgDescription, a, b };
345 for (
const auto &required : requireds) {
346 REQUIRE(help.find(required) < help.size());
354int main(
int argc,
char *argv [])
356 Catch::Session session;
357 session.applyCommandLine(argc, argv);
359 int numFailed = session.run();
error that can be emitted by ViSP classes.
Command line argument parsing with support for JSON files. If a JSON file is supplied,...