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/ConfigurePython.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.getParameter<int>("test_int") == 9);
53 CHECK(parameters.getParameter<double>("test_double") == Approx(7.7));
54 CHECK(parameters.getParameter<std::string>("test_string") == "Yay!");
55
56 // Check dictionary
57 auto test_dict{
58 parameters.getParameter<framework::config::Parameters>("test_dict")};
59 CHECK(test_dict.getParameter<int>("one") == 1);
60 CHECK(test_dict.getParameter<double>("two") == 2.0);
61
62 // Check int vector
63 std::vector<int> int_vect{1, 2, 3};
64 auto test_int_vec{
65 parameters.getParameter<std::vector<int>>("test_int_vec")};
66 REQUIRE(test_int_vec.size() == int_vect.size());
67 for (std::size_t i{0}; i < test_int_vec.size(); i++)
68 CHECK(test_int_vec.at(i) == int_vect.at(i));
69
70 // Check double vec
71 std::vector<double> double_vec{0.1, 0.2, 0.3};
72 auto test_double_vec{
73 parameters.getParameter<std::vector<double>>("test_double_vec")};
74 REQUIRE(test_double_vec.size() == double_vec.size());
75 for (std::size_t i{0}; i < test_double_vec.size(); i++)
76 CHECK(test_double_vec.at(i) == double_vec.at(i));
77
78 // Check string vector
79 std::vector<std::string> string_vec{"first", "second", "third"};
80 auto test_string_vec{
81 parameters.getParameter<std::vector<std::string>>("test_string_vec")};
82 REQUIRE(test_string_vec.size() == string_vec.size());
83 for (std::size_t i{0}; i < test_string_vec.size(); i++)
84 CHECK(test_string_vec.at(i) == string_vec.at(i));
85
86 // check 2d vector
87 std::vector<std::vector<int>> twod_vec{
88 {11, 12, 13}, {21, 22}, {31, 32, 33, 34}};
89 auto test_2d_vec{
90 parameters.getParameter<std::vector<std::vector<int>>>("test_2dlist")};
91 REQUIRE(test_2d_vec.size() == twod_vec.size());
92 for (std::size_t i{0}; i < twod_vec.size(); i++) {
93 REQUIRE(test_2d_vec.at(i).size() == twod_vec.at(i).size());
94 for (std::size_t j{0}; j < twod_vec.at(i).size(); j++) {
95 CHECK(test_2d_vec.at(i).at(j) == twod_vec.at(i).at(j));
96 }
97 }
98 }
99
100 // I don't do anything.
101 virtual void produce(framework::Event &) override {}
102};
103} // namespace test
104} // namespace framework
105
106DECLARE_PRODUCER_NS(framework::test, TestConfig)
107
108
118TEST_CASE("Configure Python Test", "[Framework][functionality]") {
119 const std::string config_file_name{"config_python_test_config.py"};
120
121 // Arguments to pass to ConfigurePython constructor
122 char *args[1];
123
124 // Process handle
126
127 // Run a check of the python configuration class without arguments.
128 SECTION("No arguments to python script") {
129 framework::ConfigurePython cfg(config_file_name, args, 0);
130 p = cfg.makeProcess();
131
132 CHECK(p->getPassName() == "test");
133 }
134
135 // Update the python config so we can pass the log frequency as a parameter.
136 std::ifstream in_file;
137 std::ofstream out_file;
138
139 in_file.open(config_file_name.c_str(), std::ios::in | std::ios::binary);
140
141 const std::string config_file_name_arg{
142 "/tmp/config_python_test_config_arg.py"};
143 out_file.open(config_file_name_arg, std::ios::out | std::ios::binary);
144 out_file << in_file.rdbuf();
145 out_file << "import sys" << std::endl;
146 out_file << "p.logFrequency = int(sys.argv[1])" << std::endl;
147
148 in_file.close();
149 out_file.close();
150
151 // Pass the log frequency as a parameter to the process and check that it
152 // was set correctly.
153 auto correct_log_freq{9000};
154 SECTION("Single argument to python script") {
155 args[0] = (char *)"9000";
156 framework::ConfigurePython cfg(config_file_name_arg, args, 1);
157 p = cfg.makeProcess();
158
159 CHECK(p->getLogFrequency() == correct_log_freq);
160 }
161
162 // add a malformed parameter to test failing
163 in_file.open(config_file_name.c_str(), std::ios::in | std::ios::binary);
164
165 out_file.open(config_file_name_arg, std::ios::out | std::ios::binary);
166 out_file << in_file.rdbuf();
167 out_file << "p.sequence[0].bad_param = ('tuples','are','not','supported')"
168 << std::endl;
169
170 in_file.close();
171 out_file.close();
172
173 // warning: this test will fail if the repr of a tuple changes format
174 SECTION("Bad parameter exception test") {
175 REQUIRE_THROWS_WITH(
176 std::make_unique<framework::ConfigurePython>(config_file_name_arg, args,
177 0),
178 ContainsSubstring("('tuples', 'are', 'not', 'supported')"));
179 // we need to manually close up our python interpreter
180 // because we left during an exception without closing it above
181 Py_FinalizeEx();
182 }
183}
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.
Utility class which reads/executes a python script and creates a Process object based on the input.
Implements an event buffer system for storing event data.
Definition Event.h:41
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:27
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.
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:248