Project
Loading...
Searching...
No Matches
test_DeviceSpecHelpers.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
12#include "Mocking.h"
17#include "../src/DeviceSpecHelpers.h"
18#include <catch_amalgamated.hpp>
19#include <algorithm>
20#include <sstream>
21#include <cstring>
22#include <vector>
23#include <map>
24#include <cstring>
25#include "../src/SimpleResourceManager.h"
26#include "../src/ComputingResourceHelpers.h"
27
28namespace o2::framework
29{
30
31using CheckMatrix = std::map<std::string, std::vector<std::pair<std::string, std::string>>>;
32
33// search for an option in the device execution
34bool search(DeviceExecution const& execution, std::string const& option, std::string const& argument)
35{
36 bool foundOption = false;
37 for (auto const& execArg : execution.args) {
38 if (execArg == nullptr) {
39 break;
40 }
41 if (!foundOption) {
42 foundOption = option == execArg;
43 } else if (argument == execArg) {
44 return true;
45 } else {
46 // the required argument to the option is not found
47 foundOption = false;
48 }
49 }
50 return false;
51}
52
53// create the device execution from the device specs and process the command line arguments
54// the check matrix contains a map of options to be founf per processor
55void check(const std::vector<std::string>& arguments,
56 const std::vector<ConfigParamSpec>& workflowOptions,
57 const std::vector<DeviceSpec>& deviceSpecs,
58 CheckMatrix& matrix)
59{
60 std::stringstream output;
61 for (auto const& arg : arguments) {
62 output << " " << arg;
63 }
64
65 std::vector<DeviceExecution> deviceExecutions(deviceSpecs.size());
66 std::vector<DeviceControl> deviceControls(deviceSpecs.size());
67 std::vector<DataProcessorInfo> dataProcessorInfos;
68 for (auto& [name, _] : matrix) {
69 dataProcessorInfos.push_back(DataProcessorInfo{
70 name,
71 "executable-name",
73 workflowOptions,
74 });
75 }
76 DriverConfig driverConfig{};
77 DeviceSpecHelpers::prepareArguments(true, true, false, 8080,
78 driverConfig,
79 dataProcessorInfos,
80 deviceSpecs,
81 deviceExecutions,
82 deviceControls, {},
83 "workflow-id");
84
85 for (size_t index = 0; index < deviceSpecs.size(); index++) {
86 const auto& deviceSpec = deviceSpecs[index];
87 const auto& deviceExecution = deviceExecutions[index];
88 std::stringstream execArgs;
89 for (const auto& arg : deviceExecution.args) {
90 if (arg == nullptr) {
91 // the nullptr terminates the argument list
92 break;
93 }
94 execArgs << " " << arg;
95 }
96 for (auto const& testCase : matrix[deviceSpec.name]) {
97 INFO(std::string("can not find option: ") + testCase.first + " " + testCase.second);
98 REQUIRE(search(deviceExecution, testCase.first, testCase.second));
99 }
100 }
101}
102
103TEST_CASE("test_prepareArguments")
104{
105 std::vector<ConfigParamSpec> workflowOptions{
106 {"foo", VariantType::String, "bar", {"the famous foo option"}},
107 {"depth", VariantType::Int, 1, {"number of processors"}},
108 };
109
110 auto algorithm = [](ProcessingContext& ctx) {};
111
112 WorkflowSpec workflow{
113 {"processor0",
114 {},
115 {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
116 AlgorithmSpec(algorithm),
117 Options{
118 {"mode", VariantType::String, "default", {"The Mode"}},
119 }},
120 {"processor1",
121 {InputSpec{"input", "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
122 {},
123 AlgorithmSpec(algorithm),
124 Options{
125 {"mode", VariantType::String, "default", {"The Mode"}},
126 }},
127 };
128
129 std::vector<DeviceSpec> deviceSpecs;
130
131 std::vector<ComputingResource> resources = {ComputingResourceHelpers::getLocalhostResource()};
132 auto rm = std::make_unique<SimpleResourceManager>(resources);
133
134 auto configContext = makeEmptyConfigContext();
135 auto channelPolicies = ChannelConfigurationPolicy::createDefaultPolicies(*configContext);
136 auto callbacksPolicies = CallbacksPolicy::createDefaultPolicies();
137
139 channelPolicies,
141 callbacksPolicies,
142 deviceSpecs,
143 *rm, "workflow-id", *configContext);
144
145 // Now doing the test cases
146 CheckMatrix matrix;
147
148 // checking with empty command line arguments, all processors must have the options with
149 // default arguments
150 matrix["processor0"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "default"}};
151 matrix["processor1"] = matrix["processor0"];
152 check({}, workflowOptions, deviceSpecs, matrix);
153
154 // checking with unknown arguments, silently ignored, same test matrix
155 check({"--unknown", "option"}, workflowOptions, deviceSpecs, matrix);
156
157 // configuring mode, both devices must have the option set
158 matrix["processor0"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "silly"}};
159 matrix["processor1"] = matrix["processor0"];
160 check({"--mode", "silly"}, workflowOptions, deviceSpecs, matrix);
161
162 // configuring option group, only processor0 must have the option set, processor1 default
163 matrix["processor0"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "silly"}};
164 matrix["processor1"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "default"}};
165 check({"--processor0", "--mode silly"}, workflowOptions, deviceSpecs, matrix);
166
167 // processor0 must have the mode set to silly via option group, processor1 advanced from the argument
168 matrix["processor0"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "silly"}};
169 matrix["processor1"] = {{"--depth", "1"}, {"--foo", "bar"}, {"--mode", "advanced"}};
170 check({"--mode", "advanced", "--processor0", "--mode silly"}, workflowOptions, deviceSpecs, matrix);
171
172 // both devices have the workflow option propagated, others defaulted
173 matrix["processor0"] = {{"--depth", "2"}, {"--foo", "bar"}, {"--mode", "default"}};
174 matrix["processor1"] = matrix["processor0"];
175 check({"--depth", "2"}, workflowOptions, deviceSpecs, matrix);
176
177 // both devices have the workflow option propagated, processor0 mode silly via option group
178 matrix["processor0"] = {{"--depth", "2"}, {"--foo", "bar"}, {"--mode", "silly"}};
179 matrix["processor1"] = {{"--depth", "2"}, {"--foo", "bar"}, {"--mode", "default"}};
180 check({"--depth", "2", "--processor0", "--mode silly"}, workflowOptions, deviceSpecs, matrix);
181}
182
183TEST_CASE("CheckOptionReworking")
184{
185 {
186 std::vector<DataProcessorInfo> infos = {
187 {{}, {}, {"--driver-client-backend", "foo"}},
188 {}};
189 DeviceSpecHelpers::reworkHomogeneousOption(infos, "--driver-client-backend", "stdout://");
190 REQUIRE(infos[0].cmdLineArgs[1] == "foo");
191 REQUIRE(infos[1].cmdLineArgs[1] == "foo");
192 }
193 {
194 std::vector<DataProcessorInfo> infos = {
195 {{}, {}, {"--driver-client-backend", "foo"}},
196 {{}, {}, {"--driver-client-backend", "bar"}}};
197 REQUIRE_THROWS_AS(DeviceSpecHelpers::reworkHomogeneousOption(infos, "--driver-client-backend", "stdout://"), o2::framework::RuntimeErrorRef);
198 }
199 {
200 std::vector<DataProcessorInfo> infos = {
201 {{}, {}, {"--driver-client-backend", "foo"}},
202 {{}, {}, {"--driver-client-backend", "foo"}}};
203 DeviceSpecHelpers::reworkHomogeneousOption(infos, "--driver-client-backend", "stdout://");
204 REQUIRE(infos[0].cmdLineArgs[1] == "foo");
205 REQUIRE(infos[1].cmdLineArgs[1] == "foo");
206 }
207 {
208 std::vector<DataProcessorInfo> infos = {
209 {{}, {}, {"foo", "bar"}},
210 {{}, {}, {"fnjcnak", "foo"}}};
211 DeviceSpecHelpers::reworkHomogeneousOption(infos, "--driver-client-backend", "stdout://");
212 REQUIRE(infos[0].cmdLineArgs[3] == "stdout://");
213 REQUIRE(infos[1].cmdLineArgs[3] == "stdout://");
214 }
215 {
216 std::vector<DataProcessorInfo> infos = {
217 {{}, {}, {"foo", "bar", "--driver-client-backend", "bar"}},
218 {{}, {}, {"fnjcnak", "foo"}}};
219 DeviceSpecHelpers::reworkHomogeneousOption(infos, "--driver-client-backend", "stdout://");
220 REQUIRE(infos[0].cmdLineArgs[3] == "bar");
221 REQUIRE(infos[1].cmdLineArgs[3] == "bar");
222 }
223}
224
225TEST_CASE("CheckIntegerReworking")
226{
227 {
228 std::vector<DataProcessorInfo> infos = {
229 {{}, {}, {"--readers", "2"}},
230 {}};
232 infos, "--readers", nullptr, 1, [](long long x, long long y) { return x > y ? x : y; });
233 REQUIRE(infos[0].cmdLineArgs[1] == "2");
234 REQUIRE(infos[1].cmdLineArgs[1] == "2");
235 }
236 {
237 std::vector<DataProcessorInfo> infos = {
238 {},
239 {}};
241 infos, "--readers", nullptr, 1, [](long long x, long long y) { return x > y ? x : y; });
242 REQUIRE(infos[0].cmdLineArgs.size() == 0);
243 REQUIRE(infos[1].cmdLineArgs.size() == 0);
244 }
245 {
246 std::vector<DataProcessorInfo> infos = {
247 {},
248 {}};
250 infos, "--readers", []() { return 1; }, 3, [](long long x, long long y) { return x > y ? x : y; });
251 REQUIRE(infos[0].cmdLineArgs.size() == 2);
252 REQUIRE(infos[1].cmdLineArgs.size() == 2);
253 REQUIRE(infos[0].cmdLineArgs[1] == "1");
254 REQUIRE(infos[1].cmdLineArgs[1] == "1");
255 }
256 {
257 std::vector<DataProcessorInfo> infos = {
258 {{}, {}, {"--readers", "2"}},
259 {{}, {}, {"--readers", "3"}}};
261 infos, "--readers", []() { return 1; }, 1, [](long long x, long long y) { return x > y ? x : y; });
262 REQUIRE(infos[0].cmdLineArgs.size() == 2);
263 REQUIRE(infos[1].cmdLineArgs.size() == 2);
264 REQUIRE(infos[0].cmdLineArgs[1] == "3");
265 REQUIRE(infos[1].cmdLineArgs[1] == "3");
266 }
267 {
268 std::vector<DataProcessorInfo> infos = {
269 {{}, {}, {"--readers", "3"}},
270 {{}, {}, {"--readers", "2"}}};
272 infos, "--readers", []() { return 1; }, 1, [](long long x, long long y) { return x > y ? x : y; });
273 REQUIRE(infos[0].cmdLineArgs.size() == 2);
274 REQUIRE(infos[1].cmdLineArgs.size() == 2);
275 REQUIRE(infos[0].cmdLineArgs[1] == "3");
276 REQUIRE(infos[1].cmdLineArgs[1] == "3");
277 }
278 {
279 std::vector<DataProcessorInfo> infos = {
280 {{}, {}, {"foo", "bar", "--readers", "3"}},
281 {{}, {}, {"--readers", "2"}}};
283 infos, "--readers", []() { return 1; }, 1, [](long long x, long long y) { return x > y ? x : y; });
284 REQUIRE(infos[0].cmdLineArgs.size() == 4);
285 REQUIRE(infos[1].cmdLineArgs.size() == 2);
286 REQUIRE(infos[0].cmdLineArgs[3] == "3");
287 REQUIRE(infos[1].cmdLineArgs[1] == "3");
288 }
289}
290
291TEST_CASE("Check validity of the workflow")
292{
293 WorkflowSpec workflow1{
294 {
295 .name = "processor0",
296 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
297 },
298 {
299 .name = "processor1",
300 .inputs = {InputSpec{"input", "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
301 },
302 };
303 REQUIRE_NOTHROW(DeviceSpecHelpers::validate(workflow1));
304
305 WorkflowSpec workflow2{
306 {
307 .name = "processor0",
308 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe},
309 OutputSpec{{"output2"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
310 },
311 {
312 .name = "processor1",
313 .inputs = {InputSpec{"input", "TST", "DUMMYDATA", 0, Lifetime::Timeframe}},
314 },
315 };
316 REQUIRE_THROWS(DeviceSpecHelpers::validate(workflow2));
317
318 WorkflowSpec workflow3{
319 {
320 .name = "processor0",
321 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe},
322 OutputSpec{{"output2"}, "TST", "DUMMYDATA", 2, Lifetime::Sporadic}},
323 },
324 {
325 .name = "processor1",
326 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 1, Lifetime::Timeframe},
327 OutputSpec{{"output2"}, "TST", "DUMMYDATA", 2, Lifetime::Sporadic}},
328 },
329 {
330 .name = "processor2",
331 .inputs = {InputSpec{"input", "TST", "DUMMYDATA", 2, Lifetime::Timeframe}},
332 },
333 };
334 REQUIRE_NOTHROW(DeviceSpecHelpers::validate(workflow3));
335
336 WorkflowSpec workflow4{
337 {
338 .name = "processor0",
339 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe},
340 OutputSpec{{"output2"}, "TST", "DUMMYDATA", 2, Lifetime::Sporadic}},
341 },
342 {
343 .name = "processor1",
344 .outputs = {OutputSpec{{"output"}, "TST", "DUMMYDATA", 0, Lifetime::Timeframe},
345 OutputSpec{{"output2"}, "TST", "DUMMYDATA", 2, Lifetime::Sporadic}},
346 },
347 {
348 .name = "processor2",
349 .inputs = {InputSpec{"input", "TST", "DUMMYDATA", 2, Lifetime::Timeframe}},
350 },
351 };
352 REQUIRE_THROWS(DeviceSpecHelpers::validate(workflow4));
353}
354
355TEST_CASE("CheckReworkingEnv")
356{
357 DeviceSpec spec{.inputTimesliceId = 1};
358 std::string env = "FOO={timeslice0}";
359 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1");
360 env = "FOO={timeslice0} BAR={timeslice4}";
361 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1 BAR=5");
362 env = "FOO={timeslice0} BAR={timeslice4} BAZ={timeslice5}";
363 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1 BAR=5 BAZ=6");
364 env = "";
365 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "");
366 env = "Plottigat";
367 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "Plottigat");
368 env = "{timeslice}";
369 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "{timeslice}");
370 env = "{timeslicepluto";
371 REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "{timeslicepluto");
372}
373
374} // namespace o2::framework
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
std::unique_ptr< ConfigContext > makeEmptyConfigContext()
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLint y
Definition glcorearb.h:270
bpo::variables_map arguments
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
void check(const std::vector< std::string > &arguments, const std::vector< ConfigParamSpec > &workflowOptions, const std::vector< DeviceSpec > &deviceSpecs, CheckMatrix &matrix)
TEST_CASE("test_prepareArguments")
bool search(DeviceExecution const &execution, std::string const &option, std::string const &argument)
std::vector< DataProcessorSpec > WorkflowSpec
std::vector< ConfigParamSpec > Options
std::map< std::string, std::vector< std::pair< std::string, std::string > > > CheckMatrix
static std::vector< CallbacksPolicy > createDefaultPolicies()
static std::vector< ChannelConfigurationPolicy > createDefaultPolicies(ConfigContext const &configContext)
Default policies to use, based on the contents of the @configContex content.
static std::vector< CompletionPolicy > createDefaultPolicies()
Helper to create the default configuration.
std::vector< char * > args
The options passed to a given device.
static void validate(WorkflowSpec const &workflow)
static std::string reworkTimeslicePlaceholder(std::string const &str, DeviceSpec const &spec)
static void prepareArguments(bool defaultQuiet, bool defaultStopped, bool intereactive, unsigned short driverPort, DriverConfig const &driverConfig, std::vector< DataProcessorInfo > const &processorInfos, std::vector< DeviceSpec > const &deviceSpecs, std::vector< DeviceExecution > &deviceExecutions, std::vector< DeviceControl > &deviceControls, std::vector< ConfigParamSpec > const &detectedOptions, std::string const &uniqueWorkflowId)
static void reworkHomogeneousOption(std::vector< DataProcessorInfo > &infos, char const *name, char const *defaultValue)
static void reworkIntegerOption(std::vector< DataProcessorInfo > &infos, char const *name, std::function< long long()> defaultValueCallback, long long startValue, std::function< long long(long long, long long)> bestValue)
static void dataProcessorSpecs2DeviceSpecs(const WorkflowSpec &workflow, std::vector< ChannelConfigurationPolicy > const &channelPolicies, std::vector< CompletionPolicy > const &completionPolicies, std::vector< DispatchPolicy > const &dispatchPolicies, std::vector< ResourcePolicy > const &resourcePolicies, std::vector< CallbacksPolicy > const &callbacksPolicies, std::vector< SendingPolicy > const &sendingPolicy, std::vector< ForwardingPolicy > const &forwardingPolicies, std::vector< DeviceSpec > &devices, ResourceManager &resourceManager, std::string const &uniqueWorkflowId, ConfigContext const &configContext, bool optimizeTopology=false, unsigned short resourcesMonitoringInterval=0, std::string const &channelPrefix="", OverrideServiceSpecs const &overrideServices={})
size_t inputTimesliceId
The time pipelining id of this particular device.
Definition DeviceSpec.h:68