From fc7aae32ba873cc185aa11f99235a6f4cb0ad784 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 17 May 2023 12:42:48 +0100 Subject: [PATCH 1/8] Load: Mapping validation: Check field set before optional --- src/load.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/load.c b/src/load.c index ff17356..d29dda1 100644 --- a/src/load.c +++ b/src/load.c @@ -1133,13 +1133,13 @@ static cyaml_err_t cyaml__mapping_bitfieid_validate( unsigned count = state->mapping.fields_count; for (unsigned i = 0; i < count; i++) { - if (state->mapping.fields[i].value.flags & CYAML_FLAG_OPTIONAL) { - continue; - } if (state->mapping.fields_set[i / CYAML_BITFIELD_BITS] & (1u << (i % CYAML_BITFIELD_BITS))) { continue; } + if (state->mapping.fields[i].value.flags & CYAML_FLAG_OPTIONAL) { + continue; + } cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Missing required mapping field: %s\n", state->mapping.fields[i].key); From 1e534396fa387192a2dd219c8882f186335f9ccf Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Wed, 17 May 2023 12:45:09 +0100 Subject: [PATCH 2/8] Load: Mapping validation: Use flag check helper --- src/load.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/load.c b/src/load.c index d29dda1..9653d9e 100644 --- a/src/load.c +++ b/src/load.c @@ -1137,7 +1137,8 @@ static cyaml_err_t cyaml__mapping_bitfieid_validate( (1u << (i % CYAML_BITFIELD_BITS))) { continue; } - if (state->mapping.fields[i].value.flags & CYAML_FLAG_OPTIONAL) { + if (cyaml__flag_check_all(state->mapping.fields[i].value.flags, + CYAML_FLAG_OPTIONAL)) { continue; } cyaml__log(ctx->config, CYAML_LOG_ERROR, From 8d354b290741f176b434d7dc683a8267b79b7aed Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 15:20:01 +0100 Subject: [PATCH 3/8] Load: Mapping validation: Simplify field indexing --- src/load.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/load.c b/src/load.c index 9653d9e..c0b3d9f 100644 --- a/src/load.c +++ b/src/load.c @@ -1133,17 +1133,19 @@ static cyaml_err_t cyaml__mapping_bitfieid_validate( unsigned count = state->mapping.fields_count; for (unsigned i = 0; i < count; i++) { + const cyaml_schema_field_t *field = state->mapping.fields + i; + if (state->mapping.fields_set[i / CYAML_BITFIELD_BITS] & (1u << (i % CYAML_BITFIELD_BITS))) { continue; } - if (cyaml__flag_check_all(state->mapping.fields[i].value.flags, + if (cyaml__flag_check_all(field->value.flags, CYAML_FLAG_OPTIONAL)) { continue; } cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Missing required mapping field: %s\n", - state->mapping.fields[i].key); + field->key); return CYAML_ERR_MAPPING_FIELD_MISSING; } From d159cfe9b4a3b913a7f101d2f19474d6160c07f5 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 15:20:53 +0100 Subject: [PATCH 4/8] API: Load: Add support for default values Default values are applied when an optional mapping field is missing. To use them, a `missing` value must be supplied in the schema. Closes #96. --- include/cyaml/cyaml.h | 106 +++++++++++++++++++++- src/load.c | 201 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 303 insertions(+), 4 deletions(-) diff --git a/include/cyaml/cyaml.h b/include/cyaml/cyaml.h index cc74bf7..fd07ac1 100644 --- a/include/cyaml/cyaml.h +++ b/include/cyaml/cyaml.h @@ -22,6 +22,7 @@ extern "C" #include #include #include +#include /** * CYAML library version string. @@ -382,6 +383,50 @@ typedef struct cyaml_schema_value { uint32_t data_size; /** Anonymous union containing type-specific attributes. */ union { + /** \ref CYAML_INT type-specific schema data. */ + struct { + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + int64_t missing; + } integer; + /** \ref CYAML_UINT type-specific schema data. */ + struct { + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + uint64_t missing; + } unsigned_integer; + /** \ref CYAML_BOOL type-specific schema data. */ + struct { + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + bool missing; + } boolean; + /** \ref CYAML_FLOAT type-specific schema data. */ + struct { + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + double missing; + } floating_point; /** \ref CYAML_STRING type-specific schema data. */ struct { /** @@ -398,6 +443,17 @@ typedef struct cyaml_schema_value { * must be no more than `data_size - 1`. */ uint32_t max; + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + * + * \note This is may be NULL, if no default sequence is + * wanted for a missing field in the YAML. + */ + const char *missing; } string; /** \ref CYAML_MAPPING type-specific schema data. */ struct { @@ -409,6 +465,17 @@ typedef struct cyaml_schema_value { * and \ref CYAML_FIELD_END for more info. */ const struct cyaml_schema_field *fields; + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + * + * \note This is may be NULL, if no default sequence is + * wanted for a missing field in the YAML. + */ + const void *missing; } mapping; /** \ref CYAML_BITFIELD type-specific schema data. */ struct { @@ -416,6 +483,14 @@ typedef struct cyaml_schema_value { const struct cyaml_bitdef *bitdefs; /** Entry count for bitdefs array. */ uint32_t count; + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + uint64_t missing; } bitfield; /** * \ref CYAML_SEQUENCE and \ref CYAML_SEQUENCE_FIXED @@ -447,6 +522,21 @@ typedef struct cyaml_schema_value { * CYAML_SEQUENCE_FIXED. */ uint32_t max; + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + * + * \note This is may be NULL, if no default sequence is + * wanted for a missing field in the YAML. + */ + const void *missing; + /** + * Number of entries in missing array. + */ + uint32_t missing_count; } sequence; /** * \ref CYAML_ENUM and \ref CYAML_FLAGS type-specific schema @@ -457,6 +547,14 @@ typedef struct cyaml_schema_value { const cyaml_strval_t *strings; /** Entry count for strings array. */ uint32_t count; + /** + * Value to use for missing YAML field. + * + * This is only used when the value is used for a + * mapping field with the \ref CYAML_FLAG_OPTIONAL flag + * set. + */ + int64_t missing; } enumeration; }; } cyaml_schema_value_t; @@ -640,17 +738,17 @@ typedef enum cyaml_err { ((_flags) & (~CYAML_FLAG_POINTER)) /** Type to \ref cyaml_schema_value union member for INT. */ -#define CYAML__UNION_MEMBER_INT .enumeration +#define CYAML__UNION_MEMBER_INT .integer /** Type to \ref cyaml_schema_value union member for UINT. */ -#define CYAML__UNION_MEMBER_UINT .enumeration +#define CYAML__UNION_MEMBER_UINT .unsigned_integer /** Type to \ref cyaml_schema_value union member for BOOL. */ -#define CYAML__UNION_MEMBER_BOOL .enumeration +#define CYAML__UNION_MEMBER_BOOL .boolean /** Type to \ref cyaml_schema_value union member for ENUM. */ #define CYAML__UNION_MEMBER_ENUM .enumeration /** Type to \ref cyaml_schema_value union member for FLAGS. */ #define CYAML__UNION_MEMBER_FLAGS .enumeration /** Type to \ref cyaml_schema_value union member for FLOAT. */ -#define CYAML__UNION_MEMBER_FLOAT .enumeration +#define CYAML__UNION_MEMBER_FLOAT .floating_point /** Type to \ref cyaml_schema_value union member for STRING. */ #define CYAML__UNION_MEMBER_STRING .string /** Type to \ref cyaml_schema_value union member for MAPPING. */ diff --git a/src/load.c b/src/load.c index c0b3d9f..055b2d7 100644 --- a/src/load.c +++ b/src/load.c @@ -1113,6 +1113,198 @@ static bool cyaml__mapping_bitfieid_check( (1u << (idx % CYAML_BITFIELD_BITS)); } +/** + * Check whether a numerical value has a zero-value missing entry. + * + * \param[in] schema The schema for a value. + * \return true if numerical value has zero missing value, false otherwise. + */ +static bool cyaml__numerical_default_zero( + const cyaml_schema_value_t *schema) +{ + switch (schema->type) { + case CYAML_INT: + return 0 == schema->integer.missing; + case CYAML_ENUM: + return 0 == schema->enumeration.missing; + case CYAML_UINT: + return 0 == schema->unsigned_integer.missing; + case CYAML_FLAGS: + return 0 == schema->enumeration.missing; + case CYAML_BITFIELD: + return 0 == schema->bitfield.missing; + case CYAML_BOOL: + return 0 == schema->boolean.missing; + case CYAML_FLOAT: + return 0 == schema->floating_point.missing; + default: + return false; + } +} + +/** + * Store any scalar default value for a missing field. + * + * \param[in] ctx The CYAML loading context. + * \param[in] field The missing field's schema. + * \param[in] data Client data pointer for parent mapping. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__field_scalar_apply_default( + const cyaml_ctx_t *ctx, + const cyaml_schema_field_t *field, + uint8_t *data) +{ + const cyaml_schema_value_t *schema = &field->value; + + if (cyaml__numerical_default_zero(schema)) { + return CYAML_OK; + } + + if (schema->type == CYAML_STRING) { + if (schema->string.missing == NULL) { + return CYAML_OK; + } + } + + if (schema->flags & CYAML_FLAG_POINTER) { + uint8_t *value_data = NULL; + size_t size; + + switch (schema->type) { + case CYAML_STRING: + size = strlen(schema->string.missing) + 1; + break; + default: + size = schema->data_size; + break; + } + + value_data = cyaml__realloc(ctx->config, value_data, 0, + size, true); + if (value_data == NULL) { + return CYAML_ERR_OOM; + } + + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Load: Allocation: %p (%zu bytes)\n", + value_data, size); + + cyaml_data_write_pointer(value_data, data); + data = value_data; + } + + switch (schema->type) { + case CYAML_INT: + return cyaml__store_int(ctx, schema, + schema->integer.missing, data); + case CYAML_ENUM: + return cyaml__store_int(ctx, schema, + schema->enumeration.missing, data); + case CYAML_UINT: + return cyaml__store_uint(ctx, schema, + schema->unsigned_integer.missing, data); + case CYAML_FLAGS: + return cyaml__store_int(ctx, schema, + schema->enumeration.missing, data); + case CYAML_BITFIELD: + return cyaml__store_uint(ctx, schema, + schema->bitfield.missing, data); + case CYAML_BOOL: + return cyaml__store_bool(ctx, schema, + schema->boolean.missing, data); + case CYAML_FLOAT: + return cyaml__store_float(ctx, schema, + schema->floating_point.missing, data); + case CYAML_STRING: + return cyaml__store_string(ctx, schema, + schema->string.missing, data); + default: + return CYAML_ERR_INTERNAL_ERROR; + } +} + +/** + * Store any default value for a missing field. + * + * \param[in] ctx The CYAML loading context. + * \param[in] field The missing field's schema. + * \param[in] data Client data pointer for parent mapping. + * \return \ref CYAML_OK on success, or appropriate error code otherwise. + */ +static cyaml_err_t cyaml__field_apply_default( + const cyaml_ctx_t *ctx, + const cyaml_schema_field_t *field, + uint8_t *data) +{ + const cyaml_schema_value_t *schema = &field->value; + const cyaml_data_t *schema_default; + uint64_t seq_count = 0; + cyaml_err_t err; + bool ptr; + + 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_FLAGS: /* Fall through */ + case CYAML_FLOAT: /* Fall through */ + case CYAML_STRING: /* Fall through */ + case CYAML_BITFIELD: + return cyaml__field_scalar_apply_default(ctx, field, data); + case CYAML_MAPPING: + if (schema->mapping.missing == NULL) { + return CYAML_OK; + } + schema_default = schema->mapping.missing; + break; + case CYAML_SEQUENCE: /* Fall through */ + case CYAML_SEQUENCE_FIXED: + if (schema->sequence.missing == NULL) { + return CYAML_OK; + } + schema_default = schema->sequence.missing; + if (schema->type == CYAML_SEQUENCE) { + seq_count = schema->sequence.missing_count; + } else { + seq_count = schema->sequence.max; + } + break; + case CYAML_IGNORE: + return CYAML_OK; + default: + return CYAML_ERR_BAD_TYPE_IN_SCHEMA; + } + + ptr = cyaml__flag_check_all(schema->flags, CYAML_FLAG_POINTER); + err = cyaml_copy(ctx->config, schema, + schema_default, + (unsigned) seq_count, + ptr ? (cyaml_data_t **) data : + (cyaml_data_t **)&data); + if (err != CYAML_OK) { + cyaml__log(ctx->config, CYAML_LOG_ERROR, + "Load: Failed to copy default value " + "for field %s to %p\n", + field->key, data); + goto exit; + } + + if (schema->type == CYAML_SEQUENCE) { + err = cyaml_data_write(seq_count, + field->count_size, + ctx->state->data + field->count_offset); + if (err != CYAML_OK) { + cyaml__log(ctx->config, CYAML_LOG_ERROR, + "Load: Failed writing sequence count\n"); + } + } + +exit: + return err; +} + /** * Check a mapping had all the required fields. * @@ -1141,6 +1333,15 @@ static cyaml_err_t cyaml__mapping_bitfieid_validate( } if (cyaml__flag_check_all(field->value.flags, CYAML_FLAG_OPTIONAL)) { + cyaml_err_t err; + cyaml__log(ctx->config, CYAML_LOG_DEBUG, + "Load: Using default value for: %s\n", + field->key); + err = cyaml__field_apply_default(ctx, field, + state->data + field->data_offset); + if (err != CYAML_OK) { + return err; + } continue; } cyaml__log(ctx->config, CYAML_LOG_ERROR, From d0e0734d3f3dd81b5c81e5fd391b12fe38b83ce1 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 15:22:56 +0100 Subject: [PATCH 5/8] Test: Load: Add tests for use of default values --- test/units/load.c | 2886 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2886 insertions(+) diff --git a/test/units/load.c b/test/units/load.c index 38b1942..f1362ef 100644 --- a/test/units/load.c +++ b/test/units/load.c @@ -102,6 +102,2845 @@ static bool test_load_mapping_entry_int_pos( return ttest_pass(&tc); } +/** + * Test loading a positive unsigned 8-bit integer with a default 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_load_mapping_field_default_u8( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t test = 0x55; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(UINT, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a positive signed integer with a default 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_load_mapping_field_default_int( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int value = 90; + const int default_value = -1; + const unsigned trample = 0xdeadbeef; + static const unsigned char yaml[] = + "test_int: 90\n" + "trample: 0xdeadbeef\n"; + struct target_struct { + int test_value_int; + int default_int; + unsigned trample; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, + struct target_struct, test_value_int), + CYAML_FIELD(INT, "default", CYAML_FLAG_OPTIONAL, + struct target_struct, default_int, + { .missing = default_value }), + CYAML_FIELD_UINT("trample", CYAML_FLAG_DEFAULT, + struct target_struct, trample), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + if (data_tgt->default_int != default_value) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->trample != trample) { + return ttest_fail(&tc, "Trampled structure"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a boolean with a default 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_load_mapping_field_default_bool( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const bool test = true; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + bool test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(BOOL, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading an enum with a default 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_load_mapping_field_default_enum( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const enum enum_test { + ENUM_TEST_BAT, + ENUM_TEST_DOG, + ENUM_TEST_CAT, + } test = ENUM_TEST_CAT; + static const cyaml_strval_t enum_test_schema[] = { + { .str = "cat", .val = ENUM_TEST_CAT }, + { .str = "bat", .val = ENUM_TEST_BAT }, + { .str = "dog", .val = ENUM_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum enum_test test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(ENUM, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .strings = enum_test_schema, + .count = CYAML_ARRAY_LEN(enum_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading flags with a default 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_load_mapping_field_default_flags( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const enum flags_test { + FLAGS_TEST_BAT = 1 << 0, + FLAGS_TEST_DOG = 1 << 1, + FLAGS_TEST_CAT = 1 << 2, + } test = FLAGS_TEST_CAT | FLAGS_TEST_BAT; + static const cyaml_strval_t flags_test_schema[] = { + { .str = "cat", .val = FLAGS_TEST_CAT }, + { .str = "bat", .val = FLAGS_TEST_BAT }, + { .str = "dog", .val = FLAGS_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum flags_test test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(FLAGS, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .strings = flags_test_schema, + .count = CYAML_ARRAY_LEN(flags_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a float with a default 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_load_mapping_field_default_float( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const float test = 3.14159f; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + float test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(FLOAT, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %f, got: %f)", + test, data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a double precision float with a default 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_load_mapping_field_default_double( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const double test = 3.14159; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + double test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(FLOAT, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %f, got: %f)", + test, data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a string with a default 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_load_mapping_field_default_string( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const char * const test = "My cat is best cat!"; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + char test[20]; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(STRING, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, .min = 0, .max = 19 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (strcmp(data_tgt->test, test) != 0) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %s, got: %s)", + test, data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a bitfield with a default 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_load_mapping_field_default_bitfield( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint64_t test = 0xFFFFFFFFFFFFFFFFu; + static const cyaml_bitdef_t bitvals[] = { + { .name = "a", .offset = 0, .bits = 3 }, + { .name = "b", .offset = 3, .bits = 7 }, + { .name = "c", .offset = 10, .bits = 32 }, + { .name = "d", .offset = 42, .bits = 8 }, + { .name = "e", .offset = 50, .bits = 14 }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint64_t test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(BITFIELD, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .bitdefs = bitvals, + .count = CYAML_ARRAY_LEN(bitvals) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a mapping with a default 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_load_mapping_field_default_mapping_small( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const struct test_struct { + uint8_t value; + } test = { + .value = 0x55, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + struct test_struct test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field test_schema[] = { + CYAML_FIELD_UINT("value", CYAML_FLAG_DEFAULT, + struct test_struct, value), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(MAPPING, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = &test, + .fields = test_schema, + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test.value != test.value) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %u, got: %u)", + test.value, data_tgt->test.value); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a mapping with a default 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_load_mapping_field_default_mapping_large( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static long values[] = { 4, 3, 2, 1 }; + const uint8_t before = 1; + static const struct test_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; + bool l; + } test = { + .a = (char *) "Hello", + .b = "World!", + .c = 0, + .d = { 0, 0, 0, 0 }, + .e = values, + .e_count = CYAML_ARRAY_LEN(values), + .f = (char *) "Required!", + .g = NULL, + .h = "\0", + .i = 9876, + .j = { 1, 2, 3, 4 }, + .k = NULL, + .l = false, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + struct test_struct test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value sequence_entry = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, sizeof(long)), + }; + static const struct cyaml_schema_field test_schema[] = { + CYAML_FIELD_STRING_PTR("a", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, a, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("b", CYAML_FLAG_OPTIONAL, + struct test_struct, b, 0), + CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, + struct test_struct, c), + CYAML_FIELD_SEQUENCE_FIXED("d", CYAML_FLAG_OPTIONAL, + struct test_struct, d, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("e", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, e, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("f", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, f, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("g", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, g, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("h", CYAML_FLAG_OPTIONAL, + struct test_struct, h, 0), + CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, + struct test_struct, i), + CYAML_FIELD_SEQUENCE_FIXED("j", CYAML_FLAG_OPTIONAL, + struct test_struct, j, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("k", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, k, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_BOOL("l", + CYAML_FLAG_OPTIONAL, + struct test_struct, l), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(MAPPING, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = &test, + .fields = test_schema, + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (strcmp(data_tgt->test.a, test.a) != 0) { + return ttest_fail(&tc, "Incorrect value for entry a: " + "Expected: %s, got: %s", + test.a, data_tgt->test.a); + } + if (strcmp(data_tgt->test.b, test.b) != 0) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->test.c != test.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test.d[i] != test.d[i]) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + } + if (data_tgt->test.e_count != test.e_count) { + return ttest_fail(&tc, "Bad sequence element count for e"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test.e[i] != test.e[i]) { + return ttest_fail(&tc, "Incorrect value for entry e " + "Index: %u: Expected: %ld, got: %ld", + i, test.e[i], data_tgt->test.e[i]); + } + } + if (strcmp(data_tgt->test.f, test.f) != 0) { + return ttest_fail(&tc, "Incorrect value for entry f: " + "Expected: %s, got: %s", + test.f, data_tgt->test.f); + } + if (data_tgt->test.g != test.g) { + return ttest_fail(&tc, "Incorrect value for entry g: " + "Expected: %s, got: %s", + test.g, data_tgt->test.g); + } + if (strcmp(data_tgt->test.h, test.h) != 0) { + return ttest_fail(&tc, "Incorrect value for entry h"); + } + if (data_tgt->test.i != test.i) { + return ttest_fail(&tc, "Incorrect value for entry i"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test.j[i] != test.j[i]) { + return ttest_fail(&tc, "Incorrect value for entry j"); + } + } + if (data_tgt->test.k != test.k) { + return ttest_fail(&tc, "Incorrect value for entry k"); + } + + if (data_tgt->test.l != test.l) { + return ttest_fail(&tc, "Incorrect value for entry l"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a sequence with a default 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_load_mapping_field_default_sequence_small( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint8_t test[] = { 99 }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t test[CYAML_ARRAY_LEN(test)]; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, uint8_t), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(SEQUENCE_FIXED, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .entry = &entry_schema, + .min = CYAML_ARRAY_LEN(test), + .max = CYAML_ARRAY_LEN(test), + .missing = test, }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + for (size_t i = 0; i < CYAML_ARRAY_LEN(test); i++) { + if (data_tgt->test[i] != test[i]) { + return ttest_fail(&tc, "Incorrect default value"); + } + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a sequence with a default 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_load_mapping_field_default_sequence_large( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint8_t test[] = { + 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t test[CYAML_ARRAY_LEN(test)]; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, uint8_t), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD(SEQUENCE_FIXED, "test", CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .entry = &entry_schema, + .min = CYAML_ARRAY_LEN(test), + .max = CYAML_ARRAY_LEN(test), + .missing = test, }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + for (size_t i = 0; i < CYAML_ARRAY_LEN(test); i++) { + if (data_tgt->test[i] != test[i]) { + return ttest_fail(&tc, "Incorrect default value"); + } + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for an unsigned integer 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_load_mapping_field_default_u8_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t test = 0x55; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(UINT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a signed integer 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_load_mapping_field_default_int_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + int value = 90; + const int default_value = -1; + static const unsigned char yaml[] = + "test_int: 90\n"; + struct target_struct { + int *test_value_int; + int *default_int; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_INT_PTR("test_int", CYAML_FLAG_POINTER, + struct target_struct, test_value_int), + CYAML_FIELD_PTR(INT, "default", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, default_int, + { .missing = default_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_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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->test_value_int == NULL) { + return ttest_fail(&tc, "Expected allocation for value"); + } + + if (*data_tgt->test_value_int != value) { + return ttest_fail(&tc, "Incorrect value"); + } + + if (data_tgt->default_int == NULL) { + return ttest_fail(&tc, "Expected allocated default value"); + } + + if (*data_tgt->default_int != default_value) { + return ttest_fail(&tc, "Incorrect default value"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a boolean 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_load_mapping_field_default_bool_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const bool test = true; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + bool *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(BOOL, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a enum 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_load_mapping_field_default_enum_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const enum enum_test { + ENUM_TEST_BAT, + ENUM_TEST_DOG, + ENUM_TEST_CAT, + } test = ENUM_TEST_CAT; + static const cyaml_strval_t enum_test_schema[] = { + { .str = "cat", .val = ENUM_TEST_CAT }, + { .str = "bat", .val = ENUM_TEST_BAT }, + { .str = "dog", .val = ENUM_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum enum_test *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(ENUM, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .strings = enum_test_schema, + .count = CYAML_ARRAY_LEN(enum_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a flags 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_load_mapping_field_default_flags_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const enum flags_test { + FLAGS_TEST_BAT = 1 << 0, + FLAGS_TEST_DOG = 1 << 1, + FLAGS_TEST_CAT = 1 << 2, + } test = FLAGS_TEST_CAT | FLAGS_TEST_BAT; + static const cyaml_strval_t flags_test_schema[] = { + { .str = "cat", .val = FLAGS_TEST_CAT }, + { .str = "bat", .val = FLAGS_TEST_BAT }, + { .str = "dog", .val = FLAGS_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum flags_test *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLAGS, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .strings = flags_test_schema, + .count = CYAML_ARRAY_LEN(flags_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a float 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_load_mapping_field_default_float_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const float test = 3.14159f; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + float *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLOAT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %f, got: %f)", + test, *data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a double precision float 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_load_mapping_field_default_double_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const double test = 3.14159; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + double *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLOAT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %f, got: %f)", + test, *data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a string 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_load_mapping_field_default_string_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const char * const test = "Hello World!"; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + char *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(STRING, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .min = 0, .max = CYAML_UNLIMITED }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (strcmp(data_tgt->test, test) != 0) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %s, got: %s)", + test, *data_tgt->test); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a bitfield 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_load_mapping_field_default_bitfield_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint64_t test = 0xFFFFFFFFFFFFFFFFu; + static const cyaml_bitdef_t bitvals[] = { + { .name = "a", .offset = 0, .bits = 3 }, + { .name = "b", .offset = 3, .bits = 7 }, + { .name = "c", .offset = 10, .bits = 32 }, + { .name = "d", .offset = 42, .bits = 8 }, + { .name = "e", .offset = 50, .bits = 14 }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint64_t *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(BITFIELD, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = test, + .bitdefs = bitvals, + .count = CYAML_ARRAY_LEN(bitvals) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (*data_tgt->test != test) { + return ttest_fail(&tc, "Incorrect default value"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a mapping 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_load_mapping_field_default_mapping_small_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const struct test_struct { + uint8_t value; + } test = { + .value = 0x55, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + struct test_struct *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field test_schema[] = { + CYAML_FIELD_UINT("value", CYAML_FLAG_DEFAULT, + struct test_struct, value), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(MAPPING, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = &test, + .fields = test_schema, + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (data_tgt->test->value != test.value) { + return ttest_fail(&tc, "Incorrect default value " + "(expected: %u, got: %u)", + test.value, data_tgt->test->value); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a mapping 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_load_mapping_field_default_mapping_large_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static long values[] = { 4, 3, 2, 1 }; + const uint8_t before = 1; + static const struct test_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; + bool l; + } test = { + .a = (char *) "Hello", + .b = "World!", + .c = 0, + .d = { 0, 0, 0, 0 }, + .e = values, + .e_count = CYAML_ARRAY_LEN(values), + .f = (char *) "Required!", + .g = NULL, + .h = "\0", + .i = 9876, + .j = { 1, 2, 3, 4 }, + .k = NULL, + .l = false, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + struct test_struct *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value sequence_entry = { + CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, sizeof(long)), + }; + static const struct cyaml_schema_field test_schema[] = { + CYAML_FIELD_STRING_PTR("a", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, a, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("b", CYAML_FLAG_OPTIONAL, + struct test_struct, b, 0), + CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, + struct test_struct, c), + CYAML_FIELD_SEQUENCE_FIXED("d", CYAML_FLAG_OPTIONAL, + struct test_struct, d, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("e", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, e, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("f", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, f, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING_PTR("g", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, g, 0, CYAML_UNLIMITED), + CYAML_FIELD_STRING("h", CYAML_FLAG_OPTIONAL, + struct test_struct, h, 0), + CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, + struct test_struct, i), + CYAML_FIELD_SEQUENCE_FIXED("j", CYAML_FLAG_OPTIONAL, + struct test_struct, j, &sequence_entry, 4), + CYAML_FIELD_SEQUENCE("k", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct test_struct, k, &sequence_entry, + 0, CYAML_UNLIMITED), + CYAML_FIELD_BOOL("l", + CYAML_FLAG_OPTIONAL, + struct test_struct, l), + CYAML_FIELD_END + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(MAPPING, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = &test, + .fields = test_schema, + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (strcmp(data_tgt->test->a, test.a) != 0) { + return ttest_fail(&tc, "Incorrect value for entry a: " + "Expected: %s, got: %s", + test.a, data_tgt->test->a); + } + if (strcmp(data_tgt->test->b, test.b) != 0) { + return ttest_fail(&tc, "Incorrect value for entry b"); + } + if (data_tgt->test->c != test.c) { + return ttest_fail(&tc, "Incorrect value for entry c"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test->d[i] != test.d[i]) { + return ttest_fail(&tc, "Incorrect value for entry d"); + } + } + if (data_tgt->test->e_count != test.e_count) { + return ttest_fail(&tc, "Bad sequence element count for e"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test->e[i] != test.e[i]) { + return ttest_fail(&tc, "Incorrect value for entry e " + "Index: %u: Expected: %ld, got: %ld", + i, test.e[i], data_tgt->test->e[i]); + } + } + if (strcmp(data_tgt->test->f, test.f) != 0) { + return ttest_fail(&tc, "Incorrect value for entry f: " + "Expected: %s, got: %s", + test.f, data_tgt->test->f); + } + if (data_tgt->test->g != test.g) { + return ttest_fail(&tc, "Incorrect value for entry g: " + "Expected: %s, got: %s", + test.g, data_tgt->test->g); + } + if (strcmp(data_tgt->test->h, test.h) != 0) { + return ttest_fail(&tc, "Incorrect value for entry h"); + } + if (data_tgt->test->i != test.i) { + return ttest_fail(&tc, "Incorrect value for entry i"); + } + for (unsigned i = 0; i < 4; i++) { + if (data_tgt->test->j[i] != test.j[i]) { + return ttest_fail(&tc, "Incorrect value for entry j"); + } + } + if (data_tgt->test->k != test.k) { + return ttest_fail(&tc, "Incorrect value for entry k"); + } + + if (data_tgt->test->l != test.l) { + return ttest_fail(&tc, "Incorrect value for entry l"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a sequence 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_load_mapping_field_default_sequence_small_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint8_t test[] = { 99 }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t *test; + unsigned test_count; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, uint8_t), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(SEQUENCE, "test", + CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER, + struct target_struct, test, + { .entry = &entry_schema, + .min = CYAML_ARRAY_LEN(test), + .max = CYAML_ARRAY_LEN(test), + .missing = test, + .missing_count = CYAML_ARRAY_LEN(test) }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (data_tgt->test_count != CYAML_ARRAY_LEN(test)) { + return ttest_fail(&tc, "Incorrect entry count"); + } + + for (size_t i = 0; i < CYAML_ARRAY_LEN(test); i++) { + if (data_tgt->test[i] != test[i]) { + return ttest_fail(&tc, "Incorrect default value"); + } + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a sequence 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_load_mapping_field_default_sequence_large_ptr( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const uint8_t test[] = { + 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t *test; + unsigned test_count; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_value entry_schema = { + CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, uint8_t), + }; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(SEQUENCE, "test", + CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER, + struct target_struct, test, + { .entry = &entry_schema, + .min = CYAML_ARRAY_LEN(test), + .max = CYAML_ARRAY_LEN(test), + .missing = test, + .missing_count = CYAML_ARRAY_LEN(test) }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test == NULL) { + return ttest_fail(&tc, "Failed to get default allocation"); + } + + if (data_tgt->test_count != CYAML_ARRAY_LEN(test)) { + return ttest_fail(&tc, "Incorrect entry count"); + } + + for (size_t i = 0; i < CYAML_ARRAY_LEN(test); i++) { + if (data_tgt->test[i] != test[i]) { + return ttest_fail(&tc, "Incorrect default value"); + } + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a positive unsigned 8-bit integer via 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_load_mapping_field_default_u8_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint8_t *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(UINT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a signed integer 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_load_mapping_field_default_int_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + int *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(INT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a boolean 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_load_mapping_field_default_bool_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + bool *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(BOOL, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a enum 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_load_mapping_field_default_enum_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + enum enum_test { + ENUM_TEST_BAT, + ENUM_TEST_DOG, + ENUM_TEST_CAT, + }; + static const cyaml_strval_t enum_test_schema[] = { + { .str = "cat", .val = ENUM_TEST_CAT }, + { .str = "bat", .val = ENUM_TEST_BAT }, + { .str = "dog", .val = ENUM_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum enum_test *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(ENUM, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0, + .strings = enum_test_schema, + .count = CYAML_ARRAY_LEN(enum_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a flags 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_load_mapping_field_default_flags_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + enum flags_test { + FLAGS_TEST_BAT = 1 << 0, + FLAGS_TEST_DOG = 1 << 1, + FLAGS_TEST_CAT = 1 << 2, + }; + static const cyaml_strval_t flags_test_schema[] = { + { .str = "cat", .val = FLAGS_TEST_CAT }, + { .str = "bat", .val = FLAGS_TEST_BAT }, + { .str = "dog", .val = FLAGS_TEST_DOG }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + enum flags_test *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLAGS, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0, + .strings = flags_test_schema, + .count = CYAML_ARRAY_LEN(flags_test_schema) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a float 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_load_mapping_field_default_float_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + float *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLOAT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a double precision float 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_load_mapping_field_default_double_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + double *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(FLOAT, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0 }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + +/** + * Test loading a default value for a bitfield 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_load_mapping_field_default_bitfield_ptr_zero( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + const uint8_t before = 1; + static const cyaml_bitdef_t bitvals[] = { + { .name = "a", .offset = 0, .bits = 3 }, + { .name = "b", .offset = 3, .bits = 7 }, + { .name = "c", .offset = 10, .bits = 32 }, + { .name = "d", .offset = 42, .bits = 8 }, + { .name = "e", .offset = 50, .bits = 14 }, + }; + const uint8_t after = 0xff; + static const unsigned char yaml[] = + "before: 1\n" + "after: 0xff\n"; + struct target_struct { + uint8_t before; + uint64_t *test; + uint8_t after; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_UINT("before", CYAML_FLAG_DEFAULT, + struct target_struct, before), + CYAML_FIELD_PTR(BITFIELD, "test", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct target_struct, test, + { .missing = 0, + .bitdefs = bitvals, + .count = CYAML_ARRAY_LEN(bitvals) + }), + CYAML_FIELD_UINT("after", CYAML_FLAG_DEFAULT, + struct target_struct, after), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->before != before) { + return ttest_fail(&tc, "Incorrect value before default"); + } + + if (data_tgt->test != NULL) { + return ttest_fail(&tc, "Shouldn't have an allocation"); + } + + if (data_tgt->after != after) { + return ttest_fail(&tc, "Incorrect value after default"); + } + + return ttest_pass(&tc); +} + /** * Test loading a negative signed integer. * @@ -5704,6 +8543,7 @@ static bool test_load_mapping_with_optional_fields( .c = 0, .d = { 0, 0, 0, 0 }, .e = values, + .e_count = CYAML_ARRAY_LEN(values), .f = (char *) "Required!", .g = NULL, .h = "\0", @@ -5796,6 +8636,9 @@ static bool test_load_mapping_with_optional_fields( return ttest_fail(&tc, "Incorrect value for entry d"); } } + if (data_tgt->e_count != data.e_count) { + return ttest_fail(&tc, "Bad sequence element count for e"); + } for (unsigned i = 0; i < 4; i++) { if (data_tgt->e[i] != data.e[i]) { return ttest_fail(&tc, "Incorrect value for entry e " @@ -7721,5 +10564,48 @@ bool load_tests( pass &= test_load_anchor_updated_anchor(rc, &config); + ttest_heading(rc, "Load tests: default values"); + + pass &= test_load_mapping_field_default_u8(rc, &config); + pass &= test_load_mapping_field_default_int(rc, &config); + pass &= test_load_mapping_field_default_bool(rc, &config); + pass &= test_load_mapping_field_default_enum(rc, &config); + pass &= test_load_mapping_field_default_flags(rc, &config); + pass &= test_load_mapping_field_default_float(rc, &config); + pass &= test_load_mapping_field_default_double(rc, &config); + pass &= test_load_mapping_field_default_string(rc, &config); + pass &= test_load_mapping_field_default_bitfield(rc, &config); + pass &= test_load_mapping_field_default_mapping_large(rc, &config); + pass &= test_load_mapping_field_default_mapping_small(rc, &config); + pass &= test_load_mapping_field_default_sequence_large(rc, &config); + pass &= test_load_mapping_field_default_sequence_small(rc, &config); + + ttest_heading(rc, "Load tests: default values (pointers)"); + + pass &= test_load_mapping_field_default_u8_ptr(rc, &config); + pass &= test_load_mapping_field_default_int_ptr(rc, &config); + pass &= test_load_mapping_field_default_bool_ptr(rc, &config); + pass &= test_load_mapping_field_default_enum_ptr(rc, &config); + pass &= test_load_mapping_field_default_flags_ptr(rc, &config); + pass &= test_load_mapping_field_default_float_ptr(rc, &config); + pass &= test_load_mapping_field_default_double_ptr(rc, &config); + pass &= test_load_mapping_field_default_string_ptr(rc, &config); + pass &= test_load_mapping_field_default_bitfield_ptr(rc, &config); + pass &= test_load_mapping_field_default_mapping_large_ptr(rc, &config); + pass &= test_load_mapping_field_default_mapping_small_ptr(rc, &config); + pass &= test_load_mapping_field_default_sequence_large_ptr(rc, &config); + pass &= test_load_mapping_field_default_sequence_small_ptr(rc, &config); + + ttest_heading(rc, "Load tests: default values zero (pointers)"); + + pass &= test_load_mapping_field_default_u8_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_int_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_bool_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_enum_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_flags_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_float_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_double_ptr_zero(rc, &config); + pass &= test_load_mapping_field_default_bitfield_ptr_zero(rc, &config); + return pass; } From 15ff005f214dccff439e8fb40868c6f5317ede83 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 16:27:23 +0100 Subject: [PATCH 6/8] Test: Load: Add test for optional ignored value --- test/units/load.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/units/load.c b/test/units/load.c index f1362ef..1bbe819 100644 --- a/test/units/load.c +++ b/test/units/load.c @@ -4341,6 +4341,56 @@ static bool test_load_mapping_entry_ignore_scalar( return ttest_pass(&tc); } +/** + * Test loading an optional 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_load_mapping_entry_ignore_optional_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; + static const struct cyaml_schema_field mapping_schema[] = { + CYAML_FIELD_IGNORE("ignore", CYAML_FLAG_DEFAULT), + CYAML_FIELD_IGNORE("optional", CYAML_FLAG_OPTIONAL), + 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_OK) { + return ttest_fail(&tc, cyaml_strerror(err)); + } + + if (data_tgt->foo != false) { + return ttest_fail(&tc, "Incorrect value"); + } + + return ttest_pass(&tc); +} + /** * Test loading a flag word. * @@ -10448,6 +10498,7 @@ bool load_tests( pass &= test_load_mapping_entry_string_ptr_empty(rc, &config); pass &= test_load_mapping_entry_string_ptr_null_str(rc, &config); pass &= test_load_mapping_entry_string_ptr_null_empty(rc, &config); + pass &= test_load_mapping_entry_ignore_optional_scalar(rc, &config); ttest_heading(rc, "Load single entry mapping tests: complex types"); From 54be5e3e4078050c6c445eb174e0a3e2e072a050 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 16:28:11 +0100 Subject: [PATCH 7/8] Test: Errors: Add default values to OOM test --- test/units/errs.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/units/errs.c b/test/units/errs.c index 261eeca..6a23af1 100644 --- a/test/units/errs.c +++ b/test/units/errs.c @@ -6460,6 +6460,7 @@ static bool test_err_load_alloc_oom_1( const cyaml_config_t *config) { cyaml_config_t cfg = *config; + static const int test[] = { 99, 98, 97 }; static const unsigned char yaml[] = "animals:\n" " - kind: cat\n" @@ -6472,6 +6473,9 @@ static bool test_err_load_alloc_oom_1( char *kind; char *sound; int *position; + int *default_int; + int *default_sequence; + unsigned default_sequence_count; }; struct target_struct { struct animal_s **animal; @@ -6488,6 +6492,18 @@ static bool test_err_load_alloc_oom_1( CYAML_FIELD_SEQUENCE_FIXED("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 3), + CYAML_FIELD_PTR(INT, "default_int", + CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, + struct animal_s, default_int, + { .missing = 9 }), + CYAML_FIELD_PTR(SEQUENCE, "default_sequence", + CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER, + struct animal_s, default_sequence, + { .entry = &position_entry_schema, + .min = CYAML_ARRAY_LEN(test), + .max = CYAML_ARRAY_LEN(test), + .missing = test, + .missing_count = CYAML_ARRAY_LEN(test) }), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { From 95afbb813bbe2ee43f1128fb0f48536d5c0aba3c Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 28 May 2023 19:56:52 +0100 Subject: [PATCH 8/8] Test: Errors: Add bad schema tests for default values --- test/units/errs.c | 134 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/test/units/errs.c b/test/units/errs.c index 6a23af1..7e1a288 100644 --- a/test/units/errs.c +++ b/test/units/errs.c @@ -994,6 +994,63 @@ static bool test_err_load_schema_bad_type( return ttest_pass(&tc); } +/** + * Test loading with schema default value 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_default( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const unsigned char yaml[] = + "{}\n"; + struct target_struct { + int value; + } *data_tgt = NULL; + static const struct cyaml_schema_field mapping_schema[] = { + { + .key = "key", + .value = { + .type = 99999, + .flags = CYAML_FLAG_OPTIONAL, + .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_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_BAD_TYPE_IN_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 saving with schema with bad type. * @@ -1809,6 +1866,80 @@ static bool test_err_load_schema_bad_data_size_9( return ttest_pass(&tc); } +/** + * Test loading with schema with data size (9) for 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_data_size_10( + ttest_report_ctx_t *report, + const cyaml_config_t *config) +{ + static const int missing[] = { 1, 2, 3, 4 }; + static const unsigned char yaml[] = + "{}\n"; + struct target_struct { + int value[4]; + 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_field mapping_schema[] = { + { + .key = "key", + .value = { + .type = CYAML_SEQUENCE, + .flags = CYAML_FLAG_OPTIONAL, + .data_size = sizeof(*(data_tgt->value)), + .sequence = { + .min = 0, + .max = 4, + .entry = &entry_schema, + .missing = missing, + .missing_count = + CYAML_ARRAY_LEN(missing), + }, + }, + .data_offset = offsetof(struct target_struct, value), + .count_size = 9, + .count_offset = offsetof( + struct target_struct, + value_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_load_data(yaml, YAML_LEN(yaml), config, &top_schema, + (cyaml_data_t **) &data_tgt, NULL); + if (err != CYAML_ERR_INVALID_DATA_SIZE) { + 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 saving with schema with data size (0). * @@ -2212,7 +2343,6 @@ static bool test_err_save_schema_bad_data_size_7( .entry = &entry_schema, .min = 0, .max = 10, - }, }, .data_offset = offsetof(struct target_struct, seq), @@ -8313,9 +8443,11 @@ bool errs_tests( 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_bad_data_size_10(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_type_default(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);