PDI 1.11.0

the PDI data interface

testing.h
1/*******************************************************************************
2 * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of CEA nor the names of its contributors may be used to
13 * endorse or promote products derived from this software without specific
14 * prior written permission.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 ******************************************************************************/
24
25#ifndef PDI_TESTING_H_
26#define PDI_TESTING_H_
27
28// this is a header-only file because we don't want to depend on a given version of gtest/gmock
64
65#include <algorithm>
66#include <filesystem>
67#include <random>
68#include <ranges>
69#include <type_traits>
70
71#include <gmock/gmock.h>
72#include <gtest/gtest.h>
73
74#include <paraconf.h>
75#include <pdi.h>
76
77namespace PDI {
78
82template < class T, class G >
83concept initializable_from = requires(T t, G& gen) {
84 {
85 t.init_from(gen)
86 };
87};
88
92template <class T, class G>
93concept buildable_from = std::constructible_from<T, G&> && requires(G& g) {
94 {
95 T(g)
96 };
97 {
98 ::new T(g)
99 };
100};
101
106static inline void random_init(std::uniform_random_bit_generator auto& gen, std::integral auto& t)
107{
108 using T = std::remove_reference_t<decltype(t)>;
109 t = std::uniform_int_distribution<T>(std::numeric_limits<T>::min())(gen);
110}
111
116static inline void random_init(std::uniform_random_bit_generator auto& gen, std::floating_point auto& t)
117{
118 t = std::normal_distribution<std::remove_reference_t<decltype(t)>>{}(gen);
119}
120
125static inline void random_init(std::uniform_random_bit_generator auto& gen, std::ranges::input_range auto& t)
126{
127 std::for_each(std::begin(t), std::end(t), [&](std::ranges::range_value_t<decltype(t)>& v) { random_init(gen, v); });
128}
129
134static inline void random_init(std::uniform_random_bit_generator auto& gen, initializable_from<decltype(gen)> auto& t)
135{
136 t.init_from(gen);
137}
138
143template <typename T, std::uniform_random_bit_generator G>
144requires(!std::ranges::input_range<T> && !initializable_from<T, G> && buildable_from<T, G> && std::assignable_from<T&, T &&>)
145static inline void random_init(G& gen, T& t)
146{
147 t = T(gen);
148}
149
153template <typename T, std::uniform_random_bit_generator G>
155static inline T make_random(G& gen)
156{
157 return T(gen);
158}
159
163template <typename T, std::uniform_random_bit_generator G>
164requires(!buildable_from<T, G>, std::default_initializable<T>)
165static inline T make_random(G& gen)
166{
167 T result;
168 random_init(gen, result);
169 return result;
170}
171
178// this is a header-only class because we don't want to depend on a given version of gtest/gmock
179class PdiTest: public ::testing::Test
180{
182 std::filesystem::path m_workdir;
183
185 std::filesystem::path m_tmpdir;
186
188 PC_tree_t m_conf = {PC_NODE_NOT_FOUND, nullptr, nullptr};
189
191 std::mt19937_64 m_random_generator;
192
194 static void s_pdi_errhandler(PDI_status_t status, const char* message, void* context)
195 {
196 static_cast<PdiTest*>(context)->PdiError(status, message);
197 }
198
199protected:
200 PdiTest();
201
202 ~PdiTest();
203
206 template <typename T>
207 inline T make_a()
208 {
209 return ::PDI::make_random<T>(m_random_generator);
210 }
211
219 inline void InitPdi(PC_tree_t tree);
220
225 inline void FinalizePdi();
226
227 MOCK_METHOD(void, PdiError, (PDI_status_t, char const *), (const));
228};
229
231 : m_workdir(std::filesystem::canonical(std::filesystem::current_path()))
232{
233 static thread_local auto random_generator([]() {
234 if (int gseed = ::testing::UnitTest::GetInstance()->random_seed()) {
235 return std::mt19937_64(gseed);
236 } else {
237 std::random_device source;
238 std::array<unsigned, std::mt19937_64::state_size> random_seed_data;
239 std::generate(std::begin(random_seed_data), std::end(random_seed_data), std::ref(source));
240 std::seed_seq seed(std::begin(random_seed_data), std::end(random_seed_data));
241 return std::mt19937_64(seed);
242 }
243 }());
244 auto const filename = [&]() {
245 static constexpr char const VALID_FILE_CHARS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
246 std::uniform_int_distribution<size_t> random(0, std::ranges::size(VALID_FILE_CHARS) - 1);
247 std::string filename(16, '\0');
248 std::generate(filename.begin(), filename.end(), [&]() { return VALID_FILE_CHARS[random(random_generator)]; });
249 auto&& test_info = *::testing::UnitTest::GetInstance()->current_test_info();
250 return std::string("pdi_tst_dir.") + test_info.test_suite_name() + "." + test_info.name() + "." + filename;
251 }();
252 m_tmpdir = m_workdir / filename;
253 std::filesystem::create_directory(m_tmpdir);
254 std::filesystem::current_path(m_tmpdir);
255 EXPECT_CALL(*this, PdiError(testing::_, testing::_)).Times(0);
256}
257
258inline void PdiTest::InitPdi(PC_tree_t tree)
259{
260 FinalizePdi();
261 m_conf = tree;
262 ASSERT_EQ(PC_OK, PC_status(m_conf));
263 ASSERT_EQ(PDI_OK, PDI_init(m_conf));
264 PDI_errhandler({s_pdi_errhandler, this});
265}
266
268{
269 if (!PC_status(m_conf)) {
270 ASSERT_EQ(PDI_OK, PDI_finalize());
271 EXPECT_EQ(PC_OK, PC_tree_destroy(&m_conf));
272 m_conf = {PC_NODE_NOT_FOUND, nullptr, nullptr};
273 }
274}
275
277{
278 FinalizePdi();
279 std::filesystem::current_path(m_workdir);
280 std::filesystem::remove_all(m_tmpdir);
281}
282
283
284} // namespace PDI
285
286#endif
void InitPdi(PC_tree_t tree)
Initialize PDI with the provided PC_tree.
Definition testing.h:258
PdiTest()
Definition testing.h:230
MOCK_METHOD(void, PdiError,(PDI_status_t, char const *),(const))
T make_a()
make a new repeatably randomly initialized object
Definition testing.h:207
void FinalizePdi()
Finalize PDI.
Definition testing.h:267
~PdiTest()
Definition testing.h:276
An object is buildable_from<G> if it can be constructed using the init_from member function from a re...
Definition testing.h:93
An object is initializable_from<G> if it can be initalized using the init_from member function from a...
Definition testing.h:83
PDI_errhandler_t PDI_errhandler(PDI_errhandler_t handler)
Sets the error handler to use.
PDI_status_t
Error codes of PDI.
Definition pdi.h:80
@ PDI_OK
everything went well
Definition pdi.h:82
PDI_status_t PDI_init(PC_tree_t conf)
Initializes PDI.
PDI_status_t PDI_finalize(void)
Finalizes PDI.
Definition array_datatype.h:38
Definition ref_any.h:737