diff --git a/Makefile b/Makefile index 5d7c7bc..613cf56 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ endif BUILDDIR_SHARED = $(BUILDDIR)/shared BUILDDIR_STATIC = $(BUILDDIR)/static -LIB_SRC_FILES = mem.c free.c load.c save.c util.c utf8.c +LIB_SRC_FILES = mem.c free.c load.c save.c copy.c util.c utf8.c LIB_SRC := $(addprefix src/,$(LIB_SRC_FILES)) LIB_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(LIB_SRC))) LIB_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(LIB_SRC))) @@ -116,7 +116,8 @@ ifeq ($(UNAME_S),Darwin) endif TEST_SRC_FILES = units/free.c units/load.c units/test.c units/util.c \ - units/errs.c units/file.c units/save.c units/utf8.c + units/errs.c units/file.c units/save.c units/copy.c \ + units/utf8.c TEST_SRC := $(addprefix test/,$(TEST_SRC_FILES)) TEST_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(TEST_SRC))) TEST_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(TEST_SRC))) diff --git a/include/cyaml/cyaml.h b/include/cyaml/cyaml.h index cb113a4..cc74bf7 100644 --- a/include/cyaml/cyaml.h +++ b/include/cyaml/cyaml.h @@ -609,6 +609,7 @@ typedef enum cyaml_err { CYAML_ERR_BAD_CONFIG_NULL_MEMFN, /**< Client gave NULL mem function. */ CYAML_ERR_BAD_PARAM_NULL_CONFIG, /**< Client gave NULL config arg. */ CYAML_ERR_BAD_PARAM_NULL_SCHEMA, /**< Client gave NULL schema arg. */ + CYAML_ERR_DATA_TARGET_NON_NULL, /**< Data target must be NULL ptr */ CYAML_ERR_LIBYAML_EMITTER_INIT, /**< Failed to initialise libyaml. */ CYAML_ERR_LIBYAML_PARSER_INIT, /**< Failed to initialise libyaml. */ CYAML_ERR_LIBYAML_EVENT_INIT, /**< Failed to initialise libyaml. */ @@ -1760,6 +1761,36 @@ extern cyaml_err_t cyaml_save_data( const cyaml_data_t *data, unsigned seq_count); +/** + * Copy a loaded document. + * + * This performs a deep-clone, creating a new copy of everything in the + * document, including allocations. + * + * This is a convenience function, which is exposed here just in case it + * is useful to clients. Clients would be better off writing their own copy + * function for the specific data once loaded. + * + * \note The input `data` parameter may be NULL if it is allowed by the schema. + * For example, if there is a top level mapping, containing only optional + * fields, and none of them are set, the provided data may be NULL. + * + * \param[in] config Client's CYAML configuration structure. + * \param[in] schema CYAML schema for the YAML to be copied. + * \param[in] data The caller-owned data to be copied. + * \param[in] seq_count If top level type is sequence, this should be the + * entry count, otherwise it is ignored. + * \param[out] data_out Returns the caller-owned loaded data on success. + * Untouched on failure. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +extern cyaml_err_t cyaml_copy( + const cyaml_config_t *config, + const cyaml_schema_value_t *schema, + const cyaml_data_t *data, + unsigned seq_count, + cyaml_data_t **data_out); + /** * Free data returned by a CYAML load function. * diff --git a/src/copy.c b/src/copy.c new file mode 100644 index 0000000..e17e0b6 --- /dev/null +++ b/src/copy.c @@ -0,0 +1,757 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (C) 2018-2020 Michael Drake + */ + +/** + * \file + * \brief Deep clone of client data-structure, using schema. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mem.h" +#include "data.h" +#include "util.h" + +/** + * A CYAML copy state machine stack entry. + */ +typedef struct cyaml_state { + /** Current copy state machine state. */ + enum cyaml_state_e state; + /** Schema for the expected value in this state. */ + const cyaml_schema_value_t *schema; + /** Anonymous union for schema type specific state. */ + union { + /** Additional state for \ref CYAML_STATE_IN_STREAM state. */ + struct { + /** Number of documents read in stream. */ + uint32_t doc_count; + } stream; + /** + * Additional state for \ref CYAML_STATE_IN_MAP_KEY and + * \ref CYAML_STATE_IN_MAP_VALUE states. + */ + struct { + uint8_t *data; + uint8_t *c; + const cyaml_schema_field_t *field; + } mapping; + /** Additional state for \ref CYAML_STATE_IN_SEQUENCE state. */ + struct { + uint8_t *data; + uint8_t *count_data; + uint64_t entry; + uint64_t count; + uint64_t count_size; + } sequence; + }; + const uint8_t *data; /**< Start of client value data for this state. */ + uint8_t *copy; /**< Where to write client value data for this state. */ +} cyaml_state_t; + +/** + * Internal YAML copying context. + */ +typedef struct cyaml_ctx { + const cyaml_config_t *config; /**< Settings provided by client. */ + cyaml_state_t *state; /**< Current entry in state stack, or NULL. */ + cyaml_state_t *stack; /**< State stack */ + uint32_t stack_idx; /**< Next (empty) state stack slot */ + uint32_t stack_max; /**< Current stack allocation limit. */ + unsigned seq_count; /**< Top-level sequence count. */ +} cyaml_ctx_t; + +/** + * Helper to check if schema is for a \ref CYAML_SEQUENCE type. + * + * \param[in] schema The schema entry for a type. + * \return true iff schema is for a \ref CYAML_SEQUENCE type, + * false otherwise. + */ +static inline bool cyaml__is_sequence(const cyaml_schema_value_t *schema) +{ + return ((schema->type == CYAML_SEQUENCE) || + (schema->type == CYAML_SEQUENCE_FIXED)); +} + +/** + * Ensure that the CYAML copy context has space for a new stack entry. + * + * \param[in] ctx The CYAML copying context. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__stack_ensure( + cyaml_ctx_t *ctx) +{ + cyaml_state_t *temp; + uint32_t max = ctx->stack_max + 16; + + CYAML_UNUSED(ctx); + + if (ctx->stack_idx < ctx->stack_max) { + return CYAML_OK; + } + + temp = cyaml__realloc(ctx->config, ctx->stack, 0, + sizeof(*ctx->stack) * max, false); + if (temp == NULL) { + return CYAML_ERR_OOM; + } + + ctx->stack = temp; + ctx->stack_max = max; + ctx->state = ctx->stack + ctx->stack_idx - 1; + + return CYAML_OK; +} + +/** + * Push a new entry onto the CYAML copy context's stack. + * + * \param[in] ctx The CYAML copying context. + * \param[in] state The CYAML copy state we're pushing a stack entry for. + * \param[in] schema The CYAML schema for the value expected in state. + * \param[in] data Pointer to where value's data should be read from. + * \param[in] copy Pointer to where value's data should be written to. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__stack_push( + cyaml_ctx_t *ctx, + enum cyaml_state_e state, + const cyaml_schema_value_t *schema, + const cyaml_data_t *data, + cyaml_data_t *copy) +{ + cyaml_err_t err; + cyaml_state_t s = { + .copy = copy, + .data = data, + .state = state, + .schema = schema, + }; + + err = cyaml__stack_ensure(ctx); + if (err != CYAML_OK) { + return err; + } + + switch (state) { + case CYAML_STATE_IN_MAP_KEY: + assert(schema->type == CYAML_MAPPING); + s.mapping.field = schema->mapping.fields; + break; + case CYAML_STATE_IN_SEQUENCE: + assert(cyaml__is_sequence(schema)); + if (schema->type == CYAML_SEQUENCE_FIXED) { + if (schema->sequence.min != schema->sequence.max) { + return CYAML_ERR_SEQUENCE_FIXED_COUNT; + } + } else { + if (ctx->state->state == CYAML_STATE_IN_SEQUENCE) { + return CYAML_ERR_SEQUENCE_IN_SEQUENCE; + + } else if (ctx->state->state == + CYAML_STATE_IN_MAP_KEY) { + const cyaml_schema_field_t *field = + ctx->state->mapping.field - 1; + s.sequence.count_data = ctx->state->copy + + field->count_offset; + s.sequence.count_size = field->count_size; + } else { + assert(ctx->state->state == CYAML_STATE_START); + s.sequence.count_data = (void *)&ctx->seq_count; + s.sequence.count_size = sizeof(ctx->seq_count); + } + } + break; + default: + break; + } + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Copy: PUSH[%u]: %s\n", ctx->stack_idx, + cyaml__state_to_str(state)); + + ctx->stack[ctx->stack_idx] = s; + ctx->state = ctx->stack + ctx->stack_idx; + ctx->stack_idx++; + + return CYAML_OK; +} + +/** + * Pop the current entry on the CYAML copy context's stack. + * + * This frees any resources owned by the stack entry. + * + * \param[in] ctx The CYAML copying context. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__stack_pop( + cyaml_ctx_t *ctx) +{ + uint32_t idx = ctx->stack_idx; + + assert(idx != 0); + + idx--; + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Copy: POP[%u]: %s\n", idx, + cyaml__state_to_str(ctx->state->state)); + + ctx->state = (idx == 0) ? NULL : &ctx->stack[idx - 1]; + ctx->stack_idx = idx; + + return CYAML_OK; +} + +/** + * Helper to handle \ref CYAML_FLAG_POINTER. + * + * The current CYAML copying context's state is updated with new allocation + * address, where necessary. + * + * \param[in] ctx The CYAML copying context. + * \param[in] schema The schema for value to get data pointer for. + * \param[in,out] value_data_io Current address of value's input data to be + * copied. Updated to new address if value is + * allocation requiring an allocation. + * \param[in,out] value_copy_io Current address of value's output data to be + * written. Updated to new address if value is + * allocation requiring an allocation. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__data_handle_pointer( + const cyaml_ctx_t *ctx, + const cyaml_schema_value_t *schema, + const uint8_t **value_data_io, + uint8_t **value_copy_io) +{ + cyaml_state_t *state = ctx->state; + + *value_data_io = cyaml_data_save_handle_pointer(ctx->config, schema, + *value_data_io, "Copy"); + + if (schema->flags & CYAML_FLAG_POINTER) { + /* Need to create/extend an allocation. */ + size_t delta = schema->data_size; + uint8_t *value_copy = NULL; + + if (*value_data_io == NULL) { + return CYAML_ERR_BAD_PARAM_NULL_DATA; + } + + switch (schema->type) { + case CYAML_STRING: + /* For a string the allocation size is the string + * size from the event, plus trailing NULL. */ + delta = strlen((const char *) *value_data_io) + 1; + break; + case CYAML_SEQUENCE: + delta *= state->sequence.count; + break; + case CYAML_SEQUENCE_FIXED: + delta *= schema->sequence.max; + break; + default: + break; + } + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Copy: Allocating: (%zu bytes)\n", delta); + + value_copy = cyaml__alloc(ctx->config, delta, true); + if (value_copy == NULL) { + return CYAML_ERR_OOM; + } + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Copy: Writing allocation address %p into %p\n", + value_copy, *value_copy_io); + + /* Write the allocation pointer into the data structure. */ + cyaml_data_write_pointer(value_copy, *value_copy_io); + + *value_copy_io = value_copy; + } + + return CYAML_OK; +} + +/** + * Dump a backtrace to the log. + * + * \param[in] ctx The CYAML copying context. + */ +static void cyaml__backtrace( + const cyaml_ctx_t *ctx) +{ + if (ctx->stack_idx > 1) { + cyaml__log(ctx->config, CYAML_LOG_ERROR, "Copy: Backtrace:\n"); + } else { + return; + } + + for (uint32_t idx = ctx->stack_idx - 1; idx != 0; idx--) { + cyaml_state_t *state = ctx->stack + idx; + switch (state->state) { + case CYAML_STATE_IN_MAP_KEY: /* Fall through. */ + case CYAML_STATE_IN_MAP_VALUE: + assert(state->mapping.field != NULL); + cyaml__log(ctx->config, CYAML_LOG_ERROR, + " in mapping field: %s\n", + state->mapping.field->key); + break; + case CYAML_STATE_IN_SEQUENCE: + cyaml__log(ctx->config, CYAML_LOG_ERROR, + " in sequence entry: %"PRIu64"\n", + state->sequence.count); + break; + default: + break; + } + } +} + +/** + * Read a value of numerical type. + * + * \param[in] ctx The CYAML copying context. + * \param[in] schema The schema for the value to be copied. + * \param[in] data The place to read the value from in the client data. + * \param[in] copy The place to write the value to in the client data. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_number( + const cyaml_ctx_t *ctx, + const cyaml_schema_value_t *schema, + const uint8_t *data, + uint8_t *copy) +{ + CYAML_UNUSED(ctx); + + memcpy(copy, data, schema->data_size); + + return CYAML_OK; +} + +/** + * Read a value of type \ref CYAML_STRING. + * + * \param[in] ctx The CYAML copying context. + * \param[in] schema The schema for the value to be copied. + * \param[in] data The place to read the value from in the client data. + * \param[in] copy The place to write the value to in the client data. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_string( + const cyaml_ctx_t *ctx, + const cyaml_schema_value_t *schema, + const uint8_t *data, + uint8_t *copy) +{ + CYAML_UNUSED(ctx); + CYAML_UNUSED(schema); + + memcpy(copy, data, strlen((const char *)data) + 1); + + return CYAML_OK; +} + +/** + * Write a sequence entry count to the client data structure. + * + * \param[in] ctx The CYAML copying context. + * \param[in] schema The schema for the value to be copied. + * \param[in] seq_count The sequence entry count to write. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__write_sequence_count( + cyaml_ctx_t *ctx, + const cyaml_schema_value_t *schema, + uint64_t seq_count) +{ + cyaml_err_t err; + + if (schema->type != CYAML_SEQUENCE) { + return CYAML_OK; + } + + err = cyaml_data_write(seq_count, + ctx->state->sequence.count_size, + ctx->state->sequence.count_data); + + /* Can't go wrong, because the count_size was changed when reading. */ + assert(err == CYAML_OK); + + return err; +} + +/** + * Handle a YAML event corresponding to a YAML data value. + * + * \param[in] ctx The CYAML copying context. + * \param[in] schema CYAML schema for the expected value. + * \param[in] data The place to read the value from in the client data. + * \param[in] seq_count Entry count for sequence values. Unused for + * non-sequence values. + * \param[in] copy Pointer to where value's data should be written. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_value( + cyaml_ctx_t *ctx, + const cyaml_schema_value_t *schema, + const uint8_t *data, + uint64_t seq_count, + uint8_t *copy) +{ + cyaml_err_t err; + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Copy: Cloning value of type '%s'%s\n", + cyaml__type_to_str(schema->type), + schema->flags & CYAML_FLAG_POINTER ? " (pointer)" : ""); + +// if (schema->type == CYAML_MAPPING && +// schema->flags & CYAML_FLAG_POINTER) { +// const uint8_t *mapping_data = data; +// mapping_data = cyaml_data_save_handle_pointer(ctx->config, +// schema, mapping_data, "Copy"); +// if (mapping_data == NULL) { +// return CYAML_ERR_BAD_PARAM_NULL_DATA; +// } +// } + + if (!cyaml__is_sequence(schema)) { + /* Since sequences extend their allocation for each entry, + * they're handled in the sequence-specific code. + */ + err = cyaml__data_handle_pointer(ctx, schema, &data, ©); + if (err != CYAML_OK) { + return err; + } + } + + switch (schema->type) { + case CYAML_INT: /* Fall through. */ + case CYAML_UINT: /* Fall through. */ + case CYAML_BOOL: /* Fall through. */ + case CYAML_ENUM: /* Fall through. */ + case CYAML_FLOAT: /* Fall through. */ + case CYAML_FLAGS: /* Fall through. */ + case CYAML_BITFIELD: + err = cyaml__clone_number(ctx, schema, data, copy); + break; + case CYAML_STRING: + err = cyaml__clone_string(ctx, schema, data, copy); + break; + case CYAML_MAPPING: + err = cyaml__stack_push(ctx, CYAML_STATE_IN_MAP_KEY, + schema, data, copy); + break; + case CYAML_SEQUENCE_FIXED: /* Fall through. */ + case CYAML_SEQUENCE: + err = cyaml__stack_push(ctx, CYAML_STATE_IN_SEQUENCE, + schema, data, copy); + if (err != CYAML_OK) { + return err; + } + ctx->state->sequence.count = seq_count; + err = cyaml__data_handle_pointer(ctx, schema, &data, ©); + if (err != CYAML_OK) { + return err; + } + err = cyaml__write_sequence_count(ctx, schema, seq_count); + ctx->state->data = data; + ctx->state->copy = copy; + break; + default: + err = CYAML_ERR_BAD_TYPE_IN_SCHEMA; + break; + } + + return err; +} + +/** + * YAML saving handler for the \ref CYAML_STATE_START state. + * + * \param[in] ctx The CYAML copying context. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_start( + cyaml_ctx_t *ctx) +{ + cyaml_state_t *state = ctx->state; + return cyaml__clone_value(ctx, state->schema, + state->data, ctx->seq_count, state->copy); +} + +/** + * YAML copying handler for the \ref CYAML_STATE_IN_MAP_KEY and \ref + * CYAML_STATE_IN_MAP_VALUE states. + * + * \param[in] ctx The CYAML copying context. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_mapping( + cyaml_ctx_t *ctx) +{ + cyaml_err_t err = CYAML_OK; + cyaml_state_t *state = ctx->state; + const cyaml_schema_field_t *field = state->mapping.field; + + if (field != NULL && field->key != NULL) { + uint64_t seq_count = 0; + + if (field->value.type == CYAML_IGNORE) { + ctx->state->mapping.field++; + return CYAML_OK; + } + cyaml__log(ctx->config, CYAML_LOG_INFO, + "Copy: [%s]\n", field->key); + + /* Advance the field before writing value, since writing the + * value can put a new state entry on the stack. */ + ctx->state->mapping.field++; + + if (field->value.type == CYAML_SEQUENCE) { + seq_count = cyaml_data_read(field->count_size, + ctx->state->data + field->count_offset, + &err); + if (err != CYAML_OK) { + return err; + } + cyaml__log(ctx->config, CYAML_LOG_INFO, + "Copy: Sequence entry count: %"PRIu64"\n", + seq_count); + + } else if (field->value.type == CYAML_SEQUENCE_FIXED) { + seq_count = field->value.sequence.min; + } + + err = cyaml__clone_value(ctx, + &field->value, + state->data + field->data_offset, + seq_count, + state->copy + field->data_offset); + if (err == CYAML_ERR_BAD_PARAM_NULL_DATA) { + if (!(field->value.flags & CYAML_FLAG_OPTIONAL)) { + return CYAML_ERR_MAPPING_FIELD_MISSING; + } + err = CYAML_OK; + } + } else { + err = cyaml__stack_pop(ctx); + } + + return err; +} + +/** + * YAML copying handler for the \ref CYAML_STATE_IN_SEQUENCE state. + * + * \param[in] ctx The CYAML copying context. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__clone_sequence( + cyaml_ctx_t *ctx) +{ + cyaml_err_t err = CYAML_OK; + + if (ctx->state->sequence.entry < ctx->state->sequence.count) { + const cyaml_schema_value_t *schema = ctx->state->schema; + const cyaml_schema_value_t *value = schema->sequence.entry; + unsigned seq_count = 0; + size_t data_size; + size_t offset; + + if (value->type == CYAML_SEQUENCE_FIXED) { + seq_count = value->sequence.max; + } + + if (value->flags & CYAML_FLAG_POINTER) { + data_size = sizeof(NULL); + } else { + data_size = value->data_size; + if (value->type == CYAML_SEQUENCE_FIXED) { + data_size *= seq_count; + } + } + offset = data_size * ctx->state->sequence.entry; + + cyaml__log(ctx->config, CYAML_LOG_INFO, + "Copy: Sequence entry %"PRIu64" of %"PRIu64"\n", + ctx->state->sequence.entry + 1, + ctx->state->sequence.count); + + /* Advance the entry before writing value, since writing the + * value can put a new state entry on the stack. */ + ctx->state->sequence.entry++; + + err = cyaml__clone_value(ctx, value, + ctx->state->data + offset, + seq_count, + ctx->state->copy + offset); + } else { + err = cyaml__stack_pop(ctx); + } + + return err; +} + +/** + * Check that common copy params from client are valid. + * + * \param[in] config The client's CYAML library config. + * \param[in] schema The schema describing the content of data. + * \param[in] seq_count Top level sequence count. + * \param[in] data_out Points to client's address to write data to. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static inline cyaml_err_t cyaml__validate_copy_params( + const cyaml_config_t *config, + const cyaml_schema_value_t *schema, + unsigned seq_count, + cyaml_data_t **data_out) +{ + if (config == NULL) { + return CYAML_ERR_BAD_PARAM_NULL_CONFIG; + } + if (config->mem_fn == NULL) { + return CYAML_ERR_BAD_CONFIG_NULL_MEMFN; + } + if (schema == NULL) { + return CYAML_ERR_BAD_PARAM_NULL_SCHEMA; + } + if (cyaml__is_sequence(schema) != (seq_count != 0)) { + return CYAML_ERR_BAD_PARAM_SEQ_COUNT; + } + if (data_out == NULL) { + return CYAML_ERR_BAD_PARAM_NULL_DATA; + } + if ((schema->flags & CYAML_FLAG_POINTER) && (*data_out != NULL)) { + return CYAML_ERR_DATA_TARGET_NON_NULL; + } + return CYAML_OK; +} + +/** + * Get the total length of an allocation. + * + * \param[in] schema Schema for the data value to get the size of. + * \param[in] seq_count If top level type is sequence, this should be the + * entry count, otherwise it is ignored. + * \return the byte length of the data. + */ +static size_t cyaml__get_data_len( + const cyaml_schema_value_t *schema, + unsigned seq_count) +{ + size_t len = cyaml__is_sequence(schema) ? + schema->sequence.entry->data_size : + schema->data_size; + + if (schema->type == CYAML_SEQUENCE) { + len *= seq_count; + } else if (schema->type == CYAML_SEQUENCE_FIXED) { + len *= schema->sequence.max; + } + + return len; +} + +/* Exported function, documented in include/cyaml/cyaml.h */ +cyaml_err_t cyaml_copy( + const cyaml_config_t *config, + const cyaml_schema_value_t *schema, + const cyaml_data_t *data, + unsigned seq_count, + cyaml_data_t **data_out) +{ + cyaml_data_t *copy = NULL; + cyaml_ctx_t ctx = { + .config = config, + .seq_count = seq_count, + }; + typedef cyaml_err_t (* const cyaml_clone_fn)( + cyaml_ctx_t *ctx); + static const cyaml_clone_fn fn[CYAML_STATE__COUNT] = { + [CYAML_STATE_START] = cyaml__clone_start, + [CYAML_STATE_IN_MAP_KEY] = cyaml__clone_mapping, + [CYAML_STATE_IN_MAP_VALUE] = cyaml__clone_mapping, + [CYAML_STATE_IN_SEQUENCE] = cyaml__clone_sequence, + }; + cyaml_err_t err = CYAML_OK; + + err = cyaml__validate_copy_params(config, schema, seq_count, data_out); + if (err != CYAML_OK) { + if (ctx.config != NULL) { + cyaml__log(ctx.config, CYAML_LOG_ERROR, + "Copy: Bad call parameters\n"); + } + return err; + } + + if (!(schema->flags & CYAML_FLAG_POINTER)) { + size_t len = cyaml__get_data_len(schema, seq_count); + copy = cyaml__alloc(config, len, true); + if (copy == NULL) { + return CYAML_ERR_OOM; + } + } + + err = cyaml__stack_push(&ctx, CYAML_STATE_START, schema, + (schema->flags & CYAML_FLAG_POINTER) ? &data : data, + (schema->flags & CYAML_FLAG_POINTER) ? © : copy); + if (err != CYAML_OK) { + goto out; + } + + do { + cyaml__log(ctx.config, CYAML_LOG_DEBUG, + "Copy: Handle state %s\n", + cyaml__state_to_str(ctx.state->state)); + err = fn[ctx.state->state](&ctx); + if (err != CYAML_OK) { + goto out; + } + } while (ctx.stack_idx > 1); + + cyaml__stack_pop(&ctx); + + assert(ctx.stack_idx == 0); + + if (!(schema->flags & CYAML_FLAG_POINTER)) { + size_t len = cyaml__get_data_len(schema, seq_count); + if (*data_out == NULL) { + err = CYAML_ERR_BAD_PARAM_NULL_DATA; + goto out; + } + memcpy(*data_out, copy, len); + } else { + *data_out = copy; + } +out: + if (err != CYAML_OK) { + cyaml_free(config, schema, copy, ctx.seq_count); + cyaml__backtrace(&ctx); + } + if (!(schema->flags & CYAML_FLAG_POINTER)) { + cyaml__free(config, copy); + } + while (ctx.stack_idx > 0) { + cyaml__stack_pop(&ctx); + } + cyaml__free(config, ctx.stack); + return err; +} diff --git a/src/data.h b/src/data.h index 60190b8..8b76a9c 100644 --- a/src/data.h +++ b/src/data.h @@ -123,4 +123,35 @@ static inline uint8_t * cyaml_data_read_pointer( return (void *)cyaml_data_read(sizeof(char *), data, &err); } +/** + * Find address of actual value. + * + * If the value has the pointer flag, the pointer is read, otherwise the + * address is returned unchanged. + * + * \param[in] config The CYAML client configuration object. + * \param[in] schema CYAML schema for the expected value. + * \param[in] data_in The address to read from. + * \param[in] prefix String prefix to use in log messages. + * \return New address or for \ref CYAML_FLAG_POINTER, or data_in. + */ +static inline const uint8_t * cyaml_data_save_handle_pointer( + const cyaml_config_t *config, + const cyaml_schema_value_t *schema, + const uint8_t *data_in, + const char *prefix) +{ + if (schema->flags & CYAML_FLAG_POINTER) { + const uint8_t *data = cyaml_data_read_pointer(data_in); + + cyaml__log(config, CYAML_LOG_DEBUG, + "%s: Handle pointer: %p --> %p\n", + prefix, data_in, data); + + return data; + } + + return data_in; +} + #endif diff --git a/src/free.c b/src/free.c index c9406ea..266cd7b 100644 --- a/src/free.c +++ b/src/free.c @@ -156,6 +156,8 @@ cyaml_err_t cyaml_free( return CYAML_ERR_BAD_PARAM_NULL_SCHEMA; } cyaml__log(config, CYAML_LOG_DEBUG, "Free: Top level data: %p\n", data); - cyaml__free_value(config, schema, (void *)&data, seq_count); + cyaml__free_value(config, schema, + (schema->flags & CYAML_FLAG_POINTER) ? &data : data, + seq_count); return CYAML_OK; } diff --git a/src/save.c b/src/save.c index 5b118df..73bb90a 100644 --- a/src/save.c +++ b/src/save.c @@ -432,35 +432,6 @@ static cyaml_err_t cyaml__stack_pop( return CYAML_OK; } -/** - * Find address of actual value. - * - * If the value has the pointer flag, the pointer is read, otherwise the - * address is returned unchanged. - * - * \param[in] config The CYAML client configuration object. - * \param[in] schema CYAML schema for the expected value. - * \param[in] data_in The address to read from. - * \return New address or for \ref CYAML_FLAG_POINTER, or data_in. - */ -static const uint8_t * cyaml__data_handle_pointer( - const cyaml_config_t *config, - const cyaml_schema_value_t *schema, - const uint8_t *data_in) -{ - if (schema->flags & CYAML_FLAG_POINTER) { - const uint8_t *data = cyaml_data_read_pointer(data_in); - - cyaml__log(config, CYAML_LOG_DEBUG, - "Save: Handle pointer: %p --> %p\n", - data_in, data); - - return data; - } - - return data_in; -} - /** * Dump a backtrace to the log. * @@ -1018,7 +989,8 @@ static cyaml_err_t cyaml__write_value( cyaml__type_to_str(schema->type), schema->flags & CYAML_FLAG_POINTER ? " (pointer)" : ""); - data = cyaml__data_handle_pointer(ctx->config, schema, data); + data = cyaml_data_save_handle_pointer(ctx->config, schema, + data, "Save"); if (data == NULL) { if (cyaml__flag_check_all(schema->flags, diff --git a/src/util.c b/src/util.c index 427e055..6ba6129 100644 --- a/src/util.c +++ b/src/util.c @@ -108,6 +108,7 @@ const char * cyaml_strerror( [CYAML_ERR_BAD_CONFIG_NULL_MEMFN] = "Bad config: NULL mem function", [CYAML_ERR_BAD_PARAM_NULL_CONFIG] = "Bad parameter: NULL config", [CYAML_ERR_BAD_PARAM_NULL_SCHEMA] = "Bad parameter: NULL schema", + [CYAML_ERR_DATA_TARGET_NON_NULL] = "Data target must be NULL pointer", [CYAML_ERR_LIBYAML_EMITTER_INIT] = "libyaml emitter init failed", [CYAML_ERR_LIBYAML_PARSER_INIT] = "libyaml parser init failed", [CYAML_ERR_LIBYAML_EVENT_INIT] = "libyaml event init failed", diff --git a/test/units/copy.c b/test/units/copy.c new file mode 100644 index 0000000..0d86b53 --- /dev/null +++ b/test/units/copy.c @@ -0,0 +1,8077 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (C) 2018-2020 Michael Drake + */ + +#include +#include +#include +#include + +#include + +#include "../../src/data.h" +#include "ttest.h" +#include "test.h" + +/** Macro to squash unused variable compiler warnings. */ +#define UNUSED(_x) ((void)(_x)) + +/** Helper macro to count bytes of YAML input data. */ +#define YAML_LEN(_y) (sizeof(_y) - 1) + +/** + * Unit test context data. + */ +typedef struct test_data { + cyaml_data_t **data; + cyaml_data_t **copy; + cyaml_data_t *copy2; + unsigned *seq_count; + const struct cyaml_config *config; + const struct cyaml_schema_value *schema; + const struct cyaml_schema_value *schema2; +} test_data_t; + +/** + * Common clean up function to free data loaded by tests. + * + * \param[in] data The unit test context data. + */ +static void cyaml_cleanup(void *data) +{ + struct test_data *td = data; + unsigned seq_count = 0; + + if (td->seq_count != NULL) { + seq_count = *(td->seq_count); + } + + if (td->data != NULL) { + cyaml_free(td->config, td->schema, *(td->data), seq_count); + } + + if (td->copy != NULL) { + cyaml_free(td->config, td->schema, *(td->copy), seq_count); + } + + if (td->copy2 != NULL) { + cyaml_free(td->config, td->schema2, td->copy2, seq_count); + } +} + +/** + * Test copying a positive signed integer. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_int_pos( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int value = 90; + static const unsigned char yaml[] = + "test_int: 90\n"; + struct target_struct { + int test_value_int; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_int), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a negative signed integer. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_int_neg( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int value = -77; + static const unsigned char yaml[] = + "test_int: -77\n"; + struct target_struct { + int test_value_int; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_int), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an unsigned integer. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_uint( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + unsigned value = 9999; + static const unsigned char yaml[] = + "test_uint: 9999\n"; + struct target_struct { + unsigned test_value_uint; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_uint), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_uint != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_uint != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a floating point value as a float. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_float( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + float value = 3.14159f; + static const unsigned char yaml[] = + "test_fp: 3.14159\n"; + struct target_struct { + float test_value_fp; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_fp), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_fp != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: %f, got: %f", + value, data_cpy->test_value_fp); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_fp != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: %f, got: %f", + value, data_cpy2.test_value_fp); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a floating point value as a double. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_double( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + double value = 3.14159; + static const unsigned char yaml[] = + "test_fp: 3.14159\n"; + struct target_struct { + double test_value_fp; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_fp), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_fp != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: %lf, got: %lf", + value, data_cpy->test_value_fp); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_fp != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: %lf, got: %lf", + value, data_cpy2.test_value_fp); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a boolean value (true). + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_bool_true( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + bool value = true; + static const unsigned char yaml[] = + "test_bool: true\n"; + struct target_struct { + unsigned test_value_bool; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_bool), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_bool != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_bool != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a boolean value (false). + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_bool_false( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + bool value = false; + static const unsigned char yaml[] = + "test_bool: false\n"; + struct target_struct { + unsigned test_value_bool; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_bool), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_bool != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_bool != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an enumeration. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_enum( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST, + TEST_ENUM_SECOND, + TEST_ENUM_THIRD, + TEST_ENUM__COUNT, + } value = TEST_ENUM_SECOND; + static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { + [TEST_ENUM_FIRST] = { "first", 0 }, + [TEST_ENUM_SECOND] = { "second", 1 }, + [TEST_ENUM_THIRD] = { "third", 2 }, + }; + static const unsigned char yaml[] = + "test_enum: second\n"; + struct target_struct { + enum test_enum test_value_enum; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_enum, + strings, TEST_ENUM__COUNT), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sparse enumeration. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_enum_sparse( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST = 3, + TEST_ENUM_SECOND = 77, + TEST_ENUM_THIRD = 183, + TEST_ENUM_FOURTH = 9900, + } value = TEST_ENUM_SECOND; + static const cyaml_strval_t strings[] = { + { "first", TEST_ENUM_FIRST }, + { "second", TEST_ENUM_SECOND }, + { "third", TEST_ENUM_THIRD }, + { "fourth", TEST_ENUM_FOURTH }, + }; + static const unsigned char yaml[] = + "test_enum: second\n"; + struct target_struct { + enum test_enum test_value_enum; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_enum, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a strict enumeration. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_enum_strict( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST = 3, + TEST_ENUM_SECOND = 77, + TEST_ENUM_THIRD = 183, + TEST_ENUM_FOURTH = 9900, + } value = TEST_ENUM_SECOND; + static const cyaml_strval_t strings[] = { + { "first", TEST_ENUM_FIRST }, + { "second", TEST_ENUM_SECOND }, + { "third", TEST_ENUM_THIRD }, + { "fourth", TEST_ENUM_FOURTH }, + }; + static const unsigned char yaml[] = + "test_enum: second\n"; + struct target_struct { + enum test_enum test_value_enum; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_STRICT, + struct target_struct, test_value_enum, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_enum != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a string to a character array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_string( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *value = "Hello World!"; + static const unsigned char yaml[] = + "test_string: Hello World!\n"; + struct target_struct { + char test_value_string[50]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING("test_string", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_string, 0), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy->test_value_string, value) != 0) { + fprintf(stderr, "expected: %s\n", value); + for (unsigned i = 0; i < strlen(value) + 1; i++) { + fprintf(stderr, "%2.2x ", value[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " got: %s\n", data_cpy->test_value_string); + for (unsigned i = 0; i < sizeof(data_cpy->test_value_string); i++) { + fprintf(stderr, "%2.2x ", value[i]); + } + fprintf(stderr, "\n"); + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy2.test_value_string, value) != 0) { + fprintf(stderr, "expected: %s\n", value); + for (unsigned i = 0; i < strlen(value) + 1; i++) { + fprintf(stderr, "%2.2x ", value[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " got: %s\n", data_cpy2.test_value_string); + for (unsigned i = 0; i < sizeof(data_cpy2.test_value_string); i++) { + fprintf(stderr, "%2.2x ", value[i]); + } + fprintf(stderr, "\n"); + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a string to a allocated char pointer. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_string_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *value = "Hello World!"; + static const unsigned char yaml[] = + "test_string: Hello World!\n"; + struct target_struct { + char * test_value_string; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, + struct target_struct, test_value_string, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy->test_value_string, value) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy2.test_value_string, value) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an ignored value with descendants. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_ignore_deep( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "ignore:\n" + " foo: bar\n" + " bar:\n" + " - 1\n" + " - 2\n"; + struct target_struct { + bool foo; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_IGNORE("ignore", CYAML_FLAG_DEFAULT), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->foo != false) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.foo != false) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an ignored value. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_ignore_scalar( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "ignore: foo\n"; + struct target_struct { + bool foo; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_IGNORE("ignore", CYAML_FLAG_DEFAULT), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->foo != false) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.foo != false) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a flag word. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_flags( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 1), + TEST_FLAGS_THIRD = (1 << 2), + TEST_FLAGS_FOURTH = (1 << 3), + TEST_FLAGS_FIFTH = (1 << 4), + TEST_FLAGS_SIXTH = (1 << 5), + } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024; + static const cyaml_strval_t strings[] = { + { "none", TEST_FLAGS_NONE }, + { "first", TEST_FLAGS_FIRST }, + { "second", TEST_FLAGS_SECOND }, + { "third", TEST_FLAGS_THIRD }, + { "fourth", TEST_FLAGS_FOURTH }, + { "fifth", TEST_FLAGS_FIFTH }, + { "sixth", TEST_FLAGS_SIXTH }, + }; + static const unsigned char yaml[] = + "test_flags:\n" + " - second\n" + " - fifth\n" + " - 1024\n"; + struct target_struct { + enum test_flags test_value_flags; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_flags, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy->test_value_flags); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy2.test_value_flags); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an empty flag word. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_flags_empty( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 1), + TEST_FLAGS_THIRD = (1 << 2), + TEST_FLAGS_FOURTH = (1 << 3), + TEST_FLAGS_FIFTH = (1 << 4), + TEST_FLAGS_SIXTH = (1 << 5), + } value = TEST_FLAGS_NONE; + static const cyaml_strval_t strings[] = { + { "none", TEST_FLAGS_NONE }, + { "first", TEST_FLAGS_FIRST }, + { "second", TEST_FLAGS_SECOND }, + { "third", TEST_FLAGS_THIRD }, + { "fourth", TEST_FLAGS_FOURTH }, + { "fifth", TEST_FLAGS_FIFTH }, + { "sixth", TEST_FLAGS_SIXTH }, + }; + static const unsigned char yaml[] = + "test_flags: []\n"; + struct target_struct { + enum test_flags test_value_flags; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_flags, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy->test_value_flags); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy2.test_value_flags); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sparse flag word. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_flags_sparse( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 4), + TEST_FLAGS_THIRD = (1 << 7), + TEST_FLAGS_FOURTH = (1 << 11), + TEST_FLAGS_FIFTH = (1 << 14), + TEST_FLAGS_SIXTH = (1 << 20), + } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH; + static const cyaml_strval_t strings[] = { + { "none", TEST_FLAGS_NONE }, + { "first", TEST_FLAGS_FIRST }, + { "second", TEST_FLAGS_SECOND }, + { "third", TEST_FLAGS_THIRD }, + { "fourth", TEST_FLAGS_FOURTH }, + { "fifth", TEST_FLAGS_FIFTH }, + { "sixth", TEST_FLAGS_SIXTH }, + }; + static const unsigned char yaml[] = + "test_flags:\n" + " - second\n" + " - fifth\n"; + struct target_struct { + enum test_flags test_value_flags; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_flags, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy->test_value_flags); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy2.test_value_flags); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a strict flag word. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_flags_strict( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 4), + TEST_FLAGS_THIRD = (1 << 7), + TEST_FLAGS_FOURTH = (1 << 11), + TEST_FLAGS_FIFTH = (1 << 14), + TEST_FLAGS_SIXTH = (1 << 20), + } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH; + static const cyaml_strval_t strings[] = { + { "none", TEST_FLAGS_NONE }, + { "first", TEST_FLAGS_FIRST }, + { "second", TEST_FLAGS_SECOND }, + { "third", TEST_FLAGS_THIRD }, + { "fourth", TEST_FLAGS_FOURTH }, + { "fifth", TEST_FLAGS_FIFTH }, + { "sixth", TEST_FLAGS_SIXTH }, + }; + static const unsigned char yaml[] = + "test_flags:\n" + " - second\n" + " - fifth\n"; + struct target_struct { + enum test_flags test_value_flags; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_STRICT, + struct target_struct, test_value_flags, + strings, CYAML_ARRAY_LEN(strings)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy->test_value_flags); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.test_value_flags != value) { + return ttest_fail(&tc, "Incorrect value: " + "expected: 0x%x, got: 0x%x\n", + value, data_cpy2.test_value_flags); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping, to an internal structure. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_mapping( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } value; + static const unsigned char yaml[] = + "mapping:\n" + " a: 123\n" + " b: 9999\n"; + struct target_struct { + struct value_s test_value_mapping; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_MAPPING("mapping", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_mapping, + test_mapping_schema), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + memset(&value, 0, sizeof(value)); + value.a = 123; + value.b = 9999; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (memcmp(&data_cpy->test_value_mapping, &value, sizeof(value)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (memcmp(&data_cpy2.test_value_mapping, &value, sizeof(value)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping, to an allocated structure. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_mapping_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } value; + static const unsigned char yaml[] = + "mapping:\n" + " a: 123\n" + " b: 9999\n"; + struct target_struct { + struct value_s *test_value_mapping; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_MAPPING_PTR("mapping", CYAML_FLAG_POINTER, + struct target_struct, test_value_mapping, + test_mapping_schema), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + memset(&value, 0, sizeof(value)); + value.a = 123; + value.b = 9999; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (memcmp(data_cpy->test_value_mapping, &value, sizeof(value)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (memcmp(data_cpy2.test_value_mapping, &value, sizeof(value)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers into an int[]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { .seq_count = 0, }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of enums into an enum test_enum[]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_enum( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST, + TEST_ENUM_SECOND, + TEST_ENUM_THIRD, + TEST_ENUM__COUNT, + } ref[] = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }; + static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { + [TEST_ENUM_FIRST] = { "first", 0 }, + [TEST_ENUM_SECOND] = { "second", 1 }, + [TEST_ENUM_THIRD] = { "third", 2 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - first\n" + " - second\n" + " - third\n"; + struct target_struct { + enum test_enum seq[CYAML_ARRAY_LEN(ref)]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, + *(data_tgt->seq), strings, TEST_ENUM__COUNT), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of unsigned integers into an unsigned[]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_uint( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 99999\n" + " - 99998\n" + " - 99997\n" + " - 99996\n" + " - 99995\n" + " - 99994\n"; + struct target_struct { + unsigned seq[CYAML_ARRAY_LEN(ref)]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of boolean values into an bool[]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_bool( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + bool ref[] = { true, false, true, false, true, false, true, false }; + static const unsigned char yaml[] = + "sequence:\n" + " - true\n" + " - false\n" + " - yes\n" + " - no\n" + " - enable\n" + " - disable\n" + " - 1\n" + " - 0\n"; + struct target_struct { + bool seq[CYAML_ARRAY_LEN(ref)]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of flag sequences into an array of flag words. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_flags( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 1), + TEST_FLAGS_THIRD = (1 << 2), + TEST_FLAGS_FOURTH = (1 << 3), + TEST_FLAGS_FIFTH = (1 << 4), + TEST_FLAGS_SIXTH = (1 << 5), + } ref[3] = { + TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, + TEST_FLAGS_FIRST, + TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH + }; + #define TEST_FLAGS__COUNT 6 + static const cyaml_strval_t strings[TEST_FLAGS__COUNT] = { + { "first", (1 << 0) }, + { "second", (1 << 1) }, + { "third", (1 << 2) }, + { "fourth", (1 << 3) }, + { "fifth", (1 << 4) }, + { "sixth", (1 << 5) }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - - second\n" + " - fifth\n" + " - 1024\n" + " - - first\n" + " - - fourth\n" + " - sixth\n"; + struct target_struct { + enum test_flags seq[3]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data_tgt->seq), + strings, TEST_FLAGS__COUNT), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(data_tgt->seq)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of strings into an array of char[7]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_string( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *ref[] = { + "This", + "is", + "merely", + "a", + "test", + }; + static const unsigned char yaml[] = + "sequence:\n" + " - This\n" + " - is\n" + " - merely\n" + " - a\n" + " - test\n"; + struct target_struct { + char seq[5][7]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data_tgt->seq), 0, 6), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy->seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy2.seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of strings into an array of allocated strings. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_string_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *ref[] = { + "This", + "is", + "merely", + "a", + "test", + }; + static const unsigned char yaml[] = + "sequence:\n" + " - This\n" + " - is\n" + " - merely\n" + " - a\n" + " - test\n"; + struct target_struct { + char *seq[5]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data_tgt->seq), + 0, CYAML_UNLIMITED), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy->seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy2.seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of mappings into an array of structures. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_mapping( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } ref[3]; + static const unsigned char yaml[] = + "sequence:\n" + " - a: 123\n" + " b: 9999\n" + " - a: 4000\n" + " b: 62000\n" + " - a: 1\n" + " b: 765\n"; + struct target_struct { + struct value_s seq[3]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct value_s, test_mapping_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(data_tgt->seq)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + memset(ref, 0, sizeof(ref)); + ref[0].a = 123; + ref[0].b = 9999; + ref[1].a = 4000; + ref[1].b = 62000; + ref[2].a = 1; + ref[2].b = 765; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + if (memcmp(data_cpy->seq, ref, sizeof(ref)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + if (memcmp(data_cpy2.seq, ref, sizeof(ref)) != 0) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of mappings into an array of pointers to structs. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_mapping_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } ref[3] = { + { .a = 123, .b = 9999, }, + { .a = 4000, .b = 62000, }, + { .a = 1, .b = 765, }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - a: 123\n" + " b: 9999\n" + " - a: 4000\n" + " b: 62000\n" + " - a: 1\n" + " b: 765\n"; + struct target_struct { + struct value_s *seq[3]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct value_s, test_mapping_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(data_tgt->seq)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy->seq[i]->a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy->seq[i]->b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy2.seq[i]->a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy2.seq[i]->b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of int into int[4][3]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_sequence_fixed_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int seq[4][3]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data_tgt->seq)), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_DEFAULT, **(data_tgt->seq), + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j][i], ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j][i], ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of int into int*[4]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_sequence_fixed_ptr_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int *seq[4]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data_tgt->seq)), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_POINTER, **(data_tgt->seq), + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j][i], ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j][i], ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of int into one-dimensional int[]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_sequence_fixed_flat_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int seq[12]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_DEFAULT, int, + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "sequence", + .value = { + .type = CYAML_SEQUENCE, + .flags = CYAML_FLAG_DEFAULT, + .data_size = sizeof(int[3]), + .sequence = { + .entry = &entry_schema, + .min = 0, + .max = CYAML_UNLIMITED, + } + }, + .data_offset = offsetof(struct target_struct, seq), + .count_size = sizeof(data_tgt->seq_count), + .count_offset = offsetof(struct target_struct, seq_count), + }, + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Note: count is count of entries of the outer sequence entries, + * so, 4, not 12. */ + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j * CYAML_ARRAY_LEN(*ref) + i], + ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Note: count is count of entries of the outer sequence entries, + * so, 4, not 12. */ + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j * CYAML_ARRAY_LEN(*ref) + i], + ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers to allocated int* array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of enums to allocated enum test_enum* array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_enum( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST, + TEST_ENUM_SECOND, + TEST_ENUM_THIRD, + TEST_ENUM__COUNT, + } ref[] = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }; + static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { + [TEST_ENUM_FIRST] = { "first", 0 }, + [TEST_ENUM_SECOND] = { "second", 1 }, + [TEST_ENUM_THIRD] = { "third", 2 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - first\n" + " - second\n" + " - third\n"; + struct target_struct { + enum test_enum *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, + *(data_tgt->seq), strings, TEST_ENUM__COUNT), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of unsigned integers to allocated unsigned* array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_uint( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 99999\n" + " - 99998\n" + " - 99997\n" + " - 99996\n" + " - 99995\n" + " - 99994\n"; + struct target_struct { + unsigned *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of boolean values to allocated bool* array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_bool( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + bool ref[] = { true, false, true, false, true, false, true, false }; + static const unsigned char yaml[] = + "sequence:\n" + " - true\n" + " - false\n" + " - yes\n" + " - no\n" + " - enable\n" + " - disable\n" + " - 1\n" + " - 0\n"; + struct target_struct { + bool *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of flag sequences to allocated flag words array. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_flags( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_flags { + TEST_FLAGS_NONE = 0, + TEST_FLAGS_FIRST = (1 << 0), + TEST_FLAGS_SECOND = (1 << 1), + TEST_FLAGS_THIRD = (1 << 2), + TEST_FLAGS_FOURTH = (1 << 3), + TEST_FLAGS_FIFTH = (1 << 4), + TEST_FLAGS_SIXTH = (1 << 5), + } ref[3] = { + TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, + TEST_FLAGS_FIRST, + TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH + }; + #define TEST_FLAGS__COUNT 6 + static const cyaml_strval_t strings[TEST_FLAGS__COUNT] = { + { "first", (1 << 0) }, + { "second", (1 << 1) }, + { "third", (1 << 2) }, + { "fourth", (1 << 3) }, + { "fifth", (1 << 4) }, + { "sixth", (1 << 5) }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - - second\n" + " - fifth\n" + " - 1024\n" + " - - first\n" + " - - fourth\n" + " - sixth\n"; + struct target_struct { + enum test_flags *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data_tgt->seq), + strings, TEST_FLAGS__COUNT), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of strings to allocated array of char[7]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_string( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *ref[] = { + "This", + "is", + "merely", + "a", + "test", + }; + static const unsigned char yaml[] = + "sequence:\n" + " - This\n" + " - is\n" + " - merely\n" + " - a\n" + " - test\n"; + struct target_struct { + char (*seq)[7]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data_tgt->seq), 0, 6), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy->seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy2.seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of strings to allocated array of + * allocated strings. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_string_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const char *ref[] = { + "This", + "is", + "merely", + "a", + "test", + }; + static const unsigned char yaml[] = + "sequence:\n" + " - This\n" + " - is\n" + " - merely\n" + " - a\n" + " - test\n"; + struct target_struct { + char **seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data_tgt->seq), + 0, CYAML_UNLIMITED), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy->seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (strcmp(data_cpy2.seq[i], ref[i]) != 0) { + return ttest_fail(&tc, "Incorrect value (i=%u)", i); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of mappings to allocated array mapping structs. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_mapping( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } ref[3] = { + { .a = 123, .b = 9999, }, + { .a = 4000, .b = 62000, }, + { .a = 1, .b = 765, }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - a: 123\n" + " b: 9999\n" + " - a: 4000\n" + " b: 62000\n" + " - a: 1\n" + " b: 765\n"; + struct target_struct { + struct value_s *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct value_s, test_mapping_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy->seq[i].a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy->seq[i].b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy2.seq[i].a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy2.seq[i].b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of mappings to allocated array of pointers to + * mapping structs. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_mapping_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct value_s { + short a; + long b; + } ref[3] = { + { .a = 123, .b = 9999, }, + { .a = 4000, .b = 62000, }, + { .a = 1, .b = 765, }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - a: 123\n" + " b: 9999\n" + " - a: 4000\n" + " b: 62000\n" + " - a: 1\n" + " b: 765\n"; + struct target_struct { + struct value_s **seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct value_s, test_mapping_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy->seq[i]->a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy->seq[i]->b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (ref[i].a != data_cpy2.seq[i]->a) { + return ttest_fail(&tc, "Incorrect value"); + } + if (ref[i].b != data_cpy2.seq[i]->b) { + return ttest_fail(&tc, "Incorrect value"); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of integers to allocated array + * of int[3]. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_sequence_fixed_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int (*seq)[3]; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_DEFAULT, int, + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j][i], ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j][i], ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of integers to allocated array + * of allocated arrays of integers. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_sequence_fixed_ptr_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int **seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_POINTER, int, + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, + struct target_struct, seq, &entry_schema, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j][i], ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j][i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j][i], ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of sequences of integers a one-dimensional allocated + * array of integers. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_ptr_sequence_fixed_flat_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[4][3] = { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 7, 8, 9 }, + { 10, 11, 12 }, + }; + static const unsigned char yaml[] = + "sequence:\n" + " - [ 1, 2, 3 ]\n" + " - [ 4, 5, 6 ]\n" + " - [ 7, 8, 9 ]\n" + " - [ 10, 11, 12 ]\n"; + struct target_struct { + int *seq; + uint32_t seq_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema_int = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_SEQUENCE_FIXED( + CYAML_FLAG_DEFAULT, int, + &entry_schema_int, CYAML_ARRAY_LEN(*ref)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "sequence", + .value = { + .type = CYAML_SEQUENCE, + .flags = CYAML_FLAG_POINTER, + .data_size = sizeof(int[3]), + .sequence = { + .entry = &entry_schema, + .min = 0, + .max = CYAML_UNLIMITED, + } + }, + .data_offset = offsetof(struct target_struct, seq), + .count_size = sizeof(data_tgt->seq_count), + .count_offset = offsetof(struct target_struct, seq_count), + }, + CYAML_FIELD_END, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Note: count is count of entries of the outer sequence entries, + * so, 4, not 12. */ + if (CYAML_ARRAY_LEN(ref) != data_cpy->seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy->seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy->seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy->seq[j * CYAML_ARRAY_LEN(*ref) + i], + ref[j][i]); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Note: count is count of entries of the outer sequence entries, + * so, 4, not 12. */ + if (CYAML_ARRAY_LEN(ref) != data_cpy2.seq_count) { + return ttest_fail(&tc, "Incorrect sequence count: " + "expected %u, got %u", + CYAML_ARRAY_LEN(ref), data_cpy2.seq_count); + } + + for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { + for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { + if (data_cpy2.seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { + return ttest_fail(&tc, + "Incorrect value " + "(i=%u, j=%u): " + "got: %i, expected: %i", i, j, + data_cpy2.seq[j * CYAML_ARRAY_LEN(*ref) + i], + ref[j][i]); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 1 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[1]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 2 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_2( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[2]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 3 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_3( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[3]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 4 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_4( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[4]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 5 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_5( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[5]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 6 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_6( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[6]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 7 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_7( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[7]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence of integers with 8 byte sequence count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_entry_sequence_count_8( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int ref[] = { 1, 1, 2, 3, 5, 8 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 1\n" + " - 1\n" + " - 2\n" + " - 3\n" + " - 5\n" + " - 8\n"; + struct target_struct { + int seq[CYAML_ARRAY_LEN(ref)]; + uint8_t seq_count[8]; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, + struct target_struct, seq, &entry_schema, + 0, CYAML_ARRAY_LEN(ref)), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy->seq_count), + data_cpy->seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->seq[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_cpy2.seq_count), + data_cpy2.seq_count, &err)) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.seq[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.seq[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying with schema with scalar top level type. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_schema_top_level_scalar( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "7\n"; + int *value = NULL; + int *value_copy = NULL; + int value_copy2 = 0; + int *value_copy_ptr = &value_copy2; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) + }; + test_data_t td = { + .data = (cyaml_data_t **) &value, + .copy = (cyaml_data_t **) &value_copy, + .copy2 = (cyaml_data_t *) &value_copy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &value, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) value, 0, + (cyaml_data_t **) &value_copy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (value_copy == NULL) { + return ttest_fail(&tc, "Data NULL on success."); + } + + if (*value_copy != 7) { + return ttest_fail(&tc, "Bad value."); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) value, 0, + (cyaml_data_t **) &value_copy_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (value_copy2 != 7) { + return ttest_fail(&tc, "Bad value."); + } + + return ttest_pass(&tc); +} + +/** + * Test copying with schema with sequence_fixed top level type. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_schema_top_level_sequence( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "- 7\n" + "- 6\n" + "- 5\n"; + int *value = NULL; + int *value_copy = NULL; + int value_copy2[3] = { 0, 0, 0 }; + int *value_copy2_ptr = value_copy2; + unsigned count = 0; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, + &entry_schema, 0, CYAML_UNLIMITED) + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_SEQUENCE(CYAML_FLAG_DEFAULT, int, + &entry_schema, 0, CYAML_UNLIMITED) + }; + test_data_t td = { + .data = (cyaml_data_t **) &value, + .copy = (cyaml_data_t **) &value_copy, + .copy2 = (cyaml_data_t *) &value_copy2, + .seq_count = &count, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &value, &count); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) value, count, + (cyaml_data_t **) &value_copy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (count != 3) { + return ttest_fail(&tc, "Unexpected sequence count."); + } + + if (value_copy == NULL) { + return ttest_fail(&tc, "Data NULL on success."); + } + + if ((value_copy[0] != 7) && + (value_copy[1] != 6) && + (value_copy[2] != 5)) { + return ttest_fail(&tc, "Bad value."); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) value, count, + (cyaml_data_t **) &value_copy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if ((value_copy2[0] != 7) && + (value_copy2[1] != 6) && + (value_copy2[2] != 5)) { + return ttest_fail(&tc, "Bad value."); + } + + return ttest_pass(&tc); +} + +/** + * Test copying with schema with sequence_fixed top level type. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_schema_top_level_sequence_fixed( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "- 7\n" + "- 6\n" + "- 5\n"; + int *value = NULL; + int *value_copy = NULL; + int value_copy2[3] = { 0, 0, 0 }; + int *value_copy2_ptr = value_copy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_SEQUENCE_FIXED(CYAML_FLAG_POINTER, int, + &entry_schema, 3) + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_SEQUENCE_FIXED(CYAML_FLAG_DEFAULT, int, + &entry_schema, 3) + }; + test_data_t td = { + .data = (cyaml_data_t **) &value, + .copy = (cyaml_data_t **) &value_copy, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &value, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) value, 3, + (cyaml_data_t **) &value_copy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (value_copy == NULL) { + return ttest_fail(&tc, "Data NULL on success."); + } + + if ((value_copy[0] != 7) && + (value_copy[1] != 6) && + (value_copy[2] != 5)) { + return ttest_fail(&tc, "Bad value."); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) value, 3, + (cyaml_data_t **) &value_copy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if ((value_copy2[0] != 7) && + (value_copy2[1] != 6) && + (value_copy2[2] != 5)) { + return ttest_fail(&tc, "Bad value."); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a stream with more than one document. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_multiple_documents_ignored( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + } data = { + .a = 9, + }; + static const unsigned char yaml[] = + "a: 9\n" + "---\n" + "b: foo\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping multiple fields. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_with_multiple_fields( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "a: 9\n" + "b: 90\n" + "c: 900\n" + "d: 9000\n" + "e: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("c", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("d", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("e", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with optional fields. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_with_optional_fields( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + long values[] = { 4, 3, 2, 1 }; + struct target_struct { + char *a; + char b[10]; + int c; + long d[4]; + long *e; + unsigned e_count; + char *f; + char *g; + char h[10]; + int i; + long j[4]; + long *k; + unsigned k_count; + } data = { + .a = (char *) "Hello", + .b = "World!", + .c = 0, + .d = { 0, 0, 0, 0 }, + .e = values, + .f = (char *) "Required!", + .g = NULL, + .h = "\0", + .i = 9876, + .j = { 1, 2, 3, 4 }, + .k = NULL, + }; + static const unsigned char yaml[] = + "a: Hello\n" + "b: World!\n" + "e: [ 4, 3, 2, 1 ]\n" + "f: Required!\n" + "i: 9876\n" + "j: [ 1, 2, 3, 4 ]\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value sequence_entry = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, sizeof(long)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING_PTR("a", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, a, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("b", CYAML_FLAG_OPTIONAL, + struct target_struct, b, 0), + CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, + struct target_struct, c), + CYAML_FIELD_SEQUENCE_FIXED("d", CYAML_FLAG_OPTIONAL, + struct target_struct, d, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("e", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, e, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("f", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, f, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("g", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, g, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("h", CYAML_FLAG_OPTIONAL, + struct target_struct, h, 0), + CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, + struct target_struct, i), + CYAML_FIELD_SEQUENCE_FIXED("j", CYAML_FLAG_OPTIONAL, + struct target_struct, j, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("k", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, k, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy->a, data.a) != 0) { + return ttest_fail(&tc, "Incorrect value for entry a: " + "Expected: %s, got: %s", + data.a, data_cpy->a); + } + if (strcmp(data_cpy->b, data.b) != 0) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy->d[i] != data.d[i]) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy->e[i] != data.e[i]) { + return ttest_fail(&tc, "Incorrect value for entry e " + "Index: %u: Expected: %ld, got: %ld", + i, data.e[i], data_cpy->e[i]); + } + } + if (strcmp(data_cpy->f, data.f) != 0) { + return ttest_fail(&tc, "Incorrect value for entry f: " + "Expected: %s, got: %s", + data.f, data_cpy->f); + } + if (data_cpy->g != data.g) { + return ttest_fail(&tc, "Incorrect value for entry g: " + "Expected: %s, got: %s", + data.g, data_cpy->g); + } + if (strcmp(data_cpy->h, data.h) != 0) { + return ttest_fail(&tc, "Incorrect value for entry h"); + } + if (data_cpy->i != data.i) { + return ttest_fail(&tc, "Incorrect value for entry i"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy->j[i] != data.j[i]) { + return ttest_fail(&tc, "Incorrect value for entry j"); + } + } + if (data_cpy->k != data.k) { + return ttest_fail(&tc, "Incorrect value for entry k"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy2.a, data.a) != 0) { + return ttest_fail(&tc, "Incorrect value for entry a: " + "Expected: %s, got: %s", + data.a, data_cpy2.a); + } + if (strcmp(data_cpy2.b, data.b) != 0) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy2.d[i] != data.d[i]) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy2.e[i] != data.e[i]) { + return ttest_fail(&tc, "Incorrect value for entry e " + "Index: %u: Expected: %ld, got: %ld", + i, data.e[i], data_cpy2.e[i]); + } + } + if (strcmp(data_cpy2.f, data.f) != 0) { + return ttest_fail(&tc, "Incorrect value for entry f: " + "Expected: %s, got: %s", + data.f, data_cpy2.f); + } + if (data_cpy2.g != data.g) { + return ttest_fail(&tc, "Incorrect value for entry g: " + "Expected: %s, got: %s", + data.g, data_cpy2.g); + } + if (strcmp(data_cpy2.h, data.h) != 0) { + return ttest_fail(&tc, "Incorrect value for entry h"); + } + if (data_cpy2.i != data.i) { + return ttest_fail(&tc, "Incorrect value for entry i"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_cpy2.j[i] != data.j[i]) { + return ttest_fail(&tc, "Incorrect value for entry j"); + } + } + if (data_cpy2.k != data.k) { + return ttest_fail(&tc, "Incorrect value for entry k"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with optional mapping missing. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_with_optional_mapping_missing( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct_map { + char *str; + }; + struct target_struct { + char *str; + struct target_struct_map *map; + } data = { + .str = (char *) "Hello", + .map = NULL, + }; + static const unsigned char yaml[] = + "str: Hello\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_STRING_PTR("str", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING_PTR("str", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_MAPPING_PTR("map", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, map, + test_mapping_schema), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy->str, data.str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry str: " + "Expected: %s, got: %s", + data.str, data_cpy->str); + } + + if (data_cpy->map != NULL) { + return ttest_fail(&tc, "Missing field map found"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy2.str, data.str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry str: " + "Expected: %s, got: %s", + data.str, data_cpy2.str); + } + + if (data_cpy2.map != NULL) { + return ttest_fail(&tc, "Missing field map found"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with optional mapping supplied. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_with_optional_mapping_supplied( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct_map { + char *str; + } data_map = { + .str = (char *) "hello", + }; + struct target_struct { + char *str; + struct target_struct_map *map; + } data = { + .str = (char *) "Hello", + .map = &data_map, + }; + static const unsigned char yaml[] = + "str: Hello\n" + "map:\n" + " str: hello"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_STRING_PTR("str", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING_PTR("str", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_MAPPING_PTR("map", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, map, + test_mapping_schema), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy->str, data.str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry str: " + "Expected: %s, got: %s", + data.str, data_cpy->str); + } + + if (data_cpy->map == NULL) { + return ttest_fail(&tc, "Supplied field map missing"); + } + + if (strcmp(data_cpy->map->str, data.map->str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry map.str: " + "Expected: %s, got: %s", + data_cpy->map->str, data.map->str); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (strcmp(data_cpy2.str, data.str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry str: " + "Expected: %s, got: %s", + data.str, data_cpy2.str); + } + + if (data_cpy2.map == NULL) { + return ttest_fail(&tc, "Supplied field map missing"); + } + + if (strcmp(data_cpy2.map->str, data.map->str) != 0) { + return ttest_fail(&tc, "Incorrect value for entry map.str: " + "Expected: %s, got: %s", + data_cpy2.map->str, data.map->str); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with only optional fields. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_only_optional_fields( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct_optional { + int c; + int i; + }; + struct target_struct { + struct target_struct_optional *s; + }; + static const unsigned char yaml[] = + "s: {}\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema_optional[] = { + CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, + struct target_struct_optional, c), + CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, + struct target_struct_optional, i), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_MAPPING_PTR("s", CYAML_FLAG_DEFAULT, + struct target_struct, s, + mapping_schema_optional), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy == NULL) { + return ttest_fail(&tc, "Should have the top level allocation"); + } + + if (data_cpy->s == NULL) { + return ttest_fail(&tc, "Should have the mapping allocation"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.s == NULL) { + return ttest_fail(&tc, "Should have the mapping allocation"); + } + + if (data_cpy2.s->c != 0) { + return ttest_fail(&tc, "Missing optional field should be zero"); + } + + if (data_cpy2.s->i != 0) { + return ttest_fail(&tc, "Missing optional field should be zero"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with unknown keys ignored by config. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_ignored_unknown_keys( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + short b; + int c; + long d; + long long e; + } data = { + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "a: 9\n" + "b: 90\n" + "c: 900\n" + "d: 9000\n" + "e: 90000\n" + "f: 900000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("c", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("d", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("e", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + cfg.flags |= CYAML_CFG_IGNORE_UNKNOWN_KEYS; + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence with max size 4, and only 2 entries in YAML. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_sequence_without_max_entries( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + const char *seq[4]; + unsigned seq_count; + } data = { + .seq = { "1", "2", NULL, NULL }, + .seq_count = 2, + }; + static const unsigned char yaml[] = + "seq: [ 1, 2 ]\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value sequence_entry = { + CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char *, + 0, CYAML_UNLIMITED) + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("seq", CYAML_FLAG_OPTIONAL, + struct target_struct, seq, &sequence_entry, + 0, 4), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_tgt->seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_tgt->seq[i] != NULL) { + if (strcmp(data_tgt->seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_cpy->seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_cpy->seq[i] != NULL) { + if (strcmp(data_cpy->seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_cpy2.seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_cpy2.seq[i] != NULL) { + if (strcmp(data_cpy2.seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying without a logging function. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_no_log( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + cyaml_config_t cfg = *config; + struct target_struct { + const char *seq[4]; + unsigned seq_count; + } data = { + .seq = { "1", "2", NULL, NULL }, + .seq_count = 2, + }; + static const unsigned char yaml[] = + "seq: [ 1, 2 ]\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value sequence_entry = { + CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char *, + 0, CYAML_UNLIMITED) + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("seq", CYAML_FLAG_OPTIONAL, + struct target_struct, seq, &sequence_entry, + 0, 4), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.log_fn = NULL; + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_tgt->seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_tgt->seq[i] != NULL) { + if (strcmp(data_tgt->seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_cpy->seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_cpy->seq[i] != NULL) { + if (strcmp(data_cpy->seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.seq_count != 2) { + return ttest_fail(&tc, "Incorrect sequence entry count"); + } + for (unsigned i = 0; i < 4; i++) { + if ((data_cpy2.seq[i] == NULL) != (data.seq[i] == NULL)) { + return ttest_fail(&tc, "Incorrect value for entry %u", + i); + } + if (data_cpy2.seq[i] != NULL) { + if (strcmp(data_cpy2.seq[i], data.seq[i]) != 0) { + return ttest_fail(&tc, + "Incorrect value for entry %u", + i); + } + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying a sequence with arbitrary C struct member name for entry count. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_schema_sequence_entry_count_member( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; + static const unsigned char yaml[] = + "sequence:\n" + " - 99999\n" + " - 99998\n" + " - 99997\n" + " - 99996\n" + " - 99995\n" + " - 99994\n"; + struct target_struct { + unsigned *entries; + uint32_t n_entries; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->entries)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE_COUNT("sequence", CYAML_FLAG_POINTER, + struct target_struct, entries, n_entries, + &entry_schema, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = config, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_tgt->n_entries) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_tgt->entries[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_tgt->entries[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy->n_entries) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy->entries[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy->entries[i], ref[i]); + } + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (CYAML_ARRAY_LEN(ref) != data_cpy2.n_entries) { + return ttest_fail(&tc, "Incorrect sequence count"); + } + + for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { + if (data_cpy2.entries[i] != ref[i]) { + return ttest_fail(&tc, "Incorrect value (i=%u): " + "got: %i, expected: %i", i, + data_cpy2.entries[i], ref[i]); + } + } + + return ttest_pass(&tc); +} + +/** + * Test copying an enum with case insensitive string matching configured. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_enum_insensitive( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_ENUM_FIRST, + TEST_ENUM_SECOND, + TEST_ENUM_THIRD, + TEST_ENUM__COUNT, + } ref = TEST_ENUM_SECOND; + static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { + { "first", TEST_ENUM_FIRST }, + { "second", TEST_ENUM_SECOND }, + { "third", TEST_ENUM_THIRD }, + }; + static const unsigned char yaml[] = + "SECOND\n"; + enum test_enum *data_tgt = NULL; + enum test_enum *data_cpy = NULL; + enum test_enum data_cpy2 = 0; + enum test_enum *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_ENUM(CYAML_FLAG_POINTER, + *data_tgt, strings, TEST_ENUM__COUNT), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, + *data_tgt, strings, TEST_ENUM__COUNT), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (*data_tgt != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (*data_cpy != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2 != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a flags value with case insensitive string matching configured. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_flags_insensitive( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_enum { + TEST_FLAG_FIRST = (1 << 1), + TEST_FLAG_SECOND = (1 << 3), + TEST_FLAG_THIRD = (1 << 5), + } ref = TEST_FLAG_FIRST | TEST_FLAG_THIRD; + static const cyaml_strval_t strings[] = { + { "first", TEST_FLAG_FIRST }, + { "second", TEST_FLAG_SECOND }, + { "third", TEST_FLAG_THIRD }, + }; + static const unsigned char yaml[] = + "- First\n" + "- Third\n"; + enum test_enum *data_tgt = NULL; + enum test_enum *data_cpy = NULL; + enum test_enum data_cpy2 = 0; + enum test_enum *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_FLAGS(CYAML_FLAG_POINTER, + *data_tgt, strings, CYAML_ARRAY_LEN(strings)), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, + *data_tgt, strings, CYAML_ARRAY_LEN(strings)), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (*data_tgt != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (*data_cpy != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2 != ref) { + return ttest_fail(&tc, "Incorrect value for enum"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with case insensitive string matching configured. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_fields_cfg_insensitive_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "Lollipop: 9\n" + "Squiggle: 90\n" + "Unicorns: 900\n" + "Cheerful: 9000\n" + "LibCYAML: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("lollipop", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_INT("squiggle", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("unicorns", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("cheerful", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("libcyaml", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with case insensitive string matching configured. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_fields_cfg_insensitive_2( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "Plinth: 9\n" + "..Cusp?: 90\n" + "..Cusp!: 900\n" + "Bleat: 9000\n" + "Foo~-|Bar: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("plinth", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_INT("..cusp?", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("..cusp!", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("bleat", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("foO~-|baR", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with case insensitive string matching configured. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_fields_cfg_insensitive_3( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "Pling: 9\n" + "Plin: 90\n" + "Pli: 900\n" + "Pl: 9000\n" + "P: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("plin", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("pli", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("pl", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("p", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with case sensitive string matching for value. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_fields_value_sensitive_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "pling: 9\n" + "PLing: 90\n" + "PLINg: 900\n" + "pliNG: 9000\n" + "PLING: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_INT("PLing", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("PLINg", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("pliNG", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("PLING", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING( + CYAML_FLAG_POINTER | CYAML_FLAG_CASE_SENSITIVE, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING( + CYAML_FLAG_CASE_SENSITIVE, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Test copying a mapping with case insensitive string matching for value. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_copy_mapping_fields_value_insensitive_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + signed char a; + short b; + int c; + long d; + long long e; + } data = { + .a = 9, + .b = 90, + .c = 900, + .d = 9000, + .e = 90000, + }; + static const unsigned char yaml[] = + "Pling: 9\n" + "Plin: 90\n" + "Pli: 900\n" + "Pl: 9000\n" + "P: 90000\n"; + struct target_struct *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + struct target_struct data_cpy2 = { 0 }; + struct target_struct *data_cpy2_ptr = &data_cpy2; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("plin", CYAML_FLAG_DEFAULT, + struct target_struct, b), + CYAML_FIELD_INT("pli", CYAML_FLAG_DEFAULT, + struct target_struct, c), + CYAML_FIELD_INT("pl", CYAML_FLAG_DEFAULT, + struct target_struct, d), + CYAML_FIELD_INT("p", CYAML_FLAG_DEFAULT, + struct target_struct, e), + CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING( + CYAML_FLAG_POINTER | + CYAML_FLAG_CASE_INSENSITIVE, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING( + CYAML_FLAG_CASE_INSENSITIVE, + struct target_struct, mapping_schema), + }; + cyaml_config_t cfg = *config; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .copy2 = (cyaml_data_t *) &data_cpy2, + .config = &cfg, + .schema = &top_schema, + .schema2 = &top_schema2, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_tgt->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_tgt->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_tgt->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy->a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy->b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy->c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy->d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy->e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + err = cyaml_copy(config, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy2_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy2.a != data.a) { + return ttest_fail(&tc, "Incorrect value for entry a"); + } + if (data_cpy2.b != data.b) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_cpy2.c != data.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + if (data_cpy2.d != data.d) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + if (data_cpy2.e != data.e) { + return ttest_fail(&tc, "Incorrect value for entry e"); + } + + return ttest_pass(&tc); +} + +/** + * Run the YAML copying unit tests. + * + * \param[in] rc The ttest report context. + * \param[in] log_level CYAML log level. + * \param[in] log_fn CYAML logging function, or NULL. + * \return true iff all unit tests pass, otherwise false. + */ +bool copy_tests( + ttest_report_ctx_t *rc, + cyaml_log_t log_level, + cyaml_log_fn_t log_fn) +{ + bool pass = true; + cyaml_config_t config = { + .log_fn = log_fn, + .mem_fn = cyaml_mem, + .log_level = log_level, + .flags = CYAML_CFG_DEFAULT, + }; + + ttest_heading(rc, "Copy single entry mapping tests: simple types"); + + pass &= test_copy_mapping_entry_enum(rc, &config); + pass &= test_copy_mapping_entry_uint(rc, &config); + pass &= test_copy_mapping_entry_float(rc, &config); + pass &= test_copy_mapping_entry_double(rc, &config); + pass &= test_copy_mapping_entry_string(rc, &config); + pass &= test_copy_mapping_entry_int_pos(rc, &config); + pass &= test_copy_mapping_entry_int_neg(rc, &config); + pass &= test_copy_mapping_entry_bool_true(rc, &config); + pass &= test_copy_mapping_entry_bool_false(rc, &config); + pass &= test_copy_mapping_entry_string_ptr(rc, &config); + pass &= test_copy_mapping_entry_enum_sparse(rc, &config); + pass &= test_copy_mapping_entry_enum_strict(rc, &config); + pass &= test_copy_mapping_entry_ignore_deep(rc, &config); + pass &= test_copy_mapping_entry_ignore_scalar(rc, &config); + + ttest_heading(rc, "Copy single entry mapping tests: complex types"); + + pass &= test_copy_mapping_entry_flags(rc, &config); + pass &= test_copy_mapping_entry_mapping(rc, &config); + pass &= test_copy_mapping_entry_mapping_ptr(rc, &config); + pass &= test_copy_mapping_entry_flags_empty(rc, &config); + pass &= test_copy_mapping_entry_flags_sparse(rc, &config); + pass &= test_copy_mapping_entry_flags_strict(rc, &config); + + ttest_heading(rc, "Copy single entry mapping tests: sequences"); + + pass &= test_copy_mapping_entry_sequence_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_enum(rc, &config); + pass &= test_copy_mapping_entry_sequence_uint(rc, &config); + pass &= test_copy_mapping_entry_sequence_bool(rc, &config); + pass &= test_copy_mapping_entry_sequence_flags(rc, &config); + pass &= test_copy_mapping_entry_sequence_string(rc, &config); + pass &= test_copy_mapping_entry_sequence_mapping(rc, &config); + pass &= test_copy_mapping_entry_sequence_string_ptr(rc, &config); + pass &= test_copy_mapping_entry_sequence_mapping_ptr(rc, &config); + pass &= test_copy_mapping_entry_sequence_sequence_fixed_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_sequence_fixed_ptr_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_sequence_fixed_flat_int(rc, &config); + + ttest_heading(rc, "Copy single entry mapping tests: ptr sequences"); + + pass &= test_copy_mapping_entry_sequence_ptr_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_enum(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_uint(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_bool(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_flags(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_string(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_mapping(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_string_ptr(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_mapping_ptr(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_sequence_fixed_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_sequence_fixed_ptr_int(rc, &config); + pass &= test_copy_mapping_entry_sequence_ptr_sequence_fixed_flat_int(rc, &config); + + ttest_heading(rc, "Copy tests: sequence count sizes"); + + pass &= test_copy_mapping_entry_sequence_count_1(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_2(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_3(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_4(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_5(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_6(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_7(rc, &config); + pass &= test_copy_mapping_entry_sequence_count_8(rc, &config); + + ttest_heading(rc, "Copy tests: various"); + + pass &= test_copy_no_log(rc, &config); + pass &= test_copy_schema_top_level_scalar(rc, &config); + pass &= test_copy_schema_top_level_sequence(rc, &config); + pass &= test_copy_multiple_documents_ignored(rc, &config); + pass &= test_copy_mapping_with_multiple_fields(rc, &config); + pass &= test_copy_mapping_with_optional_fields(rc, &config); + pass &= test_copy_mapping_only_optional_fields(rc, &config); + pass &= test_copy_mapping_ignored_unknown_keys(rc, &config); + pass &= test_copy_sequence_without_max_entries(rc, &config); + pass &= test_copy_schema_top_level_sequence_fixed(rc, &config); + pass &= test_copy_schema_sequence_entry_count_member(rc, &config); + pass &= test_copy_mapping_with_optional_mapping_missing(rc, &config); + pass &= test_copy_mapping_with_optional_mapping_supplied(rc, &config); + + ttest_heading(rc, "Copy tests: case sensitivity"); + + pass &= test_copy_enum_insensitive(rc, &config); + pass &= test_copy_flags_insensitive(rc, &config); + pass &= test_copy_mapping_fields_cfg_insensitive_1(rc, &config); + pass &= test_copy_mapping_fields_cfg_insensitive_2(rc, &config); + pass &= test_copy_mapping_fields_cfg_insensitive_3(rc, &config); + pass &= test_copy_mapping_fields_value_sensitive_1(rc, &config); + pass &= test_copy_mapping_fields_value_insensitive_1(rc, &config); + + return pass; +} diff --git a/test/units/errs.c b/test/units/errs.c index dd8480c..261eeca 100644 --- a/test/units/errs.c +++ b/test/units/errs.c @@ -27,6 +27,7 @@ typedef struct test_data { char **buffer; cyaml_data_t **data; + cyaml_data_t **copy; unsigned *seq_count; const struct cyaml_config *config; const struct cyaml_schema_value *schema; @@ -53,6 +54,10 @@ static void cyaml_cleanup(void *data) if (td->data != NULL) { cyaml_free(td->config, td->schema, *(td->data), seq_count); } + + if (td->copy != NULL) { + cyaml_free(td->config, td->schema, *(td->copy), seq_count); + } } /** @@ -149,6 +154,102 @@ static bool test_err_save_null_data( return ttest_pass(&tc); } +/** + * Test copying with NULL data parameter. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_null_data( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + int value; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) &data_tgt, 0, + (cyaml_data_t **) NULL); + if (err != CYAML_ERR_BAD_PARAM_NULL_DATA) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + +/** + * Test copying an unsigned integer. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_null_data2( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + unsigned test_value_uint; + } data_tgt = { + .test_value_uint = 9999, + }; + struct target_struct *data_cpy = &data_tgt; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_uint), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = NULL, + .copy = NULL, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) &data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_DATA_TARGET_NON_NULL) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + return ttest_pass(&tc); +} + /** * Test loading with NULL config parameter. * @@ -237,6 +338,43 @@ static bool test_err_save_null_config( return ttest_pass(&tc); } +/** + * Test copying with NULL config parameter. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_null_config( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + void *data_tgt = NULL; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = config, + .schema = NULL, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(NULL, NULL, (const cyaml_data_t *) NULL, 0, + (cyaml_data_t **) NULL); + if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with NULL memory allocation function. * @@ -331,6 +469,46 @@ static bool test_err_save_null_mem_fn( return ttest_pass(&tc); } +/** + * Test copying with NULL memory allocation function. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_null_mem_fn( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + cyaml_config_t cfg = *config; + void *data_tgt = NULL; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = &cfg, + .schema = NULL, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + cfg.mem_fn = NULL; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(&cfg, NULL, (const cyaml_data_t *) NULL, 0, + (cyaml_data_t **) NULL); + if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with NULL schema. * @@ -410,6 +588,43 @@ static bool test_err_save_null_schema( return ttest_pass(&tc); } +/** + * Test copying with NULL schema. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_null_schema( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + void *data_tgt = NULL; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = config, + .schema = NULL, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, NULL, (const cyaml_data_t *) NULL, 0, + (cyaml_data_t **) NULL); + if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with schema with bad top level type (non-pointer). * @@ -496,6 +711,47 @@ static bool test_err_save_schema_top_level_non_pointer( return ttest_pass(&tc); } +/** + * Test copying with schema with bad top level type (non-pointer). + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_schema_top_level_non_pointer( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int *value = NULL; + int *value_cpy = NULL; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) + }; + test_data_t td = { + .data = (cyaml_data_t **) &value, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, (const cyaml_data_t *) &value, 0, + (cyaml_data_t **) &value_cpy); + if (err != CYAML_ERR_BAD_PARAM_NULL_DATA) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (value != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with schema with bad top level sequence and no seq_count. * @@ -590,29 +846,31 @@ static bool test_err_load_schema_top_level_not_sequence_count( } /** - * Test saving with schema with a non-sequence and non-zero sequence count. + * Test copying with schema with bad top level sequence and no seq_count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ -static bool test_err_save_schema_top_level_not_sequence_count( +static bool test_err_copy_schema_top_level_sequence_no_count( ttest_report_ctx_t *report, const cyaml_config_t *config) { - static const struct target_struct { - unsigned *test_uint; - } data = { - .test_uint = NULL, + struct target_struct { + int *value; + unsigned value_count; + } *data_tgt = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { - CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) + CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, + &entry_schema, 0, CYAML_UNLIMITED), }; - char *buffer = NULL; - size_t len = 0; test_data_t td = { - .buffer = &buffer, + .data = (cyaml_data_t **) &data_tgt, .config = config, + .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; @@ -621,36 +879,82 @@ static bool test_err_save_schema_top_level_not_sequence_count( return true; } - err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 42); + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) &data_tgt, 0, + (cyaml_data_t **) NULL); if (err != CYAML_ERR_BAD_PARAM_SEQ_COUNT) { return ttest_fail(&tc, cyaml_strerror(err)); } - if (buffer != NULL || len != 0) { - return ttest_fail(&tc, "Buffer/len not untouched."); + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** - * Test loading with schema with bad type. + * Test saving with schema with a non-sequence and non-zero sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ -static bool test_err_load_schema_bad_type( +static bool test_err_save_schema_top_level_not_sequence_count( ttest_report_ctx_t *report, const cyaml_config_t *config) { - static const unsigned char yaml[] = - "key:\n"; - struct target_struct { - int value; - } *data_tgt = NULL; - static const struct cyaml_schema_field mapping_schema[] = { - { + static const struct target_struct { + unsigned *test_uint; + } data = { + .test_uint = NULL, + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) + }; + char *buffer = NULL; + size_t len = 0; + test_data_t td = { + .buffer = &buffer, + .config = config, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 42); + if (err != CYAML_ERR_BAD_PARAM_SEQ_COUNT) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (buffer != NULL || len != 0) { + return ttest_fail(&tc, "Buffer/len not untouched."); + } + + return ttest_pass(&tc); +} + +/** + * Test loading with schema with bad type. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_load_schema_bad_type( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "key:\n"; + struct target_struct { + int value; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + { .key = "key", .value = { .type = 99999, @@ -747,6 +1051,65 @@ static bool test_err_save_schema_bad_type( return ttest_pass(&tc); } +/** + * Test copying with schema with bad type. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_schema_bad_type( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + int value; + } data_tgt = { + .value = 9, + }; + struct target_struct *data_cpy = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "key", + .value = { + .type = 99999, + .flags = CYAML_FLAG_DEFAULT, + .data_size = sizeof(data_tgt.value), + }, + .data_offset = offsetof(struct target_struct, value), + }, + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_cpy, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) &data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_BAD_TYPE_IN_SCHEMA) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with schema with string min greater than max. * @@ -1957,6 +2320,81 @@ static bool test_err_save_schema_bad_data_size_8( return ttest_pass(&tc); } +/** + * Test copying with schema with bad sequence count size. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_schema_bad_data_size_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + unsigned arr[] = { 1, 2, 3, 4, 5 }; + struct target_struct { + unsigned *seq; + uint32_t seq_count; + } data = { + .seq = arr, + .seq_count = CYAML_ARRAY_LEN(arr), + }; + struct target_struct *copy = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data.seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "sequence", + .value = { + .type = CYAML_SEQUENCE, + .flags = CYAML_FLAG_POINTER, + .data_size = sizeof(*(data.seq)), + .sequence = { + .entry = &entry_schema, + .min = 0, + .max = 10, + + }, + }, + .data_offset = offsetof(struct target_struct, seq), + .count_offset = offsetof(struct target_struct, seq_count), + .count_size = 9, + }, + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + char *buffer = NULL; + size_t len = 0; + test_data_t td = { + .copy = (cyaml_data_t **) ©, + .buffer = &buffer, + .config = config, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) &data, 0, + (cyaml_data_t **) ©); + if (err != CYAML_ERR_INVALID_DATA_SIZE) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (buffer != NULL || len != 0) { + return ttest_fail(&tc, "Buffer/len not untouched."); + } + + return ttest_pass(&tc); +} + /** * Test loading with schema with sequence fixed with unequal min and max. * @@ -2094,6 +2532,78 @@ static bool test_err_save_schema_sequence_min_max( return ttest_pass(&tc); } +/** + * Test copying with schema with sequence fixed with unequal min and max. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_schema_sequence_min_max( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct { + unsigned *seq; + uint32_t seq_count; + } data_tgt = { + .seq = NULL, + .seq_count = 0, + }; + struct target_struct *data_cpy = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt.seq)), + }; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "sequence", + .value = { + .type = CYAML_SEQUENCE_FIXED, + .flags = CYAML_FLAG_POINTER, + .data_size = sizeof(*(data_tgt.seq)), + .sequence = { + .entry = &entry_schema, + .min = 0, + .max = CYAML_UNLIMITED, + + }, + }, + .data_offset = offsetof(struct target_struct, seq), + .count_offset = offsetof(struct target_struct, seq_count), + .count_size = sizeof(data_tgt.seq_count), + }, + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) &data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_SEQUENCE_FIXED_COUNT) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_cpy != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + /** * Test loading with schema with data size for float. * @@ -2253,32 +2763,30 @@ static bool test_err_save_schema_sequence_in_sequence( } /** - * Test loading when schema expects uint, but value is invalid. + * Test copying with schema with sequence in sequence. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ -static bool test_err_load_schema_invalid_value_uint( +static bool test_err_copy_schema_sequence_in_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { - static const unsigned char yaml[] = - "a: scalar\n"; - struct target_struct { - unsigned a; - } *data_tgt = NULL; - static const struct cyaml_schema_field mapping_schema[] = { - CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, - struct target_struct, a), - CYAML_FIELD_END + unsigned data[2][2] = { { 1, 2 }, { 3, 4 } }; + unsigned **seq = NULL; + static const struct cyaml_schema_value inner_entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, **seq), + }; + static const struct cyaml_schema_value outer_entry_schema = { + CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned, + &inner_entry_schema, 0, CYAML_UNLIMITED) }; static const struct cyaml_schema_value top_schema = { - CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, - struct target_struct, mapping_schema), + CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned *, + &outer_entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { - .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; @@ -2289,13 +2797,14 @@ static bool test_err_load_schema_invalid_value_uint( return true; } - err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, - (cyaml_data_t **) &data_tgt, NULL); - if (err != CYAML_ERR_INVALID_VALUE) { + err = cyaml_copy(config, &top_schema, + (const cyaml_data_t *) data, 2, + (cyaml_data_t **) &seq); + if (err != CYAML_ERR_SEQUENCE_IN_SEQUENCE) { return ttest_fail(&tc, cyaml_strerror(err)); } - if (data_tgt != NULL) { + if (seq != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } @@ -2303,13 +2812,127 @@ static bool test_err_load_schema_invalid_value_uint( } /** - * Test loading with schema with string top level type, with bad value. + * Test copying a mapping with optional mapping missing. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ -static bool test_err_load_schema_invalid_value_string( +static bool test_err_copy_schema_required_mapping_missing( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + struct target_struct_map { + char *str; + }; + struct target_struct { + char *str; + struct target_struct_map *map; + } data = { + .str = (char *) "Hello", + .map = NULL, + }; + struct target_struct *data_cpy = NULL; + static const struct cyaml_schema_field test_mapping_schema[] = { + CYAML_FIELD_STRING_PTR("map-str", + CYAML_FLAG_POINTER, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_STRING_PTR("str", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, str, 0, CYAML_UNLIMITED), + CYAML_FIELD_MAPPING_PTR("map", CYAML_FLAG_POINTER, + struct target_struct, map, + test_mapping_schema), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = NULL, + .copy = (cyaml_data_t **) &data_cpy, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_copy(config, &top_schema, + (cyaml_data_t *) &data, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_MAPPING_FIELD_MISSING) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + return ttest_pass(&tc); +} + +/** + * Test loading when schema expects uint, but value is invalid. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_load_schema_invalid_value_uint( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "a: scalar\n"; + struct target_struct { + unsigned a; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, + struct target_struct, a), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .config = config, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_ERR_INVALID_VALUE) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt != NULL) { + return ttest_fail(&tc, "Data non-NULL on error."); + } + + return ttest_pass(&tc); +} + +/** + * Test loading with schema with string top level type, with bad value. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_load_schema_invalid_value_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { @@ -6385,6 +7008,416 @@ static bool test_err_save_alloc_oom_2( return ttest_pass(&tc); } +/** + * Test copying, with all memory allocation failure at every possible point. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_alloc_oom_1( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + cyaml_config_t cfg = *config; + static const unsigned char yaml[] = + "animals:\n" + " - kind: cat\n" + " sound: meow\n" + " position: [ 1, 2, 1]\n" + " - kind: snake\n" + " sound: hiss\n" + " position: [ 3, 1, 0]\n"; + struct animal_s { + char *kind; + char *sound; + int *position; + }; + struct target_struct { + struct animal_s **animal; + uint32_t animal_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + static const struct cyaml_schema_value position_entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_field animal_schema[] = { + CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, + struct animal_s, kind, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, + struct animal_s, sound, 0, CYAML_UNLIMITED), + CYAML_FIELD_SEQUENCE_FIXED("position", CYAML_FLAG_POINTER, + struct animal_s, position, + &position_entry_schema, 3), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value animal_entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), + animal_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, + struct target_struct, animal, + &animal_entry_schema, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .config = &cfg, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + struct test_cyaml_mem_ctx mem_ctx = { + .required = 0, + }; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + /* + * First we load the YAML with the counting allocation function, + * to find the number of allocations required to load the document. + * This is deterministic. + */ + cfg.mem_fn = test_cyaml_mem_count_allocs; + cfg.mem_ctx = &mem_ctx; + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(&cfg, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (mem_ctx.required == 0) { + return ttest_fail(&tc, "There were no allocations."); + } + + cyaml_free(config, &top_schema, data_cpy, 0); + data_cpy = NULL; + + /* + * Now we load the document multiple times, forcing every possible + * allocation to fail. + */ + cfg.mem_fn = test_cyaml_mem_fail; + + for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; + mem_ctx.fail++) { + mem_ctx.current = 0; + err = cyaml_copy(&cfg, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_OOM) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Now free what was loaded. */ + cyaml_free(config, &top_schema, data_cpy, 0); + data_cpy = NULL; + } + + return ttest_pass(&tc); +} + +/** + * Test copying, with all memory allocation failure at every possible point. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_alloc_oom_2( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + enum test_f { + NONE = 0, + FIRST = (1 << 0), + SECOND = (1 << 1), + THIRD = (1 << 2), + FOURTH = (1 << 3), + }; + static const cyaml_strval_t strings[] = { + { "first", (1 << 0) }, + { "second", (1 << 1) }, + { "third", (1 << 2) }, + { "fourth", (1 << 3) }, + }; + cyaml_config_t cfg = *config; + static const unsigned char yaml[] = + "animals:\n" + " - kind: cat\n" + " sound: meow\n" + " position: [ 1, 2, 1]\n" + " flags:\n" + " - first\n" + " - second\n" + " - third\n" + " - fourth\n" + " - kind: snake\n" + " sound: hiss\n" + " position: [ 3, 1, 0]\n" + " flags:\n" + " - first\n" + " - second\n" + " - third\n" + " - fourth\n"; + struct animal_s { + char *kind; + char *sound; + int **position; + unsigned position_count; + enum test_f *flags; + }; + struct target_struct { + struct animal_s **animal; + uint32_t animal_count; + } *data_tgt = NULL; + struct target_struct *data_cpy = NULL; + static const struct cyaml_schema_value position_entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_POINTER, int), + }; + static const struct cyaml_schema_field animal_schema[] = { + CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, + struct animal_s, kind, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, + struct animal_s, sound, 0, CYAML_UNLIMITED), + CYAML_FIELD_SEQUENCE("position", CYAML_FLAG_POINTER, + struct animal_s, position, + &position_entry_schema, 0, CYAML_UNLIMITED), + CYAML_FIELD_FLAGS("flags", + CYAML_FLAG_STRICT | CYAML_FLAG_POINTER, + struct animal_s, flags, strings, 4), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value animal_entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), + animal_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, + struct target_struct, animal, + &animal_entry_schema, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + struct test_cyaml_mem_ctx mem_ctx = { + .required = 0, + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = (cyaml_data_t **) &data_cpy, + .config = &cfg, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + /* + * First we load the YAML with the counting allocation function, + * to find the number of allocations required to load the document. + * This is deterministic. + */ + cfg.mem_fn = test_cyaml_mem_count_allocs; + cfg.mem_ctx = &mem_ctx; + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(&cfg, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (mem_ctx.required == 0) { + return ttest_fail(&tc, "There were no allocations."); + } + + cyaml_free(config, &top_schema, data_cpy, 0); + data_cpy = NULL; + + /* + * Now we load the document multiple times, forcing every possible + * allocation to fail. + */ + cfg.mem_fn = test_cyaml_mem_fail; + + for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; + mem_ctx.fail++) { + mem_ctx.current = 0; + err = cyaml_copy(&cfg, &top_schema, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy); + if (err != CYAML_ERR_OOM) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Now free what was loaded. */ + cyaml_free(config, &top_schema, data_cpy, 0); + data_cpy = NULL; + } + + return ttest_pass(&tc); +} + +/** + * Test copying, with all memory allocation failure at every possible point. + * + * \param[in] report The test report context. + * \param[in] config The CYAML config to use for the test. + * \return true if test passes, false otherwise. + */ +static bool test_err_copy_alloc_oom_3( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + cyaml_config_t cfg = *config; + static const unsigned char yaml[] = + "animals:\n" + " - kind: cat\n" + " sound: meow\n" + " position: [ 1, 2, 1]\n" + " - kind: snake\n" + " sound: hiss\n" + " position: [ 3, 1, 0]\n"; + struct animal_s { + char *kind; + char *sound; + int *position; + }; + struct target_struct { + struct animal_s **animal; + uint32_t animal_count; + } *data_tgt = NULL; + struct target_struct data_cpy = { 0 }; + struct target_struct *data_cpy_ptr = &data_cpy; + static const struct cyaml_schema_value position_entry_schema = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), + }; + static const struct cyaml_schema_field animal_schema[] = { + CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, + struct animal_s, kind, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, + struct animal_s, sound, 0, CYAML_UNLIMITED), + CYAML_FIELD_SEQUENCE_FIXED("position", CYAML_FLAG_POINTER, + struct animal_s, position, + &position_entry_schema, 3), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value animal_entry_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), + animal_schema), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, + struct target_struct, animal, + &animal_entry_schema, 0, CYAML_UNLIMITED), + CYAML_FIELD_END + }; + static const struct cyaml_schema_value top_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, + struct target_struct, mapping_schema), + }; + static const struct cyaml_schema_value top_schema2 = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, + struct target_struct, mapping_schema), + }; + test_data_t td = { + .data = (cyaml_data_t **) &data_tgt, + .copy = NULL, + .config = &cfg, + .schema = &top_schema, + }; + cyaml_err_t err; + ttest_ctx_t tc; + struct test_cyaml_mem_ctx mem_ctx = { + .required = 0, + }; + + if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { + return true; + } + + /* + * First we load the YAML with the counting allocation function, + * to find the number of allocations required to load the document. + * This is deterministic. + */ + cfg.mem_fn = test_cyaml_mem_count_allocs; + cfg.mem_ctx = &mem_ctx; + + err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + err = cyaml_copy(&cfg, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy_ptr); + if (err != CYAML_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (mem_ctx.required == 0) { + return ttest_fail(&tc, "There were no allocations."); + } + + cyaml_free(config, &top_schema2, data_cpy_ptr, 0); + data_cpy_ptr = &data_cpy; + memset(data_cpy_ptr, 0, sizeof(*data_cpy_ptr)); + + /* + * Now we load the document multiple times, forcing every possible + * allocation to fail. + */ + cfg.mem_fn = test_cyaml_mem_fail; + + for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; + mem_ctx.fail++) { + mem_ctx.current = 0; + err = cyaml_copy(&cfg, &top_schema2, + (cyaml_data_t *) data_tgt, 0, + (cyaml_data_t **) &data_cpy_ptr); + if (err != CYAML_ERR_OOM) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + /* Now free what was loaded. */ + cyaml_free(config, &top_schema2, data_cpy_ptr, 0); + data_cpy_ptr = &data_cpy; + memset(data_cpy_ptr, 0, sizeof(*data_cpy_ptr)); + } + + return ttest_pass(&tc); +} + /** * Test loading a flag with an aliased value. * @@ -7219,15 +8252,22 @@ bool errs_tests( pass &= test_err_load_null_data(rc, &config); pass &= test_err_save_null_data(rc, &config); + pass &= test_err_copy_null_data(rc, &config); + pass &= test_err_copy_null_data2(rc, &config); pass &= test_err_load_null_config(rc, &config); pass &= test_err_save_null_config(rc, &config); + pass &= test_err_copy_null_config(rc, &config); pass &= test_err_load_null_mem_fn(rc, &config); pass &= test_err_save_null_mem_fn(rc, &config); + pass &= test_err_copy_null_mem_fn(rc, &config); pass &= test_err_load_null_schema(rc, &config); pass &= test_err_save_null_schema(rc, &config); + pass &= test_err_copy_null_schema(rc, &config); pass &= test_err_load_schema_top_level_non_pointer(rc, &config); pass &= test_err_save_schema_top_level_non_pointer(rc, &config); + pass &= test_err_copy_schema_top_level_non_pointer(rc, &config); pass &= test_err_load_schema_top_level_sequence_no_count(rc, &config); + pass &= test_err_copy_schema_top_level_sequence_no_count(rc, &config); pass &= test_err_load_schema_top_level_not_sequence_count(rc, &config); pass &= test_err_save_schema_top_level_not_sequence_count(rc, &config); @@ -7235,6 +8275,7 @@ bool errs_tests( pass &= test_err_load_schema_bad_type(rc, &config); pass &= test_err_save_schema_bad_type(rc, &config); + pass &= test_err_copy_schema_bad_type(rc, &config); pass &= test_err_load_schema_bad_bitfield(rc, &config); pass &= test_err_save_schema_bad_bitfield(rc, &config); pass &= test_err_load_schema_string_min_max(rc, &config); @@ -7255,11 +8296,15 @@ bool errs_tests( pass &= test_err_save_schema_bad_data_size_6(rc, &config); pass &= test_err_save_schema_bad_data_size_7(rc, &config); pass &= test_err_save_schema_bad_data_size_8(rc, &config); + pass &= test_err_copy_schema_bad_data_size_1(rc, &config); pass &= test_err_load_schema_sequence_min_max(rc, &config); pass &= test_err_save_schema_sequence_min_max(rc, &config); + pass &= test_err_copy_schema_sequence_min_max(rc, &config); pass &= test_err_load_schema_bad_data_size_float(rc, &config); pass &= test_err_load_schema_sequence_in_sequence(rc, &config); pass &= test_err_save_schema_sequence_in_sequence(rc, &config); + pass &= test_err_copy_schema_sequence_in_sequence(rc, &config); + pass &= test_err_copy_schema_required_mapping_missing(rc, &config); ttest_heading(rc, "YAML / schema mismatch: bad values"); @@ -7354,6 +8399,9 @@ bool errs_tests( pass &= test_err_load_alloc_oom_2(rc, &config); pass &= test_err_save_alloc_oom_1(rc, &config); pass &= test_err_save_alloc_oom_2(rc, &config); + pass &= test_err_copy_alloc_oom_1(rc, &config); + pass &= test_err_copy_alloc_oom_2(rc, &config); + pass &= test_err_copy_alloc_oom_3(rc, &config); ttest_heading(rc, "Alias tests"); diff --git a/test/units/test.c b/test/units/test.c index 4fc35c1..542e6f8 100644 --- a/test/units/test.c +++ b/test/units/test.c @@ -78,6 +78,7 @@ int main(int argc, char *argv[]) pass &= errs_tests(&rc, log_level, log_fn); pass &= file_tests(&rc, log_level, log_fn); pass &= save_tests(&rc, log_level, log_fn); + pass &= copy_tests(&rc, log_level, log_fn); ttest_report(&rc); diff --git a/test/units/test.h b/test/units/test.h index 2445750..642f9cd 100644 --- a/test/units/test.h +++ b/test/units/test.h @@ -49,4 +49,10 @@ extern bool save_tests( cyaml_log_t log_level, cyaml_log_fn_t log_fn); +/** In copy.c */ +extern bool copy_tests( + ttest_report_ctx_t *rc, + cyaml_log_t log_level, + cyaml_log_fn_t log_fn); + #endif