Project
Loading...
Searching...
No Matches
test_BoostOptionsRetriever.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
18#include <boost/program_options.hpp>
19#include <boost/program_options/parsers.hpp>
20#include <catch_amalgamated.hpp>
21#include <sstream>
22#include <vector>
23
24using namespace o2::framework;
25
26TEST_CASE("TrivialBoostOptionsRetrieverTest")
27{
28 using namespace o2::framework;
29 namespace bpo = boost::program_options;
30
31 auto specs = std::vector<ConfigParamSpec>{
32 {"someInt", VariantType::Int, 2, {"some int option"}},
33 {"someInt8", VariantType::Int8, static_cast<int8_t>(2), {"some int8 option"}},
34 {"someInt16", VariantType::Int16, static_cast<int16_t>(2), {"some int16 option"}},
35 {"someUInt8", VariantType::UInt8, static_cast<uint8_t>(2u), {"some uint8 option"}},
36 {"someUInt16", VariantType::UInt16, static_cast<uint16_t>(2u), {"some uint16 option"}},
37 {"someUInt32", VariantType::UInt32, 2u, {"some uint32 option"}},
38 {"someUInt64", VariantType::UInt64, static_cast<uint64_t>(2ul), {"some uint64 option"}},
39 {"someInt64", VariantType::Int64, 4ll, {"some int64 option"}},
40 {"someBool", VariantType::Bool, false, {"some bool option"}},
41 {"someFloat", VariantType::Float, 2.0f, {"some float option"}},
42 {"someDouble", VariantType::Double, 2.0, {"some double option"}},
43 {"someString", VariantType::String, strdup("barfoo"), {"some string option"}}};
44 const char* args[] = {
45 "test",
46 "--someBool",
47 "--someInt", "1",
48 "--someInt8", "1",
49 "--someInt16", "1",
50 "--someUInt8", "1",
51 "--someUInt16", "1",
52 "--someUInt32", "1",
53 "--someUInt64", "1",
54 "--someInt64", "50000000000000",
55 "--someFloat", "0.5",
56 "--someDouble", "0.5",
57 "--someString", "foobar"};
58 bpo::variables_map vm;
59 bpo::options_description opts;
60
62
63 bpo::store(parse_command_line(sizeof(args) / sizeof(char*), args, opts), vm);
64 bpo::notify(vm);
65 REQUIRE(vm["someInt"].as<int>() == 1);
66 REQUIRE(vm["someInt8"].as<int8_t>() == '1');
67 REQUIRE(vm["someInt16"].as<int16_t>() == 1);
68 REQUIRE(vm["someUInt8"].as<uint8_t>() == '1');
69 REQUIRE(vm["someUInt16"].as<uint16_t>() == 1);
70 REQUIRE(vm["someUInt32"].as<uint32_t>() == 1);
71 REQUIRE(vm["someUInt64"].as<uint64_t>() == 1);
72 REQUIRE(vm["someInt64"].as<int64_t>() == 50000000000000ll);
73 REQUIRE(vm["someBool"].as<bool>() == true);
74 REQUIRE(vm["someString"].as<std::string>() == "foobar");
75 REQUIRE(vm["someFloat"].as<float>() == 0.5);
76 REQUIRE(vm["someDouble"].as<double>() == 0.5);
77}
78
79namespace
80{
81constexpr char const* fullLabeledJSON = R"({"labels_rows":["a","b"],"labels_cols":["x","y"],"values":[[5,6],[7,8]]})";
82
83LabeledArray<int> defaultLabeledArray()
84{
85 int m[2][2] = {{1, 2}, {3, 4}};
86 return LabeledArray<int>{&m[0][0], 2, 2, {"r1", "r2"}, {"c1", "c2"}};
87}
88
89std::unique_ptr<ConfigParamRegistry> registryFromCommandLine(std::vector<ConfigParamSpec> const& specs, std::vector<char const*> args)
90{
91 args.insert(args.begin(), "test");
92 std::vector<std::unique_ptr<ParamRetriever>> retrievers;
93 retrievers.emplace_back(std::make_unique<BoostOptionsRetriever>(true, args.size(), const_cast<char**>(args.data())));
94 auto store = std::make_unique<ConfigParamStore>(specs, std::move(retrievers));
95 store->preload();
96 store->activate();
97 return std::make_unique<ConfigParamRegistry>(std::move(store));
98}
99} // namespace
100
101TEST_CASE("LabeledArrayBoostOptionsTest")
102{
103 namespace bpo = boost::program_options;
104 auto specs = std::vector<ConfigParamSpec>{
105 {"someLabeledArray", VariantType::LabeledArrayInt, defaultLabeledArray(), {"some labeled array option"}}};
106
107 // the option is registered as a string holding the JSON representation
108 bpo::options_description opts;
110 const char* args[] = {"test", "--someLabeledArray", fullLabeledJSON};
111 bpo::variables_map vm;
112 bpo::store(parse_command_line(sizeof(args) / sizeof(char*), args, opts), vm);
113 bpo::notify(vm);
114 REQUIRE(vm["someLabeledArray"].as<std::string>() == fullLabeledJSON);
115
116 // the default is the JSON representation of the default value
117 bpo::variables_map vmDefault;
118 const char* noArgs[] = {"test"};
119 bpo::store(parse_command_line(1, noArgs, opts), vmDefault);
120 REQUIRE(vmDefault["someLabeledArray"].as<std::string>() == specs[0].defaultValue.asString());
121}
122
123TEST_CASE("LabeledArrayBoostOptionsRetrieverTest")
124{
125 auto specs = std::vector<ConfigParamSpec>{
126 {"someLabeledArray", VariantType::LabeledArrayInt, defaultLabeledArray(), {"some labeled array option"}}};
127
128 // full JSON on the command line, its labels are kept
129 auto registry = registryFromCommandLine(specs, {"--someLabeledArray", fullLabeledJSON});
130 auto la = registry->get<LabeledArray<int>>("someLabeledArray");
131 REQUIRE(la.getLabelsRows() == std::vector<std::string>{"a", "b"});
132 REQUIRE(la.getLabelsCols() == std::vector<std::string>{"x", "y"});
133 REQUIRE(la.get("b", "y") == 8);
134
135 // absent from the command line: the default value
136 registry = registryFromCommandLine(specs, {});
137 la = registry->get<LabeledArray<int>>("someLabeledArray");
138 REQUIRE(la.get("r2", "c2") == 4);
139
140 // only values on the command line: the default labels are used
141 registry = registryFromCommandLine(specs, {"--someLabeledArray", R"({"values":[[5,6],[7,8]]})"});
142 la = registry->get<LabeledArray<int>>("someLabeledArray");
143 REQUIRE(la.getLabelsRows() == std::vector<std::string>{"r1", "r2"});
144 REQUIRE(la.getLabelsCols() == std::vector<std::string>{"c1", "c2"});
145 REQUIRE(la.get("r2", "c1") == 7);
146
147 // values which do not match the default labels
148 REQUIRE_THROWS(registryFromCommandLine(specs, {"--someLabeledArray", R"({"values":[[5,6,7]]})"}));
149}
150
151TEST_CASE("DefaultValueForHelpTest")
152{
155 REQUIRE(ConfigParamsHelper::defaultValueForHelp("short") == "short");
156 std::string atLimit(max, 'x');
157 REQUIRE(ConfigParamsHelper::defaultValueForHelp(atLimit) == atLimit);
158
159 std::string longValue = std::string("BEGIN") + std::string(100, '-') + "END";
160 auto shown = ConfigParamsHelper::defaultValueForHelp(longValue);
161 REQUIRE(shown.size() == max);
162 REQUIRE(shown.starts_with("BEGIN"));
163 REQUIRE(shown.ends_with("END"));
164 REQUIRE(shown.find("...") != std::string::npos);
165 REQUIRE(ConfigParamsHelper::defaultValueForHelp(std::string(max + 1, 'x')).size() == max);
166}
167
168TEST_CASE("LongDefaultInHelpTest")
169{
170 namespace bpo = boost::program_options;
171 std::vector<int> values(30, 12345);
172 std::vector<std::string> rows{"row"};
173 std::vector<std::string> cols;
174 for (auto i = 0U; i < values.size(); ++i) {
175 cols.push_back("column" + std::to_string(i));
176 }
177 LabeledArray<int> la{values.data(), 1, static_cast<uint32_t>(values.size()), rows, cols};
178 auto workflowOptions = std::vector<ConfigParamSpec>{
179 {"longLabeledArray", VariantType::LabeledArrayInt, la, {"a labeled array option with a long default"}}};
180 auto full = workflowOptions[0].defaultValue.asString();
181 REQUIRE(full.size() > ConfigParamsHelper::MaxHelpDefaultLength);
182
183 // the help shows the abbreviated default only
184 auto description = ConfigParamsHelper::prepareOptionDescriptions(std::vector<DataProcessorSpec>{}, workflowOptions);
185 std::stringstream help;
186 help << description;
187 REQUIRE(help.str().find(ConfigParamsHelper::defaultValueForHelp(full)) != std::string::npos);
188 REQUIRE(help.str().find(full) == std::string::npos);
189
190 // the default value used when parsing is still the full one
191 bpo::options_description forwarded;
192 ConfigParamsHelper::dpl2BoostOptions(workflowOptions, forwarded);
193 const char* noArgs[] = {"test"};
194 bpo::variables_map vm;
195 bpo::store(parse_command_line(1, noArgs, forwarded), vm);
196 REQUIRE(vm["longLabeledArray"].as<std::string>() == full);
197
198 // also for the options registered for the devices
199 bpo::options_description deviceOptions;
200 ConfigParamsHelper::populateBoostProgramOptions(deviceOptions, workflowOptions);
201 std::stringstream deviceHelp;
202 deviceHelp << deviceOptions;
203 REQUIRE(deviceHelp.str().find(full) == std::string::npos);
204 bpo::variables_map deviceVm;
205 bpo::store(parse_command_line(1, noArgs, deviceOptions), deviceVm);
206 REQUIRE(deviceVm["longLabeledArray"].as<std::string>() == full);
207}
header::DataDescription description
int32_t i
uint32_t cols
const GLfloat * m
Definition glcorearb.h:4066
GLsizeiptr size
Definition glcorearb.h:659
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
static constexpr size_t MaxHelpDefaultLength
static boost::program_options::options_description prepareOptionDescriptions(ContainerType const &workflow, std::vector< ConfigParamSpec > const &currentWorkflowOptions, options_description vetos=options_description(), std::string mode="full")
populate boost program options for a complete workflow
static bool dpl2BoostOptions(const std::vector< ConfigParamSpec > &spec, options_description &options, boost::program_options::options_description const &vetos=options_description())
static void populateBoostProgramOptions(options_description &options, const std::vector< ConfigParamSpec > &specs, options_description vetos=options_description())
static std::string defaultValueForHelp(std::string const &value)
constexpr size_t max
TEST_CASE("TrivialBoostOptionsRetrieverTest")
std::vector< ReadoutWindowData > rows