PDI 1.13.0

the PDI data interface

testing.h
1/*******************************************************************************
2 * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA)
3 * Copyright (C) 2026 Julien Bigot <julien@julien-bigot.fr>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of CEA nor the names of its contributors may be used to
14 * endorse or promote products derived from this software without specific
15 * prior written permission.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 ******************************************************************************/
25
26#ifndef PDI_TESTING_H_
27#define PDI_TESTING_H_
28
29// this is a header-only file because we don't want to depend on a given version of gtest/gmock
65
66#include <algorithm>
67#include <concepts>
68#include <filesystem>
69#include <limits>
70#include <random>
71#include <ranges>
72#include <type_traits>
73
74#include <gmock/gmock.h>
75#include <gtest/gtest.h>
76
77#include <paraconf.h>
78#include <pdi.h>
79
80namespace PDI {
81
85template < class T, class G >
86concept initializable_from = requires(T t, G& gen) {
87 {
88 t.init_from(gen)
89 };
90};
91
95template <class T, class G>
96concept buildable_from = std::constructible_from<T, G&> && requires(G& g) {
97 {
98 T(g)
99 };
100 {
101 ::new T(g)
102 };
103};
104
107template <class T>
109 = std::same_as<T, short> || std::same_as<T, unsigned short> || std::same_as<T, int> || std::same_as<T, unsigned int> || std::same_as<T, long>
110 || std::same_as<T, unsigned long> || std::same_as<T, long long> || std::same_as<T, unsigned long long>;
111
116static inline void random_init(std::uniform_random_bit_generator auto& gen, distribution_safe_integral auto& t)
117{
118 using T = std::remove_reference_t<decltype(t)>;
119 t = std::uniform_int_distribution<T>(std::numeric_limits<T>::min())(gen);
120}
121
126static inline void random_init(std::uniform_random_bit_generator auto& gen, std::floating_point auto& t)
127{
128 t = std::normal_distribution<std::remove_reference_t<decltype(t)>>{}(gen);
129}
130
135static inline void random_init(std::uniform_random_bit_generator auto& gen, std::ranges::input_range auto& t)
136{
137 std::for_each(std::begin(t), std::end(t), [&](std::ranges::range_value_t<decltype(t)>& v) { random_init(gen, v); });
138}
139
144static inline void random_init(std::uniform_random_bit_generator auto& gen, initializable_from<decltype(gen)> auto& t)
145{
146 t.init_from(gen);
147}
148
153template <typename T, std::uniform_random_bit_generator G>
154requires(!std::ranges::input_range<T> && !initializable_from<T, G> && buildable_from<T, G> && std::assignable_from<T&, T &&>)
155static inline void random_init(G& gen, T& t)
156{
157 t = T(gen);
158}
159
163template <typename T, std::uniform_random_bit_generator G>
165static inline T make_random(G& gen)
166{
167 return T(gen);
168}
169
173template <typename T, std::uniform_random_bit_generator G>
174requires(!buildable_from<T, G>, std::default_initializable<T>)
175static inline T make_random(G& gen)
176{
177 T result;
178 random_init(gen, result);
179 return result;
180}
181
188// this is a header-only class because we don't want to depend on a given version of gtest/gmock
189class PdiTest: public ::testing::Test
190{
192 std::filesystem::path m_workdir;
193
195 std::filesystem::path m_tmpdir;
196
198 PC_tree_t m_conf = {PC_NODE_NOT_FOUND, nullptr, nullptr};
199
201 std::mt19937_64 m_random_generator;
202
204 static void s_pdi_errhandler(PDI_status_t status, const char* message, void* context)
205 {
206 static_cast<PdiTest*>(context)->PdiError(status, message);
207 }
208
209protected:
210 inline PdiTest();
211
212 inline ~PdiTest();
213
216 template <typename T>
217 inline T make_a()
218 {
219 return ::PDI::make_random<T>(m_random_generator);
220 }
221
226 inline void with_file(std::filesystem::path file) { std::filesystem::create_symlink((m_workdir / file), (m_tmpdir / file.filename())); }
227
235 inline void InitPdi(PC_tree_t tree);
236
241 inline void FinalizePdi();
242
243 MOCK_METHOD(void, PdiError, (PDI_status_t, char const *), (const));
244};
245
247 : m_workdir(std::filesystem::canonical(std::filesystem::current_path()))
248{
249 static thread_local auto random_generator([]() {
250 if (int gseed = ::testing::UnitTest::GetInstance()->random_seed()) {
251 return std::mt19937_64(gseed);
252 } else {
253 std::random_device source;
254 std::array<unsigned, std::mt19937_64::state_size> random_seed_data;
255 std::generate(std::begin(random_seed_data), std::end(random_seed_data), std::ref(source));
256 std::seed_seq seed(std::begin(random_seed_data), std::end(random_seed_data));
257 return std::mt19937_64(seed);
258 }
259 }());
260 auto const filename = [&]() {
261 static constexpr char const VALID_FILE_CHARS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
262 // -1 for closed set and -1 for ending \0 => -2
263 std::uniform_int_distribution<size_t> random(0, std::ranges::size(VALID_FILE_CHARS) - 2);
264 std::string filename(16, '\0');
265 std::generate(filename.begin(), filename.end(), [&]() { return VALID_FILE_CHARS[random(random_generator)]; });
266 auto&& test_info = *::testing::UnitTest::GetInstance()->current_test_info();
267 return std::string("pdi_tst_dir.") + test_info.test_suite_name() + "." + test_info.name() + "." + filename;
268 }();
269 m_tmpdir = m_workdir / filename;
270 std::filesystem::create_directory(m_tmpdir);
271 std::filesystem::current_path(m_tmpdir);
272 EXPECT_CALL(*this, PdiError(testing::_, testing::_)).Times(0);
273}
274
275inline void PdiTest::InitPdi(PC_tree_t tree)
276{
277 FinalizePdi();
278 ASSERT_EQ(PC_OK, PC_status(tree));
279 PDI_errhandler({s_pdi_errhandler, this});
280 if (!PDI_init(tree)) {
281 m_conf = tree;
282 }
283}
284
286{
287 if (!PC_status(m_conf)) {
288 ASSERT_EQ(PDI_OK, PDI_finalize());
289 EXPECT_EQ(PC_OK, PC_tree_destroy(&m_conf));
290 m_conf = {PC_NODE_NOT_FOUND, nullptr, nullptr};
291 }
292}
293
295{
296 FinalizePdi();
297 std::filesystem::current_path(m_workdir);
298 std::filesystem::remove_all(m_tmpdir);
299}
300
301} // namespace PDI
302
303#endif
void with_file(std::filesystem::path file)
Requires a file from the original working-directory to be present on the effective working-directory.
Definition testing.h:226
void InitPdi(PC_tree_t tree)
Initialize PDI with the provided PC_tree.
Definition testing.h:275
PdiTest()
Definition testing.h:246
MOCK_METHOD(void, PdiError,(PDI_status_t, char const *),(const))
T make_a()
make a new repeatably randomly initialized object
Definition testing.h:217
void FinalizePdi()
Finalize PDI.
Definition testing.h:285
~PdiTest()
Definition testing.h:294
An object is buildable_from<G> if it can be constructed using the init_from member function from a re...
Definition testing.h:96
Bridge the gap between integral and IntType, by disabling bool and char.
Definition testing.h:109
An object is initializable_from<G> if it can be initalized using the init_from member function from a...
Definition testing.h:86
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