LDMX Software
ConfigurePythonTest.cxx
1
2#include <catch2/catch_approx.hpp>
3#include <catch2/catch_test_macros.hpp>
4#include <catch2/matchers/catch_matchers_string.hpp>
5#include <fstream> // ifstream, ofstream
6
7#include "Framework/Configure/Python.h"
9#include "Framework/Process.h"
10#include "Python.h"
11
12using Catch::Approx;
13using Catch::Matchers::ContainsSubstring;
14
15namespace framework {
16namespace test {
17
24 public:
33 TestConfig(const std::string &name, framework::Process &process)
34 : framework::Producer(name, process) {
35 CHECK(name == "test_instance");
36 }
37
50 void configure(framework::config::Parameters &parameters) final override {
51 // Check parameters
52 CHECK(parameters.get<int>("test_int") == 9);
53 CHECK(parameters.get<double>("test_double") == Approx(7.7));
54 CHECK(parameters.get<std::string>("test_string") == "Yay!");
55
56 // Check dictionary
57 auto test_dict{parameters.get<framework::config::Parameters>("test_dict")};
58 CHECK(test_dict.get<int>("one") == 1);
59 CHECK(test_dict.get<double>("two") == 2.0);
60
61 // Check int vector
62 std::vector<int> int_vect{1, 2, 3};
63 auto test_int_vec{parameters.get<std::vector<int>>("test_int_vec")};
64 REQUIRE(test_int_vec.size() == int_vect.size());
65 for (std::size_t i{0}; i < test_int_vec.size(); i++)
66 CHECK(test_int_vec.at(i) == int_vect.at(i));
67
68 // Check double vec
69 std::vector<double> double_vec{0.1, 0.2, 0.3};
70 auto test_double_vec{
71 parameters.get<std::vector<double>>("test_double_vec")};
72 REQUIRE(test_double_vec.size() == double_vec.size());
73 for (std::size_t i{0}; i < test_double_vec.size(); i++)
74 CHECK(test_double_vec.at(i) == double_vec.at(i));
75
76 // Check string vector
77 std::vector<std::string> string_vec{"first", "second", "third"};
78 auto test_string_vec{
79 parameters.get<std::vector<std::string>>("test_string_vec")};
80 REQUIRE(test_string_vec.size() == string_vec.size());
81 for (std::size_t i{0}; i < test_string_vec.size(); i++)
82 CHECK(test_string_vec.at(i) == string_vec.at(i));
83
84 // check 2d vector
85 std::vector<std::vector<int>> twod_vec{
86 {11, 12, 13}, {21, 22}, {31, 32, 33, 34}};
87 auto test_2d_vec{
88 parameters.get<std::vector<std::vector<int>>>("test_2dlist")};
89 REQUIRE(test_2d_vec.size() == twod_vec.size());
90 for (std::size_t i{0}; i < twod_vec.size(); i++) {
91 REQUIRE(test_2d_vec.at(i).size() == twod_vec.at(i).size());
92 for (std::size_t j{0}; j < twod_vec.at(i).size(); j++) {
93 CHECK(test_2d_vec.at(i).at(j) == twod_vec.at(i).at(j));
94 }
95 }
96 }
97
98 // I don't do anything.
99 virtual void produce(framework::Event &) override {}
100};
101} // namespace test
102} // namespace framework
103
104DECLARE_PRODUCER_NS(framework::test, TestConfig)
105
106
116TEST_CASE("Configure Python Test", "[Framework][functionality]") {
117 const std::string config_file_name{"config_python_test_config.py"};
118
119 // Arguments to pass to ConfigurePython constructor
120 char *args[1];
121
122 // Process handle
124
125 // Run a check of the python configuration class without arguments.
126 SECTION("No arguments to python script") {
128 "ldmxcfg.Process.lastProcess", config_file_name, args, 0)};
129 p = std::make_unique<framework::Process>(config);
130
131 CHECK(p->getPassName() == "test");
132 }
133
134 // Update the python config so we can pass the log frequency as a parameter.
135 std::ifstream in_file;
136 std::ofstream out_file;
137
138 in_file.open(config_file_name.c_str(), std::ios::in | std::ios::binary);
139
140 const std::string config_file_name_arg{
141 "/tmp/config_python_test_config_arg.py"};
142 out_file.open(config_file_name_arg, std::ios::out | std::ios::binary);
143 out_file << in_file.rdbuf();
144 out_file << "import sys" << std::endl;
145 out_file << "p.logFrequency = int(sys.argv[1])" << std::endl;
146
147 in_file.close();
148 out_file.close();
149
150 // Pass the log frequency as a parameter to the process and check that it
151 // was set correctly.
152 auto correct_log_freq{9000};
153 SECTION("Single argument to python script") {
154 args[0] = (char *)"9000";
155 auto config{framework::config::run("ldmxcfg.Process.lastProcess",
156 config_file_name_arg, args, 1)};
157 p = std::make_unique<framework::Process>(config);
158 CHECK(p->getLogFrequency() == correct_log_freq);
159 }
160
161 // add a malformed parameter to test failing
162 in_file.open(config_file_name.c_str(), std::ios::in | std::ios::binary);
163
164 out_file.open(config_file_name_arg, std::ios::out | std::ios::binary);
165 out_file << in_file.rdbuf();
166 out_file << "p.sequence[0].bad_param = ('tuples','are','not','supported')"
167 << std::endl;
168
169 in_file.close();
170 out_file.close();
171
172 // warning: this test will fail if the repr of a tuple changes format
173 SECTION("Bad parameter exception test") {
174 REQUIRE_THROWS_WITH(
175 framework::config::run("ldmxcfg.Process.lastProcess",
176 config_file_name_arg, args, 0),
177 ContainsSubstring("('tuples', 'are', 'not', 'supported')"));
178 // we need to manually close up our python interpreter
179 // because we left during an exception without closing it above
180 Py_FinalizeEx();
181 }
182}
Base classes for all user event processing components to extend.
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class which represents the process under execution.
Implements an event buffer system for storing event data.
Definition Event.h:42
Class which represents the process under execution.
Definition Process.h:36
Base class for a module which produces a data product.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
Defines a test Producer to test the passing of configuration variables.
virtual void produce(framework::Event &) override
Process the event and put new data products into it.
TestConfig(const std::string &name, framework::Process &process)
Constructor.
void configure(framework::config::Parameters &parameters) final override
Configure function.
Parameters run(const std::string &root_object, const std::string &pythonScript, char *args[], int nargs)
run the python script and extract the parameters
Definition Python.cxx:292
All classes in the ldmx-sw project use this namespace.
Definition PerfDict.cxx:45
std::unique_ptr< Process > ProcessHandle
A handle to the current process Used to pass a process from ConfigurePython to fire....
Definition Process.h:233