From 118b45add93971693e9d339d3df3f42a11cfd6a9 Mon Sep 17 00:00:00 2001 From: Julien Bigot Date: Thu, 7 May 2026 16:15:16 +0200 Subject: [PATCH] Support include in yaml Co-authored-by: Julian Auriac --- .github/pull_request_template.md | 2 +- CHANGELOG.md | 2 + CMakeLists.txt | 8 + pdi/CMakeLists.txt | 1 + pdi/docs/Specification_tree_ref.md | 237 +++++++-------- pdi/docs/Yaml.md | 6 +- pdi/include/pdi/context.h | 2 +- pdi/include/pdi/context_proxy.h | 2 +- pdi/src/data_descriptor_impl.cxx | 8 + pdi/src/data_descriptor_impl.h | 3 + pdi/src/global_context.cxx | 270 ++++++++++++++++-- pdi/src/global_context.h | 9 +- pdi/src/plugin_store.cxx | 24 +- pdi/src/plugin_store.h | 8 +- pdi/tests/PDI_context.cxx | 56 +++- pdi/tests/mocks/context_mock.h | 2 +- tests/include_yaml_tests/AUTHORS | 2 + tests/include_yaml_tests/CHANGELOG.md | 21 ++ tests/include_yaml_tests/CMakeLists.txt | 62 ++++ tests/include_yaml_tests/LICENSE | 26 ++ tests/include_yaml_tests/cmake/runtest-dir | 18 ++ tests/include_yaml_tests/test_07.c | 94 ++++++ tests/include_yaml_tests/test_07.yml | 29 ++ .../test_07_record_data.yml | 12 + tests/include_yaml_tests/test_08.yml | 29 ++ tests/include_yaml_tests/test_08_subdata.yml | 2 + tests/include_yaml_tests/test_09.yml | 23 ++ .../include_yaml_tests/test_09_array_data.yml | 10 + .../test_09_record_data.yml | 15 + tests/include_yaml_tests/test_09_subdata.yml | 2 + tests/include_yaml_tests/test_10.yml | 28 ++ .../test_10_record_data.yml | 15 + tests/include_yaml_tests/test_10_subdata.yml | 5 + tests/include_yaml_tests/test_11.yml | 26 ++ .../test_11_hdf5_plugin.yml | 11 + .../test_11_serialize_plugin.yml | 6 + tests/include_yaml_tests/test_11_subdata.yml | 2 + 37 files changed, 918 insertions(+), 160 deletions(-) create mode 100644 tests/include_yaml_tests/AUTHORS create mode 100644 tests/include_yaml_tests/CHANGELOG.md create mode 100644 tests/include_yaml_tests/CMakeLists.txt create mode 100644 tests/include_yaml_tests/LICENSE create mode 100755 tests/include_yaml_tests/cmake/runtest-dir create mode 100644 tests/include_yaml_tests/test_07.c create mode 100644 tests/include_yaml_tests/test_07.yml create mode 100644 tests/include_yaml_tests/test_07_record_data.yml create mode 100644 tests/include_yaml_tests/test_08.yml create mode 100644 tests/include_yaml_tests/test_08_subdata.yml create mode 100644 tests/include_yaml_tests/test_09.yml create mode 100644 tests/include_yaml_tests/test_09_array_data.yml create mode 100644 tests/include_yaml_tests/test_09_record_data.yml create mode 100644 tests/include_yaml_tests/test_09_subdata.yml create mode 100644 tests/include_yaml_tests/test_10.yml create mode 100644 tests/include_yaml_tests/test_10_record_data.yml create mode 100644 tests/include_yaml_tests/test_10_subdata.yml create mode 100644 tests/include_yaml_tests/test_11.yml create mode 100644 tests/include_yaml_tests/test_11_hdf5_plugin.yml create mode 100644 tests/include_yaml_tests/test_11_serialize_plugin.yml create mode 100644 tests/include_yaml_tests/test_11_subdata.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index acef804c8..6d3cdd272 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,7 +7,7 @@ Before merging your code, please check the following: * [ ] you have added a line describing your changes to the Changelog; * [ ] you have added unit tests for any new or improved feature; * [ ] in case you updated dependencies, you have checked pdi/docs/CheckList.md; -* [ ] in case of a change in pdi.h, this same change must be reflected in no-pdi/include/pdi.h; +* [ ] in case of a change in pdi.h, this same change must be reflected in mock_pdi/pdi.h; * you have checked your code format: - [ ] you have checked that you respect all conventions specified in CONTRIBUTING.md; - [ ] you have checked that the indentation and formatting conforms to the `.clang-format`; diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ebee3cf..f56b5ab84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -161,6 +161,8 @@ and this project adheres to #### Added * Added tests for using an installed PDI +* Support an include mechanism for PDI config, + to include other YAML files into root YAML document. #### Fixed * Support multiple consecutive calls to `find_package(PDI)` diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b077743a..509c33e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -529,6 +529,14 @@ sbuild_add_module(PDI_COMBINATION_TESTS SUBSTEPS test ) +sbuild_add_module(PDI_TEST_INCLUDE_YAML + ENABLE_BUILD_FLAG BUILD_TESTING + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/include_yaml_tests" + DEPENDS PDI + INSTALL_COMMAND "" + SUBSTEPS test +) + sbuild_add_module(PDI_API_TESTS ENABLE_BUILD_FLAG BUILD_TESTING SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/api_tests" diff --git a/pdi/CMakeLists.txt b/pdi/CMakeLists.txt index b67ff5224..1a513a316 100644 --- a/pdi/CMakeLists.txt +++ b/pdi/CMakeLists.txt @@ -94,6 +94,7 @@ if("${BUILD_FORTRAN}") list(APPEND PARACONF_COMPONENTS "f90") endif() set(PDI_REQUIRED_PARACONF_VERSION 1.0) # used in PDIConfig too +# set(PDI_REQUIRED_PARACONF_VERSION 1.1) # used in PDIConfig too find_package(paraconf "${PDI_REQUIRED_PARACONF_VERSION}" REQUIRED COMPONENTS ${PARACONF_COMPONENTS}) # must match PDIConfig.cmake.in set(PDI_REQUIRED_SPDLOG_VERSION 1.9) # used in PDIConfig too find_package(spdlog "${PDI_REQUIRED_SPDLOG_VERSION}" REQUIRED) # must match PDIConfig.cmake.in diff --git a/pdi/docs/Specification_tree_ref.md b/pdi/docs/Specification_tree_ref.md index efb8ddf0b..111937c1c 100644 --- a/pdi/docs/Specification_tree_ref.md +++ b/pdi/docs/Specification_tree_ref.md @@ -9,28 +9,34 @@ The *specification tree root* is a **mapping** that contains the following keys: |key|value| |:--|:----| -|`"types"` (*optional*)|a \ref types_map_node| -|`"data"` (*optional*)|a \ref data_map_node| +|`"include"` (*optional*)|a \ref include_node| +|`"logging"` (*optional*)|a \ref logging_node| |`"metadata"` (*optional*)|a \ref data_map_node| +|`"data"` (*optional*)|a \ref data_map_node| |`"plugins"` (*optional*)|a \ref plugin_map_node| -|`"logging"` (*optional*)|a \ref logging_node| |`"plugin_path"` (*optional*)|a \ref plugin_path_map_node| +|`"types"` (*optional*)|a \ref types_map_node| |`".*"` (*optional*)| *anything* | -* the `types` section specifies user-defined datatypes -* the `data` and `metadata` sections specify the type of the data in buffers +* the `include` section specify other YAML configuration part of a same + Paraconf tree +* the `logging` section specify logger properties +* the `metadata` and `data` sections specify the type of the data in buffers exposed by the application; for `metadata`, %PDI keeps a copy while it only keeps references for `data`, * the `plugins` section specifies the list of plugins to load and their configuration, * the `plugin_path` section specifies the path to a directory where %PDI should search for plugins -* the `logging` section specify logger properties +* the `types` section specifies user-defined datatypes * additional sections are ignored. ### Example: ```{.python} +include: + - my_other_configuration_file.yml +logging: trace metadata: my_metadata: int data: @@ -43,6 +49,116 @@ plugins: mpi: #... ``` +## include {#include_node} + +An *include* is a sequence of scalars, each scalar representing a path to +a \subpage YAML configuration file. + +An *include* is fully supported in both the root YAML configuration and +further included YAML configuration. + +Examples: + +```yaml +include: ////subfile.yml +``` +Paraconf 1.1+ also enables relative path : +```yaml +include: subfile.yml +``` + +* entirely optional, without default value + + +## logging {#logging_node} + +A *logging* can be **any of**: +* a \ref logging_map_node, +* a \ref logging_level_node, + +A *logging* is fully supported in \ref root_node and +any \ref plugin_map_node "plugin_map_node". + + +## logging_level {#logging_level_node} + +A *logging_level* is a scalar which determines verbosity level. It can be set to + (from the most to the least verbose): +* `"debug"` - shows a log when a normal situation of the execution might be + useful to understand the behavior of the library, +* `"info"` - shows a log when a normal situation of the execution is likely + useful to understand the behavior of the library, +* `"warn"` - shows a log when a very likely invalid situation has been detected + by the library (user input that is technically valid, but very unusual for + example), +* `"error"` - shows a log when an invalid situation has been detected by the + library (invalid user input, invalid hardware behaviour, etc.), +* `"off"` - logs are disabled. + +Example: + +```yaml +logging: "debug" +``` + +* by default `level` is set to `info` + + +## logging_map {#logging_map_node} + +A *logging_map* is a **mapping** that contains the following keys: + +|key|value| +|:--|:----| +|`"level"` (*optional*)|a \ref logging_level_node| +|`"pattern"` (*optional*)|a logger prefix pattern of spdlog| +|`"output"` (*optional*)|a \ref logging_output_map_node| + +* spdlog pattern is a string that is parsed by spdlog library, + (see more: https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) +* %PDI introduces new special flag `%{}`, where `` represents a + \ref expression_node "string-valued $-expression"|a $-expression that will be + evaluated just after all plugins have been initialized, +* *pattern* by default is set to (where %n is `PDI` or a plugin name): + ``` + [%T][%n] *** %^%l%$: %v + ``` + for serial execution and: + ``` + [%T][%{MPI_COMM_WORLD.rank:06d}][%n] *** %^%l%$: %v + ``` + when running application with MPI/ + +Example: + +```yaml +logging: + level: "debug" + pattern: "[%{MPI_COMM_WORLD.rank:04d}][%n][%l]" +``` + + +## logging_output_map {#logging_output_map_node} + +A *logging_output_map* is a **mapping** that contains the following keys: + +|key|value| +|:--|:----| +|`"file"` (*optional*)|a path of the file where to write logs| +|`"console"` (*optional*)|`on` or `off`| + +* by default when `file` is defined, `console` is set to `off`. + +Example: + +```yaml +logging: + level: "debug" + output: + file: "test.log" + console: "on" +``` + ## array_type {#array_type_node} @@ -774,115 +890,6 @@ type: intptr ``` -## logging {#logging_node} - -A *logging* can be **any of**: -* a \ref logging_map_node, -* a \ref logging_level_node, - -A *logging* is fully supported in \ref root_node and any \ref plugin_map_node . - - -## logging_level {#logging_level_node} - -A *logging_level* is a scalar which determines verbosity level. It can be set to - (from the most to the least verbose): -* `"debug"` - shows a log when a normal situation of the execution might be - useful to understand the behavior of the library, -* `"info"` - shows a log when a normal situation of the execution is likely - useful to understand the behavior of the library, -* `"warn"` - shows a log when a very likely invalid situation has been detected - by the library (user input that is technically valid, but very unusual for - example), -* `"error"` - shows a log when an invalid situation has been detected by the - library (invalid user input, invalid hardware behaviour, etc.), -* `"off"` - logs are disabled. - -Examples: - -```yaml -logging: "debug" -``` - -* by default `level` is set to `info` - - -## logging_map {#logging_map_node} - -A *logging_map* is a **mapping** that contains the following keys: - -|key|value| -|:--|:----| -|`"level"` (*optional*)|a \ref logging_level_node| -|`"pattern"` (*optional*)|a logger prefix pattern of spdlog| -|`"output"` (*optional*)|a \ref logging_output_map_node| - -* spdlog pattern is a string that is parsed by spdlog library, - (see more: https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) -* %PDI introduces new special flag `%{}`, where `` represents a - \ref expression_node "string-valued $-expression"|a $-expression that will be - evaluated just after all plugins have been initialized, -* *pattern* by default is set to (where %n is `PDI` or a plugin name): - ``` - [%T][%n] *** %^%l%$: %v - ``` - for serial execution and: - ``` - [%T][%{MPI_COMM_WORLD.rank:06d}][%n] *** %^%l%$: %v - ``` - when running application with MPI/ - -Example: - -```yaml -logging: - level: "debug" - pattern: "[%{MPI_COMM_WORLD.rank:04d}][%n][%l]" -``` - - -## logging_output_map {#logging_output_map_node} - -A *logging_output_map* is a **mapping** that contains the following keys: - -|key|value| -|:--|:----| -|`"file"` (*optional*)|a path of the file where to write logs| -|`"console"` (*optional*)|`on` or `off`| - -* by default when `file` is defined, `console` is set to `off`. - -Example: - -```yaml -logging: - level: "debug" - output: - file: "test.log" - console: "on" -``` - -### Example: - -```{.python} -type: struct -members: - - my_char: char -``` - -```{.python} -type: struct -members: - - my_long: int64 - - my_array: - type: array - subtype: int64 - size: [10, 10] -``` - -See \ref struct_type_node for more examples. - - ## logical_type {#logical_type_node} A *logical_type* is a **mapping** that contains the following keys: diff --git a/pdi/docs/Yaml.md b/pdi/docs/Yaml.md index 81e87a927..e7b129d72 100644 --- a/pdi/docs/Yaml.md +++ b/pdi/docs/Yaml.md @@ -104,9 +104,9 @@ complete syntax). ## YAML Parsing with Paraconf -The PDI_init function gets as parameter a tree with `logging`, `data`, -`metadata` and `plugins` maps defined in its root. User can define its own -values in yaml and pass to %PDI only the subtree: +The PDI_init function gets as parameter a tree with `logging`, `include`, +`data`, `metadata` and `plugins` maps defined in its root. User can define its +own values in yaml and pass to %PDI only the subtree: ```yaml duration: 0.75 diff --git a/pdi/include/pdi/context.h b/pdi/include/pdi/context.h index b24b2f6cd..8d5ae0e8e 100644 --- a/pdi/include/pdi/context.h +++ b/pdi/include/pdi/context.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * diff --git a/pdi/include/pdi/context_proxy.h b/pdi/include/pdi/context_proxy.h index 2cefc9ed9..f31655393 100644 --- a/pdi/include/pdi/context_proxy.h +++ b/pdi/include/pdi/context_proxy.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2021-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2019-2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * diff --git a/pdi/src/data_descriptor_impl.cxx b/pdi/src/data_descriptor_impl.cxx index 46795f9d3..7d0864dac 100644 --- a/pdi/src/data_descriptor_impl.cxx +++ b/pdi/src/data_descriptor_impl.cxx @@ -76,6 +76,14 @@ Data_descriptor_impl::Data_descriptor_impl(Global_context& ctx, const char* name , m_metadata{false} {} +Data_descriptor_impl::Data_descriptor_impl(Global_context& ctx, const char* name, PC_tree_t source_node) + : m_context{ctx} + , m_type{UNDEF_TYPE} + , m_name{name} + , m_metadata{false} + , m_source_node{source_node} +{} + Data_descriptor_impl::Data_descriptor_impl(Data_descriptor_impl&&) = default; Data_descriptor_impl::~Data_descriptor_impl() diff --git a/pdi/src/data_descriptor_impl.h b/pdi/src/data_descriptor_impl.h index 5f2119c11..49557f4eb 100644 --- a/pdi/src/data_descriptor_impl.h +++ b/pdi/src/data_descriptor_impl.h @@ -59,11 +59,14 @@ class PDI_EXPORT Data_descriptor_impl: public Data_descriptor bool m_metadata; + PC_tree_t m_source_node; ///< YAML key node of the first definition, for duplicate error reporting /** Create an empty descriptor */ Data_descriptor_impl(Global_context& ctx, const char* name); + Data_descriptor_impl(Global_context& ctx, const char* name, PC_tree_t source_node); + Data_descriptor_impl(const Data_descriptor_impl&) = delete; Data_descriptor_impl& operator= (const Data_descriptor_impl&) = delete; diff --git a/pdi/src/global_context.cxx b/pdi/src/global_context.cxx index 3e06b52a7..12550c61b 100644 --- a/pdi/src/global_context.cxx +++ b/pdi/src/global_context.cxx @@ -45,6 +45,12 @@ #include "global_context.h" +#include +#include +#include +#include + +namespace fs = std::filesystem; using std::exception; using std::forward_as_tuple; @@ -57,24 +63,178 @@ using std::unordered_map; using std::unordered_set; using std::vector; +#if defined(PARACONF_VERSION) +#if (PARACONF_UNTYPED_VERSION >= PARACONF_UNTYPED_COMPUTE_VERSION(1, 1, 0)) +#define PDI_HAS_PC_PATH 1 +#else +#define PDI_HAS_PC_PATH 0 +#endif +#else +#define PDI_HAS_PC_PATH 0 +#endif + namespace PDI { namespace { -void load_data(Context& ctx, PC_tree_t node, bool is_metadata) +void load_data(Global_context& ctx, PC_tree_t node, bool is_metadata) { int map_len = len(node); - for (int map_id = 0; map_id < map_len; ++map_id) { - Data_descriptor& dsc = ctx.desc(to_string(PC_get(node, "{%d}", map_id)).c_str()); + PC_tree_t key_node = PC_get(node, "{%d}", map_id); + Data_descriptor& dsc = ctx.make_and_check_descriptor(key_node); // Both defining a descriptor and checking for duplicate dsc.metadata(is_metadata); dsc.default_type(ctx.datatype(PC_get(node, "<%d>", map_id))); } - if (is_metadata) { - ctx.logger().trace("Loaded {} metadata", map_len); - } else { - ctx.logger().trace("Loaded {} data", map_len); + ctx.logger().trace("Loaded {} {}", map_len, is_metadata ? "metadata" : "data"); +} + +// Forward definition, used by collect_ordered_nodes_impl() below +void collect_ordered_nodes( + Context& ctx, + PC_tree_t conf, + std::unordered_set& globally_loaded, + std::unordered_set& include_chain, + std::vector>& ordered_nodes, + const std::string& known_path = {}, + PC_tree_t include_directive = {}, + const std::string& parent_id = "" +); + +/// Definition of collect_ordered_nodes() to handle local no_path_counter +void collect_ordered_nodes_impl( + Context& ctx, + PC_tree_t conf, + std::unordered_set& globally_loaded, ///< all files loaded so far across all branches, to detect diamond includes + std::unordered_set& include_chain, ///< files currently open in the active include branch, to detect circular includes + std::vector>& ordered_nodes, + std::size_t& no_path_counter, // to be sure that we do not reuse a unique counter/id, in the case we do not have paths + const std::string& known_path, + PC_tree_t include_directive, // exact line + const std::string& parent_id // includer id +) +{ + // ── Step 1: identity ───────────────────────────────────────────────────── + std::string current_id; + bool has_real_path = false; + + if (!known_path.empty()) { + current_id = known_path; + has_real_path = true; + } +#if PDI_HAS_PC_PATH + else + { + const char* raw_path = PC_path(conf); + if (raw_path && fs::exists(raw_path)) { + current_id = fs::canonical(raw_path).string(); + has_real_path = true; + } + } +#endif + if (!has_real_path) { + current_id = ""; } + + // ── Step 2: circular detection ─────────────────────────────────────────── + if (has_real_path && include_chain.count(current_id)) { + if (!parent_id.empty()) { + throw Spectree_error( + include_directive, + "Circular include detected: '{}' is already being loaded (included from '{}')", + current_id, + parent_id + ); + } else { + throw Spectree_error(include_directive, "Circular include detected: '{}' is already being loaded", current_id); + } + } + + // ── Step 3: diamond detection ──────────────────────────────────────────── + if (globally_loaded.count(current_id)) { + if (!parent_id.empty()) { + // m_logger.warn("Diamond include: '{}' has already been loaded, skipping (included again from '{}')", current_id, parent_id); + ctx.logger().warn("Diamond include: '{}' has already been loaded, skipping (included again from '{}')", current_id, parent_id); + } else { + // m_logger.warn("Diamond include: '{}' has already been loaded, skipping", current_id); + ctx.logger().warn("Diamond include: '{}' has already been loaded, skipping", current_id); + } + return; + } + + include_chain.insert(current_id); + globally_loaded.insert(current_id); + + // ── Step 4: recurse into includes (post-order: children before parent) ─── + PC_tree_t includes = PC_get(conf, ".include"); + if (!PC_status(includes)) { + PDI::each(includes, [&](PC_tree_t node) { + // `node` is the scalar YAML node of the include entry + // Spectree_error(node, ...) gives the exact line of the offending directive + const std::string inc = PDI::to_string(node); + const bool is_absolute = fs::path(inc).is_absolute(); + +#if PDI_HAS_PC_PATH + if (!is_absolute && !has_real_path) { + throw Spectree_error( + node, + "Relative include '{}' cannot be resolved: " + "the root config was parsed from a string (no file path available)", + inc + ); + } + fs::path full_path = is_absolute ? fs::path(inc) : fs::path(fs::path(current_id).parent_path()) / inc; +#else + if (!is_absolute) { + throw Spectree_error(node, + "Relative include '{}' is not supported: " + "Paraconf >= 1.1 is required for relative path resolution", + inc + ); + } + fs::path full_path = fs::path(inc); +#endif + + if (!fs::exists(full_path)) { + // has_real_path tells us whether we can name the includer file + if (has_real_path) { + throw Spectree_error(node, "Included file not found: '{}' (included from '{}')", full_path.string(), current_id); + } else { + throw Spectree_error(node, "Included file not found: '{}'", full_path.string()); + } + } + full_path = fs::canonical(full_path); + + PC_tree_t sub = PC_parse_path(full_path.string().c_str()); + if (PC_status(sub) != PC_OK) { + throw Spectree_error(node, "Failed to parse included file: '{}'", full_path.string()); + } + + collect_ordered_nodes(ctx, sub, globally_loaded, include_chain, ordered_nodes, full_path.string(), node, current_id); + }); + } + + // ── Step 5: append this node after its children (post-order) ──────────── + ordered_nodes.emplace_back(current_id, conf); + include_chain.erase(current_id); +} + +/// Traverses the include tree once in post-order (deepest includes first), +/// filling `ordered_nodes` with (canonical_id, PC_tree_t) pairs. +/// Diamond/circular detection is handled here. +void collect_ordered_nodes( + Context& ctx, + PC_tree_t conf, + std::unordered_set& globally_loaded, + std::unordered_set& include_chain, + std::vector>& ordered_nodes, + const std::string& known_path, + PC_tree_t include_directive, + const std::string& parent_id +) +{ + std::size_t no_path_counter = 0; + collect_ordered_nodes_impl(ctx, conf, globally_loaded, include_chain, ordered_nodes, no_path_counter, known_path, include_directive, parent_id); } } // namespace @@ -109,31 +269,12 @@ Global_context::Global_context(PC_tree_t conf) { // load basic datatypes Datatype_template::load_basic_datatypes(*this); - // load user datatypes - Datatype_template::load_user_datatypes(*this, PC_get(conf, ".types")); - m_plugins.load_plugins(); + load_pdi_config(conf); // single tree traversal + two flat sub-passes (one for the plugins, one for the types/metadata/data) // evaluate pattern after loading plugins m_logger.evaluate_pattern(*this); - // no metadata is not an error - PC_tree_t metadata = PC_get(conf, ".metadata"); - if (!PC_status(metadata)) { - load_data(*this, metadata, true); - } else { - m_logger.debug("Metadata is not defined in specification tree"); - } - - // no data is spurious, but not an error - PC_tree_t data = PC_get(conf, ".data"); - if (!PC_status(data)) { - load_data(*this, data, false); - } else { - m_logger.warn("Data is not defined in specification tree"); - } - - m_callbacks.call_init_callbacks(); m_logger.info("Initialization successful"); } @@ -228,6 +369,81 @@ void Global_context::finalize_and_exit() exit(0); } +void Global_context::load_pdi_config(PC_tree_t conf) +{ + std::unordered_set globally_loaded; + std::unordered_set include_chain; + std::vector> ordered_nodes; + collect_ordered_nodes(*this, conf, globally_loaded, include_chain, ordered_nodes); + + // Sub-pass A: user types first, plugins may reference them during initialization + for (auto& [id, node]: ordered_nodes) + Datatype_template::load_user_datatypes(*this, PC_get(node, ".types")); + + // Sub-pass B: register and load plugins, now that user types are available + for (auto& [id, node]: ordered_nodes) + m_plugins.register_plugins(node); + m_plugins.load_plugins(); + + // Sub-pass C: metadata, data + for (auto& [id, node]: ordered_nodes) { + if (auto m = PC_get(node, ".metadata"); !PC_status(m)) load_data(*this, m, true); + if (auto d = PC_get(node, ".data"); !PC_status(d)) load_data(*this, d, false); + } +} + +Data_descriptor& Global_context::make_and_check_descriptor(PC_tree_t key_node) +{ + std::string descriptor_name = to_string(key_node); + + auto [descriptor_entry, is_new_entry] = m_descriptors.emplace( + descriptor_name, + std::unique_ptr(new Data_descriptor_impl(*this, descriptor_name.c_str(), key_node)) + ); + + if (!is_new_entry) { + // first_definition_node: from the deepest included file (first encountered in post-order) + PC_tree_t first_definition_node = static_cast(*descriptor_entry->second).m_source_node; + + // redefinition_node: from the parent/root config (encountered later in post-order) + PC_tree_t redefinition_node = key_node; + +#if PDI_HAS_PC_PATH + const char* first_definition_path = PC_path(first_definition_node); + const char* redefinition_path = PC_path(redefinition_node); + bool first_has_path = first_definition_path && fs::exists(first_definition_path); + bool redef_has_path = redefinition_path && fs::exists(redefinition_path); + + if (first_has_path && redef_has_path) { + throw Spectree_error( + redefinition_node, + "Duplicate definition of '{}', first defined in '{}', then redefined in '{}'", + descriptor_name, + redefinition_path, // root/parent file = first from user's perspective + first_definition_path // deepest included file = redefinition from user's perspective + ); + } else if (first_has_path) { + throw Spectree_error( + redefinition_node, + "Duplicate definition of '{}', first defined in a string config, then redefined in '{}'", + descriptor_name, + first_definition_path + ); + } else if (redef_has_path) { + throw Spectree_error( + redefinition_node, + "Duplicate definition of '{}', first defined in '{}', then redefined in a string config", + descriptor_name, + redefinition_path + ); + } +#endif + throw Spectree_error(redefinition_node, "Duplicate definition of '{}'", descriptor_name); + } + + return *descriptor_entry->second; +} + Global_context::~Global_context() { m_logger.info("Finalization"); diff --git a/pdi/src/global_context.h b/pdi/src/global_context.h index 42e6a560e..6d1189568 100644 --- a/pdi/src/global_context.h +++ b/pdi/src/global_context.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2015-2025 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -26,12 +26,14 @@ #ifndef PDI_GLOBAL_CONTEXT_H_ #define PDI_GLOBAL_CONTEXT_H_ +#include #include #include #include #include #include #include +#include #include "pdi/pdi_fwd.h" #include "pdi/callbacks.h" @@ -127,6 +129,11 @@ class PDI_EXPORT Global_context: public Context void finalize_and_exit() override; + void load_pdi_config(PC_tree_t conf); + + /// Creates a descriptor for `key_node`, throwing Spectree_error if already defined. + Data_descriptor& make_and_check_descriptor(PC_tree_t key_node); + ~Global_context() override; }; diff --git a/pdi/src/plugin_store.cxx b/pdi/src/plugin_store.cxx index 63cdb4987..4c9a53c11 100644 --- a/pdi/src/plugin_store.cxx +++ b/pdi/src/plugin_store.cxx @@ -241,14 +241,8 @@ Plugin_store::Plugin_store(Context& ctx, PC_tree_t conf) : m_ctx(ctx) { initialize_path(PC_get(conf, ".plugin_path")); - - // pre-load the plugins - int nb_plugins = len(PC_get(conf, ".plugins"), 0); - m_ctx.logger().trace("Loading {} plugin(s)", nb_plugins); - for (int plugin_id = 0; plugin_id < nb_plugins; ++plugin_id) { - string plugin_name = to_string(PC_get(conf, ".plugins{%d}", plugin_id)); - m_plugins.emplace(plugin_name, make_shared(m_ctx, *this, plugin_name, PC_get(conf, ".plugins<%d>", plugin_id))); - } + // Plugin registration is deferred to register_plugins(), + // called for each node (root included) by load_pdi_config_impl(). } void Plugin_store::load_plugins() @@ -269,4 +263,18 @@ void Plugin_store::load_plugins() rethrow_with_context(errors, ""); } +void Plugin_store::register_plugins(PC_tree_t conf) +{ + int nb_plugins = len(PC_get(conf, ".plugins"), 0); + if (nb_plugins > 0) m_ctx.logger().trace("Registering {} plugin(s)", nb_plugins); + for (int plugin_id = 0; plugin_id < nb_plugins; ++plugin_id) { + string plugin_name = to_string(PC_get(conf, ".plugins{%d}", plugin_id)); + if (m_plugins.count(plugin_name)) { + m_ctx.logger().warn("Plugin `{}' already registered, skipping", plugin_name); + continue; + } + m_plugins.emplace(plugin_name, make_shared(m_ctx, *this, plugin_name, PC_get(conf, ".plugins<%d>", plugin_id))); + } +} + } // namespace PDI diff --git a/pdi/src/plugin_store.h b/pdi/src/plugin_store.h index ea5095911..577bfcc13 100644 --- a/pdi/src/plugin_store.h +++ b/pdi/src/plugin_store.h @@ -1,6 +1,6 @@ /******************************************************************************* * Copyright (C) 2018 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) - * Copyright (C) 2020 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2020-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -124,10 +124,16 @@ class Plugin_store * \param conf the configuration specifying the plugin path & list of plugins to load */ Plugin_store(Context& ctx, PC_tree_t conf); + // Plugin_store(Context& ctx); /** Actually load the plugins */ void load_plugins(); + + /** Register plugins from an additional config node (e.g. an included file) + * Actual loading still happens in load_plugins() + */ + void register_plugins(PC_tree_t conf); }; } // namespace PDI diff --git a/pdi/tests/PDI_context.cxx b/pdi/tests/PDI_context.cxx index 9f549ca42..be89983d6 100644 --- a/pdi/tests/PDI_context.cxx +++ b/pdi/tests/PDI_context.cxx @@ -1,6 +1,6 @@ /******************************************************************************* * Copyright (C) 2018 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) - * Copyright (C) 2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -298,3 +298,57 @@ TEST_F(ContextTest, iterator_operator_equal_equal) ASSERT_EQ(counter_false, 2); } } + +/* + * Name: ContextTest.check_duplicate + * + * Tested functions: PDI::Context::check_duplicate + * + * Description: Checks if duplicate definitions + * throw System_error. + */ +TEST_F(ContextTest, check_duplicate) +{ + PC_tree_t conf_a = PC_parse_string("variable_A: int"); + PC_tree_t conf_b = PC_parse_string("variable_B: int\nvariable_C: int"); + + auto* ctx = static_cast(this->test_context.get()); + + // Test 1: First definition should succeed + ASSERT_NO_THROW(ctx->make_and_check_descriptor(PC_get(conf_a, "{0}"))); + + // Test 2: Redefining the same name should throw Spectree_error + EXPECT_THROW(ctx->make_and_check_descriptor(PC_get(conf_a, "{0}")), Spectree_error); + + // Test 3: Different names should both succeed + ASSERT_NO_THROW(ctx->make_and_check_descriptor(PC_get(conf_b, "{0}"))); // variable_B + ASSERT_NO_THROW(ctx->make_and_check_descriptor(PC_get(conf_b, "{1}"))); // variable_C + + PC_tree_destroy(&conf_a); + PC_tree_destroy(&conf_b); +} + +/* + * Name: ContextTest.load_pdi_config_duplicate_data_via_inclusion + * + * Tested functions: PDI::load_pdi_config (recursive inclusion), through multiple PC_parse_string + * + * Description: Checks if including Paraconf trees with duplicate data fields + * throws System_error (simulates test_07.yml and test_07_data.yml). + */ +TEST_F(ContextTest, load_pdi_config_duplicate_data_via_inclusion) +{ + PC_tree_t main_conf = PC_parse_string("data: {scalar_data: int}"); + PC_tree_t included_conf = PC_parse_string("data: {scalar_data: double}"); + + auto* ctx = static_cast(this->test_context.get()); + + // Test 1: Defining a variable is not an issue + ASSERT_NO_THROW(ctx->load_pdi_config(main_conf)); + + // Test 2: Defining the same variable again in a second load is an issue and should throw System_error + EXPECT_THROW(ctx->load_pdi_config(included_conf), Spectree_error); + + PC_tree_destroy(&main_conf); + PC_tree_destroy(&included_conf); +} diff --git a/pdi/tests/mocks/context_mock.h b/pdi/tests/mocks/context_mock.h index 566c855c1..8473baa3a 100644 --- a/pdi/tests/mocks/context_mock.h +++ b/pdi/tests/mocks/context_mock.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2021-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2018-2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * diff --git a/tests/include_yaml_tests/AUTHORS b/tests/include_yaml_tests/AUTHORS new file mode 100644 index 000000000..eb33207f5 --- /dev/null +++ b/tests/include_yaml_tests/AUTHORS @@ -0,0 +1,2 @@ +Julian Auriac - CEA (julian.auriac@cea.fr) +* Add YAML's include tests diff --git a/tests/include_yaml_tests/CHANGELOG.md b/tests/include_yaml_tests/CHANGELOG.md new file mode 100644 index 000000000..65b5be64e --- /dev/null +++ b/tests/include_yaml_tests/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog for PDI API tests project +All notable changes to the test_api project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + + +## [Unreleased] + +### Added +* Add YAML's include tests + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + diff --git a/tests/include_yaml_tests/CMakeLists.txt b/tests/include_yaml_tests/CMakeLists.txt new file mode 100644 index 000000000..d15102648 --- /dev/null +++ b/tests/include_yaml_tests/CMakeLists.txt @@ -0,0 +1,62 @@ +#============================================================================= +# Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of CEA nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific +# prior written permission. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +#============================================================================= + +cmake_minimum_required(VERSION 3.22...4.2) +project(pdi_include_yaml LANGUAGES C) + +set(RUNTEST_DIR "${CMAKE_CURRENT_LIST_DIR}/../cmake/runtest-dir") + +include(CTest) + +find_package(paraconf REQUIRED COMPONENTS C) +find_package(PDI REQUIRED COMPONENTS C) + +if("${BUILD_DECL_HDF5_PLUGIN}" AND "${BUILD_SERIALIZE_PLUGIN}") + +add_executable(test_simple_yaml_include test_07.c) +target_link_libraries(test_simple_yaml_include paraconf::paraconf PDI::PDI_C) +add_test(NAME test_simple_yaml_include COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_07.yml") +set_tests_properties(test_simple_yaml_include PROPERTIES DISABLED TRUE) # Disabled as missing absolute paths on online CI + +add_executable(test_duplicate_yaml_include test_07.c) +target_link_libraries(test_duplicate_yaml_include paraconf::paraconf PDI::PDI_C) +add_test(NAME test_duplicate_yaml_include COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_08.yml") +set_tests_properties(test_duplicate_yaml_include PROPERTIES DISABLED TRUE) # Disabled as expected to fail on correct behaviour (and also missing absolute paths on online CI) + +add_executable(test_diamond_yaml_include test_07.c) +target_link_libraries(test_diamond_yaml_include paraconf::paraconf PDI::PDI_C) +add_test(NAME test_diamond_yaml_include COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_09.yml") +set_tests_properties(test_diamond_yaml_include PROPERTIES DISABLED TRUE) # Disabled as missing absolute paths on online CI + +add_executable(test_circular_yaml_include test_07.c) +target_link_libraries(test_circular_yaml_include paraconf::paraconf PDI::PDI_C) +add_test(NAME test_circular_yaml_include COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_10.yml") +set_tests_properties(test_circular_yaml_include PROPERTIES DISABLED TRUE) # Disabled as expected to fail on correct behaviour (and also missing absolute paths on online CI) + +add_executable(test_plugins_yaml_include test_07.c) +target_link_libraries(test_plugins_yaml_include paraconf::paraconf PDI::PDI_C) +add_test(NAME test_plugins_yaml_include COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_11.yml") +set_tests_properties(test_plugins_yaml_include PROPERTIES DISABLED TRUE) # Disabled as missing absolute paths on online CI + +endif("${BUILD_DECL_HDF5_PLUGIN}" AND "${BUILD_SERIALIZE_PLUGIN}") diff --git a/tests/include_yaml_tests/LICENSE b/tests/include_yaml_tests/LICENSE new file mode 100644 index 000000000..5f4aa28fb --- /dev/null +++ b/tests/include_yaml_tests/LICENSE @@ -0,0 +1,26 @@ +BSD 3-Clause License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/include_yaml_tests/cmake/runtest-dir b/tests/include_yaml_tests/cmake/runtest-dir new file mode 100755 index 000000000..e617dbc92 --- /dev/null +++ b/tests/include_yaml_tests/cmake/runtest-dir @@ -0,0 +1,18 @@ +#!/bin/bash + +WKDIR="$(mktemp -p "${PWD}" -d run-XXXXXXXXXX)" +function finish { + rm -rf "${WKDIR}" +} +trap finish EXIT + +while [ '--runtest-dir-copy-file' = "$1" ] +do + shift + cp "$1" "${WKDIR}/" + shift +done + +cd "${WKDIR}" + +"$@" diff --git a/tests/include_yaml_tests/test_07.c b/tests/include_yaml_tests/test_07.c new file mode 100644 index 000000000..86287ca75 --- /dev/null +++ b/tests/include_yaml_tests/test_07.c @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include + +struct Record_data { + int a; + int* b; +} typedef Record_data; + +int main(int argc, char* argv[]) +{ + PDI_init(PC_parse_path(argv[1])); + int input = 0; + PDI_expose("input", &input, PDI_OUT); + + int scalar_data = 42; + PDI_expose("scalar_data", &scalar_data, PDI_OUT); + + int scalar_subdata = 43; + PDI_expose("scalar_subdata", &scalar_subdata, PDI_OUT); + + int array_data[8]; + for (int i = 0; i < 8; i++) { + array_data[i] = 42 + i; + } + PDI_expose("array_data", array_data, PDI_OUT); + + Record_data record_data; + record_data.a = 50; + int b = 51; + record_data.b = &b; + PDI_expose("record_data", &record_data, PDI_OUT); + + input = 1; + PDI_expose("input", &input, PDI_OUT); + + int scalar_data_read; + PDI_expose("scalar_data", &scalar_data_read, PDI_IN); + printf("%d ?== %d\n", scalar_data, scalar_data_read); + if (scalar_data != scalar_data_read) { + fprintf(stderr, "Assertion failed: %d != %d\n", scalar_data, scalar_data_read); + exit(EXIT_FAILURE); + } + + int scalar_subdata_read; + PDI_expose("scalar_subdata", &scalar_subdata_read, PDI_IN); + printf("%d ?== %d\n", scalar_subdata, scalar_subdata_read); + if (scalar_subdata != scalar_subdata_read) { + fprintf(stderr, "Assertion failed: %d != %d\n", scalar_subdata, scalar_subdata_read); + exit(EXIT_FAILURE); + } + + int array_data_read[8]; + PDI_expose("array_data", array_data_read, PDI_IN); + for (int i = 2; i < 6; i++) { + printf("[%d] %d ?== %d\n", i, array_data[i], array_data_read[i]); + assert(array_data[i] == array_data_read[i]); + } + + int b_read; + Record_data record_data_read; + record_data_read.b = &b_read; + PDI_expose("record_data", &record_data_read, PDI_IN); + printf("%d ?== %d\n", record_data.a, record_data_read.a); + assert(record_data.a == record_data_read.a); + printf("%d ?== %d\n", b, b_read); + assert(b == b_read); + + PDI_finalize(); + return 0; +} diff --git a/tests/include_yaml_tests/test_07.yml b/tests/include_yaml_tests/test_07.yml new file mode 100644 index 000000000..d152fef30 --- /dev/null +++ b/tests/include_yaml_tests/test_07.yml @@ -0,0 +1,29 @@ +include: + - test_07_record_data.yml + +logging: trace +metadata: + input: int +data: + scalar_data: int + scalar_subdata: int + array_data: + type: array + subtype: int + size: 8 + subsize: 4 + start: 2 + +plugins: + serialize: + scalar_data: scalar_data_serialized + scalar_subdata: scalar_subdata_serialized + array_data: array_data_serialized + record_data: record_data_serialized + decl_hdf5: + - file: test_07.h5 + when: $input=0 + write: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] + - file: test_07.h5 + when: $input=1 + read: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] diff --git a/tests/include_yaml_tests/test_07_record_data.yml b/tests/include_yaml_tests/test_07_record_data.yml new file mode 100644 index 000000000..cbf3393e9 --- /dev/null +++ b/tests/include_yaml_tests/test_07_record_data.yml @@ -0,0 +1,12 @@ +data: + record_data: + type: record + buffersize: 16 + members: + a: + disp: 0 + type: int + b: + disp: 8 + type: pointer + subtype: int diff --git a/tests/include_yaml_tests/test_08.yml b/tests/include_yaml_tests/test_08.yml new file mode 100644 index 000000000..e6a748d7e --- /dev/null +++ b/tests/include_yaml_tests/test_08.yml @@ -0,0 +1,29 @@ +include: + - test_08_subdata.yml + +logging: trace +metadata: + input: int +data: + scalar_data: int + scalar_subdata: int + array_data: + type: array + subtype: int + size: 8 + subsize: 4 + start: 2 + +plugins: + serialize: + scalar_data: scalar_data_serialized + scalar_subdata: scalar_subdata_serialized + array_data: array_data_serialized + record_data: record_data_serialized + decl_hdf5: + - file: test_08.h5 + when: $input=0 + write: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] + - file: test_08.h5 + when: $input=1 + read: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] diff --git a/tests/include_yaml_tests/test_08_subdata.yml b/tests/include_yaml_tests/test_08_subdata.yml new file mode 100644 index 000000000..23bb9259e --- /dev/null +++ b/tests/include_yaml_tests/test_08_subdata.yml @@ -0,0 +1,2 @@ +data: + scalar_subdata: double diff --git a/tests/include_yaml_tests/test_09.yml b/tests/include_yaml_tests/test_09.yml new file mode 100644 index 000000000..302eb13c5 --- /dev/null +++ b/tests/include_yaml_tests/test_09.yml @@ -0,0 +1,23 @@ +include: + - test_09_array_data.yml + - test_09_record_data.yml + +logging: trace +metadata: + input: int +data: + scalar_data: int + +plugins: + serialize: + scalar_data: scalar_data_serialized + scalar_subdata: scalar_subdata_serialized + array_data: array_data_serialized + record_data: record_data_serialized + decl_hdf5: + - file: test_09.h5 + when: $input=0 + write: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] + - file: test_09.h5 + when: $input=1 + read: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] diff --git a/tests/include_yaml_tests/test_09_array_data.yml b/tests/include_yaml_tests/test_09_array_data.yml new file mode 100644 index 000000000..40e217db2 --- /dev/null +++ b/tests/include_yaml_tests/test_09_array_data.yml @@ -0,0 +1,10 @@ +include: + - test_09_subdata.yml + +data: + array_data: + type: array + subtype: int + size: 8 + subsize: 4 + start: 2 diff --git a/tests/include_yaml_tests/test_09_record_data.yml b/tests/include_yaml_tests/test_09_record_data.yml new file mode 100644 index 000000000..dfd973140 --- /dev/null +++ b/tests/include_yaml_tests/test_09_record_data.yml @@ -0,0 +1,15 @@ +include: + - test_09_subdata.yml + +data: + record_data: + type: record + buffersize: 16 + members: + a: + disp: 0 + type: int + b: + disp: 8 + type: pointer + subtype: int diff --git a/tests/include_yaml_tests/test_09_subdata.yml b/tests/include_yaml_tests/test_09_subdata.yml new file mode 100644 index 000000000..c94cd455d --- /dev/null +++ b/tests/include_yaml_tests/test_09_subdata.yml @@ -0,0 +1,2 @@ +data: + scalar_subdata: int diff --git a/tests/include_yaml_tests/test_10.yml b/tests/include_yaml_tests/test_10.yml new file mode 100644 index 000000000..b38ebc7f8 --- /dev/null +++ b/tests/include_yaml_tests/test_10.yml @@ -0,0 +1,28 @@ +include: + - test_10_record_data.yml + +logging: trace +metadata: + input: int +data: + scalar_data: int + array_data: + type: array + subtype: int + size: 8 + subsize: 4 + start: 2 + +plugins: + serialize: + scalar_data: scalar_data_serialized + scalar_subdata: scalar_subdata_serialized + array_data: array_data_serialized + record_data: record_data_serialized + decl_hdf5: + - file: test_10.h5 + when: $input=0 + write: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] + - file: test_10.h5 + when: $input=1 + read: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] diff --git a/tests/include_yaml_tests/test_10_record_data.yml b/tests/include_yaml_tests/test_10_record_data.yml new file mode 100644 index 000000000..53e50994e --- /dev/null +++ b/tests/include_yaml_tests/test_10_record_data.yml @@ -0,0 +1,15 @@ +include: + - test_10_subdata.yml + +data: + record_data: + type: record + buffersize: 16 + members: + a: + disp: 0 + type: int + b: + disp: 8 + type: pointer + subtype: int diff --git a/tests/include_yaml_tests/test_10_subdata.yml b/tests/include_yaml_tests/test_10_subdata.yml new file mode 100644 index 000000000..c9a5608a8 --- /dev/null +++ b/tests/include_yaml_tests/test_10_subdata.yml @@ -0,0 +1,5 @@ +include: + - test_10.yml + +data: + scalar_subdata: int diff --git a/tests/include_yaml_tests/test_11.yml b/tests/include_yaml_tests/test_11.yml new file mode 100644 index 000000000..e382eab0d --- /dev/null +++ b/tests/include_yaml_tests/test_11.yml @@ -0,0 +1,26 @@ +include: + - test_11_serialize_plugin.yml + - test_11_hdf5_plugin.yml + +logging: trace +metadata: + input: int +data: + scalar_data: int + array_data: + type: array + subtype: int + size: 8 + subsize: 4 + start: 2 + record_data: + type: record + buffersize: 16 + members: + a: + disp: 0 + type: int + b: + disp: 8 + type: pointer + subtype: int diff --git a/tests/include_yaml_tests/test_11_hdf5_plugin.yml b/tests/include_yaml_tests/test_11_hdf5_plugin.yml new file mode 100644 index 000000000..bc09459bc --- /dev/null +++ b/tests/include_yaml_tests/test_11_hdf5_plugin.yml @@ -0,0 +1,11 @@ +include: + - test_11_subdata.yml + +plugins: + decl_hdf5: + - file: test_11.h5 + when: $input=0 + write: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] + - file: test_11.h5 + when: $input=1 + read: [scalar_data_serialized, scalar_subdata_serialized, array_data_serialized, record_data_serialized] diff --git a/tests/include_yaml_tests/test_11_serialize_plugin.yml b/tests/include_yaml_tests/test_11_serialize_plugin.yml new file mode 100644 index 000000000..ed8702ade --- /dev/null +++ b/tests/include_yaml_tests/test_11_serialize_plugin.yml @@ -0,0 +1,6 @@ +plugins: + serialize: + scalar_data: scalar_data_serialized + scalar_subdata: scalar_subdata_serialized + array_data: array_data_serialized + record_data: record_data_serialized \ No newline at end of file diff --git a/tests/include_yaml_tests/test_11_subdata.yml b/tests/include_yaml_tests/test_11_subdata.yml new file mode 100644 index 000000000..c94cd455d --- /dev/null +++ b/tests/include_yaml_tests/test_11_subdata.yml @@ -0,0 +1,2 @@ +data: + scalar_subdata: int