pax_global_header00006660000000000000000000000064144346236040014520gustar00rootroot0000000000000052 comment=e2bea44c472719daff095541b22b038fa54c274b libcyaml-1.4.1/000077500000000000000000000000001443462360400133175ustar00rootroot00000000000000libcyaml-1.4.1/.github/000077500000000000000000000000001443462360400146575ustar00rootroot00000000000000libcyaml-1.4.1/.github/workflows/000077500000000000000000000000001443462360400167145ustar00rootroot00000000000000libcyaml-1.4.1/.github/workflows/ci-devel.yaml000066400000000000000000000031341443462360400212710ustar00rootroot00000000000000name: CI-devel on: [push] jobs: linux-gcc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y gcc git libyaml-dev make valgrind - name: build run: make - name: test run: make test - name: valgrind test run: make valgrind-quiet - name: sanitize test run: make VARIANT=san test linux-clang: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y clang git libyaml-dev make valgrind - name: build run: make CC=clang - name: test run: make test CC=clang - name: valgrind test run: CFLAGS=-gdwarf-4 make -B valgrind-quiet CC=clang - name: sanitize test run: make CC=clang VARIANT=san test mac-gcc: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: brew install deps run: brew update && brew install libyaml - name: build run: make - name: test run: make test mac-clang: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: brew install deps run: brew update && brew install libyaml - name: build run: make CC=clang - name: test run: make test CC=clang libcyaml-1.4.1/.github/workflows/ci.yaml000066400000000000000000000031601443462360400201730ustar00rootroot00000000000000name: CI on: push: branches: - main jobs: linux-gcc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y gcc git libyaml-dev make valgrind - name: build run: make - name: test run: make test - name: valgrind test run: make valgrind-quiet - name: sanitize test run: make VARIANT=san test linux-clang: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y clang git libyaml-dev make valgrind - name: build run: make CC=clang - name: test run: make test CC=clang - name: valgrind test run: CFLAGS=-gdwarf-4 make -B valgrind-quiet CC=clang - name: sanitize test run: make CC=clang VARIANT=san test mac-gcc: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: brew install deps run: brew update && brew install libyaml - name: build run: make - name: test run: make test mac-clang: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: brew install deps run: brew update && brew install libyaml - name: build run: make CC=clang - name: test run: make test CC=clang libcyaml-1.4.1/.github/workflows/coverage.yaml000066400000000000000000000015771443462360400214050ustar00rootroot00000000000000name: Coverage on: pull_request: branches: - main push: branches: - main jobs: coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Python uses: actions/setup-python@master with: version: 3.10 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y gcc gcovr git libyaml-dev make - name: coverage test run: make coverage - name: filter output run: find build/coverage -name "*.gc*" -type f -delete; - name: upload coverage uses: codecov/codecov-action@v3 with: token: ${{secrets.CODECOV_TOKEN}} files: build/coverage.xml flags: unittests name: codecov-umbrella verbose: true libcyaml-1.4.1/.github/workflows/static-analysis.yaml000066400000000000000000000025021443462360400227070ustar00rootroot00000000000000name: "Static Analysis" on: [push] jobs: codeql: name: CodeQL runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: ['cpp'] steps: - name: Checkout repository uses: actions/checkout@v3 with: fetch-depth: 1 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y libyaml-dev - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - run: | make - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 scan-build: name: scan-build runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: ['cpp'] steps: - name: Checkout repository uses: actions/checkout@v3 with: fetch-depth: 1 - name: apt-get install packages run: sudo apt-get update -qq && sudo apt-get install --no-install-recommends -y libyaml-dev clang-tools - name: scan-build run: | make clean scan-build -sarif -o build/sarif make - name: upload scan-build uses: github/codeql-action/upload-sarif@v2 with: sarif_file: build/sarif libcyaml-1.4.1/.gitignore000066400000000000000000000000071443462360400153040ustar00rootroot00000000000000build/ libcyaml-1.4.1/CHANGES.md000066400000000000000000000060051443462360400147120ustar00rootroot00000000000000LibCYAML: Change Log ==================== ## LibCYAML v1.4.1 * **Loading**: * Log valid enum values on error parsing enums. * Split out storage of scaler values to client data structure. * **General**: * Updated code coverage CI to latest action versions. No changes are required for client applications to upgrade. ## LibCYAML v1.4.0 * **Loading**: * Reject numerical values with trailing junk. * New `CYAML_CFG_IGNORED_KEY_WARNING` to generate warnings when mapping keys are ignored either explicitly by the `CYAML_IGNORE` type or implicitly by the `CYAML_CFG_IGNORE_UNKNOWN_KEYS` config setting. * **Buildsystem**: * Avoid using GNU `-D` install flag. * **General**: * Improved API documentation for `CYAML_SEQUENCE_FIXED`. * Minor optimisation for host endian detection for older GCCs. * Minor CI updates. No changes are required for client applications to upgrade. ## LibCYAML v1.3.1 * **Loading**: * Fixed value out-of-range detection limits for signed integers. No changes are required for client applications to upgrade. ## LibCYAML v1.3.0 * **Saving**: * New flags allow control over scalar output style. - For example to force single or double quote style. * **General**: * Buildsystem changes to allow use of CPPFLAGS from the environment. No changes are required for client applications to upgrade. ## LibCYAML v1.2.1 * **General**: * Support for dynamic library build on Mac OS X. * Ordered designated initialisers in public header for C++ compatibility. No changes are required for client applications to upgrade. ## LibCYAML v1.2.0 * **Loading**: * Allow mappings with zero fields in the schema. * Improved logging of errors. * `CYAML_BOOL` type now treats "Off" as false. * Allow loading of float values that overflow or underflow unless `CYAML_FLAG_STRICT` set. * Added line and column numbers to backtraces. * **General**: * Update tests to handle libyaml 0.2.5 output format change. * Buildsystem improvements. * Made public header C++ compatible. * Test runner supports running individual tests. No changes are required for client applications to upgrade. ## LibCYAML v1.1.0 * **Loading**: * Significantly optimised handling of aliases and anchors. * Fixed handling of duplicate mapping keys. * **Saving**: * Increased precision for double precision floating point values. * **General**: * Fixed data handling on big endian systems. No changes are required for client applications to upgrade. ## LibCYAML v1.0.2 * **Loading**: * Fixed invalid read on error path for bitfield handling. * **Buildsystem**: * Fixed to link against libraries after listing objects. * Added `check` target as alias for `test`. No changes are required for client applications to upgrade. ## LibCYAML v1.0.1 * **Loading**: * Fixed mapping and sequence values with `CYAML_FLAG_POINTER_NULL`. * **Buildsystem**: * Installation: Explicitly create leading directories. No changes are required for client applications to upgrade. ## LibCYAML v1.0.0 * Initial release. libcyaml-1.4.1/LICENSE000066400000000000000000000013421443462360400143240ustar00rootroot00000000000000Copyright (c) 2017-2021, Michael Drake Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. libcyaml-1.4.1/Makefile000066400000000000000000000163341443462360400147660ustar00rootroot00000000000000# SPDX-License-Identifier: ISC # # Copyright (C) 2017-2023 Michael Drake # CYAML's versioning is ..[-DEVEL] # The main branch will usually be DEVEL. The release process is: # 0. Create branch for release. # 1. Update the CHANGES.md file. # 2. Set MAJOR, MINOR, and PATCH versions appropriately for changes. # 3. Create PR, review, and merge to main. # 4. Set VESION_DEVEL to 0, commit to main. # 5. Tag the release: `git tag -a vN.N.N -m "libcyaml N.N.N"` # 6. Set VESION_DEVEL to 1, commit to main. VERSION_MAJOR = 1 VERSION_MINOR = 4 VERSION_PATCH = 1 VERSION_DEVEL = 0 VERSION_STR = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) # Default variant depends on whether it's a development build. ifeq ($(VERSION_DEVEL), 1) VARIANT = debug else VARIANT = release endif # Unfortunately ASan is incompatible with valgrind, so we have a special # variant for running with sanitisers. VALID_VARIANTS := release debug san ifneq ($(filter $(VARIANT),$(VALID_VARIANTS)),) else $(error VARIANT must be 'debug' (default), 'san', or 'release') endif UNAME_S := $(shell uname -s) LIB_NAME = libcyaml LIB_PKGCON = $(LIB_NAME).pc LIB_STATIC = $(LIB_NAME).a LIB_SHARED = $(LIB_NAME).so LIB_SH_MAJ = $(LIB_SHARED).$(VERSION_MAJOR) LIB_SH_VER = $(LIB_SHARED).$(VERSION_STR) .IMPLICIT = PREFIX ?= /usr/local LIBDIR ?= lib INCLUDEDIR ?= include Q ?= @ CC ?= gcc AR ?= ar MKDIR = mkdir -p INSTALL ?= install -c VALGRIND = valgrind --leak-check=full --track-origins=yes VERSION_FLAGS = -DVERSION_MAJOR=$(VERSION_MAJOR) \ -DVERSION_MINOR=$(VERSION_MINOR) \ -DVERSION_PATCH=$(VERSION_PATCH) \ -DVERSION_DEVEL=$(VERSION_DEVEL) LIBYAML = yaml-0.1 LIBYAML_CFLAGS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --cflags $(LIBYAML)),) LIBYAML_LIBS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --libs $(LIBYAML)),-lyaml) INCLUDE = -I include CPPFLAGS += $(VERSION_FLAGS) -MMD -MP CFLAGS += $(INCLUDE) $(LIBYAML_CFLAGS) CFLAGS += -std=c11 -Wall -Wextra -pedantic \ -Wconversion -Wwrite-strings -Wcast-align -Wpointer-arith \ -Winit-self -Wshadow -Wstrict-prototypes -Wmissing-prototypes \ -Wredundant-decls -Wundef -Wvla -Wdeclaration-after-statement LDFLAGS += $(LIBYAML_LIBS) LDFLAGS_SHARED = -Wl,-soname=$(LIB_SH_MAJ) -shared ifeq ($(VARIANT), debug) CFLAGS += -O0 -g else ifeq ($(VARIANT), san) CFLAGS += -O0 -g -fsanitize=address -fsanitize=undefined -fno-sanitize-recover LDFLAGS += -fsanitize=address -fsanitize=undefined -fno-sanitize-recover else CFLAGS += -O2 -DNDEBUG endif ifneq ($(filter coverage,$(MAKECMDGOALS)),) BUILDDIR = build/coverage/$(VARIANT) CFLAGS_COV = --coverage -DNDEBUG LDFLAGS_COV = --coverage else BUILDDIR = build/$(VARIANT) CFLAGS_COV = LDFLAGS_COV = endif BUILDDIR_SHARED = $(BUILDDIR)/shared BUILDDIR_STATIC = $(BUILDDIR)/static LIB_SRC_FILES = mem.c free.c load.c save.c util.c utf8.c LIB_SRC := $(addprefix src/,$(LIB_SRC_FILES)) LIB_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(LIB_SRC))) LIB_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(LIB_SRC))) LIB_OBJ_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_OBJ)) LIB_OBJ_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_OBJ)) LIB_DEP_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_DEP)) LIB_DEP_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_DEP)) LIB_PATH = LD_LIBRARY_PATH=$(BUILDDIR) ifeq ($(UNAME_S),Darwin) LIB_SHARED = $(LIB_NAME).dylib LIB_SH_MAJ = $(LIB_NAME).$(VERSION_MAJOR).dylib LIB_SH_VER = $(LIB_NAME).$(VERSION_STR).dylib LDFLAGS_SHARED = -dynamiclib -install_name "$(LIB_SH_MAJ)" -current_version $(VERSION_STR) LIB_PATH = DYLD_FALLBACK_LIBRARY_PATH=$(BUILDDIR) endif TEST_SRC_FILES = units/free.c units/load.c units/test.c units/util.c \ units/errs.c units/file.c units/save.c units/utf8.c TEST_SRC := $(addprefix test/,$(TEST_SRC_FILES)) TEST_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(TEST_SRC))) TEST_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(TEST_SRC))) TEST_BINS = \ $(BUILDDIR)/test/units/cyaml-shared \ $(BUILDDIR)/test/units/cyaml-static all: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) examples coverage: test-verbose $(Q)$(MKDIR) $(BUILDDIR) $(Q)gcovr -e 'test/.*' -r . $(Q)gcovr -e 'test/.*' -x -o build/coverage.xml -r . $(Q)gcovr -e 'test/.*' --html --html-details -o build/coverage.html -r . test test-quiet test-verbose test-debug: $(TEST_BINS) $(Q)for i in $(^); do $(LIB_PATH) $$i $(subst test,,$(subst test-,--,$@)) "$(TESTLIST)" || exit; done valgrind valgrind-quiet valgrind-verbose valgrind-debug: $(TEST_BINS) $(Q)for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i $(subst valgrind,,$(subst valgrind-,--,$@)) "$(TESTLIST)" || exit; done check: test $(BUILDDIR)/$(LIB_PKGCON): $(LIB_PKGCON).in sed \ -e 's#PREFIX#$(PREFIX)#' \ -e 's#LIBDIR#$(LIBDIR)#' \ -e 's#INCLUDEDIR#$(INCLUDEDIR)#' \ -e 's#VERSION#$(VERSION_STR)#' \ $(LIB_PKGCON).in >$(BUILDDIR)/$(LIB_PKGCON) $(BUILDDIR)/$(LIB_STATIC): $(LIB_OBJ_STATIC) $(AR) -rcs $@ $^ $(BUILDDIR)/$(LIB_SH_MAJ): $(LIB_OBJ_SHARED) $(CC) -o $@ $^ $(LDFLAGS) $(LDFLAGS_COV) $(LDFLAGS_SHARED) $(LIB_OBJ_STATIC): $(BUILDDIR_STATIC)/%.o : %.c $(Q)$(MKDIR) $(dir $@) $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $< $(LIB_OBJ_SHARED): $(BUILDDIR_SHARED)/%.o : %.c $(Q)$(MKDIR) $(dir $@) $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(CFLAGS_COV) -c -o $@ $< docs: $(MKDIR) build/docs/api $(MKDIR) build/docs/devel doxygen docs/api.doxygen.conf doxygen docs/devel.doxygen.conf clean: rm -rf build/ install: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) $(BUILDDIR)/$(LIB_PKGCON) $(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR) $(INSTALL) $(BUILDDIR)/$(LIB_SH_MAJ) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_SH_VER) (cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SH_MAJ) || { rm -f $(LIB_SH_MAJ) && ln -s $(LIB_SH_VER) $(LIB_SH_MAJ); }; }) (cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SHARED) || { rm -f $(LIB_SHARED) && ln -s $(LIB_SH_VER) $(LIB_SHARED); }; }) $(INSTALL) $(BUILDDIR)/$(LIB_STATIC) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC) chmod 644 $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC) $(INSTALL) -d $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml $(INSTALL) -m 644 include/cyaml/* $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml $(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig $(INSTALL) -m 644 $(BUILDDIR)/$(LIB_PKGCON) $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig/$(LIB_PKGCON) examples: $(BUILDDIR)/planner $(BUILDDIR)/numerical $(BUILDDIR)/planner: examples/planner/main.c $(BUILDDIR)/$(LIB_STATIC) $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(BUILDDIR)/numerical: examples/numerical/main.c $(BUILDDIR)/$(LIB_STATIC) $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) -include $(LIB_DEP_SHARED) $(LIB_DEP_STATIC) $(TEST_DEP) .PHONY: all test test-quiet test-verbose test-debug \ valgrind valgrind-quiet valgrind-verbose valgrind-debug \ clean coverage docs install examples check $(BUILDDIR)/test/units/cyaml-static: $(TEST_OBJ) $(BUILDDIR)/$(LIB_STATIC) $(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS) $(BUILDDIR)/test/units/cyaml-shared: $(TEST_OBJ) $(BUILDDIR)/$(LIB_SH_MAJ) $(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS) $(TEST_OBJ): $(BUILDDIR)/%.o : %.c $(Q)$(MKDIR) $(dir $@) $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $< libcyaml-1.4.1/README.md000066400000000000000000000065141443462360400146040ustar00rootroot00000000000000LibCYAML: Schema-based YAML parsing and serialisation ===================================================== [![Build Status](https://github.com/tlsa/libcyaml/workflows/CI/badge.svg)](https://github.com/tlsa/libcyaml/actions) [![Static Analysis](https://github.com/tlsa/libcyaml/actions/workflows/static-analysis.yaml/badge.svg)](https://github.com/tlsa/libcyaml/actions/workflows/static-analysis.yaml) [![Code Coverage](https://codecov.io/gh/tlsa/libcyaml/branch/main/graph/badge.svg)](https://codecov.io/gh/tlsa/libcyaml) LibCYAML is a C library for reading and writing structured YAML documents. It is written in ISO C11 and licensed under the ISC licence. Overview -------- The fundamental idea behind CYAML is to allow applications to construct schemas which describe both the permissible structure of the YAML documents to read/write, and the C data structure(s) in which the loaded data is arranged in memory. ### Goals * Make it easy to load YAML into client's custom C data structures. * Good compromise between flexibility and simplicity. ### Features * Load, Save and Free functions. * Reads and writes arbitrary client data structures. * Schema-driven, allowing control over permitted YAML, for example: - Required / optional mapping fields. - Allowed / disallowed values. - Minimum / maximum sequence entry count. - etc... * Enumerations and flag words. * YAML backtraces make it simple for users to fix their YAML to conform to your schema. * Uses standard [`libyaml`](https://github.com/yaml/libyaml) library for low-level YAML read / write. * Support for YAML aliases and anchors. Building -------- To build the library, run: make You can control the optimisation and building of asserts by setting the build variant: make VARIANT=debug make VARIANT=release Another debug build variant which is built with address sanitiser (incompatible with valgrind) can be built with: make VARIANT=san Installation ------------ To install a release version of the library, run: make install VARIANT=release It will install to the PREFIX `/usr/local` by default, and it will use DESTDIR and PREFIX from the environment if set. Testing ------- To run the tests, run any of the following, which generate various levels of output verbosity (optionally setting `VARIANT=release`, or `VARIANT=san`): make test make test-quiet make test-verbose make test-debug To run the tests under `valgrind`, a similar set of targets is available: make valgrind make valgrind-quiet make valgrind-verbose make valgrind-debug To run a single test or a subset of tests, use the `TESTLIST` variable, which expects a space and/or comma separated list of test names: make test-debug TESTLIST=test_load_mapping_without_any_fields make valgrind-debug TESTLIST="test_load_no_log test_util_state_invalid" To generate a test coverage report, `gcovr` is required: make coverage Documentation ------------- To generate both public API documentation, and documentation of CYAML's internals, `doxygen` is required: make docs Alternatively, the read the API documentation directly from the [cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h) header file. There is also a [tutorial](docs/guide.md). Examples -------- In addition to the documentation, you can study the [examples](examples/). libcyaml-1.4.1/docs/000077500000000000000000000000001443462360400142475ustar00rootroot00000000000000libcyaml-1.4.1/docs/api.doxygen.conf000066400000000000000000000007171443462360400173500ustar00rootroot00000000000000# SPDX-License-Identifier: ISC # This generates documentation for developers using libcyaml. PROJECT_NAME = "CYAML" OUTPUT_DIRECTORY = "build/docs/api" OPTIMIZE_OUTPUT_FOR_C = YES EXTRACT_STATIC = NO MARKDOWN_SUPPORT = YES INPUT = include README.md docs/guide.md RECURSIVE = YES USE_MDFILE_AS_MAINPAGE = README.md GENERATE_HTML = YES GENERATE_LATEX = NO QUIET = YES libcyaml-1.4.1/docs/devel.doxygen.conf000066400000000000000000000007451443462360400176770ustar00rootroot00000000000000# SPDX-License-Identifier: ISC # This generates documentation for developers developing libcyaml. PROJECT_NAME = "CYAML Internals" OUTPUT_DIRECTORY = "build/docs/devel" OPTIMIZE_OUTPUT_FOR_C = YES EXTRACT_STATIC = YES MARKDOWN_SUPPORT = YES INPUT = include src README.md docs/guide.md RECURSIVE = YES USE_MDFILE_AS_MAINPAGE = README.md GENERATE_HTML = YES GENERATE_LATEX = NO QUIET = YES libcyaml-1.4.1/docs/guide.md000066400000000000000000000113161443462360400156700ustar00rootroot00000000000000LibCYAML: Tutorial ================== This document is intended for C developers wishing to make use of [LibCYAML](https://github.com/tlsa/libcyaml). Overview -------- If you want to use LibCYAML you'll need to have two things: 1. A consistent structure to the sort of YAML you want to load/save. 2. Some C data structure to load/save to/from. LibCYAML's aim is to make this as simple as possible for the programmer. However, LibCYAML knows nothing about either your data structure or the "shape" of the YAML you want to load. You provide this information by defining "schemas", and passing them to LibCYAML. > **Note**: If you need to handle arbitrary "free-form" YAML (e.g. for a tool > to convert between YAML and JSON), then LibCYAML would not be much help. > In such a case, I'd recommend using [libyaml](https://github.com/yaml/libyaml) > directly. A simple example: loading YAML ------------------------------ Let's say you want to load the following YAML document: ```yaml name: Fibonacci data: - 1 - 1 - 2 - 3 - 5 - 8 ``` And you want to load it into the following C data structure: ```c struct numbers { char *name; int *data; unsigned data_count; }; ``` Then we need to define a CYAML schema to describe these to LibCYAML. > **Note**: Use the doxygen API documentation, or else the documentation in > [cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h) > in conjunction with this guide. At the top level of the YAML is a mapping with two fields, "name" and "data". ```yaml name: data: ``` The first field is just a simple scalar value (it's neither a mapping nor a sequence). The second field has a sequence value. We'll start by defining the CYAML schema for the "data" sequence, since since that's the "deepest" non-scalar type. The reason for starting here will become clear later. ```c /* CYAML value schema for entries of the data sequence. */ static const cyaml_schema_value_t data_entry = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; ``` Here we're making a `cyaml_schema_value_t` for the entries in the sequence. There are various `CYAML_VALUE_{TYPE}` macros to assist with this. Here we're using `CYAML_VALUE_INT`, because the value is a signed integer. The parameters passed to the macro are `enum cyaml_flag`, and the C data type of the value. Now we can write the schema for the mapping. First we'll construct an array of `cyaml_schema_field_t` entries that describe each field in the mapping. ```c /* CYAML mapping schema fields array for the top level mapping. */ static const cyaml_schema_field_t top_mapping_schema[] = { CYAML_FIELD_STRING_PTR( "name", CYAML_FLAG_POINTER, struct numbers, name, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE( "data", CYAML_FLAG_POINTER, struct numbers, data, &data_entry, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; ``` There are `CYAML_FIELD_{TYPE}` helper macros to construct the mapping field entries. The array must be terminated by a `CYAML_FIELD_END` entry. The helper macro parameters are specific to each `CYAML_FIELD_{TYPE}` macro. The entry for the name field is of type string pointer. You can consult the documentation for the `CYAML_FIELD_{TYPE}` macros to see what the parameters mean. > **Note**: The field for the sequence takes a pointer to the sequence entry > data type that we defined earlier as `data_entry`. Finally we can define the schema for the top level value that gets passed to the LibCYAML. ```c /* CYAML value schema for the top level mapping. */ static const cyaml_schema_value_t top_schema = { CYAML_VALUE_MAPPING( CYAML_FLAG_POINTER, struct numbers, top_mapping_schema), }; ``` In this case our top level value is a mapping type. One of the parameters needed for mappings is the array of field definitions. In this case we're passing the `top_mapping_schema` that we defined above. ```c /* Create our CYAML configuration. */ static const cyaml_config_t config = { .log_fn = cyaml_log, /* Use the default logging function. */ .mem_fn = cyaml_mem, /* Use the default memory allocator. */ .log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ }; /* Where to store the loaded data */ struct numbers *n; /* Load the file into n */ cyaml_err_t err = cyaml_load_file(argv[ARG_PATH_IN], &config, &top_schema, (cyaml_data_t **)&n, NULL); if (err != CYAML_OK) { /* Handle error */ } /* Use the data. */ printf("%s:\n", n->name); for (unsigned i = 0; i < n->data_count; i++) { printf(" - %i\n", n->data[i]); } /* Free the data */ err = cyaml_free(&config, &top_schema, n, 0); if (err != CYAML_OK) { /* Handle error */ } ``` And that's it, the YAML is loaded into the custom C data structure. You can find the code for this in the "numerical" example in the [examples](../examples) directory. libcyaml-1.4.1/examples/000077500000000000000000000000001443462360400151355ustar00rootroot00000000000000libcyaml-1.4.1/examples/numerical/000077500000000000000000000000001443462360400171145ustar00rootroot00000000000000libcyaml-1.4.1/examples/numerical/README.md000066400000000000000000000003511443462360400203720ustar00rootroot00000000000000Simple LibCYAML example ======================= This has a simple YAML document for the Fibonacci sequence, and an example LibCYAML binding to load/free the data from C. The [tutorial](../../docs/guide.md) explains how this works. libcyaml-1.4.1/examples/numerical/fibonacci.yaml000066400000000000000000000000721443462360400217140ustar00rootroot00000000000000name: Fibonacci data: - 1 - 1 - 2 - 3 - 5 - 8 libcyaml-1.4.1/examples/numerical/main.c000066400000000000000000000055721443462360400202150ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018 Michael Drake */ #include #include #include /****************************************************************************** * C data structure for storing a project plan. * * This is what we want to load the YAML into. ******************************************************************************/ /* Structure for storing numerical sequence */ struct numbers { char *name; int *data; unsigned data_count; }; /****************************************************************************** * CYAML schema to tell libcyaml about both expected YAML and data structure. * * (Our CYAML schema is just a bunch of static const data.) ******************************************************************************/ /* CYAML value schema for entries of the data sequence. */ static const cyaml_schema_value_t data_entry = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; /* CYAML mapping schema fields array for the top level mapping. */ static const cyaml_schema_field_t top_mapping_schema[] = { CYAML_FIELD_STRING_PTR("name", CYAML_FLAG_POINTER, struct numbers, name, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("data", CYAML_FLAG_POINTER, struct numbers, data, &data_entry, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; /* CYAML value schema for the top level mapping. */ static const cyaml_schema_value_t top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct numbers, top_mapping_schema), }; /****************************************************************************** * Actual code to load and save YAML doc using libcyaml. ******************************************************************************/ /* Our CYAML config. * * If you want to change it between calls, don't make it const. * * Here we have a very basic config. */ static const cyaml_config_t config = { .log_fn = cyaml_log, /* Use the default logging function. */ .mem_fn = cyaml_mem, /* Use the default memory allocator. */ .log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ }; /* Main entry point from OS. */ int main(int argc, char *argv[]) { cyaml_err_t err; struct numbers *n; enum { ARG_PROG_NAME, ARG_PATH_IN, ARG__COUNT, }; /* Handle args */ if (argc != ARG__COUNT) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " %s \n", argv[ARG_PROG_NAME]); return EXIT_FAILURE; } /* Load input file. */ err = cyaml_load_file(argv[ARG_PATH_IN], &config, &top_schema, (cyaml_data_t **)&n, NULL); if (err != CYAML_OK) { fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err)); return EXIT_FAILURE; } /* Use the data. */ printf("%s:\n", n->name); for (unsigned i = 0; i < n->data_count; i++) { printf(" - %i\n", n->data[i]); } /* Free the data */ cyaml_free(&config, &top_schema, n, 0); return EXIT_SUCCESS; } libcyaml-1.4.1/examples/planner/000077500000000000000000000000001443462360400165745ustar00rootroot00000000000000libcyaml-1.4.1/examples/planner/README.md000066400000000000000000000004261443462360400200550ustar00rootroot00000000000000Simple LibCYAML example ======================= This has a simple YAML document for project planning, and an example LibCYAML binding to load/modify/save/free the data from C. The C source code is heavliy documented to help explain how to write a CYAML schema data structure. libcyaml-1.4.1/examples/planner/main.c000066400000000000000000000330611443462360400176670ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2019 Michael Drake */ #include #include #include /****************************************************************************** * C data structure for storing a project plan. * * This is what we want to load the YAML into. ******************************************************************************/ /* Enumeration of months of the year */ enum months { MONTH_JAN = 1, MONTH_FEB, MONTH_MAR, MONTH_APR, MONTH_MAY, MONTH_JUN, MONTH_JUL, MONTH_AUG, MONTH_SEP, MONTH_OCT, MONTH_NOV, MONTH_DEC }; /* Structure for storing dates */ struct date { uint8_t day; enum months month; uint16_t year; }; /* Structure for storing durations */ struct duration { uint8_t hours; unsigned days; unsigned weeks; unsigned years; }; /* Enumeration of task flags */ enum task_flags { FLAGS_NONE = 0, FLAGS_IMPORTANT = (1 << 0), FLAGS_ENGINEERING = (1 << 1), FLAGS_DOCUMENTATION = (1 << 2), FLAGS_MANAGEMENT = (1 << 3), }; /* Structure for storing a task */ struct task { const char *name; enum task_flags flags; struct duration estimate; const char **depends; unsigned depends_count; const char **people; unsigned n_people; }; /* Top-level structure for storing a plan */ struct plan { const char *name; struct date *start; const char **people; unsigned n_people; struct task *tasks; uint64_t tasks_count; }; /****************************************************************************** * CYAML schema to tell libcyaml about both expected YAML and data structure. * * (Our CYAML schema is just a bunch of static const data.) ******************************************************************************/ /* Mapping from "task_flags" strings to enum values for schema. */ static const cyaml_strval_t task_flags_strings[] = { { "None", FLAGS_NONE }, { "Important", FLAGS_IMPORTANT }, { "Engineering", FLAGS_ENGINEERING }, { "Documentation", FLAGS_DOCUMENTATION }, { "Management", FLAGS_MANAGEMENT }, }; /* Mapping from "month" strings to flag values for schema. */ static const cyaml_strval_t month_strings[] = { { "January", MONTH_JAN }, { "February", MONTH_FEB }, { "March", MONTH_MAR }, { "April", MONTH_APR }, { "May", MONTH_MAY }, { "June", MONTH_JUN }, { "July", MONTH_JUL }, { "August", MONTH_AUG }, { "September", MONTH_SEP }, { "October", MONTH_OCT }, { "November", MONTH_NOV }, { "December", MONTH_DEC }, }; /* Schema for string pointer values (used in sequences of strings). */ static const cyaml_schema_value_t string_ptr_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; /* The duration mapping's field definitions schema is an array. * * All the field entries will refer to their parent mapping's structure, * in this case, `struct duration`. */ static const cyaml_schema_field_t duration_fields_schema[] = { /* The fields here are all optional unsigned integers. * * Note that if an optional field is unset in the YAML, its value * will be zero in the C data structure. * * In all cases here, the YAML mapping key name (first parameter to * the macros) matches the name of the associated member of the * `duration` structure (fourth parameter). */ CYAML_FIELD_UINT( "hours", CYAML_FLAG_OPTIONAL, struct duration, hours), CYAML_FIELD_UINT( "days", CYAML_FLAG_OPTIONAL, struct duration, days), CYAML_FIELD_UINT( "weeks", CYAML_FLAG_OPTIONAL, struct duration, weeks), CYAML_FIELD_UINT( "years", CYAML_FLAG_OPTIONAL, struct duration, years), /* The field array must be terminated by an entry with a NULL key. * Here we use the CYAML_FIELD_END macro for that. */ CYAML_FIELD_END }; /* The date mapping's field definitions schema is an array. * * All the field entries will refer to their parent mapping's structure, * in this case, `struct date`. */ static const cyaml_schema_field_t date_fields_schema[] = { /* The "day" and "year" fields are just normal UNIT CYAML types. * See the comments for duration_fields_schema for details. * The only difference is neither of these are optional. * Note: The order of the fields in this array doesn't matter. */ CYAML_FIELD_UINT( "day", CYAML_FLAG_DEFAULT, struct date, day), CYAML_FIELD_UINT( "year", CYAML_FLAG_DEFAULT, struct date, year), /* The month field is an enum. * * YAML key: "month". * C structure member for this key: "month". * Array mapping strings to values: month_strings * * Its CYAML type is ENUM, so an array of cyaml_strval_t must be * provided to map from string to values. * Note that we're not setting the strict flag here so both strings and * numbers are accepted in the YAML. (For example, both "4" and "April" * would be accepted.) */ CYAML_FIELD_ENUM( "month", CYAML_FLAG_DEFAULT, struct date, month, month_strings, CYAML_ARRAY_LEN(month_strings)), /* The field array must be terminated by an entry with a NULL key. * Here we use the CYAML_FIELD_END macro for that. */ CYAML_FIELD_END }; /* The task mapping's field definitions schema is an array. * * All the field entries will refer to their parent mapping's structure, * in this case, `struct task`. */ static const cyaml_schema_field_t task_fields_schema[] = { /* The first field in the mapping is a task name. * * YAML key: "name". * C structure member for this key: "name". * * Its CYAML type is string pointer, and we have no minimum or maximum * string length constraints. */ CYAML_FIELD_STRING_PTR( "name", CYAML_FLAG_POINTER, struct task, name, 0, CYAML_UNLIMITED), /* The flags field is a flags value. * * YAML key: "flags". * C structure member for this key: "flags". * Array mapping strings to values: task_flags_strings * * In the YAML a CYAML flags value should be a sequence of scalars. * The values of each set scalar is looked up the in array of * string/value mappings, and the values are bitwise ORed together. * * Note that we're setting the strict flag here so only strings * present in task_flags_strings are allowed, and numbers are not. * * We make the field optional so when there are no flags set, the field * can be omitted from the YAML. */ CYAML_FIELD_FLAGS( "flags", CYAML_FLAG_OPTIONAL | CYAML_FLAG_STRICT, struct task, flags, task_flags_strings, CYAML_ARRAY_LEN(task_flags_strings)), /* The next field is the task estimate. * * YAML key: "estimate". * C structure member for this key: "estimate". * * Its CYAML type is a mapping. * * Since it's a mapping type, the schema for its mapping's fields must * be provided too. In this case, it's `duration_fields_schema`. */ CYAML_FIELD_MAPPING( "estimate", CYAML_FLAG_DEFAULT, struct task, estimate, duration_fields_schema), /* The next field is the tasks that this task depends on. * * YAML key: "depends". * C structure member for this key: "depends". * * Its CYAML type is a sequence. * * Since it's a sequence type, the value schema for its entries must * be provided too. In this case, it's string_ptr_schema. * * Since it's not a sequence of a fixed-length, we must tell CYAML * where the sequence entry count is to be stored. In this case, it * goes in the "depends_count" C structure member in `struct task`. * Since this is "depends" with the "_count" postfix, we can use * the following macro, which assumes a postfix of "_count" in the * struct member name. */ CYAML_FIELD_SEQUENCE( "depends", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct task, depends, &string_ptr_schema, 0, CYAML_UNLIMITED), /* The next field is the task people. * * YAML key: "people". * C structure member for this key: "people". * * Its CYAML type is a sequence. * * Since it's a sequence type, the value schema for its entries must * be provided too. In this case, it's string_ptr_schema. * * Since it's not a sequence of a fixed-length, we must tell CYAML * where the sequence entry count is to be stored. In this case, it * goes in the "n_people" C structure member in `struct plan`. */ CYAML_FIELD_SEQUENCE_COUNT( "people", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct task, people, n_people, &string_ptr_schema, 0, CYAML_UNLIMITED), /* The field array must be terminated by an entry with a NULL key. * Here we use the CYAML_FIELD_END macro for that. */ CYAML_FIELD_END }; /* The value for a task is a mapping. * * Its fields are defined in task_fields_schema. */ static const cyaml_schema_value_t task_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct task, task_fields_schema), }; /* The plan mapping's field definitions schema is an array. * * All the field entries will refer to their parent mapping's structure, * in this case, `struct plan`. */ static const cyaml_schema_field_t plan_fields_schema[] = { /* The first field in the mapping is a project name. * * YAML key: "project". * C structure member for this key: "name". * * Its CYAML type is string pointer, and we have no minimum or maximum * string length constraints. */ CYAML_FIELD_STRING_PTR( "project", CYAML_FLAG_POINTER, struct plan, name, 0, CYAML_UNLIMITED), /* The next field is the project start date. * * YAML key: "start". * C structure member for this key: "start". * * Its CYAML type is a mapping pointer. * * Since it's a mapping type, the schema for its mapping's fields must * be provided too. In this case, it's `date_fields_schema`. */ CYAML_FIELD_MAPPING_PTR( "start", CYAML_FLAG_POINTER, struct plan, start, date_fields_schema), /* The next field is the project people. * * YAML key: "people". * C structure member for this key: "people". * * Its CYAML type is a sequence. * * Since it's a sequence type, the value schema for its entries must * be provided too. In this case, it's string_ptr_schema. * * Since it's not a sequence of a fixed-length, we must tell CYAML * where the sequence entry count is to be stored. In this case, it * goes in the "n_people" C structure member in `struct plan`. */ CYAML_FIELD_SEQUENCE_COUNT( "people", CYAML_FLAG_POINTER, struct plan, people, n_people, &string_ptr_schema, 0, CYAML_UNLIMITED), /* The next field is the project tasks. * * YAML key: "tasks". * C structure member for this key: "tasks". * * Its CYAML type is a sequence. * * Since it's a sequence type, the value schema for its entries must * be provided too. In this case, it's task_schema. * * Since it's not a sequence of a fixed-length, we must tell CYAML * where the sequence entry count is to be stored. In this case, it * goes in the "tasks_count" C structure member in `struct plan`. * Since this is "tasks" with the "_count" postfix, we can use * the following macro, which assumes a postfix of "_count" in the * struct member name. */ CYAML_FIELD_SEQUENCE( "tasks", CYAML_FLAG_POINTER, struct plan, tasks, &task_schema, 0, CYAML_UNLIMITED), /* If the YAML contains a field that our program is not interested in * we can ignore it, so the value of the field will not be loaded. * * Note that unless the OPTIONAL flag is set, the ignored field must * be present. * * Another way of handling this would be to use the config flag * to ignore unknown keys. This config is passed to libcyaml * separately from the schema. */ CYAML_FIELD_IGNORE("irrelevant", CYAML_FLAG_OPTIONAL), /* The field array must be terminated by an entry with a NULL key. * Here we use the CYAML_FIELD_END macro for that. */ CYAML_FIELD_END }; /* Top-level schema. The top level value for the plan is a mapping. * * Its fields are defined in plan_fields_schema. */ static const cyaml_schema_value_t plan_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct plan, plan_fields_schema), }; /****************************************************************************** * Actual code to load and save YAML doc using libcyaml. ******************************************************************************/ /* Our CYAML config. * * If you want to change it between calls, don't make it const. * * Here we have a very basic config. */ static const cyaml_config_t config = { .log_fn = cyaml_log, /* Use the default logging function. */ .mem_fn = cyaml_mem, /* Use the default memory allocator. */ .log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */ }; /* Main entry point from OS. */ int main(int argc, char *argv[]) { cyaml_err_t err; struct plan *plan; enum { ARG_PROG_NAME, ARG_PATH_IN, ARG_PATH_OUT, ARG__COUNT, }; /* Handle args */ if (argc != ARG__COUNT) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " %s \n", argv[ARG_PROG_NAME]); return EXIT_FAILURE; } /* Load input file. */ err = cyaml_load_file(argv[ARG_PATH_IN], &config, &plan_schema, (void **) &plan, NULL); if (err != CYAML_OK) { fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err)); return EXIT_FAILURE; } /* Use the data. */ printf("Project: %s\n", plan->name); for (unsigned i = 0; i < plan->tasks_count; i++) { printf("%u. %s\n", i + 1, plan->tasks[i].name); } /* Modify the data */ plan->tasks[0].estimate.days += 3; plan->tasks[0].estimate.weeks += 1; /* Save data to new YAML file. */ err = cyaml_save_file(argv[ARG_PATH_OUT], &config, &plan_schema, plan, 0); if (err != CYAML_OK) { fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err)); cyaml_free(&config, &plan_schema, plan, 0); return EXIT_FAILURE; } /* Free the data */ cyaml_free(&config, &plan_schema, plan, 0); return EXIT_SUCCESS; } libcyaml-1.4.1/examples/planner/project.yaml000066400000000000000000000017631443462360400211350ustar00rootroot00000000000000project: Write new browser layout engine. start: day: 1 month: June year: 2018 people: - Stephen - Neil - Alex irrelevant: - details: - the app doesn't need this stuff - so it should be able to ignore it tasks: - name: Read the HTML and CSS specs. flags: - Important estimate: weeks: 2 people: - Stephen - Neil - name: Think of name for library. estimate: hours: 1 - name: Create project repo. estimate: hours: 1 depends: - Think of name for library. people: - Alex - name: Initial design of library API. flags: - Important - Engineering depends: - Read the HTML and CSS specs. - Create project repo. estimate: days: 1 - name: Plan the initial implementation flags: - Engineering - Documentation - Management depends: - Initial design of library API. estimate: days: 6 people: - Stephen - Alex libcyaml-1.4.1/include/000077500000000000000000000000001443462360400147425ustar00rootroot00000000000000libcyaml-1.4.1/include/cyaml/000077500000000000000000000000001443462360400160475ustar00rootroot00000000000000libcyaml-1.4.1/include/cyaml/cyaml.h000066400000000000000000001641241443462360400173350ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (c) 2017-2021 Michael Drake */ /** * \file * \brief CYAML library public header. * * CYAML is a C library for parsing and serialising structured YAML documents. */ #ifndef CYAML_H #define CYAML_H #ifdef __cplusplus extern "C" { #endif #include #include #include /** * CYAML library version string. */ extern const char *cyaml_version_str; /** * CYAML library version number suitable for comparisons. * * Version number binary composition is `0bRUUUUUUUJJJJJJJJNNNNNNNNPPPPPPPP`. * * | Character | Meaning | * | --------- | -------------------------------------- | * | `R` | Release flag. If set, it's a release. | * | `U` | Unused / reserved. | * | `J` | Major component of version. | * | `N` | Minor component of version. | * | `P` | Patch component of version. | */ extern const uint32_t cyaml_version; /** * CYAML value types. * * These are the fundamental data types that apply to "values" in CYAML. * * CYAML "values" are represented in by \ref cyaml_schema_value. */ typedef enum cyaml_type { CYAML_INT, /**< Value is a signed integer. */ CYAML_UINT, /**< Value is an unsigned signed integer. */ CYAML_BOOL, /**< Value is a boolean. */ /** * Value is an enum. Values of this type require a string / value * mapping array in the schema entry, to define the list of valid * enum values. */ CYAML_ENUM, /** * Value is a flags bit field. Values of this type require a string / * value list in the schema entry, to define the list of valid flag * values. Each bit is a boolean flag. To store values of various * bit sizes, use a \ref CYAML_BITFIELD instead. * * In the YAML, a \ref CYAML_FLAGS value must be presented as a * sequence of strings. */ CYAML_FLAGS, CYAML_FLOAT, /**< Value is floating point. */ CYAML_STRING, /**< Value is a string. */ /** * Value is a mapping. Values of this type require mapping schema * array in the schema entry. */ CYAML_MAPPING, /** * Value is a bit field. Values of this type require an array of value * definitions in the schema entry. If the bit field is used to store * only single-bit flags, it may be better to use \ref CYAML_FLAGS * instead. * * In the YAML, a \ref CYAML_FLAGS value must be presented as a * mapping of bit field entry names to their numerical values. */ CYAML_BITFIELD, /** * Value is a sequence. Values of this type must be the direct * children of a mapping. They require: * * - A schema describing the type of the sequence entries. * - Offset to the array entry count member in the mapping structure. * - Size in bytes of the count member in the mapping structure. * - The minimum and maximum number of sequence count entries. * Set `max` to \ref CYAML_UNLIMITED to make array count * unconstrained. */ CYAML_SEQUENCE, /** * Value is a **fixed length** sequence. It is similar to \ref * CYAML_SEQUENCE, however: * * - Values of this type do not need to be direct children of a mapping. * - The minimum and maximum entry count must be the same. If not * \ref CYAML_ERR_SEQUENCE_FIXED_COUNT will be returned. * - Unlike with \ref CYAML_SEQUENCE, where the min and max are limits, * here they are the actual entry count. * - Since values of this type represent a constant size array, the * given entry count must not be \ref CYAML_UNLIMITED, because that * would cause it to attempt a massive allocation, which on most * systems would fail. * - The offset and size of the count structure member is unused. * Because the count is a schema-defined constant, it does not need * to be recorded. */ CYAML_SEQUENCE_FIXED, /** * Value of this type is completely ignored. This is most useful for * ignoring particular keys in a mapping, when the LivCYAML client has * not set a configuration of \ref CYAML_CFG_IGNORE_UNKNOWN_KEYS. */ CYAML_IGNORE, /** * Count of the valid CYAML types. This value is **not a valid type** * itself. */ CYAML__TYPE_COUNT, } cyaml_type_e; /** * CYAML value flags. * * These may be bitwise-ORed together. */ typedef enum cyaml_flag { CYAML_FLAG_DEFAULT = 0, /**< Default value flags (none set). */ CYAML_FLAG_OPTIONAL = (1 << 0), /**< Mapping field is optional. */ /** * Value is a pointer to its type. * * With this there must be a non-NULL value. Consider using * \ref CYAML_FLAG_POINTER_NULL or \ref CYAML_FLAG_POINTER_NULL_STR * if you want to allow NULL values. */ CYAML_FLAG_POINTER = (1 << 1), /** * Permit `NULL` values for \ref CYAML_FLAG_POINTER types. * * An empty value in the YAML is loaded as a NULL pointer, and NULL * pointers are saved in YAML as empty values. * * Note, when you set \ref CYAML_FLAG_POINTER_NULL, then * \ref CYAML_FLAG_POINTER is set automatically. */ CYAML_FLAG_POINTER_NULL = (1 << 2) | CYAML_FLAG_POINTER, /** * Permit storage of `NULL` values as special NULL strings in YAML. * * This extends \ref CYAML_FLAG_POINTER_NULL, but in addition to * treating empty values as NULL, any of the following are also treated * as NULL: * * * `null`, * * `Null`, * * `NULL`, * * `~`, * * Note that as a side effect, loading a \ref CYAML_STRING field with * one of these values will not store the literal string, it will store * NULL. * * When saving, a NULL value will be recorded in the YAML as `null`. * * Note, when you set \ref CYAML_FLAG_POINTER_NULL_STR, then both * \ref CYAML_FLAG_POINTER and \ref CYAML_FLAG_POINTER_NULL are set * automatically. */ CYAML_FLAG_POINTER_NULL_STR = (1 << 3) | CYAML_FLAG_POINTER_NULL, /** * Make value handling strict. * * For \ref CYAML_ENUM and \ref CYAML_FLAGS types, in strict mode * the YAML must contain a matching string. Without strict, numerical * values are also permitted. * * * For \ref CYAML_ENUM, the value becomes the value of the enum. * The numerical value is treated as signed. * * For \ref CYAML_FLAGS, the values are bitwise ORed together. * The numerical values are treated as unsigned. * * For \ref CYAML_FLOAT types, in strict mode floating point values * that would cause overflow or underflow are not permitted. */ CYAML_FLAG_STRICT = (1 << 4), /** * When saving, emit mapping / sequence value in block style. * * This can be used to override, for this value, any default style set * in the \ref cyaml_cfg_flags CYAML behavioural configuration flags. * * \note This is ignored unless the value's type is \ref CYAML_MAPPING, * \ref CYAML_SEQUENCE, or \ref CYAML_SEQUENCE_FIXED. * * \note If both \ref CYAML_FLAG_BLOCK and \ref CYAML_FLAG_FLOW are set, * then block style takes precedence. * * \note If neither block nor flow style set either here, or in the * \ref cyaml_cfg_flags CYAML behavioural configuration flags, * then libyaml's default behaviour is used. */ CYAML_FLAG_BLOCK = (1 << 5), /** * When saving, emit mapping / sequence value in flow style. * * This can be used to override, for this value, any default style set * in the \ref cyaml_cfg_flags CYAML behavioural configuration flags. * * \note This is ignored unless the value's type is \ref CYAML_MAPPING, * \ref CYAML_SEQUENCE, or \ref CYAML_SEQUENCE_FIXED. * * \note If both \ref CYAML_FLAG_BLOCK and \ref CYAML_FLAG_FLOW are set, * then block style takes precedence. * * \note If neither block nor flow style set either here, or in the * \ref cyaml_cfg_flags CYAML behavioural configuration flags, * then libyaml's default behaviour is used. */ CYAML_FLAG_FLOW = (1 << 6), /** * When comparing strings for this value, compare with case sensitivity. * * By default, strings are compared with case sensitivity. * If \ref CYAML_CFG_CASE_INSENSITIVE is set, this can override * the configured behaviour for this specific value. * * \note If both \ref CYAML_FLAG_CASE_SENSITIVE and * \ref CYAML_FLAG_CASE_INSENSITIVE are set, * then case insensitive takes precedence. * * \note This applies to values of types \ref CYAML_MAPPING, * \ref CYAML_ENUM, and \ref CYAML_FLAGS. For mappings, * it applies to matching of the mappings' keys. For * enums and flags it applies to the comparison of * \ref cyaml_strval strings. */ CYAML_FLAG_CASE_SENSITIVE = (1 << 7), /** * When comparing strings for this value, compare with case sensitivity. * * By default, strings are compared with case sensitivity. * If \ref CYAML_CFG_CASE_INSENSITIVE is set, this can override * the configured behaviour for this specific value. * * \note If both \ref CYAML_FLAG_CASE_SENSITIVE and * \ref CYAML_FLAG_CASE_INSENSITIVE are set, * then case insensitive takes precedence. * * \note This applies to values of types \ref CYAML_MAPPING, * \ref CYAML_ENUM, and \ref CYAML_FLAGS. For mappings, * it applies to matching of the mappings' keys. For * enums and flags it applies to the comparison of * \ref cyaml_strval strings. */ CYAML_FLAG_CASE_INSENSITIVE = (1 << 8), /** * When saving, emit scalar value with plain style (no quotes). * * \note This is ignored if the value is non-scaler. * * \note In cases where conflicting scalar style flags are set, the * the one with the highest precedence is used. From lowest to * highest precedence: * \ref CYAML_FLAG_SCALAR_PLAIN, * \ref CYAML_FLAG_SCALAR_FOLDED, * \ref CYAML_FLAG_SCALAR_LITERAL, * \ref CYAML_FLAG_SCALAR_QUOTE_SINGLE, * \ref CYAML_FLAG_SCALAR_QUOTE_DOUBLE, * * If none of these are set, libyaml's default behaviour is used. */ CYAML_FLAG_SCALAR_PLAIN = (1 << 9), /** * When saving, emit scalar value with folded style: * * ```yaml * string: > * This string * really has no line breaks! * ``` * * See the notes for \ref CYAML_FLAG_SCALAR_PLAIN for applicability * and precedence. */ CYAML_FLAG_SCALAR_FOLDED = (1 << 10), /** * When saving, emit scalar value with literal style: * * ```yaml * string: | * This is a * multi-line string! * ``` * * See the notes for \ref CYAML_FLAG_SCALAR_PLAIN for applicability * and precedence. */ CYAML_FLAG_SCALAR_LITERAL = (1 << 11), /** * When saving, emit scalar value with single quotes (`'`). * * See the notes for \ref CYAML_FLAG_SCALAR_PLAIN for applicability * and precedence. */ CYAML_FLAG_SCALAR_QUOTE_SINGLE = (1 << 12), /** * When saving, emit scalar value with double quotes (`"`). * * See the notes for \ref CYAML_FLAG_SCALAR_PLAIN for applicability * and precedence. */ CYAML_FLAG_SCALAR_QUOTE_DOUBLE = (1 << 13), } cyaml_flag_e; /** * Mapping between a string and a signed value. * * Used for \ref CYAML_ENUM and \ref CYAML_FLAGS types. */ typedef struct cyaml_strval { const char *str; /**< String representing enum or flag value. */ int64_t val; /**< Value of given string. */ } cyaml_strval_t; /** * Bitfield value info. * * Used for \ref CYAML_BITFIELD type. */ typedef struct cyaml_bitdef { const char *name; /**< String representing the value's name. */ uint8_t offset; /**< Bit offset to value in bit field. */ uint8_t bits; /**< Maximum bits available for value. */ } cyaml_bitdef_t; /** * Schema definition for a value. * * \note There are convenience macros for each of the types to assist in * building a CYAML schema data structure for your YAML documents. * * This is the fundamental building block of CYAML schemas. The load, save and * free functions take parameters of this type to explain what the top-level * type of the YAML document should be. * * Values of type \ref CYAML_SEQUENCE and \ref CYAML_SEQUENCE_FIXED * contain a reference to another \ref cyaml_schema_value representing * the type of the entries of the sequence. For example, if you want * a sequence of integers, you'd have a \ref cyaml_schema_value for the * for the sequence value type, and another for the integer value type. * * Values of type \ref CYAML_MAPPING contain an array of \ref cyaml_schema_field * entries, defining the YAML keys allowed by the mapping. Each field contains * a \ref cyaml_schema_value representing the schema for the value. */ typedef struct cyaml_schema_value { /** * The type of the value defined by this schema entry. */ enum cyaml_type type; /** Flags indicating value's characteristics. */ enum cyaml_flag flags; /** * Size of the value's client data type in bytes. * * For example, `short` `int`, `long`, `int8_t`, etc are all signed * integer types, so they would have the type \ref CYAML_INT, * however, they have different sizes. */ uint32_t data_size; /** Anonymous union containing type-specific attributes. */ union { /** \ref CYAML_STRING type-specific schema data. */ struct { /** * Minimum string length (bytes). * * \note Excludes trailing NUL. */ uint32_t min; /** * Maximum string length (bytes). * * \note Excludes trailing NULL, so for character array * strings (rather than pointer strings), this * must be no more than `data_size - 1`. */ uint32_t max; } string; /** \ref CYAML_MAPPING type-specific schema data. */ struct { /** * Array of cyaml mapping field schema definitions. * * The array must be terminated by an entry with a * NULL key. See \ref cyaml_schema_field_t * and \ref CYAML_FIELD_END for more info. */ const struct cyaml_schema_field *fields; } mapping; /** \ref CYAML_BITFIELD type-specific schema data. */ struct { /** Array of bit definitions for the bit field. */ const struct cyaml_bitdef *bitdefs; /** Entry count for bitdefs array. */ uint32_t count; } bitfield; /** * \ref CYAML_SEQUENCE and \ref CYAML_SEQUENCE_FIXED * type-specific schema data. */ struct { /** * Schema definition for the type of the entries in the * sequence. * * All of a sequence's entries must be of the same * type, and a sequence can not have an entry type of * type \ref CYAML_SEQUENCE (although \ref * CYAML_SEQUENCE_FIXED is allowed). That is, you * can't have a sequence of variable-length sequences. */ const struct cyaml_schema_value *entry; /** * Minimum number of sequence entries. * * \note min and max must be the same for \ref * CYAML_SEQUENCE_FIXED. */ uint32_t min; /** * Maximum number of sequence entries. * * \note min and max must be the same for \ref * CYAML_SEQUENCE_FIXED. */ uint32_t max; } sequence; /** * \ref CYAML_ENUM and \ref CYAML_FLAGS type-specific schema * data. */ struct { /** Array of string / value mappings defining enum. */ const cyaml_strval_t *strings; /** Entry count for strings array. */ uint32_t count; } enumeration; }; } cyaml_schema_value_t; /** * Schema definition entry for mapping fields. * * YAML mappings are key:value pairs. CYAML only supports scalar mapping keys, * i.e. the keys must be strings. Each mapping field schema contains a * \ref cyaml_schema_value to define field's value. * * The schema for mappings is composed of an array of entries of this * data structure. It specifies the name of the key, and the type of * the value. It also specifies the offset into the data at which value * data should be placed. The array is terminated by an entry with a NULL key. */ typedef struct cyaml_schema_field { /** * String for YAML mapping key that his schema entry describes, * or NULL to indicated the end of an array of \ref cyaml_schema_field * entries. */ const char *key; /** * Offset in data structure at which the value for this key should * be placed / read from. */ uint32_t data_offset; /** * \ref CYAML_SEQUENCE only: Offset to sequence * entry count member in mapping's data structure. */ uint32_t count_offset; /** * \ref CYAML_SEQUENCE only: Size in bytes of sequence * entry count member in mapping's data structure. */ uint8_t count_size; /** * Defines the schema for the mapping field's value. */ struct cyaml_schema_value value; } cyaml_schema_field_t; /** * CYAML behavioural configuration flags for clients * * These may be bitwise-ORed together. */ typedef enum cyaml_cfg_flags { /** * This indicates CYAML's default behaviour. */ CYAML_CFG_DEFAULT = 0, /** * When set, unknown mapping keys are ignored when loading YAML. * Without this flag set, CYAML's default behaviour is to return * with the error \ref CYAML_ERR_INVALID_KEY. */ CYAML_CFG_IGNORE_UNKNOWN_KEYS = (1 << 0), /** * When saving, emit mapping / sequence values in block style. * * This setting can be overridden for specific values using schema * value flags (\ref cyaml_flag). * * \note This only applies to values of type \ref CYAML_MAPPING, * \ref CYAML_SEQUENCE, or \ref CYAML_SEQUENCE_FIXED. * * \note If both \ref CYAML_CFG_STYLE_BLOCK and * \ref CYAML_CFG_STYLE_FLOW are set, then block style takes * precedence. */ CYAML_CFG_STYLE_BLOCK = (1 << 1), /** * When saving, emit mapping / sequence values in flow style. * * This setting can be overridden for specific values using schema * value flags (\ref cyaml_flag). * * \note This only applies to values of type \ref CYAML_MAPPING, * \ref CYAML_SEQUENCE, or \ref CYAML_SEQUENCE_FIXED. * * \note If both \ref CYAML_CFG_STYLE_BLOCK and * \ref CYAML_CFG_STYLE_FLOW are set, then block style takes * precedence. */ CYAML_CFG_STYLE_FLOW = (1 << 2), /** * When saving, emit "---" at document start and "..." at document end. * * If this flag isn't set, these document delimiting marks will not * be emitted. */ CYAML_CFG_DOCUMENT_DELIM = (1 << 3), /** * When comparing strings, compare without case sensitivity. * * By default, strings are compared with case sensitivity. */ CYAML_CFG_CASE_INSENSITIVE = (1 << 4), /** * When loading, don't allow YAML aliases in the document. * * If this option is enabled, anchors will be ignored, and the * error code \ref CYAML_ERR_ALIAS will be returned if an alias * is encountered. * * Setting this removes the overhead of recording anchors, so * it may be worth setting if aliases are not required, and * memory is constrained. */ CYAML_CFG_NO_ALIAS = (1 << 5), /** * Log any ignored mapping keys at \ref CYAML_LOG_WARNING level. */ CYAML_CFG_IGNORED_KEY_WARNING = (1 << 6), } cyaml_cfg_flags_t; /** * CYAML function return codes indicating success or reason for failure. * * Use \ref cyaml_strerror() to convert an error code to a human-readable * string. */ typedef enum cyaml_err { CYAML_OK, /**< Success. */ CYAML_ERR_OOM, /**< Memory allocation failed. */ CYAML_ERR_ALIAS, /**< See \ref CYAML_CFG_NO_ALIAS. */ CYAML_ERR_FILE_OPEN, /**< Failed to open file. */ CYAML_ERR_INVALID_KEY, /**< Mapping key rejected by schema. */ CYAML_ERR_INVALID_VALUE, /**< Value rejected by schema. */ CYAML_ERR_INVALID_ALIAS, /**< No anchor found for alias. */ CYAML_ERR_INTERNAL_ERROR, /**< Internal error in LibCYAML. */ CYAML_ERR_UNEXPECTED_EVENT, /**< YAML event rejected by schema. */ CYAML_ERR_STRING_LENGTH_MIN, /**< String length too short. */ CYAML_ERR_STRING_LENGTH_MAX, /**< String length too long. */ CYAML_ERR_INVALID_DATA_SIZE, /**< Value's data size unsupported. */ CYAML_ERR_TOP_LEVEL_NON_PTR, /**< Top level type must be pointer. */ CYAML_ERR_BAD_TYPE_IN_SCHEMA, /**< Schema contains invalid type. */ CYAML_ERR_BAD_MIN_MAX_SCHEMA, /**< Schema minimum exceeds maximum. */ CYAML_ERR_BAD_PARAM_SEQ_COUNT, /**< Bad seq_count param for schema. */ CYAML_ERR_BAD_PARAM_NULL_DATA, /**< Client gave NULL data argument. */ CYAML_ERR_BAD_BITVAL_IN_SCHEMA, /**< Bit value beyond bit field size. */ CYAML_ERR_SEQUENCE_ENTRIES_MIN, /**< Too few sequence entries. */ CYAML_ERR_SEQUENCE_ENTRIES_MAX, /**< Too many sequence entries. */ CYAML_ERR_SEQUENCE_FIXED_COUNT, /**< Mismatch between min and max. */ CYAML_ERR_SEQUENCE_IN_SEQUENCE, /**< Non-fixed sequence in sequence. */ CYAML_ERR_MAPPING_FIELD_MISSING, /**< Required mapping field missing. */ CYAML_ERR_BAD_CONFIG_NULL_MEMFN, /**< Client gave NULL mem function. */ CYAML_ERR_BAD_PARAM_NULL_CONFIG, /**< Client gave NULL config arg. */ CYAML_ERR_BAD_PARAM_NULL_SCHEMA, /**< Client gave NULL schema arg. */ CYAML_ERR_LIBYAML_EMITTER_INIT, /**< Failed to initialise libyaml. */ CYAML_ERR_LIBYAML_PARSER_INIT, /**< Failed to initialise libyaml. */ CYAML_ERR_LIBYAML_EVENT_INIT, /**< Failed to initialise libyaml. */ CYAML_ERR_LIBYAML_EMITTER, /**< Error inside libyaml emitter. */ CYAML_ERR_LIBYAML_PARSER, /**< Error inside libyaml parser. */ CYAML_ERR__COUNT, /**< Count of CYAML return codes. * This is **not a valid return * code** itself. */ } cyaml_err_t; /** * Value schema helper macro for values with \ref CYAML_INT type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. */ #define CYAML_VALUE_INT( \ _flags, _type) \ .type = CYAML_INT, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type) /** * Mapping schema helper macro for keys with \ref CYAML_INT type. * * Use this for integers contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_INT( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_INT(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member)), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_INT type. * * Use this for pointers to integers. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_INT_PTR( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_INT(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member))), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_UINT type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. */ #define CYAML_VALUE_UINT( \ _flags, _type) \ .type = CYAML_UINT, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type) /** * Mapping schema helper macro for keys with \ref CYAML_UINT type. * * Use this for unsigned integers contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_UINT( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_UINT(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member)), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_UINT type. * * Use this for pointers to unsigned integers. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_UINT_PTR( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_UINT(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member))), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_BOOL type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. */ #define CYAML_VALUE_BOOL( \ _flags, _type) \ .type = CYAML_BOOL, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type) /** * Mapping schema helper macro for keys with \ref CYAML_BOOL type. * * Use this for boolean types contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_BOOL( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_BOOL(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member)), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_BOOL type. * * Use this for pointers to boolean types. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_BOOL_PTR( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_BOOL(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member))), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_ENUM type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. * \param[in] _strings Array of string data for enumeration values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_VALUE_ENUM( \ _flags, _type, _strings, _strings_count) \ .type = CYAML_ENUM, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .enumeration = { \ .strings = _strings, \ .count = _strings_count, \ } /** * Mapping schema helper macro for keys with \ref CYAML_ENUM type. * * Use this for enumerated types contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _strings Array of string data for enumeration values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_FIELD_ENUM( \ _key, _flags, _structure, _member, _strings, _strings_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_ENUM(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member), \ _strings, _strings_count), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_ENUM type. * * Use this for pointers to enumerated types. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _strings Array of string data for enumeration values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_FIELD_ENUM_PTR( \ _key, _flags, _structure, _member, _strings, _strings_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_ENUM(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member)), \ _strings, _strings_count), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_FLAGS type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. * \param[in] _strings Array of string data for flag values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_VALUE_FLAGS( \ _flags, _type, _strings, _strings_count) \ .type = CYAML_FLAGS, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .enumeration = { \ .strings = _strings, \ .count = _strings_count, \ } /** * Mapping schema helper macro for keys with \ref CYAML_FLAGS type. * * Use this for flag types contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _strings Array of string data for flag values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_FIELD_FLAGS( \ _key, _flags, _structure, _member, _strings, _strings_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_FLAGS(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member), \ _strings, _strings_count), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_FLAGS type. * * Use this for pointers to flag types. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _strings Array of string data for flag values. * \param[in] _strings_count Number of entries in _strings. */ #define CYAML_FIELD_FLAGS_PTR( \ _key, _flags, _structure, _member, _strings, _strings_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_FLAGS(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member)), \ _strings, _strings_count), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_BITFIELD type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. * \param[in] _bitvals Array of bit field value data for the bit field. * \param[in] _bitvals_count Number of entries in _bitvals. */ #define CYAML_VALUE_BITFIELD( \ _flags, _type, _bitvals, _bitvals_count) \ .type = CYAML_BITFIELD, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .bitfield = { \ .bitdefs = _bitvals, \ .count = _bitvals_count, \ } /** * Mapping schema helper macro for keys with \ref CYAML_BITFIELD type. * * Use this for bit field types contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _bitvals Array of bit field value data for the bit field. * \param[in] _bitvals_count Number of entries in _bitvals. */ #define CYAML_FIELD_BITFIELD( \ _key, _flags, _structure, _member, _bitvals, _bitvals_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_BITFIELD(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member), \ _bitvals, _bitvals_count), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_BITFIELD type. * * Use this for pointers to bit field types. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _bitvals Array of bit field value data for the bit field. * \param[in] _bitvals_count Number of entries in _bitvals. */ #define CYAML_FIELD_BITFIELD_PTR( \ _key, _flags, _structure, _member, _bitvals, _bitvals_count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_BITFIELD(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member)), \ _bitvals, _bitvals_count), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_FLOAT type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. */ #define CYAML_VALUE_FLOAT( \ _flags, _type) \ .type = CYAML_FLOAT, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type) /** * Mapping schema helper macro for keys with \ref CYAML_FLOAT type. * * Use this for floating point types contained in structs. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_FLOAT( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_FLOAT(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member)), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_FLOAT type. * * Use this for pointers to floating point types. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. */ #define CYAML_FIELD_FLOAT_PTR( \ _key, _flags, _structure, _member) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_FLOAT(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member))), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_STRING type. * * \note If the string is an array (`char str[N];`) then the \ref * CYAML_FLAG_POINTER flag must **not** be set, and the max * length must be no more than `N-1`. * * If the string is a pointer (`char *str;`), then the \ref * CYAML_FLAG_POINTER flag **must be set**. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type for this value. * \param[in] _min Minimum string length in bytes. * Excludes trailing '\0'. * \param[in] _max The C type for this value. * Excludes trailing '\0'. */ #define CYAML_VALUE_STRING( \ _flags, _type, _min, _max) \ .type = CYAML_STRING, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .string = { \ .min = _min, \ .max = _max, \ } /** * Mapping schema helper macro for keys with \ref CYAML_STRING type. * * Use this for fields with C array type, e.g. `char str[N];`. This fills the * maximum string length (`N-1`) out automatically. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _min Minimum string length in bytes. Excludes '\0'. */ #define CYAML_FIELD_STRING( \ _key, _flags, _structure, _member, _min) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_STRING(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member), _min, \ sizeof(((_structure *)NULL)->_member) - 1), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_STRING type. * * Use this for fields with C pointer type, e.g. `char *str;`. This creates * a separate allocation for the string data, and fills in the pointer. * * Use `0` for _min and \ref CYAML_UNLIMITED for _max for unconstrained string * lengths. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _min Minimum string length in bytes. Excludes '\0'. * \param[in] _max Maximum string length in bytes. Excludes '\0'. */ #define CYAML_FIELD_STRING_PTR( \ _key, _flags, _structure, _member, _min, _max) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_STRING(((_flags) | CYAML_FLAG_POINTER), \ (((_structure *)NULL)->_member), \ _min, _max), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_MAPPING type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type of structure corresponding to mapping. * \param[in] _fields Pointer to mapping fields schema array. */ #define CYAML_VALUE_MAPPING( \ _flags, _type, _fields) \ .type = CYAML_MAPPING, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .mapping = { \ .fields = _fields, \ } /** * Mapping schema helper macro for keys with \ref CYAML_MAPPING type. * * Use this for structures contained within other structures. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the containing mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _fields Pointer to mapping fields schema array. */ #define CYAML_FIELD_MAPPING( \ _key, _flags, _structure, _member, _fields) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_MAPPING(((_flags) & (~CYAML_FLAG_POINTER)), \ (((_structure *)NULL)->_member), _fields), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_MAPPING type. * * Use this for pointers to structures. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the containing mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _fields Pointer to mapping fields schema array. */ #define CYAML_FIELD_MAPPING_PTR( \ _key, _flags, _structure, _member, _fields) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_MAPPING(((_flags) | CYAML_FLAG_POINTER), \ (*(((_structure *)NULL)->_member)), _fields), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_SEQUENCE type. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type of sequence **entries**. * \param[in] _entry Pointer to schema for the **entries** in sequence. * \param[in] _min Minimum number of sequence entries required. * \param[in] _max Maximum number of sequence entries required. */ #define CYAML_VALUE_SEQUENCE( \ _flags, _type, _entry, _min, _max) \ .type = CYAML_SEQUENCE, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .sequence = { \ .entry = _entry, \ .min = _min, \ .max = _max, \ } /** * Mapping schema helper macro for keys with \ref CYAML_SEQUENCE type. * * To use this, there must be a member in {_structure} called "{_member}_count", * for storing the number of entries in the sequence. * * For example, for the following structure: * * ``` * struct my_structure { * unsigned *my_sequence; * unsigned my_sequence_count; * }; * ``` * * Pass the following as parameters: * * | Parameter | Value | * | ---------- | --------------------- | * | _structure | `struct my_structure` | * | _member | `my_sequence` | * * If you want to call the structure member for storing the sequence entry * count something else, then use \ref CYAML_FIELD_SEQUENCE_COUNT instead. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _entry Pointer to schema for the **entries** in sequence. * \param[in] _min Minimum number of sequence entries required. * \param[in] _max Maximum number of sequence entries required. */ #define CYAML_FIELD_SEQUENCE( \ _key, _flags, _structure, _member, _entry, _min, _max) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .count_offset = offsetof(_structure, _member ## _count), \ .count_size = sizeof(((_structure *)NULL)->_member ## _count), \ .value = { \ CYAML_VALUE_SEQUENCE((_flags), \ (*(((_structure *)NULL)->_member)), \ _entry, _min, _max), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_SEQUENCE type. * * Compared to .\ref CYAML_FIELD_SEQUENCE, this macro takes an extra `_count` * parameter, allowing the structure member name for the sequence entry count * to be provided explicitly. * * For example, for the following structure: * * ``` * struct my_structure { * unsigned *things; * unsigned n_things; * }; * ``` * * Pass the following as parameters: * * | Parameter | Value | * | ---------- | --------------------- | * | _structure | `struct my_structure` | * | _member | `things` | * | _count | `n_things` | * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _count The member in _structure for this sequence's * entry count. * \param[in] _entry Pointer to schema for the **entries** in sequence. * \param[in] _min Minimum number of sequence entries required. * \param[in] _max Maximum number of sequence entries required. */ #define CYAML_FIELD_SEQUENCE_COUNT( \ _key, _flags, _structure, _member, _count, _entry, _min, _max) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .count_offset = offsetof(_structure, _count), \ .count_size = sizeof(((_structure *)NULL)->_count), \ .value = { \ CYAML_VALUE_SEQUENCE((_flags), \ (*(((_structure *)NULL)->_member)), \ _entry, _min, _max), \ }, \ } /** * Value schema helper macro for values with \ref CYAML_SEQUENCE_FIXED type. * * Note that since this is a fixed size sequence, it must not be used with an * excessive entry count. For example, passing \ref CYAML_UNLIMITED as the count * to a \ref CYAML_SEQUENCE_FIXED with the \ref CYAML_FLAG_POINTER flag would * cause an enormous allocation, which would fail on many systems. * * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _type The C type of sequence **entries**. * \param[in] _entry Pointer to schema for the **entries** in sequence. * \param[in] _count Number of sequence entries required. */ #define CYAML_VALUE_SEQUENCE_FIXED( \ _flags, _type, _entry, _count) \ .type = CYAML_SEQUENCE_FIXED, \ .flags = (enum cyaml_flag)(_flags), \ .data_size = sizeof(_type), \ .sequence = { \ .entry = _entry, \ .min = _count, \ .max = _count, \ } /** * Mapping schema helper macro for keys with \ref CYAML_SEQUENCE_FIXED type. * * Note that since this is a fixed size sequence, it must not be used with an * excessive entry count. For example, passing \ref CYAML_UNLIMITED as the count * to a \ref CYAML_SEQUENCE_FIXED with the \ref CYAML_FLAG_POINTER flag would * cause an enormous allocation, which would fail on many systems. * * \param[in] _key String defining the YAML mapping key for this value. * \param[in] _flags Any behavioural flags relevant to this value. * \param[in] _structure The structure corresponding to the mapping. * \param[in] _member The member in _structure for this mapping value. * \param[in] _entry Pointer to schema for the **entries** in sequence. * \param[in] _count Number of sequence entries required. */ #define CYAML_FIELD_SEQUENCE_FIXED( \ _key, _flags, _structure, _member, _entry, _count) \ { \ .key = _key, \ .data_offset = offsetof(_structure, _member), \ .value = { \ CYAML_VALUE_SEQUENCE_FIXED((_flags), \ (*(((_structure *)NULL)->_member)), \ _entry, _count), \ }, \ } /** * Mapping schema helper macro for keys with \ref CYAML_IGNORE type. * * \param[in] _key String defining the YAML mapping key to ignore. * \param[in] _flags Any behavioural flags relevant to this key. */ #define CYAML_FIELD_IGNORE( \ _key, _flags) \ { \ .key = _key, \ .value = { \ .type = CYAML_IGNORE, \ .flags = (_flags), \ }, \ } /** * Mapping schema helper macro for terminating an array of mapping fields. * * CYAML mapping schemas are formed from an array of \ref cyaml_schema_field * entries, and an entry with a NULL key indicates the end of the array. */ #define CYAML_FIELD_END { .key = NULL } /** * Identifies that a \ref CYAML_SEQUENCE has unconstrained maximum entry * count. */ #define CYAML_UNLIMITED 0xffffffff /** * Helper macro for counting array elements. * * \note Don't use this macro on pointers. * * \param[in] _a A C array. * \return Array element count. */ #define CYAML_ARRAY_LEN(_a) ((sizeof(_a)) / (sizeof(_a[0]))) /** * Data loaded or saved by CYAML has this type. CYAML schemas are used * to describe the data contained. */ typedef void cyaml_data_t; /** CYAML logging levels. */ typedef enum cyaml_log_e { CYAML_LOG_DEBUG, /**< Debug level logging. */ CYAML_LOG_INFO, /**< Info level logging. */ CYAML_LOG_NOTICE, /**< Notice level logging. */ CYAML_LOG_WARNING, /**< Warning level logging. */ CYAML_LOG_ERROR, /**< Error level logging. */ } cyaml_log_t; /** * CYAML logging function prototype. * * Clients may implement this to manage logging from CYAML themselves. * Otherwise, consider using the standard logging function, \ref cyaml_log. * * \param[in] level Log level of message to log. * \param[in] ctx Client's private logging context. * \param[in] fmt Format string for message to log. * \param[in] args Additional arguments used by fmt. */ typedef void (*cyaml_log_fn_t)( cyaml_log_t level, void *ctx, const char *fmt, va_list args); /** * CYAML memory allocation / freeing function. * * Clients may implement this to handle memory allocation / freeing. * * \param[in] ctx Client's private allocation context. * \param[in] ptr Existing allocation to resize, or NULL. * \param[in] size The new size for the allocation. \note setting 0 must * be treated as free(). * \return If `size == 0`, returns NULL. If `size > 0`, returns NULL on failure, * and any existing allocation is left untouched, or return non-NULL as * the new allocation on success, and the original pointer becomes * invalid. */ typedef void * (*cyaml_mem_fn_t)( void *ctx, void *ptr, size_t size); /** * Client CYAML configuration data. * * \todo Should provide facility for client to provide its own custom * allocation functions. */ typedef struct cyaml_config { /** * Client function to use for logging. * * Clients can implement their own logging function and set it here. * Otherwise, set `log_fn` to \ref cyaml_log if CYAML's default * logging to `stderr` is suitable (see its documentation for more * details), or set to `NULL` to suppress all logging. * * \note Useful backtraces are issued through the `log_fn` at * \ref CYAML_LOG_ERROR level. If your application needs * to load user YAML data, these backtraces can help users * figure out what's wrong with their YAML, causing it to * be rejected by your schema. */ cyaml_log_fn_t log_fn; /** * Client logging function context pointer. * * Clients using their own custom logging function can pass their * context here, which will be passed through to their log_fn. * * The default logging function, \ref cyaml_log doesn't require a * logging context, so pass NULL for the log_ctx if using that. */ void *log_ctx; /** * Client function to use for memory allocation handling. * * Clients can implement their own, or pass \ref cyaml_mem to use * CYAML's default allocator. * * \note Depending on platform, when using CYAML's default allocator, * clients may need to take care to ensure any allocated memory * is freed using \ref cyaml_mem too. */ cyaml_mem_fn_t mem_fn; /** * Client memory function context pointer. * * Clients using their own custom allocation function can pass their * context here, which will be passed through to their mem_fn. * * The default allocation function, \ref cyaml_mem doesn't require an * allocation context, so pass NULL for the mem_ctx if using that. */ void *mem_ctx; /** * Minimum logging priority level to be issued. * * Specifying e.g. \ref CYAML_LOG_WARNING will cause only warnings and * errors to emerge. */ cyaml_log_t log_level; /** CYAML behaviour flags. */ cyaml_cfg_flags_t flags; } cyaml_config_t; /** * Standard CYAML logging function. * * This logs to `stderr`. It clients want to log elsewhere they must * implement their own logging function, and pass it to CYAML in the * \ref cyaml_config_t structure. * * \note This default logging function composes single log messages from * multiple separate fprintfs to `stderr`. If the client application * writes to `stderr` from multiple threads, individual \ref cyaml_log * messages may get broken up by the client applications logging. To * avoid this, clients should implement their own \ref cyaml_log_fn_t and * pass it in via \ref cyaml_config_t. * * \param[in] level Log level of message to log. * \param[in] ctx Logging context, unused. * \param[in] fmt Format string for message to log. * \param[in] args Additional arguments used by fmt. */ extern void cyaml_log( cyaml_log_t level, void *ctx, const char *fmt, va_list args); /** * CYAML default memory allocation / freeing function. * * This is used when clients don't supply their own. It is exposed to * enable clients to use the same allocator as libcyaml used internally * to allocate/free memory when they have not provided their own allocation * function. * * \param[in] ctx Allocation context, unused. * \param[in] ptr Existing allocation to resize, or NULL. * \param[in] size The new size for the allocation. \note When `size == 0` * this frees `ptr`. * \return If `size == 0`, returns NULL. If `size > 0`, returns NULL on failure, * and any existing allocation is left untouched, or return non-NULL as * the new allocation on success, and the original pointer becomes * invalid. */ extern void * cyaml_mem( void *ctx, void *ptr, size_t size); /** * Load a YAML document from a file at the given path. * * \note In the event of the top-level mapping having only optional fields, * and the YAML not setting any of them, this function can return \ref * CYAML_OK, and `NULL` in the `data_out` parameter. * * \param[in] path Path to YAML file to load. * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be loaded. * \param[out] data_out Returns the caller-owned loaded data on success. * Untouched on failure. * \param[out] seq_count_out On success, returns the sequence entry count. * Untouched on failure. * Must be non-NULL if top-level schema type is * \ref CYAML_SEQUENCE, otherwise, must be NULL. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ extern cyaml_err_t cyaml_load_file( const char *path, const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out); /** * Load a YAML document from a data buffer. * * \note In the event of the top-level mapping having only optional fields, * and the YAML not setting any of them, this function can return \ref * CYAML_OK, and `NULL` in the `data_out` parameter. * * \param[in] input Buffer to load YAML data from. * \param[in] input_len Length of input in bytes. * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be loaded. * \param[out] data_out Returns the caller-owned loaded data on success. * Untouched on failure. * \param[out] seq_count_out On success, returns the sequence entry count. * Untouched on failure. * Must be non-NULL if top-level schema type is * \ref CYAML_SEQUENCE, otherwise, must be NULL. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ extern cyaml_err_t cyaml_load_data( const uint8_t *input, size_t input_len, const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out); /** * Save a YAML document to a file at the given path. * * \param[in] path Path to YAML file to write. * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be saved. * \param[in] data The caller-owned data to be saved. * \param[in] seq_count If top level type is sequence, this should be the * entry count, otherwise it is ignored. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ extern cyaml_err_t cyaml_save_file( const char *path, const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count); /** * Save a YAML document into a string in memory. * * This allocates a buffer containing the serialised YAML data. * * To free the returned YAML string, clients should use the \ref cyaml_mem_fn_t * function set in the \ref cyaml_config_t passed to this function. * For example: * * ``` * char *yaml; * size_t len; * err = cyaml_save_file(&yaml, &len, &config, &client_schema, client_data, 0); * if (err == CYAML_OK) { * // Use `yaml`: * printf("%*s\n", len, yaml); * // Free `yaml`: * config.mem_fn(config.mem_ctx, yaml, 0); * } * ``` * * \note The returned YAML string does not have a trailing '\0'. * * \param[out] output Returns the caller-owned serialised YAML data on * success, untouched on failure. Clients should use * the \ref cyaml_mem_fn_t function set in the \ref * cyaml_config_t to free the data. * \param[out] len Returns the length of the data in output on success, * untouched on failure. * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be saved. * \param[in] data The caller-owned data to be saved. * \param[in] seq_count If top level type is sequence, this should be the * entry count, otherwise it is ignored. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ extern cyaml_err_t cyaml_save_data( char **output, size_t *len, const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count); /** * Free data returned by a CYAML load function. * * This is a convenience function, which is here purely to minimise the * amount of code required in clients. Clients would be better off writing * their own free function for the specific data once loaded. * * \note This is a recursive operation, freeing all nested data. * * \param[in] config The client's CYAML library config. * \param[in] schema The schema describing the content of data. Must match * the schema given to the CYAML load function used to * load the data. * \param[in] data The data structure to free. * \param[in] seq_count If top level type is sequence, this should be the * entry count, otherwise it is ignored. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ extern cyaml_err_t cyaml_free( const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t *data, unsigned seq_count); /** * Convert a cyaml error code to a human-readable string. * * \param[in] err Error code code to convert. * \return String representing err. The string is '\0' terminated, and owned * by libcyaml. */ extern const char * cyaml_strerror( cyaml_err_t err); #ifdef __cplusplus } #endif #endif libcyaml-1.4.1/libcyaml.pc.in000066400000000000000000000003641443462360400160470ustar00rootroot00000000000000prefix=PREFIX exec_prefix=${prefix} libdir=${exec_prefix}/LIBDIR includedir=${prefix}/INCLUDEDIR Name: libcyaml Description: Schema-based YAML parsing and serialisation Version: VERSION Libs: -L${libdir} -lcyaml -lyaml Cflags: -I${includedir} libcyaml-1.4.1/src/000077500000000000000000000000001443462360400141065ustar00rootroot00000000000000libcyaml-1.4.1/src/data.h000066400000000000000000000063231443462360400151740ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ /** * \file * \brief CYAML functions for manipulating client data structures. */ #ifndef CYAML_DATA_H #define CYAML_DATA_H #include "cyaml/cyaml.h" #include "util.h" /** * Write a value of up to eight bytes to data_target. * * \param[in] value The value to write. * \param[in] entry_size The number of bytes of value to write. * \param[in] data_tgt The address to write to. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml_data_write( uint64_t value, uint64_t entry_size, uint8_t *data_tgt) { const uint8_t *value_bytes = (uint8_t *)&value; if (entry_size == 0 || entry_size > sizeof(value)) { return CYAML_ERR_INVALID_DATA_SIZE; } if (cyaml__host_is_big_endian()) { value_bytes += sizeof(value) - entry_size; } memcpy(data_tgt, value_bytes, entry_size); return CYAML_OK; } /** * Write a pointer to data. * * This is a wrapper for \ref cyaml_data_write that does a compile time * assertion on the pointer size, so it can never return a runtime error. * * \param[in] ptr The pointer address to write. * \param[in] data_target The address to write to. */ static inline void cyaml_data_write_pointer( const void *ptr, uint8_t *data_target) { /* Refuse to build on platforms where sizeof pointer would * lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */ static_assert(sizeof(char *) > 0, "Incompatible pointer size."); static_assert(sizeof(char *) <= sizeof(uint64_t), "Incompatible pointer size."); CYAML_UNUSED(cyaml_data_write((uint64_t)ptr, sizeof(ptr), data_target)); return; } /** * Read a value of up to eight bytes from data. * * \param[in] entry_size The number of bytes to read. * \param[in] data The address to read from. * \param[out] error_out Returns the error code. \ref CYAML_OK on success, * or appropriate error otherwise. * \return On success, returns the value read from data. * On failure, returns 0. */ static inline uint64_t cyaml_data_read( uint64_t entry_size, const uint8_t *data, cyaml_err_t *error_out) { uint64_t ret = 0; uint8_t *ret_bytes = (uint8_t *)&ret; if (entry_size == 0 || entry_size > sizeof(ret)) { *error_out = CYAML_ERR_INVALID_DATA_SIZE; return ret; } if (cyaml__host_is_big_endian()) { ret_bytes += sizeof(ret) - entry_size; } memcpy(ret_bytes, data, entry_size); *error_out = CYAML_OK; return ret; } /** * Read a pointer from data. * * This is a wrapper for \ref cyaml_data_read that does a compile time * assertion on the pointer size, so it can never return a runtime error. * * \param[in] data The address to read from. * \return Returns the value read from data. */ static inline uint8_t * cyaml_data_read_pointer( const uint8_t *data) { cyaml_err_t err; /* Refuse to build on platforms where sizeof pointer would * lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */ static_assert(sizeof(char *) > 0, "Incompatible pointer size."); static_assert(sizeof(char *) <= sizeof(uint64_t), "Incompatible pointer size."); return (void *)cyaml_data_read(sizeof(char *), data, &err); } #endif libcyaml-1.4.1/src/free.c000066400000000000000000000112151443462360400151730ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2019 Michael Drake */ /** * \file * \brief Free data structures created by the CYAML load functions. * * As described in the public API for \ref cyaml_free(), it is preferable for * clients to write their own free routines, tailored for their data structure. * * Recursion and stack usage * ------------------------- * * This generic CYAML free routine is implemented using recursion, rather * than iteration with a heap-allocated stack. This is because recursion * seems less bad than allocating within the free code, and the stack-cost * of these functions isn't huge. The maximum recursion depth is of course * bound by the schema, however schemas for recursively nesting data structures * are unbound, e.g. for a data tree structure. */ #include #include #include #include "data.h" #include "util.h" #include "mem.h" /** * Internal function for freeing a CYAML-parsed data structure. * * \param[in] cfg The client's CYAML library config. * \param[in] schema The schema describing how to free `data`. * \param[in] data The data structure to be freed. * \param[in] count If data is of type \ref CYAML_SEQUENCE, this is the * number of entries in the sequence. */ static void cyaml__free_value( const cyaml_config_t *cfg, const cyaml_schema_value_t *schema, uint8_t * data, uint64_t count); /** * Internal function for freeing a CYAML-parsed sequence. * * \param[in] cfg The client's CYAML library config. * \param[in] sequence_schema The schema describing how to free `data`. * \param[in] data The data structure to be freed. * \param[in] count The sequence's entry count. */ static void cyaml__free_sequence( const cyaml_config_t *cfg, const cyaml_schema_value_t *sequence_schema, uint8_t * const data, uint64_t count) { const cyaml_schema_value_t *schema = sequence_schema->sequence.entry; uint32_t data_size = schema->data_size; cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing sequence with count: %u\n", count); if (schema->flags & CYAML_FLAG_POINTER) { data_size = sizeof(data); } for (unsigned i = 0; i < count; i++) { cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing sequence entry: %u\n", i); cyaml__free_value(cfg, schema, data + data_size * i, 0); } } /** * Internal function for freeing a CYAML-parsed mapping. * * \param[in] cfg The client's CYAML library config. * \param[in] mapping_schema The schema describing how to free `data`. * \param[in] data The data structure to be freed. */ static void cyaml__free_mapping( const cyaml_config_t *cfg, const cyaml_schema_value_t *mapping_schema, uint8_t * const data) { const cyaml_schema_field_t *schema = mapping_schema->mapping.fields; while (schema->key != NULL) { uint64_t count = 0; cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing key: %s (at offset: %u)\n", schema->key, (unsigned)schema->data_offset); if (schema->value.type == CYAML_SEQUENCE) { cyaml_err_t err; count = cyaml_data_read(schema->count_size, data + schema->count_offset, &err); if (err != CYAML_OK) { return; } } cyaml__free_value(cfg, &schema->value, data + schema->data_offset, count); schema++; } } /* This function is documented at the forward declaration above. */ static void cyaml__free_value( const cyaml_config_t *cfg, const cyaml_schema_value_t *schema, uint8_t * data, uint64_t count) { if (schema->flags & CYAML_FLAG_POINTER) { data = cyaml_data_read_pointer(data); if (data == NULL) { return; } } if (schema->type == CYAML_MAPPING) { cyaml__free_mapping(cfg, schema, data); } else if (schema->type == CYAML_SEQUENCE || schema->type == CYAML_SEQUENCE_FIXED) { if (schema->type == CYAML_SEQUENCE_FIXED) { count = schema->sequence.max; } cyaml__free_sequence(cfg, schema, data, count); } if (schema->flags & CYAML_FLAG_POINTER) { cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing: %p\n", data); cyaml__free(cfg, data); } } /* Exported function, documented in include/cyaml/cyaml.h */ cyaml_err_t cyaml_free( const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t *data, unsigned seq_count) { if (config == NULL) { return CYAML_ERR_BAD_PARAM_NULL_CONFIG; } if (config->mem_fn == NULL) { return CYAML_ERR_BAD_CONFIG_NULL_MEMFN; } if (schema == NULL) { return CYAML_ERR_BAD_PARAM_NULL_SCHEMA; } cyaml__log(config, CYAML_LOG_DEBUG, "Free: Top level data: %p\n", data); cyaml__free_value(config, schema, (void *)&data, seq_count); return CYAML_OK; } libcyaml-1.4.1/src/load.c000066400000000000000000002250501443462360400151750ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ /** * \file * \brief Load YAML data into client's data structure. * * This uses `libyaml` to parse YAML documents, it validates the documents * against the client provided schema, and uses the schema to place the data * in the client's data structure. */ #include #include #include #include #include #include #include #include #include "mem.h" #include "data.h" #include "util.h" /** Identifies that no mapping schema entry was found for key. */ #define CYAML_FIELDS_IDX_NONE 0xffff /** * CYAML events. These correspond to `libyaml` events. */ typedef enum cyaml_event { CYAML_EVT_NO_EVENT = YAML_NO_EVENT, CYAML_EVT_STRM_START = YAML_STREAM_START_EVENT, CYAML_EVT_STRM_END = YAML_STREAM_END_EVENT, CYAML_EVT_DOC_START = YAML_DOCUMENT_START_EVENT, CYAML_EVT_DOC_END = YAML_DOCUMENT_END_EVENT, CYAML_EVT_ALIAS = YAML_ALIAS_EVENT, CYAML_EVT_SCALAR = YAML_SCALAR_EVENT, CYAML_EVT_SEQ_START = YAML_SEQUENCE_START_EVENT, CYAML_EVT_SEQ_END = YAML_SEQUENCE_END_EVENT, CYAML_EVT_MAP_START = YAML_MAPPING_START_EVENT, CYAML_EVT_MAP_END = YAML_MAPPING_END_EVENT, CYAML_EVT__COUNT, } cyaml_event_t; /** * A CYAML load state machine stack entry. */ typedef struct cyaml_state { /** Current load state machine state. */ enum cyaml_state_e state; size_t line; /**< Event start position in YAML source. */ size_t column; /**< Event start position in YAML source. */ /** Schema for the expected value in this state. */ const cyaml_schema_value_t *schema; /** Anonymous union for schema type specific state. */ union { /** Additional state for \ref CYAML_STATE_IN_STREAM state. */ struct { /** Number of documents read in stream. */ uint32_t doc_count; } stream; /** * Additional state for \ref CYAML_STATE_IN_MAP_KEY and * \ref CYAML_STATE_IN_MAP_VALUE states. */ struct { const cyaml_schema_field_t *fields; /** Bit field of mapping fields found. */ cyaml_bitfield_t *fields_set; uint16_t fields_count; uint16_t fields_idx; } mapping; /** Additional state for \ref CYAML_STATE_IN_SEQUENCE state. */ struct { uint8_t *data; uint8_t *count_data; uint32_t count; uint8_t count_size; } sequence; }; uint8_t *data; /**< Pointer to output client data for this state. */ } cyaml_state_t; /** * Anchor data. * * This is used to track the progress of recording the anchor's events, and then * the data is referenced during replay. */ typedef struct cyaml_anchor { char *name; /**< Anchor name. */ size_t len; /**< Length of anchor name. */ uint32_t start; /**< Index into \ref cyaml_event_ctx_t events array. */ uint32_t end; /**< Index into \ref cyaml_event_ctx_t events array. */ } cyaml_anchor_t; /** * Event recording context. * * This records anchor details, and any anchored events. A stack of * events is maintained to keep track of matching start/end events, in * order to end anchor recordings with the correct end event. */ typedef struct cyaml_event_record { /** Array of recorded anchor details or NULL. */ cyaml_anchor_t *complete; /** Array of recording anchor details or NULL. */ cyaml_anchor_t *progress; yaml_event_t *data; /**< Array of anchor-referenced events. */ uint32_t *events; /**< Array of event data indices. */ uint32_t *stack; /**< Stack of start event array indices. */ uint32_t complete_count; /**< Number of anchor details in `complete`. */ uint32_t progress_count; /**< Number of anchor details in `progress`. */ uint32_t events_count; /**< Number of events in events array. */ uint32_t stack_count; /**< Number of entries in the event stack. */ uint32_t data_count; /**< Number of recorded libyaml events. */ } cyaml_event_record_t; /** * Event replay context. */ typedef struct cyaml_event_replay { bool active; /**< Whether there's an active replay. */ uint32_t anchor_idx; /**< The recorded anchor data to replay. */ uint32_t event_idx; /**< Current recorded event index during replay. */ } cyaml_event_replay_t; /** * CYAML's LibYAML event context. * * Only \ref cyaml_get_next_event and friends should poke around inside here. * Everything else should use \ref cyaml__current_event to access the current * event data, and call \ref cyaml_get_next_event to pump the event stream. */ typedef struct cyaml_event_ctx { bool have_event; /**< Whether event is currently populated. */ yaml_event_t event; /**< Current event. */ cyaml_event_record_t record; /**< Event recording context. */ cyaml_event_replay_t replay; /**< Event replaying context. */ } cyaml_event_ctx_t; /** * Internal YAML loading context. */ typedef struct cyaml_ctx { const cyaml_config_t *config; /**< Settings provided by client. */ cyaml_event_ctx_t event_ctx; /**< Our LibYAML event context. */ cyaml_state_t *state; /**< Current entry in state stack, or NULL. */ cyaml_state_t *stack; /**< State stack */ uint32_t stack_idx; /**< Next (empty) state stack slot */ uint32_t stack_max; /**< Current stack allocation limit. */ unsigned seq_count; /**< Top-level sequence count. */ yaml_parser_t *parser; /**< Internal libyaml parser object. */ } cyaml_ctx_t; /** * Store a signed integer to client data structure according to schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be stored. * \param[in] value The value to store. * \param[in] location The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__store_int( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, int64_t value, uint8_t *location) { int64_t max; int64_t min; if (schema->data_size == 0 || schema->data_size > sizeof(uint64_t)) { return CYAML_ERR_INVALID_DATA_SIZE; } max = (int64_t)((UINT64_MAX >> ((8 - schema->data_size) * 8)) / 2); min = (-max) - 1; if (value < min || value > max) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: INT value out of range: '%" PRId64 "'\n", value); return CYAML_ERR_INVALID_VALUE; } return cyaml_data_write((uint64_t)value, schema->data_size, location); } /** * Store an unsigned integer to client data structure according to schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be stored. * \param[in] value The value to store. * \param[in] location The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__store_uint( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint64_t value, uint8_t *location) { uint64_t max; if (schema->data_size == 0) { return CYAML_ERR_INVALID_DATA_SIZE; } max = (~(uint64_t)0) >> ((8 - schema->data_size) * 8); if (value > max) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Invalid UINT value: '%" PRIu64 "'\n", value); return CYAML_ERR_INVALID_VALUE; } return cyaml_data_write(value, schema->data_size, location); } /** * Store a boolean to client data structure according to schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be stored. * \param[in] value The value to store. * \param[in] location The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__store_bool( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, bool value, uint8_t *location) { CYAML_UNUSED(ctx); return cyaml_data_write(value, schema->data_size, location); } /** * Store a floating point value to client data structure according to schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be stored. * \param[in] value The value to store. * \param[in] location The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__store_float( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, double value, uint8_t *location) { CYAML_UNUSED(ctx); if (schema->data_size == sizeof(float)) { float fvalue; if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) { if (value > FLT_MAX || value < -FLT_MAX) { return CYAML_ERR_INVALID_VALUE; } } fvalue = (float)value; memcpy(location, &fvalue, schema->data_size); } else if (schema->data_size == sizeof(double)) { memcpy(location, &value, schema->data_size); } else { return CYAML_ERR_INVALID_DATA_SIZE; } return CYAML_OK; } /** * Store a string value to client data structure according to schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be stored. * \param[in] value The value to store. * \param[in] location The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__store_string( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *location) { size_t str_len = strlen(value); CYAML_UNUSED(ctx); if (schema->string.min > schema->string.max) { return CYAML_ERR_BAD_MIN_MAX_SCHEMA; } else if (str_len < schema->string.min) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: STRING length < %"PRIu32": %s\n", schema->string.min, value); return CYAML_ERR_STRING_LENGTH_MIN; } else if (str_len > schema->string.max) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: STRING length > %"PRIu32": %s\n", schema->string.max, value); return CYAML_ERR_STRING_LENGTH_MAX; } memcpy(location, value, str_len + 1); return CYAML_OK; } /** * Get the CYAML event type from a `libyaml` event. * * \param[in] event The `libyaml` event. * \return corresponding CYAML event. */ static inline cyaml_event_t cyaml__get_event_type(const yaml_event_t *event) { return (cyaml_event_t)event->type; } /** * Convert a `libyaml` event to a human readable string. * * \param[in] event The `libyaml` event. * \return String representing event. */ static const char * cyaml__libyaml_event_type_str(const yaml_event_t *event) { static const char * const strings[] = { "NO_EVENT", "STREAM_START", "STREAM_END", "DOC_START", "DOC_END", "ALIAS", "SCALAR", "SEQUENCE_START", "SEQUENCE_END", "MAPPING_START", "MAPPING_END", }; return strings[event->type]; } /** * Get the anchor name for an event, or NULL if the event isn't an anchor. * * \param[in] event The `libyaml` event. * \return String representing event's anchor, or NULL. */ static const char * cyaml__get_yaml_event_anchor(const yaml_event_t *event) { switch (cyaml__get_event_type(event)) { case CYAML_EVT_SCALAR: return (const char *)event->data.scalar.anchor; case CYAML_EVT_MAP_START: return (const char *)event->data.mapping_start.anchor; case CYAML_EVT_SEQ_START: return (const char *)event->data.sequence_start.anchor; default: return NULL; } } /** * Get the anchor name for an alias event. * * \param[in] event The `libyaml` event. * \return String representing event's aliased anchor name. */ static const char * cyaml__get_yaml_event_alias(const yaml_event_t *event) { assert(cyaml__get_event_type(event) == CYAML_EVT_ALIAS); assert(event->data.alias.anchor != NULL); return (const char *)event->data.alias.anchor; } /** * Handle an alias event. * * This searches the know anchors for a match. If a matching anchor is found, * it sets the replay context up to play back the recorded events associated * with the anchor, setting the replay state to active. * * \param[in] ctx The CYAML loading context. * \param[in] event The `libyaml` event. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__handle_alias( cyaml_ctx_t *ctx, const yaml_event_t *event) { size_t len; uint32_t anchor_idx; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_replay_t *replay = &e_ctx->replay; const cyaml_event_record_t *record = &e_ctx->record; const char *alias = cyaml__get_yaml_event_alias(event); len = strlen(alias); anchor_idx = 0; for (uint32_t i = record->complete_count; i > 0; i--) { if (record->complete[i - 1].len != len) { continue; } if (memcmp(record->complete[i - 1].name, alias, len) == 0) { anchor_idx = i; break; } } if (anchor_idx == 0) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: No anchor found for alias: '%s'\n", alias); return CYAML_ERR_INVALID_ALIAS; } anchor_idx -= 1; cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: Found alias for anchor: '%s'\n", alias); replay->active = true; replay->anchor_idx = anchor_idx; replay->event_idx = record->complete[anchor_idx].start; return CYAML_OK; } /** * Create new anchor entry at the end of an anchors array. * * \param[in] ctx The CYAML loading context. * \param[in,out] anchors_count Count of anchors in array. Updated on success. * \param[in,out] anchors Anchors array, updated on success. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__new_anchor( cyaml_ctx_t *ctx, uint32_t *anchors_count, cyaml_anchor_t **anchors) { size_t anchors_size; cyaml_anchor_t *temp; anchors_size = *anchors_count * sizeof(**anchors); temp = cyaml__realloc(ctx->config, *anchors, anchors_size, anchors_size + sizeof(**anchors), true); if (temp == NULL) { return CYAML_ERR_OOM; } *anchors = temp; (*anchors_count)++; return CYAML_OK; } /** * Handle an event that establishes an anchor. * * This should not be called while replaying recorded events, or it will * try to rebuild anchors we already have. * * This is a no-op if the event doesn't establish an anchor. If the event does * create an anchor, an anchor entry is added to the recording context. * * \param[in] ctx The CYAML loading context. * \param[in] event The `libyaml` event to handle any anchor for. * \param[out] is_anchor_out Returns whether the event creates an anchor. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__handle_anchor( cyaml_ctx_t *ctx, const yaml_event_t *event, bool *is_anchor_out) { cyaml_err_t err; cyaml_anchor_t *anchor; const char *anchor_name; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_record_t *record = &e_ctx->record; anchor_name = cyaml__get_yaml_event_anchor(event); if (anchor_name == NULL) { *is_anchor_out = false; return CYAML_OK; } cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: Found anchor: '%s'\n", anchor_name); if (cyaml__get_event_type(event) == CYAML_EVT_SEQ_START || cyaml__get_event_type(event) == CYAML_EVT_MAP_START) { /* Start of multi-event recording. */ err = cyaml__new_anchor(ctx, &record->progress_count, &record->progress); if (err != CYAML_OK) { return err; } anchor = record->progress + record->progress_count - 1; } else { /* Single event anchor */ err = cyaml__new_anchor(ctx, &record->complete_count, &record->complete); if (err != CYAML_OK) { return err; } anchor = record->complete + record->complete_count - 1; } anchor->start = record->events_count; anchor->end = record->events_count; anchor->name = cyaml__strdup(ctx->config, anchor_name, &anchor->len); if (anchor->name == NULL) { return CYAML_ERR_OOM; } *is_anchor_out = true; return CYAML_OK; } /** * Push a recording stack context entry. * * \param[in] ctx The CYAML loading context. * \param[in] event_index The current event's index in the recording. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__record_stack_push( cyaml_ctx_t *ctx, uint32_t event_index) { uint32_t *stack; size_t stack_size; uint32_t stack_count; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_record_t *record = &e_ctx->record; stack_count = record->stack_count; stack_size = stack_count * sizeof(*stack); stack = cyaml__realloc(ctx->config, record->stack, stack_size, stack_size + sizeof(*stack), true); if (stack == NULL) { return CYAML_ERR_OOM; } record->stack = stack; record->stack_count++; stack[stack_count] = event_index; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Push recording stack entry for %s\n", cyaml__libyaml_event_type_str( &record->data[record->events[event_index]])); return CYAML_OK; } /** * Pop a recording stack context entry. * * Any actively recording anchors are checked, and if this event * ends the anchor, the anchor recording is moved from the progress * array to the complete array. * * \param[in] ctx The CYAML loading context. * \param[in] event_index The current event's index in the recording. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__record_stack_pop( cyaml_ctx_t *ctx, uint32_t event_index) { uint32_t index; uint32_t stack; cyaml_err_t err; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_record_t *record = &e_ctx->record; assert(record->stack_count > 0); index = record->progress_count - 1; stack = record->stack[record->stack_count - 1]; if (record->progress[index].start == stack) { record->progress[index].end = event_index; err = cyaml__new_anchor(ctx, &record->complete_count, &record->complete); if (err != CYAML_OK) { return err; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Finish recording events for '%s'\n", record->progress[index].name); record->complete[record->complete_count - 1] = record->progress[index]; record->progress_count--; } record->stack_count--; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Pop recording stack entry for %s\n", cyaml__libyaml_event_type_str( &record->data[record->events[event_index]])); return CYAML_OK; } /** * Update the anchor data in the recording context. * * \param[in] ctx The CYAML loading context. * \param[in] event_index The current event's index in the recording. * \param[in] event The `libyaml` event to fill from the recording. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__update_anchor_recordings( cyaml_ctx_t *ctx, uint32_t event_index, const yaml_event_t *event) { cyaml_err_t err; switch (cyaml__get_event_type(event)) { case CYAML_EVT_SEQ_START: /* Fall through. */ case CYAML_EVT_MAP_START: err = cyaml__record_stack_push(ctx, event_index); if (err != CYAML_OK) { return err; } break; case CYAML_EVT_SEQ_END: /* Fall through. */ case CYAML_EVT_MAP_END: err = cyaml__record_stack_pop(ctx, event_index); if (err != CYAML_OK) { return err; } break; default: break; } return CYAML_OK; } /** * Handle the recording of the current event. * * \param[in] ctx The CYAML loading context. * \param[in] event The `libyaml` to record. * \param[in] replayed_event Whether this event is a replay. * \param[in] replay_event_index Index in events array of replayed event. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml__handle_record( cyaml_ctx_t *ctx, const yaml_event_t *event, bool replayed_event, uint32_t replay_event_index) { uint32_t *events; size_t events_size; uint32_t event_index; uint32_t events_count; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_record_t *record = &e_ctx->record; if (replayed_event) { if (e_ctx->record.progress_count == 0) { return CYAML_OK; } event_index = record->events[replay_event_index]; } else { size_t data_size; yaml_event_t *data; uint32_t data_count; bool event_has_anchor = false; /* We've not already seen this event, so if it creates an * anchor then we need to record it. */ cyaml_err_t err = cyaml__handle_anchor(ctx, event, &event_has_anchor); if (err != CYAML_OK) { return err; } if (e_ctx->record.progress_count == 0 && event_has_anchor == false) { return CYAML_OK; } /* Record event data. */ data_count = record->data_count; data_size = data_count * sizeof(*data); data = cyaml__realloc(ctx->config, record->data, data_size, data_size + sizeof(*data), true); if (data == NULL) { return CYAML_ERR_OOM; } record->data = data; record->data_count++; memcpy(data + data_count, event, sizeof(*data)); e_ctx->have_event = false; event_index = data_count; } /* Record event data index. Multiple event data indexes can * reference the same event data, due to replaying of events. */ events_count = record->events_count; events_size = events_count * sizeof(*events); events = cyaml__realloc(ctx->config, record->events, events_size, events_size + sizeof(*events), true); if (events == NULL) { return CYAML_ERR_OOM; } record->events = events; record->events_count++; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Record event data %"PRIu32" at index %"PRIu32"\n", event_index, events_count); events[events_count] = event_index; return cyaml__update_anchor_recordings(ctx, events_count, event); } /** * Handle the current event replay. * * There must be an active replay before this is called. If this call * yields the final event of the anchor, the replay state is disabled. * * \param[in] ctx The CYAML loading context. * \param[out] event_out The `libyaml` event to fill from the recording. * \param[out] event_index_out The index in events array of the replayed event. */ static void cyaml__handle_replay( cyaml_ctx_t *ctx, yaml_event_t *event_out, uint32_t *event_index_out) { uint32_t event_index; cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_replay_t *replay = &e_ctx->replay; const cyaml_event_record_t *record = &e_ctx->record; const yaml_event_t *replay_event = record->data + record->events[replay->event_idx]; const cyaml_anchor_t *replay_anchor = record->complete + replay->anchor_idx; assert(replay->active); event_index = replay->event_idx; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Replaying event idx %"PRIu32": " "event data: %"PRIu32"\n", event_index, record->events[event_index]); if (event_index == replay_anchor->end) { replay->active = false; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Finishing handling of alias: '%s'\n", replay_anchor->name); } else { replay->event_idx++; } memcpy(event_out, replay_event, sizeof(*event_out)); *event_index_out = event_index; } /** * Delete any current event from the load context. * * This gets the next event from the CYAML load context's `libyaml` parser * object. * * \param[in] ctx The CYAML loading context. */ static void cyaml__delete_yaml_event( cyaml_ctx_t *ctx) { if (ctx->event_ctx.have_event) { yaml_event_delete(&ctx->event_ctx.event); ctx->event_ctx.have_event = false; } } /** * Free any recordings from the CYAML loading context. * * \param[in] ctx The CYAML loading context. */ static void cyaml__free_recording( cyaml_ctx_t *ctx) { cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; cyaml_event_record_t *record = &e_ctx->record; for (uint32_t i = 0; i < record->progress_count; i++) { cyaml__free(ctx->config, record->progress[i].name); } cyaml__free(ctx->config, record->progress); for (uint32_t i = 0; i < record->complete_count; i++) { cyaml__free(ctx->config, record->complete[i].name); } cyaml__free(ctx->config, record->complete); for (uint32_t i = 0; i < record->data_count; i++) { yaml_event_delete(&record->data[i]); } cyaml__free(ctx->config, record->events); cyaml__free(ctx->config, record->stack); cyaml__free(ctx->config, record->data); } /** * Helper function to read the next YAML input event into the context. * * This handles recording the events associated with anchors, and replaying * them when an alias event references a valid anchor. If we are not replaying * anchored events, this gets the next event from the CYAML load context's * `libyaml` parser object. * * Any existing event in the load context is deleted first. * * Callers do not always need to delete the previous event from the context * before calling this function. However, after the final call, when cleaning * up the context, any event must be deleted with a single call to \ref * cyaml__delete_yaml_event. * * \param[in] ctx The CYAML loading context. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static cyaml_err_t cyaml_get_next_event( cyaml_ctx_t *ctx) { cyaml_event_ctx_t *e_ctx = &ctx->event_ctx; yaml_event_t *event = &e_ctx->event; uint32_t replay_event_index = 0; bool replayed_event = false; cyaml_err_t err; cyaml__delete_yaml_event(ctx); if (!e_ctx->replay.active) { if (!yaml_parser_parse(ctx->parser, event)) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: libyaml: %s\n", ctx->parser->problem); return CYAML_ERR_LIBYAML_PARSER; } e_ctx->have_event = true; if (event->type == YAML_ALIAS_EVENT) { if (ctx->config->flags & CYAML_CFG_NO_ALIAS) { return CYAML_ERR_ALIAS; } else { err = cyaml__handle_alias(ctx, event); if (err != CYAML_OK) { return err; } } cyaml__delete_yaml_event(ctx); } } if (e_ctx->replay.active) { cyaml__handle_replay(ctx, event, &replay_event_index); replayed_event = true; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Event: %s\n", cyaml__libyaml_event_type_str(event)); if (ctx->config->flags & CYAML_CFG_NO_ALIAS) { return CYAML_OK; } return cyaml__handle_record(ctx, event, replayed_event, replay_event_index); } /** * Get a pointer to the load context's current event. * * The outside world should use this to get the address of the event data, * which will not change for subsequent events. The event should only be used * after \ref cyaml_get_next_event() has returned \ref CYAML_OK. * * \param[in] ctx The CYAML loading context. * \return \ref CYAML_OK on success, or appropriate error otherwise. */ static inline const yaml_event_t * cyaml__current_event( const cyaml_ctx_t *ctx) { return &ctx->event_ctx.event; } /** * Get the offset to a mapping field by key in a mapping schema array. * * \param[in] cfg The client's CYAML library config. * \param[in] schema CYAML schema for the mapping value to be loaded. * \param[in] key Key to search for in mapping schema. * \return index the mapping schema's mapping fields array for key, or * \ref CYAML_FIELDS_IDX_NONE if key is not present in schema. */ static inline uint16_t cyaml__get_mapping_field_idx( const cyaml_config_t *cfg, const cyaml_schema_value_t *schema, const char *key) { const cyaml_schema_field_t *fields = schema->mapping.fields; uint16_t index = 0; assert(schema->type == CYAML_MAPPING); /* Step through each entry in the schema */ for (; fields->key != NULL; fields++) { if (cyaml__strcmp(cfg, schema, fields->key, key) == 0) { return index; } index++; } return CYAML_FIELDS_IDX_NONE; } /** *Helper to get the current mapping field. * * \note The current load state must be \ref CYAML_STATE_IN_MAP_KEY, or * \ref CYAML_STATE_IN_MAP_VALUE, and there must be a current field * index in the state. * * \param[in] ctx The CYAML loading context. * \return Current mapping field's schema entry. */ static inline const cyaml_schema_field_t * cyaml_mapping_schema_field( const cyaml_ctx_t *ctx) { const cyaml_state_t *state = ctx->state; assert(state != NULL); assert(state->state == CYAML_STATE_IN_MAP_KEY || state->state == CYAML_STATE_IN_MAP_VALUE); assert(state->mapping.fields_idx != CYAML_FIELDS_IDX_NONE); return state->mapping.fields + state->mapping.fields_idx; } /** * Ensure that the CYAML load context has space for a new stack entry. * * \param[in] ctx The CYAML loading context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_ensure( cyaml_ctx_t *ctx) { cyaml_state_t *temp; uint32_t max = ctx->stack_max + 16; if (ctx->stack_idx < ctx->stack_max) { return CYAML_OK; } temp = cyaml__realloc(ctx->config, ctx->stack, 0, sizeof(*ctx->stack) * max, false); if (temp == NULL) { return CYAML_ERR_OOM; } ctx->stack = temp; ctx->stack_max = max; ctx->state = ctx->stack + ctx->stack_idx - 1; return CYAML_OK; } /** * Count the entries in a mapping field array schema. * * The mapping schema array must be terminated by an entry with a NULL key. * * \param[in] mapping_schema Array of mapping schema fields. * \return Number of entries in mapping_schema array. */ static uint16_t cyaml__get_mapping_field_count( const cyaml_schema_field_t *mapping_schema) { const cyaml_schema_field_t *entry = mapping_schema; while (entry->key != NULL) { entry++; } return (uint16_t)(entry - mapping_schema); } /** * Create \ref CYAML_STATE_IN_MAP_KEY state's bitfield array allocation. * * The bitfield is used to record whether the mapping as all the required * fields by mapping schema array index. * * \param[in] ctx The CYAML loading context. * \param[in] state CYAML load state for a \ref CYAML_STATE_IN_MAP_KEY state. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__mapping_bitfieid_create( cyaml_ctx_t *ctx, cyaml_state_t *state) { unsigned count = state->mapping.fields_count; if (count != 0) { cyaml_bitfield_t *bitfield; size_t size = ((count + CYAML_BITFIELD_BITS - 1) / CYAML_BITFIELD_BITS) * sizeof(*bitfield); bitfield = cyaml__alloc(ctx->config, size, true); if (bitfield == NULL) { return CYAML_ERR_OOM; } state->mapping.fields_set = bitfield; } return CYAML_OK; } /** * Destroy a \ref CYAML_STATE_IN_MAP_KEY state's bitfield array allocation. * * \param[in] ctx The CYAML loading context. * \param[in] state CYAML load state for a \ref CYAML_STATE_IN_MAP_KEY state. */ static void cyaml__mapping_bitfieid_destroy( const cyaml_ctx_t *ctx, cyaml_state_t *state) { cyaml__free(ctx->config, state->mapping.fields_set); state->mapping.fields_set = NULL; } /** * Set the bit for the current mapping's current field, to indicate it exists. * * Current CYAML load state must be \ref CYAML_STATE_IN_MAP_KEY. * * \param[in] ctx The CYAML loading context. */ static void cyaml__mapping_bitfieid_set( const cyaml_ctx_t *ctx) { cyaml_state_t *state = ctx->state; unsigned idx = state->mapping.fields_idx; state->mapping.fields_set[idx / CYAML_BITFIELD_BITS] |= 1u << (idx % CYAML_BITFIELD_BITS); } /** * Check the bit for current mapping's current field. * * Current CYAML load state must be \ref CYAML_STATE_IN_MAP_KEY. * * \param[in] ctx The CYAML loading context. */ static bool cyaml__mapping_bitfieid_check( const cyaml_ctx_t *ctx) { cyaml_state_t *state = ctx->state; unsigned idx = state->mapping.fields_idx; return state->mapping.fields_set[idx / CYAML_BITFIELD_BITS] & (1u << (idx % CYAML_BITFIELD_BITS)); } /** * Check a mapping had all the required fields. * * Checks all the bits are set in the bitfield, which correspond to * entries in the mapping schema array which are not marked with the * \ref CYAML_FLAG_OPTIONAL flag. * * Current CYAML load state must be \ref CYAML_STATE_IN_MAP_KEY. * * \param[in] ctx The CYAML loading context. * \return \ref CYAML_OK if all required fields are present, or * \ref CYAML_ERR_MAPPING_FIELD_MISSING any are missing. */ static cyaml_err_t cyaml__mapping_bitfieid_validate( const cyaml_ctx_t *ctx) { cyaml_state_t *state = ctx->state; 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; } cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Missing required mapping field: %s\n", state->mapping.fields[i].key); return CYAML_ERR_MAPPING_FIELD_MISSING; } return CYAML_OK; } /** * Helper to check if schema is for a \ref CYAML_SEQUENCE type. * * \param[in] schema The schema entry for a type. * \return true iff schema is for a \ref CYAML_SEQUENCE type, * false otherwise. */ static inline bool cyaml__is_sequence(const cyaml_schema_value_t *schema) { return ((schema->type == CYAML_SEQUENCE) || (schema->type == CYAML_SEQUENCE_FIXED)); } /** * Push a new entry onto the CYAML load context's stack. * * \param[in] ctx The CYAML loading context. * \param[in] state The CYAML load state we're pushing a stack entry for. * \param[in] event The YAML event we're pushing a stack entry for. * \param[in] schema The CYAML schema for the value expected in state. * \param[in] data Pointer to where value's data should be written. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_push( cyaml_ctx_t *ctx, enum cyaml_state_e state, const yaml_event_t *event, const cyaml_schema_value_t *schema, cyaml_data_t *data) { cyaml_err_t err; cyaml_state_t s = { .data = data, .state = state, .schema = schema, }; err = cyaml__stack_ensure(ctx); if (err != CYAML_OK) { return err; } switch (state) { case CYAML_STATE_IN_MAP_KEY: assert(schema->type == CYAML_MAPPING); s.mapping.fields = schema->mapping.fields; s.mapping.fields_count = cyaml__get_mapping_field_count( schema->mapping.fields); err = cyaml__mapping_bitfieid_create(ctx, &s); if (err != CYAML_OK) { return err; } break; case CYAML_STATE_IN_SEQUENCE: assert(cyaml__is_sequence(schema)); if (schema->type == CYAML_SEQUENCE_FIXED) { if (schema->sequence.min != schema->sequence.max) { return CYAML_ERR_SEQUENCE_FIXED_COUNT; } } else { if (ctx->state->state == CYAML_STATE_IN_SEQUENCE) { return CYAML_ERR_SEQUENCE_IN_SEQUENCE; } else if (ctx->state->state == CYAML_STATE_IN_MAP_KEY) { const cyaml_schema_field_t *field = cyaml_mapping_schema_field(ctx); s.sequence.count_data = ctx->state->data + field->count_offset; s.sequence.count_size = field->count_size; } else { assert(ctx->state->state == CYAML_STATE_IN_DOC); s.sequence.count_data = (void *)&ctx->seq_count; s.sequence.count_size = sizeof(ctx->seq_count); } } break; default: break; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: PUSH[%u]: %s\n", ctx->stack_idx, cyaml__state_to_str(state)); if (event != NULL) { s.line = event->start_mark.line; s.column = event->start_mark.column; } ctx->stack[ctx->stack_idx] = s; ctx->state = ctx->stack + ctx->stack_idx; ctx->stack_idx++; return CYAML_OK; } /** * Pop the current entry on the CYAML load context's stack. * * This frees any resources owned by the stack entry. * * \param[in] ctx The CYAML loading context. */ static void cyaml__stack_pop( cyaml_ctx_t *ctx) { uint32_t idx = ctx->stack_idx; assert(idx != 0); switch (ctx->state->state) { case CYAML_STATE_IN_MAP_KEY: /* Fall through. */ case CYAML_STATE_IN_MAP_VALUE: cyaml__mapping_bitfieid_destroy(ctx, ctx->state); break; default: break; } idx--; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: POP[%u]: %s\n", idx, cyaml__state_to_str(ctx->state->state)); ctx->state = (idx == 0) ? NULL : &ctx->stack[idx - 1]; ctx->stack_idx = idx; } /** * Validate the current event for what's expected by the schema. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for value that the event belongs to. * \param[in] event The event to be checked. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__validate_event_type_for_schema( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const yaml_event_t *event) { cyaml_event_t cyaml_event = cyaml__get_event_type(event); static const cyaml_event_t valid_event[CYAML__TYPE_COUNT] = { [CYAML_INT] = CYAML_EVT_SCALAR, [CYAML_UINT] = CYAML_EVT_SCALAR, [CYAML_BOOL] = CYAML_EVT_SCALAR, [CYAML_ENUM] = CYAML_EVT_SCALAR, [CYAML_FLOAT] = CYAML_EVT_SCALAR, [CYAML_STRING] = CYAML_EVT_SCALAR, [CYAML_FLAGS] = CYAML_EVT_SEQ_START, [CYAML_MAPPING] = CYAML_EVT_MAP_START, [CYAML_BITFIELD] = CYAML_EVT_MAP_START, [CYAML_SEQUENCE] = CYAML_EVT_SEQ_START, [CYAML_SEQUENCE_FIXED] = CYAML_EVT_SEQ_START, [CYAML_IGNORE] = CYAML_EVT__COUNT, }; if (schema->type >= CYAML__TYPE_COUNT) { return CYAML_ERR_BAD_TYPE_IN_SCHEMA; } if (schema->type == CYAML_IGNORE) { return CYAML_OK; } if (cyaml_event != valid_event[schema->type]) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Expecting %s, got event: %s\n", cyaml__type_to_str(schema->type), cyaml__libyaml_event_type_str(event)); return CYAML_ERR_INVALID_VALUE; } return CYAML_OK; } /** * Helper to make allocations for loaded YAML values. * * If the current state is sequence, this extends any existing allocation * for the sequence. * * The current CYAML loading context's state is updated with new allocation * address, where necessary. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for value to get data pointer for. * \param[in] event The YAML event value to get data pointer for. * \param[in,out] value_data_io Current address of value's data. Updated to * new address if value is allocation requiring * an allocation. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__data_handle_pointer( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const yaml_event_t *event, uint8_t **value_data_io) { cyaml_state_t *state = ctx->state; if (schema->flags & CYAML_FLAG_POINTER) { /* Need to create/extend an allocation. */ size_t data_size = schema->data_size; uint8_t *value_data = NULL; size_t offset = 0; size_t delta; switch (schema->type) { case CYAML_STRING: /* For a string the allocation size is the string * size from the event, plus trailing NULL. */ delta = strlen((const char *) event->data.scalar.value) + 1; break; case CYAML_SEQUENCE: /* Sequence; could be extending allocation. */ offset = data_size * state->sequence.count; value_data = state->sequence.data; delta = data_size; break; case CYAML_SEQUENCE_FIXED: /* Allocation is only made for full fixed size * of sequence, on creation, and not extended. */ if (state->sequence.count > 0) { *value_data_io = state->sequence.data; return CYAML_OK; } delta = data_size * schema->sequence.max; break; default: delta = data_size; break; } value_data = cyaml__realloc(ctx->config, value_data, offset, offset + delta, true); if (value_data == NULL) { return CYAML_ERR_OOM; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Allocation: %p (%zu + %zu bytes)\n", value_data, offset, delta); if (cyaml__is_sequence(schema)) { /* Updated the in sequence state so it knows the new * allocation address. */ state->sequence.data = value_data; } /* Write the allocation pointer into the data structure. */ cyaml_data_write_pointer(value_data, *value_data_io); /* Update the caller's pointer so it can write the value to * the right place. */ *value_data_io = value_data; } return CYAML_OK; } /** * Dump a backtrace to the log. * * \param[in] ctx The CYAML loading context. */ static void cyaml__backtrace( cyaml_ctx_t *ctx) { if (ctx->stack_idx > 1) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Backtrace:\n"); } else { return; } for (uint32_t idx = ctx->stack_idx - 1; idx != 0; idx--) { const cyaml_state_t *state = ctx->stack + idx; switch (state->state) { case CYAML_STATE_IN_MAP_KEY: /* Fall through. */ case CYAML_STATE_IN_MAP_VALUE: if (state->mapping.fields_idx != CYAML_FIELDS_IDX_NONE) { cyaml__log(ctx->config, CYAML_LOG_ERROR, " in mapping field '%s' " "(line: %zu, column: %zu)\n", state->mapping.fields[ state->mapping.fields_idx].key, state->line + 1, state->column + 1); } else { cyaml__log(ctx->config, CYAML_LOG_ERROR, " in mapping " "(line: %zu, column: %zu)\n", state->line + 1, state->column + 1); } break; case CYAML_STATE_IN_SEQUENCE: cyaml__log(ctx->config, CYAML_LOG_ERROR, " in sequence entry '%"PRIu32"' " "(line: %zu, column: %zu)\n", state->sequence.count, state->line + 1, state->column + 1); break; default: /** \todo \ref CYAML_STATE_IN_DOC handling for multi * document streams. */ break; } } } /** * Read a value of type \ref CYAML_INT. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_int( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { long long temp; char *end = NULL; errno = 0; temp = strtoll(value, &end, 0); if (end == value || end == NULL || *end != '\0' || errno == ERANGE) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Invalid INT value: '%s'\n", value); return CYAML_ERR_INVALID_VALUE; } return cyaml__store_int(ctx, schema, (int64_t)temp, data); } /** * Helper to read a number into a uint64_t. * * \param[in] ctx The CYAML loading context. * \param[in] value String containing scaler value. * \param[in] out The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__read_uint64_t( const cyaml_ctx_t *ctx, const char *value, uint64_t *out) { unsigned long long temp; char *end = NULL; errno = 0; temp = strtoull(value, &end, 0); if (end == value || end == NULL || *end != '\0' || errno == ERANGE) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Invalid uint64_t value: '%s'\n", value); return CYAML_ERR_INVALID_VALUE; } *out = (uint64_t)temp; return CYAML_OK; } /** * Read a value of type \ref CYAML_UINT. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_uint( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { cyaml_err_t err; uint64_t temp; err = cyaml__read_uint64_t(ctx, value, &temp); if (err != CYAML_OK) { return err; } return cyaml__store_uint(ctx, schema, temp, data); } /** * Read a value of type \ref CYAML_BOOL. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_bool( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { bool temp = true; static const char * const false_strings[] = { "false", "no", "off", "disable", "0", }; for (uint32_t i = 0; i < CYAML_ARRAY_LEN(false_strings); i++) { if (cyaml_utf8_casecmp(value, false_strings[i]) == 0) { temp = false; break; } } return cyaml__store_bool(ctx, schema, temp, data); } /** * Read a value of type \ref CYAML_ENUM. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_enum( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { const cyaml_strval_t *strings = schema->enumeration.strings; for (uint32_t i = 0; i < schema->enumeration.count; i++) { if (cyaml__strcmp(ctx->config, schema, value, strings[i].str) == 0) { return cyaml__store_int(ctx, schema, strings[i].val, data); } } if (!cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) { cyaml_err_t err; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Attempt numerical fallback for ENUM: " "'%s'\n", value); err = cyaml__read_int(ctx, schema, value, data); if (err == CYAML_OK) { return CYAML_OK; } } cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Invalid ENUM value: %s\n", value); cyaml__log(ctx->config, CYAML_LOG_NOTICE, "Load: Valid values are:\n"); for (uint32_t i = 0; i < schema->enumeration.count; i++) { cyaml__log(ctx->config, CYAML_LOG_NOTICE, "Load: - `%s`\n", strings[i].str); } return CYAML_ERR_INVALID_VALUE; } /** * Helper to read \ref CYAML_FLOAT values. * * The `data_size` of the schema entry must be the size of a known * floating point C type. * * \note The `long double` type was causing problems, so it isn't currently * supported. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_float( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { double temp; char *end = NULL; errno = 0; temp = strtod(value, &end); if (end == value || end == NULL || *end != '\0') { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Invalid FLOAT value: %s\n", value); return CYAML_ERR_INVALID_VALUE; } else if (errno == ERANGE) { cyaml_log_t level = CYAML_LOG_ERROR; if (!cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) { level = CYAML_LOG_NOTICE; } cyaml__log(ctx->config, level, "Load: FLOAT overflow/overflow: %s\n", value); if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) { return CYAML_ERR_INVALID_VALUE; } } return cyaml__store_float(ctx, schema, temp, data); } /** * Read a value of type \ref CYAML_STRING. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_string( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data) { return cyaml__store_string(ctx, schema, value, data); } /** * Read a scalar value. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] data The place to write the value in the output data. * \param[in] event The `libyaml` event providing the scalar value data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_scalar_value( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, cyaml_data_t *data, const yaml_event_t *event) { const char *value = (const char *)event->data.scalar.value; typedef cyaml_err_t (*cyaml_read_scalar_fn)( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint8_t *data_target); static const cyaml_read_scalar_fn fn[CYAML__TYPE_COUNT] = { [CYAML_INT] = cyaml__read_int, [CYAML_UINT] = cyaml__read_uint, [CYAML_BOOL] = cyaml__read_bool, [CYAML_ENUM] = cyaml__read_enum, [CYAML_FLOAT] = cyaml__read_float, [CYAML_STRING] = cyaml__read_string, }; cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: <%s>\n", value); assert(fn[schema->type] != NULL); return fn[schema->type](ctx, schema, value, data); } /** * Set a flag in a \ref CYAML_FLAGS value. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] value String containing scaler value. * \param[in,out] flags_out Current flags, updated on success. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__set_flag( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, uint64_t *flags_out) { const cyaml_strval_t *strings = schema->enumeration.strings; for (uint32_t i = 0; i < schema->enumeration.count; i++) { if (cyaml__strcmp(ctx->config, schema, value, strings[i].str) == 0) { *flags_out |= ((uint64_t)strings[i].val); return CYAML_OK; } } if (!(schema->flags & CYAML_FLAG_STRICT)) { long long temp; char *end = NULL; uint64_t max = (~(uint64_t)0) >> ((8 - schema->data_size) * 8); errno = 0; temp = strtoll(value, &end, 0); if (!(end == value || end == NULL || *end != '\0' || errno == ERANGE || temp < 0 || (uint64_t)temp > max)) { *flags_out |= ((uint64_t)temp); return CYAML_OK; } } cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Unknown flag: %s\n", value); return CYAML_ERR_INVALID_VALUE; } /** * Read a value of type \ref CYAML_FLAGS. * * Since \ref CYAML_FLAGS is a composite value (a sequence of scalars), rather * than a simple scaler, this consumes events from the YAML input stream. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_flags_value( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, cyaml_data_t *data) { bool exit = false; uint64_t value = 0; cyaml_err_t err = CYAML_OK; const yaml_event_t *const event = cyaml__current_event(ctx); if (schema->data_size == 0) { return CYAML_ERR_INVALID_DATA_SIZE; } while (!exit) { cyaml_event_t cyaml_event; err = cyaml_get_next_event(ctx); if (err != CYAML_OK) { return err; } cyaml_event = cyaml__get_event_type(event); switch (cyaml_event) { case CYAML_EVT_SCALAR: err = cyaml__set_flag(ctx, schema, (const char *)event->data.scalar.value, &value); if (err != CYAML_OK) { return err; } break; case CYAML_EVT_SEQ_END: exit = true; break; default: return CYAML_ERR_UNEXPECTED_EVENT; } } err = cyaml_data_write(value, schema->data_size, data); if (err != CYAML_OK) { return err; } cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: \n", value); return err; } /** * Get bitfield component index for name in a \ref CYAML_BITFIELD value. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[out] index_out Returns bitdefs index on success. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__get_bitval_index( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint32_t *index_out) { const yaml_event_t *const event = cyaml__current_event(ctx); const char *name = (const char *)event->data.scalar.value; const cyaml_bitdef_t *bitdef = schema->bitfield.bitdefs; uint32_t i; for (i = 0; i < schema->bitfield.count; i++) { if (bitdef[i].bits + bitdef[i].offset > schema->data_size * 8) { return CYAML_ERR_BAD_BITVAL_IN_SCHEMA; } if (cyaml__strcmp(ctx->config, schema, name, bitdef[i].name) == 0) { break; } } if (i == schema->bitfield.count) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Unknown bit value: %s\n", name); return CYAML_ERR_INVALID_VALUE; } *index_out = i; return CYAML_OK; } /** * Set some bits in a \ref CYAML_BITFIELD value. * * If the given bit value name is one expected by the schema, then this * function consumes an event from the YAML input stream. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in,out] bits_out Current bits, updated on success. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__set_bitval( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint64_t *bits_out) { const yaml_event_t *const event = cyaml__current_event(ctx); const cyaml_bitdef_t *bitdef = schema->bitfield.bitdefs; cyaml_err_t err; uint32_t index; uint64_t value; uint64_t mask; err = cyaml__get_bitval_index(ctx, schema, &index); if (err != CYAML_OK) { return err; } err = cyaml_get_next_event(ctx); if (err != CYAML_OK) { return err; } switch (cyaml__get_event_type(event)) { case CYAML_EVT_SCALAR: err = cyaml__read_uint64_t(ctx, (const char *)event->data.scalar.value, &value); if (err != CYAML_OK) { return err; } break; default: return CYAML_ERR_UNEXPECTED_EVENT; } mask = (~(uint64_t)0) >> ((8 * sizeof(uint64_t)) - bitdef[index].bits); if (value > mask) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Value too big for bits: %s\n", bitdef[index].name); return CYAML_ERR_INVALID_VALUE; } *bits_out |= value << bitdef[index].offset; return CYAML_OK; } /** * Read a value of type \ref CYAML_BITFIELD. * * Since \ref CYAML_FLAGS is a composite value (a mapping), rather * than a simple scaler, this consumes events from the YAML input stream. * * \param[in] ctx The CYAML loading context. * \param[in] schema The schema for the value to be read. * \param[in] data The place to write the value in the output data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_bitfield_value( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, cyaml_data_t *data) { bool exit = false; uint64_t value = 0; cyaml_err_t err = CYAML_OK; const yaml_event_t *const event = cyaml__current_event(ctx); while (!exit) { cyaml_event_t cyaml_event; err = cyaml_get_next_event(ctx); if (err != CYAML_OK) { return err; } cyaml_event = cyaml__get_event_type(event); switch (cyaml_event) { case CYAML_EVT_SCALAR: err = cyaml__set_bitval(ctx, schema, &value); if (err != CYAML_OK) { return err; } break; case CYAML_EVT_MAP_END: exit = true; break; default: return CYAML_ERR_UNEXPECTED_EVENT; } } err = cyaml_data_write(value, schema->data_size, data); if (err != CYAML_OK) { return err; } cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: \n", value); return err; } /** * Entirely consume an ignored value. * * This ignores all the descendants of the value, e.g. if the `ignored` key's * value is of type \ref CYAML_IGNORE, all of the following is ignored: * * ``` * ignored: * - foo: 7 * bar: 9 * - foo: 1 * bar: 2 * ``` * * \param[in] ctx The CYAML loading context. * \param[in] cyaml_event The event for the value to ignore. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__consume_ignored_value( cyaml_ctx_t *ctx, cyaml_event_t cyaml_event) { if (cyaml_event != CYAML_EVT_SCALAR) { unsigned level = 1; assert(cyaml_event == CYAML_EVT_SEQ_START || cyaml_event == CYAML_EVT_MAP_START); while (level > 0) { cyaml_err_t err; const yaml_event_t *const event = cyaml__current_event(ctx); err = cyaml_get_next_event(ctx); if (err != CYAML_OK) { return err; } switch (cyaml__get_event_type(event)) { case CYAML_EVT_SEQ_START: /* Fall through */ case CYAML_EVT_MAP_START: level++; break; case CYAML_EVT_SEQ_END: /* Fall through */ case CYAML_EVT_MAP_END: level--; break; default: break; } } } return CYAML_OK; } /** * Check whether a string represents a NULL value. * * \param[in] schema CYAML schema for the value to test. * \param[in] value String value to test. * \return true if string represents a NULL, false otherwise. */ static bool cyaml__string_is_null_ptr( const cyaml_schema_value_t *schema, const char *value) { assert(value != NULL); if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_POINTER_NULL) && value[0] == '\0') { return true; } if (!cyaml__flag_check_all(schema->flags, CYAML_FLAG_POINTER_NULL_STR)) { return false; } switch (strlen(value)) { case 1: return (strcmp(value, "~") == 0); case 4: return (strcmp(value, "null") == 0 || strcmp(value, "Null") == 0 || strcmp(value, "NULL") == 0); } return false; } /** * Handle a YAML event corresponding to a YAML data value. * * \param[in] ctx The CYAML loading context. * \param[in] schema CYAML schema for the expected value. * \param[in] data Pointer to where value's data should be written. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__read_value( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint8_t *data, const yaml_event_t *event) { cyaml_event_t cyaml_event = cyaml__get_event_type(event); cyaml_err_t err; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Reading value of type '%s'%s\n", cyaml__type_to_str(schema->type), schema->flags & CYAML_FLAG_POINTER ? " (pointer)" : ""); if (cyaml_event == CYAML_EVT_SCALAR) { if (cyaml__string_is_null_ptr(schema, (const char *)event->data.scalar.value)) { cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: \n"); return CYAML_OK; } } err = cyaml__validate_event_type_for_schema(ctx, schema, event); if (err != CYAML_OK) { return err; } if (cyaml__is_sequence(schema) == false) { /* Since sequences extend their allocation for each entry, * they're handled in the sequence-specific code. */ err = cyaml__data_handle_pointer(ctx, schema, event, &data); if (err != CYAML_OK) { return err; } } switch (schema->type) { case CYAML_INT: /* Fall through. */ case CYAML_UINT: /* Fall through. */ case CYAML_BOOL: /* Fall through. */ case CYAML_ENUM: /* Fall through. */ case CYAML_FLOAT: /* Fall through. */ case CYAML_STRING: err = cyaml__read_scalar_value(ctx, schema, data, event); break; case CYAML_FLAGS: err = cyaml__read_flags_value(ctx, schema, data); break; case CYAML_MAPPING: err = cyaml__stack_push(ctx, CYAML_STATE_IN_MAP_KEY, event, schema, data); break; case CYAML_BITFIELD: err = cyaml__read_bitfield_value(ctx, schema, data); break; case CYAML_SEQUENCE: /* Fall through. */ case CYAML_SEQUENCE_FIXED: err = cyaml__stack_push(ctx, CYAML_STATE_IN_SEQUENCE, event, schema, data); break; case CYAML_IGNORE: err = cyaml__consume_ignored_value(ctx, cyaml_event); break; default: return CYAML_ERR_INTERNAL_ERROR; } return err; } /** * YAML loading handler for start of stream in the \ref CYAML_STATE_START state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stream_start( cyaml_ctx_t *ctx, const yaml_event_t *event) { CYAML_UNUSED(event); return cyaml__stack_push(ctx, CYAML_STATE_IN_STREAM, event, ctx->state->schema, ctx->state->data); } /** * YAML loading handler for documents in the \ref CYAML_STATE_IN_STREAM state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__doc_start( cyaml_ctx_t *ctx, const yaml_event_t *event) { CYAML_UNUSED(event); if (ctx->state->stream.doc_count == 1) { cyaml__log(ctx->config, CYAML_LOG_WARNING, "Ignoring documents after first in stream\n"); cyaml__stack_pop(ctx); return CYAML_OK; } ctx->state->stream.doc_count++; return cyaml__stack_push(ctx, CYAML_STATE_IN_DOC, event, ctx->state->schema, ctx->state->data); } /** * YAML loading handler for finalising the \ref CYAML_STATE_IN_STREAM state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stream_end( cyaml_ctx_t *ctx, const yaml_event_t *event) { CYAML_UNUSED(event); cyaml__stack_pop(ctx); return CYAML_OK; } /** * YAML loading handler for the root value in the \ref CYAML_STATE_IN_DOC state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__doc_root_value( cyaml_ctx_t *ctx, const yaml_event_t *event) { return cyaml__read_value(ctx, ctx->state->schema, ctx->state->data, event); } /** * YAML loading handler for finalising the \ref CYAML_STATE_IN_DOC state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__doc_end( cyaml_ctx_t *ctx, const yaml_event_t *event) { CYAML_UNUSED(event); cyaml__stack_pop(ctx); return CYAML_OK; } /** * Log an ignored mapping key. * * \param[in] ctx The CYAML loading context. * \param[in] key The key that has been ignored. */ static inline void cyaml__log_ignored_key( const cyaml_ctx_t *ctx, const char *key) { cyaml_log_t lvl = ctx->config->flags & CYAML_CFG_IGNORED_KEY_WARNING ? CYAML_LOG_WARNING : CYAML_LOG_DEBUG; cyaml__log(ctx->config, lvl, "Load: Ignoring value for key: %s\n", key); } /** * Check the field against the schema for the current mapping key. * * \param[in] ctx The CYAML loading context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__map_key_check_field( const cyaml_ctx_t *ctx) { const cyaml_schema_value_t *schema = ctx->state->schema; const cyaml_schema_field_t *field = schema->mapping.fields + ctx->state->mapping.fields_idx; if (field->value.type != CYAML_IGNORE) { if (cyaml__mapping_bitfieid_check(ctx) == true) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Mapping field already seen: %s\n", field->key); return CYAML_ERR_UNEXPECTED_EVENT; } } else { cyaml__log_ignored_key(ctx, field->key); } cyaml__mapping_bitfieid_set(ctx); return CYAML_OK; } /** * YAML loading handler for new mapping fields in the * \ref CYAML_STATE_IN_MAP_KEY state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__map_key( cyaml_ctx_t *ctx, const yaml_event_t *event) { const char *key; cyaml_err_t err = CYAML_OK; key = (const char *)event->data.scalar.value; ctx->state->mapping.fields_idx = cyaml__get_mapping_field_idx( ctx->config, ctx->state->schema, key); cyaml__log(ctx->config, CYAML_LOG_INFO, "Load: [%s]\n", key); if (ctx->state->mapping.fields_idx == CYAML_FIELDS_IDX_NONE) { const yaml_event_t *const ignore_event = cyaml__current_event(ctx); if (!(ctx->config->flags & CYAML_CFG_IGNORE_UNKNOWN_KEYS)) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Unexpected key: %s\n", key); return CYAML_ERR_INVALID_KEY; } cyaml__log_ignored_key(ctx, key); err = cyaml_get_next_event(ctx); if (err != CYAML_OK) { return err; } return cyaml__consume_ignored_value(ctx, cyaml__get_event_type(ignore_event)); } err = cyaml__map_key_check_field(ctx); if (err != CYAML_OK) { return err; } /* Toggle mapping sub-state to value */ ctx->state->state = CYAML_STATE_IN_MAP_VALUE; ctx->state->line = event->start_mark.line; ctx->state->column = event->start_mark.column; return err; } /** * YAML loading handler for finalising the \ref CYAML_STATE_IN_MAP_KEY state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__map_end( cyaml_ctx_t *ctx, const yaml_event_t *event) { cyaml_err_t err; CYAML_UNUSED(event); err = cyaml__mapping_bitfieid_validate(ctx); if (err != CYAML_OK) { return err; } cyaml__stack_pop(ctx); return CYAML_OK; } /** * YAML loading handler for the \ref CYAML_STATE_IN_MAP_VALUE state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__map_value( cyaml_ctx_t *ctx, const yaml_event_t *event) { cyaml_state_t *state = ctx->state; const cyaml_schema_field_t *field = cyaml_mapping_schema_field(ctx); cyaml_data_t *data = state->data + field->data_offset; /* Toggle mapping sub-state back to key. Do this before * reading value, because reading value might increase the * CYAML context stack allocation, causing the state entry * to move. */ state->state = CYAML_STATE_IN_MAP_KEY; ctx->state->line = event->start_mark.line; ctx->state->column = event->start_mark.column; return cyaml__read_value(ctx, &field->value, data, event); } /** * YAML loading handler for new sequence entries in the * \ref CYAML_STATE_IN_SEQUENCE state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__seq_entry( cyaml_ctx_t *ctx, const yaml_event_t *event) { cyaml_err_t err; cyaml_state_t *state = ctx->state; uint8_t *value_data = state->data; const cyaml_schema_value_t *schema = state->schema; ctx->state->line = event->start_mark.line; ctx->state->column = event->start_mark.column; if (state->sequence.count + 1 > state->schema->sequence.max) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Excessive entries (%"PRIu32" max) " "in sequence.\n", state->schema->sequence.max); return CYAML_ERR_SEQUENCE_ENTRIES_MAX; } err = cyaml__data_handle_pointer(ctx, schema, event, &value_data); if (err != CYAML_OK) { return err; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Sequence entry: %u (%"PRIu32" bytes)\n", state->sequence.count, schema->data_size); value_data += schema->data_size * state->sequence.count; state->sequence.count++; if (schema->type != CYAML_SEQUENCE_FIXED) { err = cyaml_data_write(state->sequence.count, state->sequence.count_size, state->sequence.count_data); if (err != CYAML_OK) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Failed writing sequence count\n"); if (schema->flags & CYAML_FLAG_POINTER) { cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Freeing %p\n", state->sequence.data); cyaml__free(ctx->config, state->sequence.data); } return err; } } /* Read the actual value */ err = cyaml__read_value(ctx, schema->sequence.entry, value_data, event); if (err != CYAML_OK) { return err; } return CYAML_OK; } /** * YAML loading handler for finalising the \ref CYAML_STATE_IN_SEQUENCE state. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__seq_end( cyaml_ctx_t *ctx, const yaml_event_t *event) { cyaml_state_t *state = ctx->state; CYAML_UNUSED(event); if (state->sequence.count < state->schema->sequence.min) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Load: Insufficient entries " "(%"PRIu32" of %"PRIu32" min) in sequence.\n", state->sequence.count, state->schema->sequence.min); return CYAML_ERR_SEQUENCE_ENTRIES_MIN; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Sequence count: %u\n", state->sequence.count); cyaml__stack_pop(ctx); return CYAML_OK; } /** * Check that common load parameters from client are valid. * * \param[in] config The client's CYAML library config. * \param[in] schema The schema describing the content of data. * \param[in] data_tgt Points to client's address to write data to. * \param[in] seq_count_tgt Points to client's address to write sequence count. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__validate_load_params( const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t * const *data_tgt, const unsigned *seq_count_tgt) { if (config == NULL) { return CYAML_ERR_BAD_PARAM_NULL_CONFIG; } if (config->mem_fn == NULL) { return CYAML_ERR_BAD_CONFIG_NULL_MEMFN; } if (schema == NULL) { return CYAML_ERR_BAD_PARAM_NULL_SCHEMA; } if ((schema->type == CYAML_SEQUENCE) != (seq_count_tgt != NULL)) { return CYAML_ERR_BAD_PARAM_SEQ_COUNT; } if (!(schema->flags & CYAML_FLAG_POINTER)) { return CYAML_ERR_TOP_LEVEL_NON_PTR; } if (data_tgt == NULL) { return CYAML_ERR_BAD_PARAM_NULL_DATA; } return CYAML_OK; } /** * YAML loading helper dispatch function. * * Dispatches events to the appropriate event handler function for the * current combination of load state machine state (from the load context) * and event type. * * \param[in] ctx The CYAML loading context. * \param[in] event The YAML event to handle. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__load_event( cyaml_ctx_t *ctx, const yaml_event_t *event) { cyaml_state_t *state = ctx->state; typedef cyaml_err_t (* const cyaml_read_fn)( cyaml_ctx_t *ctx, const yaml_event_t *event); static const cyaml_read_fn fns[CYAML_STATE__COUNT][CYAML_EVT__COUNT] = { [CYAML_STATE_START] = { [CYAML_EVT_STRM_START] = cyaml__stream_start, }, [CYAML_STATE_IN_STREAM] = { [CYAML_EVT_DOC_START] = cyaml__doc_start, [CYAML_EVT_STRM_END] = cyaml__stream_end, }, [CYAML_STATE_IN_DOC] = { [CYAML_EVT_SCALAR] = cyaml__doc_root_value, [CYAML_EVT_SEQ_START] = cyaml__doc_root_value, [CYAML_EVT_MAP_START] = cyaml__doc_root_value, [CYAML_EVT_DOC_END] = cyaml__doc_end, }, [CYAML_STATE_IN_MAP_KEY] = { [CYAML_EVT_SCALAR] = cyaml__map_key, [CYAML_EVT_MAP_END] = cyaml__map_end, }, [CYAML_STATE_IN_MAP_VALUE] = { [CYAML_EVT_SCALAR] = cyaml__map_value, [CYAML_EVT_SEQ_START] = cyaml__map_value, [CYAML_EVT_MAP_START] = cyaml__map_value, }, [CYAML_STATE_IN_SEQUENCE] = { [CYAML_EVT_SCALAR] = cyaml__seq_entry, [CYAML_EVT_SEQ_START] = cyaml__seq_entry, [CYAML_EVT_MAP_START] = cyaml__seq_entry, [CYAML_EVT_SEQ_END] = cyaml__seq_end, }, }; const cyaml_read_fn fn = fns[state->state][event->type]; cyaml_err_t err = CYAML_ERR_INTERNAL_ERROR; if (fn != NULL) { cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Load: Handle state %s\n", cyaml__state_to_str(state->state)); err = fn(ctx, event); } return err; } /** * The main YAML loading function. * * The public interfaces are wrappers around this. * * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be loaded. * \param[out] data_out Returns the caller-owned loaded data on success. * Untouched on failure. * \param[out] seq_count_out On success, returns the sequence entry count. * Untouched on failure. * Must be non-NULL if top-level schema type is * \ref CYAML_SEQUENCE, otherwise, must be NULL. * \param[in] parser An initialised `libyaml` parser object * with its input set. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__load( const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out, yaml_parser_t *parser) { cyaml_data_t *data = NULL; cyaml_ctx_t ctx = { .config = config, .parser = parser, }; cyaml_err_t err = CYAML_OK; err = cyaml__validate_load_params(config, schema, data_out, seq_count_out); if (err != CYAML_OK) { return err; } err = cyaml__stack_push(&ctx, CYAML_STATE_START, NULL, schema, &data); if (err != CYAML_OK) { goto out; } do { const yaml_event_t *const event = cyaml__current_event(&ctx); err = cyaml_get_next_event(&ctx); if (err != CYAML_OK) { goto out; } err = cyaml__load_event(&ctx, event); if (err != CYAML_OK) { goto out; } } while (ctx.state->state > CYAML_STATE_START); cyaml__stack_pop(&ctx); assert(ctx.stack_idx == 0); *data_out = data; if (seq_count_out != NULL) { *seq_count_out = ctx.seq_count; } out: if (err != CYAML_OK) { cyaml_free(config, schema, data, ctx.seq_count); cyaml__backtrace(&ctx); } while (ctx.stack_idx > 0) { cyaml__stack_pop(&ctx); } cyaml__free(config, ctx.stack); cyaml__delete_yaml_event(&ctx); cyaml__free_recording(&ctx); return err; } /* Exported function, documented in include/cyaml/cyaml.h */ cyaml_err_t cyaml_load_file( const char *path, const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out) { FILE *file; cyaml_err_t err; yaml_parser_t parser; /* Initialize parser */ if (!yaml_parser_initialize(&parser)) { return CYAML_ERR_LIBYAML_PARSER_INIT; } /* Open input file. */ file = fopen(path, "r"); if (file == NULL) { yaml_parser_delete(&parser); return CYAML_ERR_FILE_OPEN; } /* Set input file */ yaml_parser_set_input_file(&parser, file); /* Parse the input */ err = cyaml__load(config, schema, data_out, seq_count_out, &parser); if (err != CYAML_OK) { yaml_parser_delete(&parser); fclose(file); return err; } /* Cleanup */ yaml_parser_delete(&parser); fclose(file); return CYAML_OK; } /* Exported function, documented in include/cyaml/cyaml.h */ cyaml_err_t cyaml_load_data( const uint8_t *input, size_t input_len, const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out) { cyaml_err_t err; yaml_parser_t parser; /* Initialize parser */ if (!yaml_parser_initialize(&parser)) { return CYAML_ERR_LIBYAML_PARSER_INIT; } /* Set input data */ yaml_parser_set_input_string(&parser, input, input_len); /* Parse the input */ err = cyaml__load(config, schema, data_out, seq_count_out, &parser); if (err != CYAML_OK) { yaml_parser_delete(&parser); return err; } /* Cleanup */ yaml_parser_delete(&parser); return CYAML_OK; } libcyaml-1.4.1/src/mem.c000066400000000000000000000010701443462360400150260ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018 Michael Drake */ /** * \file * \brief CYAML memory allocation handling. */ #include #include #include #include "mem.h" /** Macro to squash unused variable compiler warnings. */ #define CYAML_UNUSED(_x) ((void)(_x)) /* Exported function, documented in include/cyaml/cyaml.h */ void * cyaml_mem( void *ctx, void *ptr, size_t size) { CYAML_UNUSED(ctx); if (size == 0) { free(ptr); return NULL; } return realloc(ptr, size); } libcyaml-1.4.1/src/mem.h000066400000000000000000000056001443462360400150360ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2020 Michael Drake */ /** * \file * \brief CYAML memory allocation handling. */ #ifndef CYAML_MEM_H #define CYAML_MEM_H #include "cyaml/cyaml.h" /** * Helper for freeing using the client's choice of allocator routine. * * \param[in] config The CYAML client config. * \param[in] ptr Pointer to allocation to free. */ static inline void cyaml__free( const cyaml_config_t *config, void *ptr) { config->mem_fn(config->mem_ctx, ptr, 0); } /** * Helper for new allocations using the client's choice of allocator routine. * * \note On failure, any existing allocation is still owned by the caller, and * they are responsible for freeing it. * * \param[in] config The CYAML client config. * \param[in] ptr The existing allocation or NULL. * \param[in] current_size Size of the current allocation. (Only needed if * `clean != false`). * \param[in] new_size The number of bytes to resize allocation to. * \param[in] clean Only applies if `new_size > current_size`. * If `false`, the new memory is uninitialised, * if `true`, the new memory is initialised to zero. * \return Pointer to allocation on success, or `NULL` on failure. */ static inline void * cyaml__realloc( const cyaml_config_t *config, void *ptr, size_t current_size, size_t new_size, bool clean) { uint8_t *temp = config->mem_fn(config->mem_ctx, ptr, new_size); if (temp == NULL) { return NULL; } if (clean && (new_size > current_size)) { memset(temp + current_size, 0, new_size - current_size); } return temp; } /** * Helper for new allocations using the client's choice of allocator routine. * * \param[in] config The CYAML client config. * \param[in] size The number of bytes to allocate. * \param[in] clean If `false`, the memory is uninitialised, if `true`, * the memory is initialised to zero. * \return Pointer to allocation on success, or `NULL` on failure. */ static inline void * cyaml__alloc( const cyaml_config_t *config, size_t size, bool clean) { return cyaml__realloc(config, NULL, 0, size, clean); } /** * Helper for string duplication using the client's choice of allocator routine. * * \param[in] config The CYAML client config. * \param[in] str The string to duplicate. * \param[in] len_out If non-NULL, updated to length of string on success. * \return Pointer to new string on success, or `NULL` on failure. */ static inline char * cyaml__strdup( const cyaml_config_t *config, const char *str, size_t *len_out) { size_t len = strlen(str) + 1; char *dup = cyaml__alloc(config, len, false); if (dup == NULL) { return NULL; } memcpy(dup, str, len); if (len_out != NULL) { *len_out = len - 1; } return dup; } #endif libcyaml-1.4.1/src/save.c000066400000000000000000001166501443462360400152210ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2020 Michael Drake */ /** * \file * \brief Save client's data structure to YAML, using schema. * * This uses `libyaml` to emit YAML documents, it uses the client-provided * schema to access the client data, and validates it before emitting the YAML. */ #include #include #include #include #include #include "mem.h" #include "data.h" #include "util.h" /** * A CYAML save state machine stack entry. */ typedef struct cyaml_state { /** Current save state machine state. */ enum cyaml_state_e state; /** Schema for the expected value in this state. */ const cyaml_schema_value_t *schema; /** Anonymous union for schema type specific state. */ union { /** * Additional state for \ref CYAML_STATE_IN_MAP_KEY and * \ref CYAML_STATE_IN_MAP_VALUE states. */ struct { const cyaml_schema_field_t *field; } mapping; /** Additional state for \ref CYAML_STATE_IN_SEQUENCE state. */ struct { uint64_t entry; uint64_t count; } sequence; }; const uint8_t *data; /**< Start of client value data for this state. */ bool done; /**< Whether the state has been handled. */ } cyaml_state_t; /** * Internal YAML saving context. */ typedef struct cyaml_ctx { const cyaml_config_t *config; /**< Settings provided by client. */ cyaml_state_t *state; /**< Current entry in state stack, or NULL. */ cyaml_state_t *stack; /**< State stack */ uint32_t stack_idx; /**< Next (empty) state stack slot */ uint32_t stack_max; /**< Current stack allocation limit. */ unsigned seq_count; /**< Top-level sequence count. */ yaml_emitter_t *emitter; /**< Internal libyaml parser object. */ } cyaml_ctx_t; /** * Ensure that the CYAML save context has space for a new stack entry. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_ensure( cyaml_ctx_t *ctx) { cyaml_state_t *temp; uint32_t max = ctx->stack_max + 16; if (ctx->stack_idx < ctx->stack_max) { return CYAML_OK; } temp = cyaml__realloc(ctx->config, ctx->stack, 0, sizeof(*ctx->stack) * max, false); if (temp == NULL) { return CYAML_ERR_OOM; } ctx->stack = temp; ctx->stack_max = max; ctx->state = ctx->stack + ctx->stack_idx - 1; return CYAML_OK; } /** * Helper to simplify emitting libyaml events. * * This is a slightly peculiar function, but it is intended to reduce the * boilerplate required to emit events. * * Intended usage is something like: * * ``` * cyaml_err_t err; * yaml_event_t event; * int ret = yaml_mapping_end_event_initialize(&event); * err = cyaml__emit_event_helper(ctx, ret, &event); * ``` * * \param[in] ctx The CYAML saving context. * \param[in] valid Whether the event pointer is valid. Typically this * will be the value returned by the libyaml call that * initialised the event. As such, if `valid` is non-zero, * the helper will try to emit the event, otherwise, it * will return \ref CYAML_ERR_LIBYAML_EVENT_INIT. * \param[in] event The event to try to emit, if valid. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__emit_event_helper( const cyaml_ctx_t *ctx, int valid, yaml_event_t *event) { if (valid == 0) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Save: LibYAML: Failed to initialise event\n"); return CYAML_ERR_LIBYAML_EVENT_INIT; } /* Emit event and update save state stack. */ valid = yaml_emitter_emit(ctx->emitter, event); if (valid == 0) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Save: LibYAML: Failed to emit event: %s\n", ctx->emitter->problem); return CYAML_ERR_LIBYAML_EMITTER; } return CYAML_OK; } /** The style to use when emitting mappings and sequences. */ enum cyaml_emit_style { CYAML_EMIT_STYLE_DEFAULT, CYAML_EMIT_STYLE_BLOCK, CYAML_EMIT_STYLE_FLOW, }; /** * Get the style to use for mappings/sequences from value flags and config. * * As described in the API, schema flags take priority over config flags, and * block has precedence over flow, if both flags are set at the same level. * * \param[in] ctx The CYAML saving context. * \param[in] schema The CYAML schema for the value expected in state. * \return The generic style to emit the value with. */ static inline enum cyaml_emit_style cyaml__get_emit_style( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema) { if (schema->flags & CYAML_FLAG_BLOCK) { return CYAML_EMIT_STYLE_BLOCK; } else if (schema->flags & CYAML_FLAG_FLOW) { return CYAML_EMIT_STYLE_FLOW; } else if (ctx->config->flags & CYAML_CFG_STYLE_BLOCK) { return CYAML_EMIT_STYLE_BLOCK; } else if (ctx->config->flags & CYAML_CFG_STYLE_FLOW) { return CYAML_EMIT_STYLE_FLOW; } return CYAML_EMIT_STYLE_DEFAULT; } /** * Get the style to use for sequences from value flags and config. * * \param[in] ctx The CYAML saving context. * \param[in] schema The CYAML schema for the value expected in state. * \return The libyaml sequence style to emit the value with. */ static inline yaml_sequence_style_t cyaml__get_emit_style_seq( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema) { switch (cyaml__get_emit_style(ctx, schema)) { case CYAML_EMIT_STYLE_BLOCK: return YAML_BLOCK_SEQUENCE_STYLE; case CYAML_EMIT_STYLE_FLOW: return YAML_FLOW_SEQUENCE_STYLE; default: break; } return YAML_ANY_SEQUENCE_STYLE; } /** * Get the style to use for mappings from value flags and config. * * \param[in] ctx The CYAML saving context. * \param[in] schema The CYAML schema for the value expected in state. * \return The libyaml mapping style to emit the value with. */ static inline yaml_mapping_style_t cyaml__get_emit_style_map( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema) { switch (cyaml__get_emit_style(ctx, schema)) { case CYAML_EMIT_STYLE_BLOCK: return YAML_BLOCK_MAPPING_STYLE; case CYAML_EMIT_STYLE_FLOW: return YAML_FLOW_MAPPING_STYLE; default: break; } return YAML_ANY_MAPPING_STYLE; } /** * Get the style to use for scalar values from value flags. * * \param[in] schema The CYAML schema for the scaler value to be emitted. * \return The libyaml scalar style to emit the value with. */ static inline yaml_scalar_style_t cyaml__get_emit_style_scalar( const cyaml_schema_value_t *schema) { /* Consult flags in order of decreasing priority. */ if (schema->flags & CYAML_FLAG_SCALAR_QUOTE_DOUBLE) { return YAML_DOUBLE_QUOTED_SCALAR_STYLE; } else if (schema->flags & CYAML_FLAG_SCALAR_QUOTE_SINGLE) { return YAML_SINGLE_QUOTED_SCALAR_STYLE; } else if (schema->flags & CYAML_FLAG_SCALAR_LITERAL) { return YAML_LITERAL_SCALAR_STYLE; } else if (schema->flags & CYAML_FLAG_SCALAR_FOLDED) { return YAML_FOLDED_SCALAR_STYLE; } else if (schema->flags & CYAML_FLAG_SCALAR_PLAIN) { return YAML_PLAIN_SCALAR_STYLE; } return YAML_ANY_SCALAR_STYLE; } /** * Helper to discern whether to emit document delimiting marks. * * These are "---" for document start, and "..." for document end. * * \param[in] ctx The CYAML saving context. * \return true if delimiters should be emitted, false otherwise. */ static inline bool cyaml__emit_doc_delim( const cyaml_ctx_t *ctx) { return ctx->config->flags & CYAML_CFG_DOCUMENT_DELIM; } /** * Emit a YAML start event for the state being pushed to the stack. * * \param[in] ctx The CYAML saving context. * \param[in] state The CYAML save state we're pushing a stack entry for. * \param[in] schema The CYAML schema for the value expected in state. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_push_write_event( const cyaml_ctx_t *ctx, enum cyaml_state_e state, const cyaml_schema_value_t *schema) { yaml_event_t event; int ret; /* Create any appropriate event for the new state. */ switch (state) { case CYAML_STATE_START: ret = yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING); break; case CYAML_STATE_IN_STREAM: ret = yaml_document_start_event_initialize(&event, NULL, NULL, NULL, !cyaml__emit_doc_delim(ctx)); break; case CYAML_STATE_IN_DOC: return CYAML_OK; case CYAML_STATE_IN_MAP_KEY: ret = yaml_mapping_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_MAP_TAG, 1, cyaml__get_emit_style_map(ctx, schema)); break; case CYAML_STATE_IN_SEQUENCE: ret = yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, cyaml__get_emit_style_seq(ctx, schema)); break; default: return CYAML_ERR_INTERNAL_ERROR; } return cyaml__emit_event_helper(ctx, ret, &event); } /** * Push a new entry onto the CYAML save context's stack. * * \param[in] ctx The CYAML saving context. * \param[in] state The CYAML save state we're pushing a stack entry for. * \param[in] schema The CYAML schema for the value expected in state. * \param[in] data Pointer to where value's data should be read from. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_push( cyaml_ctx_t *ctx, enum cyaml_state_e state, const cyaml_schema_value_t *schema, const cyaml_data_t *data) { cyaml_err_t err; cyaml_state_t s = { .data = data, .state = state, .schema = schema, }; err = cyaml__stack_push_write_event(ctx, state, schema); if (err != CYAML_OK) { return err; } err = cyaml__stack_ensure(ctx); if (err != CYAML_OK) { return err; } switch (state) { case CYAML_STATE_IN_MAP_KEY: assert(schema->type == CYAML_MAPPING); s.mapping.field = schema->mapping.fields; break; default: break; } cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Save: PUSH[%u]: %s\n", ctx->stack_idx, cyaml__state_to_str(state)); ctx->stack[ctx->stack_idx] = s; ctx->state = ctx->stack + ctx->stack_idx; ctx->stack_idx++; return CYAML_OK; } /** * Emit a YAML end event for the state being popped from the stack. * * This frees any resources owned by the stack entry. * * \param[in] ctx The CYAML saving context. * \param[in] state The CYAML save state we're popping from the stack. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_pop_write_event( const cyaml_ctx_t *ctx, enum cyaml_state_e state) { yaml_event_t event; int ret; /* Create any appropriate event for the new state. */ switch (state) { case CYAML_STATE_START: return CYAML_OK; case CYAML_STATE_IN_STREAM: ret = yaml_stream_end_event_initialize(&event); break; case CYAML_STATE_IN_DOC: ret = yaml_document_end_event_initialize(&event, !cyaml__emit_doc_delim(ctx)); break; case CYAML_STATE_IN_MAP_KEY: ret = yaml_mapping_end_event_initialize(&event); break; case CYAML_STATE_IN_SEQUENCE: ret = yaml_sequence_end_event_initialize(&event); break; default: return CYAML_ERR_INTERNAL_ERROR; } return cyaml__emit_event_helper(ctx, ret, &event); } /** * Pop the current entry on the CYAML save context's stack. * * This frees any resources owned by the stack entry. * * \param[in] ctx The CYAML saving context. * \param[in] emit Whether end events should be emitted. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__stack_pop( cyaml_ctx_t *ctx, bool emit) { uint32_t idx = ctx->stack_idx; assert(idx != 0); if (emit) { cyaml_err_t err; err = cyaml__stack_pop_write_event(ctx, ctx->state->state); if (err != CYAML_OK) { return err; } } idx--; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Save: POP[%u]: %s\n", idx, cyaml__state_to_str(ctx->state->state)); ctx->state = (idx == 0) ? NULL : &ctx->stack[idx - 1]; ctx->stack_idx = idx; return CYAML_OK; } /** * Find address of actual value. * * If the value has the pointer flag, the pointer is read, otherwise the * address is returned unchanged. * * \param[in] config The CYAML client configuration object. * \param[in] schema CYAML schema for the expected value. * \param[in] data_in The address to read from. * \return New address or for \ref CYAML_FLAG_POINTER, or data_in. */ static const uint8_t * cyaml__data_handle_pointer( const cyaml_config_t *config, const cyaml_schema_value_t *schema, const uint8_t *data_in) { if (schema->flags & CYAML_FLAG_POINTER) { const uint8_t *data = cyaml_data_read_pointer(data_in); cyaml__log(config, CYAML_LOG_DEBUG, "Save: Handle pointer: %p --> %p\n", data_in, data); return data; } return data_in; } /** * Dump a backtrace to the log. * * \param[in] ctx The CYAML saving context. */ static void cyaml__backtrace( const cyaml_ctx_t *ctx) { if (ctx->stack_idx > 1) { cyaml__log(ctx->config, CYAML_LOG_ERROR, "Save: Backtrace:\n"); } else { return; } for (uint32_t idx = ctx->stack_idx - 1; idx != 0; idx--) { cyaml_state_t *state = ctx->stack + idx; switch (state->state) { case CYAML_STATE_IN_MAP_KEY: /* Fall through. */ case CYAML_STATE_IN_MAP_VALUE: assert(state->mapping.field != NULL); cyaml__log(ctx->config, CYAML_LOG_ERROR, " in mapping field: %s\n", state->mapping.field->key); break; case CYAML_STATE_IN_SEQUENCE: cyaml__log(ctx->config, CYAML_LOG_ERROR, " in sequence entry: %"PRIu32"\n", state->sequence.count); break; default: /** \todo \ref CYAML_STATE_IN_DOC handling for multi * document streams. */ break; } } } /** * Helper to emit YAML scalar events, using libyaml. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to emit. * \param[in] value The value to emit as a null-terminated C string. * \param[in] tag YAML tag to use for output. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__emit_scalar( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const char *value, const char *tag) { int ret; yaml_event_t event; if (schema->type == CYAML_MAPPING) { cyaml__log(ctx->config, CYAML_LOG_INFO, "Save: [%s]\n", value); } else { cyaml__log(ctx->config, CYAML_LOG_INFO, "Save: <%s>\n", value); } ret = yaml_scalar_event_initialize(&event, NULL, (yaml_char_t *)tag, (yaml_char_t *)value, (int)strlen(value), 1, 1, cyaml__get_emit_style_scalar(schema)); return cyaml__emit_event_helper(ctx, ret, &event); } /** * Convert signed integer to string. * * \param[in] value The integer to convert. * \return String conversion of the value. */ static const char * cyaml__get_int( int64_t value) { static char string[32]; sprintf(string, "%"PRIi64, value); return string; } /** * Convert unsigned integer to string. * * \param[in] value The integer to convert. * \param[in] hex Whether to render the number as hexadecimal. * \return String conversion of the value. */ static const char * cyaml__get_uint( uint64_t value, bool hex) { static char string[32]; if (hex) { sprintf(string, "0x%"PRIx64, value); } else { sprintf(string, "%"PRIu64, value); } return string; } /** * Get string representation for single precision floating point values. * * \param[in] value The value to convert. * \return String conversion of the value. */ static const char * cyaml__get_float( double value) { static char string[64]; sprintf(string, "%g", value); return string; } /** * Get string representation for double precision floating point values. * * \param[in] value The value to convert. * \return String conversion of the value. */ static const char * cyaml__get_double( double value) { static char string[64]; sprintf(string, "%.16e", value); return string; } /** * Pad a signed value that's smaller than 64-bit to an int64_t. * * This sets all the bits in the padded region. * * \param[in] raw Contains a signed value of size bytes. * \param[in] size Number of bytes used in raw. * \return Value padded to 64-bit signed. */ static int64_t cyaml_sign_pad(uint64_t raw, size_t size) { uint64_t sign_bit = (size == 0) ? UINT64_MAX : ((uint64_t)1) << (size * CHAR_BIT - 1); unsigned padding = ((unsigned)(sizeof(raw) - size)) * CHAR_BIT; if ((sign_bit & raw) && (padding != 0)) { raw |= (((uint64_t)1 << padding) - 1) << (size * CHAR_BIT); } return (int64_t)raw; } /** * Write a value of type \ref CYAML_INT. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_int( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { cyaml_err_t err; int64_t number; number = cyaml_sign_pad( cyaml_data_read(schema->data_size, data, &err), schema->data_size); if (err == CYAML_OK) { const char *string = cyaml__get_int(number); err = cyaml__emit_scalar(ctx, schema, string, YAML_INT_TAG); } return err; } /** * Write a value of type \ref CYAML_UINT. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_uint( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { uint64_t number; cyaml_err_t err; number = cyaml_data_read(schema->data_size, data, &err); if (err == CYAML_OK) { const char *string = cyaml__get_uint(number, false); err = cyaml__emit_scalar(ctx, schema, string, YAML_INT_TAG); } return err; } /** * Write a value of type \ref CYAML_BOOL. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_bool( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { uint64_t number; cyaml_err_t err; number = cyaml_data_read(schema->data_size, data, &err); if (err == CYAML_OK) { err = cyaml__emit_scalar(ctx, schema, number ? "true" : "false", YAML_BOOL_TAG); } return err; } /** * Write a value of type \ref CYAML_ENUM. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_enum( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { int64_t number; cyaml_err_t err; number = (int64_t)cyaml_data_read(schema->data_size, data, &err); if (err == CYAML_OK) { const cyaml_strval_t *strings = schema->enumeration.strings; const char *string = NULL; for (uint32_t i = 0; i < schema->enumeration.count; i++) { if (number == strings[i].val) { string = strings[i].str; break; } } if (string == NULL) { if (schema->flags & CYAML_FLAG_STRICT) { return CYAML_ERR_INVALID_VALUE; } else { return cyaml__write_int(ctx, schema, data); } } err = cyaml__emit_scalar(ctx, schema, string, YAML_STR_TAG); } return err; } /** * Write a value of type \ref CYAML_FLOAT. * * The `data_size` of the schema entry must be the size of a known * floating point C type. * * \note The `long double` type was causing problems, so it isn't currently * supported. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_float( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { const char *string = NULL; if (schema->data_size == sizeof(float)) { float number; memcpy(&number, data, schema->data_size); string = cyaml__get_float(number); } else if (schema->data_size == sizeof(double)) { double number; memcpy(&number, data, schema->data_size); string = cyaml__get_double(number); } else { return CYAML_ERR_INVALID_DATA_SIZE; } return cyaml__emit_scalar(ctx, schema, string, YAML_FLOAT_TAG); } /** * Write a value of type \ref CYAML_STRING. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_string( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data) { return cyaml__emit_scalar(ctx, schema, (const char *)data, YAML_STR_TAG); } /** * Write a scalar value. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_scalar_value( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const cyaml_data_t *data) { typedef cyaml_err_t (*cyaml_read_scalar_fn)( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data_target); static const cyaml_read_scalar_fn fn[CYAML__TYPE_COUNT] = { [CYAML_INT] = cyaml__write_int, [CYAML_UINT] = cyaml__write_uint, [CYAML_BOOL] = cyaml__write_bool, [CYAML_ENUM] = cyaml__write_enum, [CYAML_FLOAT] = cyaml__write_float, [CYAML_STRING] = cyaml__write_string, }; assert(fn[schema->type] != NULL); return fn[schema->type](ctx, schema, data); } /** * Emit a sequence of flag values. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] number The value of the flag data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__emit_flags_sequence( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint64_t number) { yaml_event_t event; cyaml_err_t err; int ret; ret = yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_ANY_SEQUENCE_STYLE); err = cyaml__emit_event_helper(ctx, ret, &event); if (err != CYAML_OK) { return err; } for (uint32_t i = 0; i < schema->enumeration.count; i++) { const cyaml_strval_t *strval = &schema->enumeration.strings[i]; uint64_t flag = (uint64_t)strval->val; if (number & flag) { err = cyaml__emit_scalar(ctx, schema, strval->str, YAML_STR_TAG); if (err != CYAML_OK) { return err; } number &= ~flag; } } if (number != 0) { if (schema->flags & CYAML_FLAG_STRICT) { return CYAML_ERR_INVALID_VALUE; } else { const char *string = cyaml__get_uint(number, false); err = cyaml__emit_scalar(ctx, schema, string, YAML_STR_TAG); if (err != CYAML_OK) { return err; } } } ret = yaml_sequence_end_event_initialize(&event); return cyaml__emit_event_helper(ctx, ret, &event); } /** * Write a value of type \ref CYAML_FLAGS. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_flags_value( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const cyaml_data_t *data) { uint64_t number; cyaml_err_t err; number = cyaml_data_read(schema->data_size, data, &err); if (err == CYAML_OK) { err = cyaml__emit_flags_sequence(ctx, schema, number); } return err; } /** * Emit a mapping of bitfield values. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] number The value of the bitfield data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__emit_bitfield_mapping( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, uint64_t number) { const cyaml_bitdef_t *bitdef = schema->bitfield.bitdefs; yaml_event_t event; cyaml_err_t err; int ret; ret = yaml_mapping_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_MAP_TAG, 1, cyaml__get_emit_style_map(ctx, schema)); err = cyaml__emit_event_helper(ctx, ret, &event); if (err != CYAML_OK) { return err; } for (uint32_t i = 0; i < schema->bitfield.count; i++) { const char *value_str; uint64_t value; uint64_t mask; if (bitdef[i].bits + bitdef[i].offset > schema->data_size * 8) { return CYAML_ERR_BAD_BITVAL_IN_SCHEMA; } mask = ((~(uint64_t)0) >> ((8 * sizeof(uint64_t)) - bitdef[i].bits) ) << bitdef[i].offset; if ((number & mask) == 0) { continue; } value = (number & mask) >> bitdef[i].offset; /* Emit bitfield value's name */ err = cyaml__emit_scalar(ctx, schema, bitdef[i].name, YAML_STR_TAG); if (err != CYAML_OK) { return err; } /* Emit bitfield value's value */ value_str = cyaml__get_uint(value, true); err = cyaml__emit_scalar(ctx, schema, value_str, YAML_INT_TAG); if (err != CYAML_OK) { return err; } } ret = yaml_mapping_end_event_initialize(&event); return cyaml__emit_event_helper(ctx, ret, &event); } /** * Write a value of type \ref CYAML_BITFIELD. * * \param[in] ctx The CYAML saving context. * \param[in] schema The schema for the value to be written. * \param[in] data The place to read the value from in the client data. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_bitfield_value( const cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const cyaml_data_t *data) { uint64_t number; cyaml_err_t err; number = cyaml_data_read(schema->data_size, data, &err); if (err == CYAML_OK) { err = cyaml__emit_bitfield_mapping(ctx, schema, number); } return err; } /** * Emit the YAML events required for a CYAML value. * * \param[in] ctx The CYAML saving context. * \param[in] schema CYAML schema for the expected value. * \param[in] data The place to read the value from in the client data. * \param[in] seq_count Entry count for sequence values. Unused for * non-sequence values. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_value( cyaml_ctx_t *ctx, const cyaml_schema_value_t *schema, const uint8_t *data, uint64_t seq_count) { cyaml_err_t err; cyaml__log(ctx->config, CYAML_LOG_DEBUG, "Save: Writing value of type '%s'%s\n", cyaml__type_to_str(schema->type), schema->flags & CYAML_FLAG_POINTER ? " (pointer)" : ""); data = cyaml__data_handle_pointer(ctx->config, schema, data); if (data == NULL) { if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_POINTER_NULL_STR)) { return cyaml__emit_scalar(ctx, schema, "null", YAML_STR_TAG); } else if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_POINTER_NULL)) { return cyaml__emit_scalar(ctx, schema, "", YAML_STR_TAG); } else { return CYAML_ERR_INVALID_VALUE; } } switch (schema->type) { case CYAML_INT: /* Fall through. */ case CYAML_UINT: /* Fall through. */ case CYAML_BOOL: /* Fall through. */ case CYAML_ENUM: /* Fall through. */ case CYAML_FLOAT: /* Fall through. */ case CYAML_STRING: err = cyaml__write_scalar_value(ctx, schema, data); break; case CYAML_FLAGS: err = cyaml__write_flags_value(ctx, schema, data); break; case CYAML_MAPPING: err = cyaml__stack_push(ctx, CYAML_STATE_IN_MAP_KEY, schema, data); break; case CYAML_BITFIELD: err = cyaml__write_bitfield_value(ctx, schema, data); break; case CYAML_SEQUENCE_FIXED: if (schema->sequence.min != schema->sequence.max) { return CYAML_ERR_SEQUENCE_FIXED_COUNT; } /* Fall through. */ case CYAML_SEQUENCE: err = cyaml__stack_push(ctx, CYAML_STATE_IN_SEQUENCE, schema, data); if (err == CYAML_OK) { ctx->state->sequence.count = seq_count; } break; default: err = CYAML_ERR_BAD_TYPE_IN_SCHEMA; break; } return err; } /** * YAML saving handler for the \ref CYAML_STATE_START state. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_start( cyaml_ctx_t *ctx) { return cyaml__stack_push(ctx, CYAML_STATE_IN_STREAM, ctx->state->schema, ctx->state->data); } /** * YAML saving handler for the \ref CYAML_STATE_IN_STREAM state. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_stream( cyaml_ctx_t *ctx) { cyaml_err_t err; if (ctx->state->done) { err = cyaml__stack_pop(ctx, true); } else { ctx->stack[CYAML_STATE_IN_STREAM].done = true; err = cyaml__stack_push(ctx, CYAML_STATE_IN_DOC, ctx->state->schema, ctx->state->data); } return err; } /** * YAML saving handler for the \ref CYAML_STATE_IN_DOC state. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_doc( cyaml_ctx_t *ctx) { cyaml_err_t err; if (ctx->state->done) { err = cyaml__stack_pop(ctx, true); } else { unsigned seq_count = ctx->seq_count; if (ctx->state->schema->type == CYAML_SEQUENCE_FIXED) { seq_count = ctx->state->schema->sequence.max; } ctx->stack[CYAML_STATE_IN_DOC].done = true; err = cyaml__write_value(ctx, ctx->state->schema, ctx->state->data, seq_count); } return err; } /** * YAML saving handler for the \ref CYAML_STATE_IN_MAP_KEY and \ref * CYAML_STATE_IN_MAP_VALUE states. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_mapping( cyaml_ctx_t *ctx) { const cyaml_schema_field_t *field = ctx->state->mapping.field; cyaml_err_t err = CYAML_OK; if (field != NULL && field->key != NULL) { uint64_t seq_count = 0; if (field->value.type == CYAML_IGNORE) { ctx->state->mapping.field++; return CYAML_OK; } if ((field->value.flags & CYAML_FLAG_OPTIONAL) && (field->value.flags & CYAML_FLAG_POINTER)) { const void *ptr = cyaml_data_read_pointer( ctx->state->data + field->data_offset); if (ptr == NULL) { ctx->state->mapping.field++; return CYAML_OK; } } err = cyaml__emit_scalar(ctx, ctx->state->schema, field->key, YAML_STR_TAG); if (err != CYAML_OK) { return err; } /* Advance the field before writing value, since writing the * value can put a new state entry on the stack. */ ctx->state->mapping.field++; if (field->value.type == CYAML_SEQUENCE) { seq_count = cyaml_data_read(field->count_size, ctx->state->data + field->count_offset, &err); if (err != CYAML_OK) { return err; } cyaml__log(ctx->config, CYAML_LOG_INFO, "Save: Sequence entry count: %u\n", seq_count); } else if (field->value.type == CYAML_SEQUENCE_FIXED) { seq_count = field->value.sequence.min; } err = cyaml__write_value(ctx, &field->value, ctx->state->data + field->data_offset, seq_count); } else { err = cyaml__stack_pop(ctx, true); } return err; } /** * YAML saving handler for the \ref CYAML_STATE_IN_SEQUENCE state. * * \param[in] ctx The CYAML saving context. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__write_sequence( cyaml_ctx_t *ctx) { cyaml_err_t err = CYAML_OK; if (ctx->state->sequence.entry < ctx->state->sequence.count) { const cyaml_schema_value_t *schema = ctx->state->schema; const cyaml_schema_value_t *value = schema->sequence.entry; unsigned seq_count = 0; size_t data_size; size_t offset; if (value->type == CYAML_SEQUENCE) { return CYAML_ERR_SEQUENCE_IN_SEQUENCE; } else if (value->type == CYAML_SEQUENCE_FIXED) { seq_count = value->sequence.max; } if (value->flags & CYAML_FLAG_POINTER) { data_size = sizeof(NULL); } else { data_size = value->data_size; if (value->type == CYAML_SEQUENCE_FIXED) { data_size *= seq_count; } } offset = data_size * ctx->state->sequence.entry; cyaml__log(ctx->config, CYAML_LOG_INFO, "Save: Sequence entry %u of %u\n", ctx->state->sequence.entry + 1, ctx->state->sequence.count); /* Advance the entry before writing value, since writing the * value can put a new state entry on the stack. */ ctx->state->sequence.entry++; err = cyaml__write_value(ctx, value, ctx->state->data + offset, seq_count); } else { err = cyaml__stack_pop(ctx, true); } return err; } /** * Check that common save params from client are valid. * * \param[in] config The client's CYAML library config. * \param[in] schema The schema describing the content of data. * \param[in] data Points to client's data. * \param[in] seq_count Top level sequence count. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static inline cyaml_err_t cyaml__validate_save_params( const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count) { if (config == NULL) { return CYAML_ERR_BAD_PARAM_NULL_CONFIG; } if (config->mem_fn == NULL) { return CYAML_ERR_BAD_CONFIG_NULL_MEMFN; } if (schema == NULL) { return CYAML_ERR_BAD_PARAM_NULL_SCHEMA; } if ((schema->type == CYAML_SEQUENCE) != (seq_count != 0)) { return CYAML_ERR_BAD_PARAM_SEQ_COUNT; } if (!(schema->flags & CYAML_FLAG_POINTER)) { return CYAML_ERR_TOP_LEVEL_NON_PTR; } if (data == NULL) { return CYAML_ERR_BAD_PARAM_NULL_DATA; } return CYAML_OK; } /** * The main YAML saving function. * * The public interfaces are wrappers around this. * * \param[in] config Client's CYAML configuration structure. * \param[in] schema CYAML schema for the YAML to be saved. * \param[in] data The caller-owned data to be saved. * \param[in] seq_count If top level type is sequence, this should be the * entry count, otherwise it is ignored. * \param[in] emitter An initialised `libyaml` emitter object * with its output set. * \return \ref CYAML_OK on success, or appropriate error code otherwise. */ static cyaml_err_t cyaml__save( const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count, yaml_emitter_t *emitter) { cyaml_ctx_t ctx = { .config = config, .emitter = emitter, .seq_count = seq_count, }; typedef cyaml_err_t (* const cyaml_write_fn)( cyaml_ctx_t *ctx); static const cyaml_write_fn fn[CYAML_STATE__COUNT] = { [CYAML_STATE_START] = cyaml__write_start, [CYAML_STATE_IN_STREAM] = cyaml__write_stream, [CYAML_STATE_IN_DOC] = cyaml__write_doc, [CYAML_STATE_IN_MAP_KEY] = cyaml__write_mapping, [CYAML_STATE_IN_MAP_VALUE] = cyaml__write_mapping, [CYAML_STATE_IN_SEQUENCE] = cyaml__write_sequence, }; cyaml_err_t err = CYAML_OK; err = cyaml__validate_save_params(config, schema, data, seq_count); if (err != CYAML_OK) { return err; } err = cyaml__stack_push(&ctx, CYAML_STATE_START, schema, &data); if (err != CYAML_OK) { goto out; } do { cyaml__log(ctx.config, CYAML_LOG_DEBUG, "Save: Handle state %s\n", cyaml__state_to_str(ctx.state->state)); err = fn[ctx.state->state](&ctx); if (err != CYAML_OK) { goto out; } } while (ctx.stack_idx > 1); cyaml__stack_pop(&ctx, true); assert(ctx.stack_idx == 0); if (!yaml_emitter_flush(emitter)) { cyaml__log(config, CYAML_LOG_ERROR, "Save: LibYAML: Failed to flush emitter: %s\n", emitter->problem); err = CYAML_ERR_LIBYAML_EMITTER; } out: if (err != CYAML_OK) { cyaml__backtrace(&ctx); } while (ctx.stack_idx > 0) { cyaml__stack_pop(&ctx, false); } cyaml__free(config, ctx.stack); return err; } /* Exported function, documented in include/cyaml/cyaml.h */ cyaml_err_t cyaml_save_file( const char *path, const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count) { FILE *file; cyaml_err_t err; yaml_emitter_t emitter; /* Initialize emitter */ if (!yaml_emitter_initialize(&emitter)) { return CYAML_ERR_LIBYAML_EMITTER_INIT; } /* Open output file. */ file = fopen(path, "w"); if (file == NULL) { yaml_emitter_delete(&emitter); return CYAML_ERR_FILE_OPEN; } /* Set output file */ yaml_emitter_set_output_file(&emitter, file); /* Serialise to the output */ err = cyaml__save(config, schema, data, seq_count, &emitter); if (err != CYAML_OK) { yaml_emitter_delete(&emitter); fclose(file); return err; } /* Cleanup */ yaml_emitter_delete(&emitter); fclose(file); return CYAML_OK; } /** CYAML save buffer context. */ typedef struct cyaml_buffer_ctx { /** Client's CYAML configuration structure. */ const cyaml_config_t *config; size_t len; /**< Current length of `data` allocation. */ size_t used; /**< Current number of bytes used in `data`. */ char *data; /**< Current allocation for serialised output. */ cyaml_err_t err; /**< Any error encounted in buffer handling. */ } cyaml_buffer_ctx_t; /** * Write handler for libyaml. * * The write handler is called when the emitter needs to flush the accumulated * characters to the output. The handler should write size bytes of the * buffer to the output. * * \todo Could be more efficient about this, but for now, this is fine. * * \param[in] data A pointer to cyaml buffer context struture. * \param[in] buffer The buffer with bytes to be written. * \param[in] size The number of bytes to be written. * \return 1 on sucess, 0 otherwise. */ static int cyaml__buffer_handler( void *data, unsigned char *buffer, size_t size) { cyaml_buffer_ctx_t *buffer_ctx = data; enum { RETURN_SUCCESS = 1, RETURN_FAILURE = 0, }; if (size > (buffer_ctx->len - buffer_ctx->used)) { char *temp = cyaml__realloc( buffer_ctx->config, buffer_ctx->data, buffer_ctx->len, buffer_ctx->used + size, false); if (temp == NULL) { buffer_ctx->err = CYAML_ERR_OOM; return RETURN_FAILURE; } buffer_ctx->data = temp; buffer_ctx->len = buffer_ctx->used + size; } memcpy(buffer_ctx->data + buffer_ctx->used, buffer, size); buffer_ctx->used += size; return RETURN_SUCCESS; } /* Exported function, documented in include/cyaml/cyaml.h */ cyaml_err_t cyaml_save_data( char **output, size_t *len, const cyaml_config_t *config, const cyaml_schema_value_t *schema, const cyaml_data_t *data, unsigned seq_count) { cyaml_err_t err; yaml_emitter_t emitter; cyaml_buffer_ctx_t buffer_ctx = { .config = config, .err = CYAML_OK, }; /* Initialize emitter */ if (!yaml_emitter_initialize(&emitter)) { return CYAML_ERR_LIBYAML_EMITTER_INIT; } /* Set output buffer */ yaml_emitter_set_output(&emitter, cyaml__buffer_handler, &buffer_ctx); /* Serialise to the output */ err = cyaml__save(config, schema, data, seq_count, &emitter); if (err != CYAML_OK) { yaml_emitter_delete(&emitter); if ((config != NULL) && (config->mem_fn != NULL)) { cyaml__free(config, buffer_ctx.data); } if (buffer_ctx.err != CYAML_OK) { err = buffer_ctx.err; } return err; } /* Cleanup */ yaml_emitter_delete(&emitter); *output = buffer_ctx.data; *len = buffer_ctx.used; return CYAML_OK; } libcyaml-1.4.1/src/utf8.c000066400000000000000000000166711443462360400151530ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2019 Michael Drake */ /** * \file * \brief CYAML functions for handling utf8 text. */ #include #include #include #include "utf8.h" /** * Get expected byte-length of UTF8 character. * * Finds the number of bytes expected for the UTF8 sequence starting with * the given byte. * * \param[in] b First byte of UTF8 sequence. * \return the byte width of the character or 0 if invalid. */ static inline unsigned cyaml_utf8_char_len(uint8_t b) { if (!(b & 0x80)) { return 1; } else switch (b >> 3) { case 0x18: case 0x19: case 0x1a: case 0x1b: return 2; case 0x1c: case 0x1d: return 3; case 0x1e: return 4; } return 0; /* Invalid */ } /* Exported function, documented in utf8.h. */ unsigned cyaml_utf8_get_codepoint( const uint8_t *s, unsigned *len) { unsigned c = 0; bool sf = false; if (*len == 1) { return s[0]; } else if ((*len > 1) && (*len <= 4)) { /* Compose first byte into codepoint. */ c |= (s[0] & ((1u << (7 - *len)) - 1u)) << ((*len - 1) * 6); /* Handle continuation bytes. */ for (unsigned i = 1; i < *len; i++) { /* Check continuation byte begins with 0b10xxxxxx. */ if ((s[i] & 0xc0) != 0x80) { /* Need to shorten length so we don't consume * this byte with this invalid character. */ *len -= i; goto invalid; } /* Compose continuation byte into codepoint. */ c |= (0x3fu & s[i]) << ((*len - i - 1) * 6); } } /* Non-shortest forms are forbidden. * * The following table shows the bits available for each * byte sequence length, as well as the bit-delta to the * shorter sequence. * * | Bytes | Bits | Bit delta | Data | * | ----- | ---- | --------- | ----------------------- | * | 1 | 7 | N/A | 0xxxxxxx | * | 2 | 11 | 4 | 110xxxxx + 10xxxxxx x 1 | * | 3 | 16 | 5 | 1110xxxx + 10xxxxxx x 2 | * | 4 | 21 | 5 | 11110xxx + 10xxxxxx x 3 | * * So here we check that the top "bit-delta" bits are not all * clear for the byte length, */ switch (*len) { case 2: sf = (c & (((1 << 4) - 1) << (11 - 4))) != 0; break; case 3: sf = (c & (((1 << 5) - 1) << (16 - 5))) != 0; break; case 4: sf = (c & (((1 << 5) - 1) << (21 - 5))) != 0; break; default: goto invalid; } if (!sf) { /* Codepoint representation was not shortest-form. */ goto invalid; } return c; invalid: return 0xfffd; /* REPLACEMENT CHARACTER */ } /** * Convert a Unicode codepoint to lower case. * * \note This only handles some of the Unicode blocks. * (Currently the Latin ones.) * * \param[in] c Codepoint to convert to lower-case, if applicable. * \return the lower-cased codepoint. */ static unsigned cyaml_utf8_to_lower(unsigned c) { if (((c >= 0x0041) && (c <= 0x005a)) /* Basic Latin */ || ((c >= 0x00c0) && (c <= 0x00d6)) /* Latin-1 Supplement */ || ((c >= 0x00d8) && (c <= 0x00de)) /* Latin-1 Supplement */ ) { return c + 32u; } else if (((c >= 0x0100) && (c <= 0x012f)) /* Latin Extended-A */ || ((c >= 0x0132) && (c <= 0x0137)) /* Latin Extended-A */ || ((c >= 0x014a) && (c <= 0x0177)) /* Latin Extended-A */ || ((c >= 0x0182) && (c <= 0x0185)) /* Latin Extended-B */ || ((c >= 0x01a0) && (c <= 0x01a5)) /* Latin Extended-B */ || ((c >= 0x01de) && (c <= 0x01ef)) /* Latin Extended-B */ || ((c >= 0x01f8) && (c <= 0x021f)) /* Latin Extended-B */ || ((c >= 0x0222) && (c <= 0x0233)) /* Latin Extended-B */ || ((c >= 0x0246) && (c <= 0x024f)) /* Latin Extended-B */ ) { return c & ~0x1u; } else if (((c >= 0x0139) && (c <= 0x0148) /* Latin Extended-A */) || ((c >= 0x0179) && (c <= 0x017e) /* Latin Extended-A */) || ((c >= 0x01b3) && (c <= 0x01b6) /* Latin Extended-B */) || ((c >= 0x01cd) && (c <= 0x01dc) /* Latin Extended-B */)) { return (c + 1) & ~0x1u; } else switch (c) { case 0x0178: return 0x00ff; /* Latin Extended-A */ case 0x0187: return 0x0188; /* Latin Extended-B */ case 0x018b: return 0x018c; /* Latin Extended-B */ case 0x018e: return 0x01dd; /* Latin Extended-B */ case 0x0191: return 0x0192; /* Latin Extended-B */ case 0x0198: return 0x0199; /* Latin Extended-B */ case 0x01a7: return 0x01a8; /* Latin Extended-B */ case 0x01ac: return 0x01ad; /* Latin Extended-B */ case 0x01af: return 0x01b0; /* Latin Extended-B */ case 0x01b7: return 0x0292; /* Latin Extended-B */ case 0x01b8: return 0x01b9; /* Latin Extended-B */ case 0x01bc: return 0x01bd; /* Latin Extended-B */ case 0x01c4: return 0x01c6; /* Latin Extended-B */ case 0x01c5: return 0x01c6; /* Latin Extended-B */ case 0x01c7: return 0x01c9; /* Latin Extended-B */ case 0x01c8: return 0x01c9; /* Latin Extended-B */ case 0x01ca: return 0x01cc; /* Latin Extended-B */ case 0x01cb: return 0x01cc; /* Latin Extended-B */ case 0x01f1: return 0x01f3; /* Latin Extended-B */ case 0x01f2: return 0x01f3; /* Latin Extended-B */ case 0x01f4: return 0x01f5; /* Latin Extended-B */ case 0x01f7: return 0x01bf; /* Latin Extended-B */ case 0x0220: return 0x019e; /* Latin Extended-B */ case 0x023b: return 0x023c; /* Latin Extended-B */ case 0x023d: return 0x019a; /* Latin Extended-B */ case 0x0241: return 0x0242; /* Latin Extended-B */ case 0x0243: return 0x0180; /* Latin Extended-B */ } return c; } /** * Find the difference between two codepoints. * * \param a First codepoint. * \param b Second codepoint. * \return the difference. */ static inline int cyaml_utf8_difference(unsigned a, unsigned b) { return (((int)a) - ((int)b)); } /* Exported function, documented in utf8.h. */ int cyaml_utf8_casecmp( const void * const str1, const void * const str2) { const uint8_t *s1 = str1; const uint8_t *s2 = str2; while (true) { unsigned len1; unsigned len2; unsigned cmp1; unsigned cmp2; /* Check for end of strings. */ if ((*s1 == 0) && (*s2 == 0)) { return 0; /* Both strings ended; match. */ } else if (*s1 == 0) { return 1; /* String 1 has ended. */ } else if (*s2 == 0) { return -1;/* String 2 has ended. */ } /* Get byte lengths of these characters. */ len1 = cyaml_utf8_char_len(*s1); len2 = cyaml_utf8_char_len(*s2); /* Compare values. */ if ((len1 == 1) && (len2 == 1)) { /* Common case: Both strings have ASCII values. */ if (*s1 != *s2) { /* They're different; need to lower case. */ cmp1 = ((*s1 >= 'A') && (*s1 <= 'Z')) ? (*s1 + 32u) : *s1; cmp2 = ((*s2 >= 'A') && (*s2 <= 'Z')) ? (*s2 + 32u) : *s2; if (cmp1 != cmp2) { return cyaml_utf8_difference( cmp1, cmp2); } } } else if ((len1 != 0) && (len2 != 0)) { /* Neither string has invalid sequence; * convert to UCS4 for comparison. */ cmp1 = cyaml_utf8_get_codepoint(s1, &len1); cmp2 = cyaml_utf8_get_codepoint(s2, &len2); if (cmp1 != cmp2) { /* They're different; need to lower case. */ cmp1 = cyaml_utf8_to_lower(cmp1); cmp2 = cyaml_utf8_to_lower(cmp2); if (cmp1 != cmp2) { return cyaml_utf8_difference( cmp1, cmp2); } } } else { if (len1 | len2) { /* One of the strings has invalid sequence. */ return cyaml_utf8_difference(len1, len2); } else { /* Both strings have an invalid sequence. */ len1 = len2 = 1; } } /* Advance each string by their current character length. */ s1 += len1; s2 += len2; } } libcyaml-1.4.1/src/utf8.h000066400000000000000000000023161443462360400151470ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018 Michael Drake */ /** * \file * \brief CYAML functions for handling utf8 text. */ #ifndef CYAML_UTF8_H #define CYAML_UTF8_H /** * Get a codepoint from the input string. * * Caller must provide the expected length given the first input byte. * * If a multi-byte character contains an invalid continuation byte, the * character length will be updated on exit to the number of bytes consumed, * and the replacement character, U+FFFD will be returned. * * \param[in] s String to read first codepoint from. * \param[in,out] len Expected length of first character, updated on exit. * \return The codepoint or `0xfffd` if character is invalid. */ unsigned cyaml_utf8_get_codepoint( const uint8_t *s, unsigned *len); /** * Case insensitive comparason. * * \note This has some limitations and only performs case insensitive * comparason over some sectons of unicode. * * \param[in] str1 First string to be compared. * \param[in] str2 Second string to be compared. * \return 0 if and only if strings are equal. */ int cyaml_utf8_casecmp( const void * const str1, const void * const str2); #endif libcyaml-1.4.1/src/util.c000066400000000000000000000103221443462360400152250ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2019 Michael Drake */ /** * \file * \brief Utility functions. */ #include #include #include #include #include "util.h" /** Flag that indicates a release in \ref cyaml_version. */ #define CYAML_RELEASE_FLAG (1u << 31) /** Stringification helper macro. */ #define CYAML_STR_HELPER(_x) #_x /** Stringification macro. */ #define CYAML_STR(_x) CYAML_STR_HELPER(_x) /* Version depends on whether we're a development build. */ #if VERSION_DEVEL /** Version string is composed from components in Makefile. */ #define CYAML_VERSION_STR \ CYAML_STR(VERSION_MAJOR) "." \ CYAML_STR(VERSION_MINOR) "." \ CYAML_STR(VERSION_PATCH) "-DEVEL" /* Exported constant, documented in include/cyaml/cyaml.h */ const uint32_t cyaml_version = ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH << 0)); #else /** Version string is composed from components in Makefile. */ #define CYAML_VERSION_STR \ CYAML_STR(VERSION_MAJOR) "." \ CYAML_STR(VERSION_MINOR) "." \ CYAML_STR(VERSION_PATCH) /* Exported constant, documented in include/cyaml/cyaml.h */ const uint32_t cyaml_version = ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH << 0) | CYAML_RELEASE_FLAG); #endif /* Exported constant, documented in include/cyaml/cyaml.h */ const char *cyaml_version_str = CYAML_VERSION_STR; /* Exported function, documented in include/cyaml/cyaml.h */ void cyaml_log( cyaml_log_t level, void *ctx, const char *fmt, va_list args) { static const char * const strings[] = { [CYAML_LOG_DEBUG] = "DEBUG", [CYAML_LOG_INFO] = "INFO", [CYAML_LOG_NOTICE] = "NOTICE", [CYAML_LOG_WARNING] = "WARNING", [CYAML_LOG_ERROR] = "ERROR", }; CYAML_UNUSED(ctx); fprintf(stderr, "libcyaml: %7.7s: ", strings[level]); vfprintf(stderr, fmt, args); } /* Exported function, documented in include/cyaml/cyaml.h */ const char * cyaml_strerror( cyaml_err_t err) { static const char * const strings[CYAML_ERR__COUNT] = { [CYAML_OK] = "Success", [CYAML_ERR_OOM] = "Memory allocation failed", [CYAML_ERR_ALIAS] = "YAML alias unsupported", [CYAML_ERR_FILE_OPEN] = "Could not open file", [CYAML_ERR_INVALID_KEY] = "Invalid key", [CYAML_ERR_INVALID_VALUE] = "Invalid value", [CYAML_ERR_INVALID_ALIAS] = "No anchor found for alias", [CYAML_ERR_INTERNAL_ERROR] = "Internal error", [CYAML_ERR_UNEXPECTED_EVENT] = "Unexpected event", [CYAML_ERR_STRING_LENGTH_MIN] = "String length too short", [CYAML_ERR_STRING_LENGTH_MAX] = "String length too long", [CYAML_ERR_INVALID_DATA_SIZE] = "Data size must be 0 < X <= 8 bytes", [CYAML_ERR_TOP_LEVEL_NON_PTR] = "Top-level schema value must be pointer", [CYAML_ERR_BAD_TYPE_IN_SCHEMA] = "Schema contains invalid type", [CYAML_ERR_BAD_MIN_MAX_SCHEMA] = "Bad schema: min exceeds max", [CYAML_ERR_BAD_PARAM_SEQ_COUNT] = "Bad parameter: seq_count", [CYAML_ERR_BAD_PARAM_NULL_DATA] = "Bad parameter: NULL data", [CYAML_ERR_BAD_BITVAL_IN_SCHEMA] = "Bit value beyond bitfield size", [CYAML_ERR_SEQUENCE_ENTRIES_MIN] = "Sequence with too few entries", [CYAML_ERR_SEQUENCE_ENTRIES_MAX] = "Sequence with too many entries", [CYAML_ERR_SEQUENCE_FIXED_COUNT] = "Sequence fixed has unequal min max", [CYAML_ERR_SEQUENCE_IN_SEQUENCE] = "Non-fixed sequence in sequence", [CYAML_ERR_MAPPING_FIELD_MISSING] = "Missing required mapping field", [CYAML_ERR_BAD_CONFIG_NULL_MEMFN] = "Bad config: NULL mem function", [CYAML_ERR_BAD_PARAM_NULL_CONFIG] = "Bad parameter: NULL config", [CYAML_ERR_BAD_PARAM_NULL_SCHEMA] = "Bad parameter: NULL schema", [CYAML_ERR_LIBYAML_EMITTER_INIT] = "libyaml emitter init failed", [CYAML_ERR_LIBYAML_PARSER_INIT] = "libyaml parser init failed", [CYAML_ERR_LIBYAML_EVENT_INIT] = "libyaml event init failed", [CYAML_ERR_LIBYAML_EMITTER] = "libyaml emitter error", [CYAML_ERR_LIBYAML_PARSER] = "libyaml parser error", }; if ((unsigned)err >= CYAML_ERR__COUNT) { return "Invalid error code"; } assert(strings[err] != NULL); return strings[err]; } libcyaml-1.4.1/src/util.h000066400000000000000000000123041443462360400152340ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ /** * \file * \brief CYAML common utility functions. */ #ifndef CYAML_UTIL_H #define CYAML_UTIL_H #include "cyaml/cyaml.h" #include "utf8.h" /** Macro to squash unused variable compiler warnings. */ #define CYAML_UNUSED(_x) ((void)(_x)) /** * Check whether the host is little endian. * * Checks whether least significant bit is in the first byte of a `uint16_t`. * * \return true if host is little endian. */ static inline bool cyaml__host_is_little_endian(void) { const uint16_t test = 1; return ((const uint8_t *) &test)[0]; } /** * Check whether the host is big endian. * * \return true if host is big endian. */ static inline bool cyaml__host_is_big_endian(void) { return !cyaml__host_is_little_endian(); } /** CYAML bitfield type. */ typedef uint32_t cyaml_bitfield_t; /** Number of bits in \ref cyaml_bitfield_t. */ #define CYAML_BITFIELD_BITS (sizeof(cyaml_bitfield_t) * CHAR_BIT) /** CYAML state machine states. */ enum cyaml_state_e { CYAML_STATE_START, /**< Initial state. */ CYAML_STATE_IN_STREAM, /**< In a stream. */ CYAML_STATE_IN_DOC, /**< In a document. */ CYAML_STATE_IN_MAP_KEY, /**< In a mapping. */ CYAML_STATE_IN_MAP_VALUE, /**< In a mapping. */ CYAML_STATE_IN_SEQUENCE, /**< In a sequence. */ CYAML_STATE__COUNT, /**< Count of states, **not a valid state itself**. */ }; /** * Convert a CYAML state into a human readable string. * * \param[in] state The state to convert. * \return String representing state. */ static inline const char * cyaml__state_to_str(enum cyaml_state_e state) { static const char * const strings[CYAML_STATE__COUNT] = { [CYAML_STATE_START] = "start", [CYAML_STATE_IN_STREAM] = "in stream", [CYAML_STATE_IN_DOC] = "in doc", [CYAML_STATE_IN_MAP_KEY] = "in mapping (key)", [CYAML_STATE_IN_MAP_VALUE] = "in mapping (value)", [CYAML_STATE_IN_SEQUENCE] = "in sequence", }; if ((unsigned)state >= CYAML_STATE__COUNT) { return ""; } return strings[state]; } /** * Convert a CYAML type into a human readable string. * * \param[in] type The state to convert. * \return String representing state. */ static inline const char * cyaml__type_to_str(cyaml_type_e type) { static const char * const strings[CYAML__TYPE_COUNT] = { [CYAML_INT] = "INT", [CYAML_UINT] = "UINT", [CYAML_BOOL] = "BOOL", [CYAML_ENUM] = "ENUM", [CYAML_FLAGS] = "FLAGS", [CYAML_FLOAT] = "FLOAT", [CYAML_STRING] = "STRING", [CYAML_MAPPING] = "MAPPING", [CYAML_BITFIELD] = "BITFIELD", [CYAML_SEQUENCE] = "SEQUENCE", [CYAML_SEQUENCE_FIXED] = "SEQUENCE_FIXED", [CYAML_IGNORE] = "IGNORE", }; if ((unsigned)type >= CYAML__TYPE_COUNT) { return ""; } return strings[type]; } /** * Log to client's logging function, if provided. * * \param[in] cfg CYAML client config structure. * \param[in] level Log level of message to log. * \param[in] fmt Format string for message to log. * \param[in] ... Additional arguments used by fmt. */ static inline void cyaml__log( const cyaml_config_t *cfg, cyaml_log_t level, const char *fmt, ...) { if (level >= cfg->log_level && cfg->log_fn != NULL) { va_list args; va_start(args, fmt); cfg->log_fn(level, cfg->log_ctx, fmt, args); va_end(args); } } /** * Check if comparason should be case sensitive. * * As described in the API, schema flags take priority over config flags. * * \param[in] config Client's CYAML configuration structure. * \param[in] schema The CYAML schema for the value to be compared. * \return Whether to use case-sensitive comparason. */ static inline bool cyaml__is_case_sensitive( const cyaml_config_t *config, const cyaml_schema_value_t *schema) { if (schema->flags & CYAML_FLAG_CASE_INSENSITIVE) { return false; } else if (schema->flags & CYAML_FLAG_CASE_SENSITIVE) { return true; } else if (config->flags & CYAML_CFG_CASE_INSENSITIVE) { return false; } return true; } /** * Compare two strings. * * Depending on the client's configuration, and the value's schema, * this will do either a case-sensitive or case-insensitive comparason. * * \param[in] config Client's CYAML configuration structure. * \param[in] schema The CYAML schema for the value to be compared. * \param[in] str1 First string to be compared. * \param[in] str2 Second string to be compared. * \return 0 if and only if strings are equal. */ static inline int cyaml__strcmp( const cyaml_config_t *config, const cyaml_schema_value_t *schema, const void * const str1, const void * const str2) { if (cyaml__is_case_sensitive(config, schema)) { return strcmp(str1, str2); } return cyaml_utf8_casecmp(str1, str2); } /** * Check of all the bits of a mask are set in a cyaml value flag word. * * \param[in] flags The value flags to test. * \param[in] mask Mask of the bits to test for in flags. * \return true if all bits of mask are set in flags. */ static inline bool cyaml__flag_check_all( enum cyaml_flag flags, enum cyaml_flag mask) { return ((flags & mask) == mask); } #endif libcyaml-1.4.1/test/000077500000000000000000000000001443462360400142765ustar00rootroot00000000000000libcyaml-1.4.1/test/data/000077500000000000000000000000001443462360400152075ustar00rootroot00000000000000libcyaml-1.4.1/test/data/basic.yaml000066400000000000000000000004231443462360400171530ustar00rootroot00000000000000animals: - kind: cat sounds: - meow - purr - kind: hippo sounds: - wheeze - grunt - roar - kind: snake sounds: - hiss cakes: - salted caramel cake - lemon drizzle cake - victoria sponge - carrot cake - yule log libcyaml-1.4.1/test/units/000077500000000000000000000000001443462360400154405ustar00rootroot00000000000000libcyaml-1.4.1/test/units/errs.c000066400000000000000000005724051443462360400165740ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2021 Michael Drake */ #include #include #include #include #include #include #include "ttest.h" #include "test.h" /** Macro to squash unused variable compiler warnings. */ #define UNUSED(_x) ((void)(_x)) /** Helper macro to count bytes of YAML input data. */ #define YAML_LEN(_y) (sizeof(_y) - 1) /** * Unit test context data. */ typedef struct test_data { char **buffer; cyaml_data_t **data; unsigned *seq_count; const struct cyaml_config *config; const struct cyaml_schema_value *schema; } test_data_t; /** * Common clean up function to free data loaded by tests. * * \param[in] data The unit test context data. */ static void cyaml_cleanup(void *data) { struct test_data *td = data; unsigned seq_count = 0; if (td->seq_count != NULL) { seq_count = *(td->seq_count); } if (td->config->mem_fn != NULL && td->buffer != NULL) { td->config->mem_fn(td->config->mem_ctx, *(td->buffer), 0); } if (td->data != NULL) { cyaml_free(td->config, td->schema, *(td->data), seq_count); } } /** * Test loading with NULL data parameter. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_null_data( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = ""; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) NULL, NULL); if (err != CYAML_ERR_BAD_PARAM_NULL_DATA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with NULL data parameter. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_null_data( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; 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_save_data(&buffer, &len, config, &top_schema, NULL, 0); if (err != CYAML_ERR_BAD_PARAM_NULL_DATA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with NULL config parameter. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_null_config( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = ""; void *data_tgt = NULL; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = NULL, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), NULL, NULL, (cyaml_data_t **) NULL, NULL); if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with NULL config parameter. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_null_config( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, NULL, &top_schema, &data, 0); if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with NULL memory allocation function. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_null_mem_fn( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = ""; cyaml_config_t cfg = *config; void *data_tgt = NULL; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = NULL, }; cyaml_err_t err; ttest_ctx_t tc; cfg.mem_fn = NULL; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, NULL, (cyaml_data_t **) NULL, NULL); if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with NULL memory allocation function. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_null_mem_fn( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.mem_fn = NULL; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, &data, 0); if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with NULL schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_null_schema( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = ""; void *data_tgt = NULL; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = NULL, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, NULL, (cyaml_data_t **) NULL, NULL); if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with NULL schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_null_schema( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, NULL, &data, 0); if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with bad top level type (non-pointer). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_top_level_non_pointer( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "7\n"; int *value = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; test_data_t td = { .data = (cyaml_data_t **) &value, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &value, NULL); if (err != CYAML_ERR_TOP_LEVEL_NON_PTR) { return ttest_fail(&tc, cyaml_strerror(err)); } if (value != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with schema with bad top level type (non-pointer). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_top_level_non_pointer( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_TOP_LEVEL_NON_PTR) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with bad top level sequence and no seq_count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_top_level_sequence_no_count( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n"; struct target_struct { int *value; unsigned value_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, CYAML_UNLIMITED), }; 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_PARAM_SEQ_COUNT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading with schema with bad top level sequence and no seq_count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_top_level_not_sequence_count( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "7\n"; int *value = NULL; unsigned count = 0; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_ERR_BAD_PARAM_SEQ_COUNT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (value != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with schema with a non-sequence and non-zero sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_top_level_not_sequence_count( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { unsigned *test_uint; } data = { .test_uint = NULL, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 42); if (err != CYAML_ERR_BAD_PARAM_SEQ_COUNT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with bad type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_bad_type( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = 99999, .flags = CYAML_FLAG_DEFAULT, .data_size = sizeof(data_tgt->value), }, .data_offset = offsetof(struct target_struct, value), }, CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_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. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_type( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { int value; } data = { .value = 99, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = 99999, .flags = CYAML_FLAG_DEFAULT, .data_size = sizeof(data.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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_BAD_TYPE_IN_SCHEMA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with string min greater than max. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_string_min_max( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "value: foo\n"; struct target_struct { const char *value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("value", CYAML_FLAG_POINTER, struct target_struct, value, 10, 9), 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_MIN_MAX_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); } /** * Only scalar mapping keys are allowed by libcyaml. * * \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_non_scalar_mapping_key( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "{1}: value"; struct target_struct { unsigned value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("key", CYAML_FLAG_DEFAULT, 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_INTERNAL_ERROR) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading with schema with data size (0). * * \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_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: 1\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_INT, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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_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 loading with schema with data size (9). * * \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_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: 1\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_INT, .flags = CYAML_FLAG_DEFAULT, .data_size = 9, }, .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_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 loading with schema with data size (0) for flags. * * \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_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_strval_t strings[] = { { "foo", 0 }, { "bar", 1 }, { "baz", 2 }, { "bat", 3 }, }; static const unsigned char yaml[] = "key:\n" " - bat\n" " - bar\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_FLAGS, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, .enumeration = { .strings = strings, .count = CYAML_ARRAY_LEN(strings), }, }, .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_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 loading with schema with data size (0) 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_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 2\n" " - 4\n"; struct target_struct { int *value; unsigned value_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(*(data_tgt->value)), .sequence = { .min = 0, .max = CYAML_UNLIMITED, .entry = &entry_schema, }, }, .data_offset = offsetof(struct target_struct, value), .count_size = 0, .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 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_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 2\n" " - 4\n"; struct target_struct { int *value; unsigned value_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(*(data_tgt->value)), .sequence = { .min = 0, .max = CYAML_UNLIMITED, .entry = &entry_schema, }, }, .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 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_6( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 2\n" " - 4\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_DEFAULT, .data_size = sizeof(*(data_tgt->value)), .sequence = { .min = 0, .max = 4, .entry = &entry_schema, }, }, .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 loading with schema with data size (0). * * \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_7( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: 1\n"; struct target_struct { unsigned value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_UINT, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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_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 loading with schema with data size (9) for bitfield. * * \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_8( ttest_report_ctx_t *report, const cyaml_config_t *config) { 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 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: 0x7\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n"; struct target_struct { uint64_t value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "test_bitfield", .value = { .type = CYAML_BITFIELD, .flags = CYAML_FLAG_DEFAULT, .data_size = 9, .bitfield = { .bitdefs = bitvals, .count = CYAML_ARRAY_LEN(bitvals), }, }, .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_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 loading with schema with data size (9) for flags. * * \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_9( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_strval_t strings[] = { { "foo", 0 }, { "bar", 1 }, { "baz", 2 }, { "bat", 3 }, }; static const unsigned char yaml[] = "key:\n" " - bat\n" " - bar\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_FLAGS, .flags = CYAML_FLAG_DEFAULT, .data_size = 9, .enumeration = { .strings = strings, .count = CYAML_ARRAY_LEN(strings), }, }, .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_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). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { int value; } data = { .value = 9, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_INT, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (0). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { unsigned value; } data = { .value = 9, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_UINT, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (0). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { bool value; } data = { .value = 1, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_BOOL, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (0). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_e { FIRST, SECOND, THIRD, FOURTH, COUNT }; static const cyaml_strval_t strings[COUNT] = { [FIRST] = { "first", 0 }, [SECOND] = { "second", 1 }, [THIRD] = { "third", 2 }, [FOURTH] = { "fourth", 3 }, }; static const struct target_struct { enum test_e value; } data = { .value = 1, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_ENUM, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, .enumeration = { .strings = strings, .count = COUNT, }, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (5 for floating point value). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { float value; } data = { .value = 3.14f, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_FLOAT, .flags = CYAML_FLAG_DEFAULT, .data_size = 5, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (0). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_6( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; static const struct target_struct { enum test_f value; } data = { .value = THIRD, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_FLAGS, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, .enumeration = { .strings = strings, .count = 4, }, }, .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), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with a bad sequence count size. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_7( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { unsigned *seq; uint32_t seq_count; } data = { .seq = NULL, }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(*(data.seq)), .sequence = { .entry = &entry_schema, .min = 0, .max = 10, }, }, .data_offset = offsetof(struct target_struct, seq), .count_offset = offsetof(struct target_struct, seq_count), .count_size = 9, }, CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test saving with schema with data size (0). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_data_size_8( ttest_report_ctx_t *report, const cyaml_config_t *config) { 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 }, }; static const struct target_struct { uint64_t test_bitfield; } data = { .test_bitfield = 0xFFFFFFFFFFFFFFFFu, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "test_bitfield", .value = { .type = CYAML_BITFIELD, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, .bitfield = { .bitdefs = bitvals, .count = CYAML_ARRAY_LEN(bitvals), }, }, .data_offset = offsetof(struct target_struct, test_bitfield), }, CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with sequence fixed with unequal min and max. * * \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_sequence_min_max( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "sequence:\n" " - \n"; struct target_struct { unsigned *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE_FIXED, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(*(data_tgt->seq)), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, }, }, .data_offset = offsetof(struct target_struct, seq), .count_offset = offsetof(struct target_struct, seq_count), .count_size = sizeof(data_tgt->seq_count), }, CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_SEQUENCE_FIXED_COUNT) { 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 sequence fixed with unequal min and max. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_sequence_min_max( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned value = 5; struct target_struct { unsigned *seq; } data = { .seq = &value, }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE_FIXED, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(*(data.seq)), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, }, }, .data_offset = offsetof(struct target_struct, seq), }, CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_SEQUENCE_FIXED_COUNT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading with schema with data size for float. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_bad_data_size_float( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: 1\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_FLOAT, .flags = CYAML_FLAG_DEFAULT, .data_size = 7, }, .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_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 loading with schema with sequence in sequence. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_sequence_in_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "- -\n"; unsigned **seq = NULL; unsigned count = 0; static const struct cyaml_schema_value inner_entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, **seq), }; static const struct cyaml_schema_value outer_entry_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned, &inner_entry_schema, 0, CYAML_UNLIMITED) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned *, &outer_entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &seq, .seq_count = &count, .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 **) &seq, &count); if (err != CYAML_ERR_SEQUENCE_IN_SEQUENCE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (seq != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving with schema with sequence in sequence. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_sequence_in_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned seq[4] = { 1, 2, 3 }; static const unsigned *data[] = { seq, seq }; static const struct cyaml_schema_value inner_entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, **data), }; static const struct cyaml_schema_value outer_entry_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned, &inner_entry_schema, 0, CYAML_UNLIMITED) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, unsigned *, &outer_entry_schema, 0, CYAML_UNLIMITED) }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 2); if (err != CYAML_ERR_SEQUENCE_IN_SEQUENCE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint, but value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: scalar\n"; struct target_struct { unsigned a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading with schema with string top level type, with bad value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "{ Hello }\n"; char *value = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, int, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &value, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading when flags expected, but numerical value has trailing junk. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_flags_junk( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - 1thousand\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags, but numerical value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_flags_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - -7\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags, but numerical value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_flags_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - 0x100000000\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags, but numerical value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_flags_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - 0x10000000000000000\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving a NULL when NULLs aren't allowed by the schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_invalid_value_null_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const int d[] = { 7, 6, 5, 4, 3, 2, 1, 0 }; static const int *data[] = { d + 0, d + 1, d + 2, NULL, d + 4, d + 5, d + 6, d + 7, }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, 3), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_STYLE_BLOCK; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data, CYAML_ARRAY_LEN(data)); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but numerical value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_bitfield_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: invalid\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value is non-scalar. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_bitfield_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: {}\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), 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_UNEXPECTED_EVENT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value is not in schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_bitfield_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " b: {}\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value too big. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_bitfield_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: 0xf\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value is of wrong 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_invalid_value_bitfield_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " {}: {}\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), 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_UNEXPECTED_EVENT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value is of wrong 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_invalid_value_bitfield_6( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 0, .bits = 3 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " []\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects bitfield, but value outside data. * * \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_bitfield( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 62, .bits = 4 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: 1\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), 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_BITVAL_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 when schema has bitfield defined outside value data. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_bad_bitfield( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_bitdef_t bitvals[] = { { .name = "a", .offset = 30, .bits = 4 }, }; static const struct target_struct { uint32_t test_bitfield; } data = { .test_bitfield = 0xFFFFFFFFu, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_BAD_BITVAL_IN_SCHEMA) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_range1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 3.5e+38\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_range2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: -3.5e+38\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_range3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 1.55331e-40f\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_range4( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: -1.55331e-40f\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_junk( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0.452*00E003\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects float but value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_float_invalid( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: Gasp\n"; struct target_struct { float a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects double but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_double_range1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 1.8e+4999\n"; struct target_struct { double a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects double but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_double_range2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: -1.8e+4999\n"; struct target_struct { double a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects double but value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_double_junk( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0.452*00E003\n"; struct target_struct { double a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects double but value is invalid. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_double_invalid( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: Gasp\n"; struct target_struct { double a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value has trailing junk. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_junk( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 3*9+4\n"; struct target_struct { int a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_range_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: -129\n"; struct target_struct { signed char a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_range_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 128\n"; struct target_struct { signed char a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_range_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x10000\n"; struct target_struct { int16_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_range_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x100000000\n"; struct target_struct { int32_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int_range_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x10000000000000000\n"; struct target_struct { int32_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value has trailing junk. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_junk( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 3*8+4\n"; struct target_struct { unsigned int a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_range_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: -1\n"; struct target_struct { unsigned char a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_range_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 256\n"; struct target_struct { unsigned char a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_range_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x10000\n"; struct target_struct { uint16_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_range_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x100000000\n"; struct target_struct { uint32_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects uint but value is out of range. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_uint_range_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 0x10000000000000000\n"; struct target_struct { uint32_t a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int8_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: -129\n"; struct target_struct { int8_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int8_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: 128\n"; struct target_struct { int8_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int16_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: -32769\n"; struct target_struct { int16_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int16_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: 32768\n"; struct target_struct { int16_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int32_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: -2147483649\n"; struct target_struct { int32_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int32_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: 2147483648\n"; struct target_struct { int32_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int64_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: -9223372036854775809\n"; struct target_struct { int64_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test integer limit. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_invalid_value_int64_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "val: 9223372036854775808\n"; struct target_struct { int64_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading when schema expects string, but it's too short. * * \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_string_min_length( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: foo\n"; struct target_struct { char *a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_DEFAULT, struct target_struct, a, 4, 4), 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_STRING_LENGTH_MIN) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects string, but it's too long. * * \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_string_max_length( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: fifth\n"; struct target_struct { char *a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_DEFAULT, struct target_struct, a, 4, 4), 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_STRING_LENGTH_MAX) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading a mapping with a duplicate field. * * \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_duplicate_mapping_field( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "test_uint: 9998\n" "test_uint: 9999\n"; struct target_struct { unsigned test_value_uint; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_value_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (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_UNEXPECTED_EVENT) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading when schema expects mapping field which is not present. * * \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_missing_mapping_field( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "a: 2\n"; struct target_struct { int a; int b; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_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_MAPPING_FIELD_MISSING) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema disallows mapping field. * * \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_unknown_mapping_field( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "wrong_key: 2\n"; struct target_struct { int a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("key", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_KEY) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects sequence, it has too few entries. * * \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_sequence_min_entries( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 1\n" " - 2\n"; struct target_struct { int *a; unsigned a_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->a)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("key", CYAML_FLAG_POINTER, struct target_struct, a, &entry_schema, 3, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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_SEQUENCE_ENTRIES_MIN) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects sequence, it has too many entries. * * \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_sequence_max_entries( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 1\n" " - 2\n" " - 3\n"; struct target_struct { int *a; unsigned a_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->a)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("key", CYAML_FLAG_POINTER, struct target_struct, a, &entry_schema, 2, 2), 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_SEQUENCE_ENTRIES_MAX) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags and finds a mapping inside. * * \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_flags_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - map:\n" " a:\n" " b:\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_STRICT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_UNEXPECTED_EVENT) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects enum, but string is not allowed. * * \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_enum_bad_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "key: fourth\n"; struct target_struct { enum test_enum a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, TEST_ENUM__COUNT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags but YAML has bad flag string. * * \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_flags_bad_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - seventh\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving when schema expects strict enum, but value not allowed. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_strict_enum_bad_value( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; struct target_struct { enum test_enum a; } data = { .a = 876, }; char *buffer = NULL; size_t len = 0; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("key", CYAML_FLAG_STRICT, struct target_struct, a, strings, TEST_ENUM__COUNT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading when schema expects strict enum but YAML has bad string. * * \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_strict_enum_bad_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "key: fourth\n"; struct target_struct { enum test_enum a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("key", CYAML_FLAG_STRICT, struct target_struct, a, strings, TEST_ENUM__COUNT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test saving when schema expects strict flags, but value not allowed. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_schema_strict_flags_bad_value( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; struct target_struct { enum test_flags a; } data = { .a = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | (1 << 9), }; char *buffer = NULL; size_t len = 0; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_STRICT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (buffer != NULL || len != 0) { return ttest_fail(&tc, "Buffer/len not untouched."); } return ttest_pass(&tc); } /** * Test loading when schema expects strict flags but YAML has bad string. * * \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_strict_flags_bad_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key:\n" " - first\n" " - seventh\n"; struct target_struct { enum test_flags a; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_STRICT, struct target_struct, a, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int, but YAML has sequence. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_schema_expect_int_read_seq( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n" " - 90"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("key", CYAML_FLAG_DEFAULT, 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_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int, but YAML ends. * * \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_expect_int_read_end_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key:\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("key", CYAML_FLAG_DEFAULT, 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_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects int, but YAML ends. * * \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_expect_int_read_end_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: |" "..."; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("key", CYAML_FLAG_DEFAULT, 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_LIBYAML_PARSER) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects flags, but YAML has scalar. * * \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_expect_flags_read_scalar( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "key: first\n"; struct target_struct { int value; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("key", CYAML_FLAG_DEFAULT, struct target_struct, value, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects mapping, but YAML has scalar. * * \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_expect_mapping_read_scalar( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: scalar\n"; struct value_s { int a; }; struct target_struct { struct value_s test_value_mapping; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_MAPPING("key", CYAML_FLAG_DEFAULT, struct target_struct, test_value_mapping, test_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading when schema expects sequence, but YAML has scalar. * * \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_expect_sequence_read_scalar( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "key: foo\n"; struct target_struct { int *a; unsigned a_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->a)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("key", CYAML_FLAG_POINTER, struct target_struct, a, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt != NULL) { return ttest_fail(&tc, "Data non-NULL on error."); } return ttest_pass(&tc); } /** * Test loading, with all memory allocation failure at every possible point. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_free_null( ttest_report_ctx_t *report, const cyaml_config_t *config) { ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) { return true; } UNUSED(config); cyaml_mem(NULL, NULL, 0); return ttest_pass(&tc); } /** Context for allocation failure tests. */ struct test_cyaml_mem_ctx { unsigned required; unsigned fail; unsigned current; }; /* * Allocation counter. * * Used to count all allocations made when loading an input. * * \param[in] ctx Allocation context. * \param[in] ptr Pointer to allocation to resize or NULL. * \param[in] size Size to set allocation to. * \return Pointer to new allocation, or NULL on failure. */ static void * test_cyaml_mem_count_allocs( void *ctx, void *ptr, size_t size) { struct test_cyaml_mem_ctx *mem_ctx = ctx; if (size == 0) { free(ptr); return NULL; } mem_ctx->required++; return realloc(ptr, size); } /** * Allocation failure tester. * * Used to make specific allocation fail. * * \param[in] ctx Allocation context. * \param[in] ptr Pointer to allocation to resize or NULL. * \param[in] size Size to set allocation to. * \return Pointer to new allocation, or NULL on failure. */ static void * test_cyaml_mem_fail( void *ctx, void *ptr, size_t size) { struct test_cyaml_mem_ctx *mem_ctx = ctx; if (size == 0) { free(ptr); return NULL; } if (mem_ctx->current == mem_ctx->fail) { return NULL; } mem_ctx->current++; return realloc(ptr, size); } /** * Test loading, with all memory allocation failure at every possible point. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_alloc_oom_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; static const unsigned char yaml[] = "animals:\n" " - kind: cat\n" " sound: meow\n" " position: [ 1, 2, 1]\n" " - kind: snake\n" " sound: hiss\n" " position: [ 3, 1, 0]\n"; struct animal_s { char *kind; char *sound; int *position; }; struct target_struct { struct animal_s **animal; uint32_t animal_count; } *data_tgt = NULL; static const struct cyaml_schema_value position_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field animal_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal_s, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, struct animal_s, sound, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE_FIXED("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 3), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), animal_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animal, &animal_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; struct test_cyaml_mem_ctx mem_ctx = { .required = 0, }; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } /* * First we load the YAML with the counting allocation function, * to find the number of allocations required to load the document. * This is deterministic. */ cfg.mem_fn = test_cyaml_mem_count_allocs; cfg.mem_ctx = &mem_ctx; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (mem_ctx.required == 0) { return ttest_fail(&tc, "There were no allocations."); } /* Now free what was loaded. */ cyaml_free(config, &top_schema, data_tgt, 0); data_tgt = NULL; /* * Now we load the document multiple times, forcing every possible * allocation to fail. */ cfg.mem_fn = test_cyaml_mem_fail; for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; mem_ctx.fail++) { mem_ctx.current = 0; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_OOM) { return ttest_fail(&tc, cyaml_strerror(err)); } /* Now free what was loaded. */ cyaml_free(config, &top_schema, data_tgt, 0); data_tgt = NULL; } return ttest_pass(&tc); } /** * Test loading, with all memory allocation failure at every possible point. * * Uses aliases and anchors, to exercies the event/anchor recording error * paths too. * * \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_alloc_oom_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; cyaml_config_t cfg = *config; static const unsigned char yaml[] = "anchors:\n" " - &a1 {" " kind: cat,\n" " sound: meow,\n" " position: &a2 [ 1, &my_value 2, 1],\n" " flags: &a3 [\n" " first,\n" " &a4 second,\n" " third,\n" " fourth,\n" " ]\n" " }\n" " - kind: snake\n" " sound: &a5 hiss\n" " position: &a6 [ 3, 1, 0]\n" " flags: &a7 [\n" " first,\n" " second,\n" " third,\n" " fourth,\n" " ]\n" "animals:\n" " - *a1\n" " - kind: snake\n" " sound: *a5\n" " position: *a6\n" " flags: *a7\n" " value: *my_value\n"; struct animal_s { char *kind; char *sound; int **position; unsigned position_count; enum test_f *flags; int value; }; struct target_struct { struct animal_s **animal; uint32_t animal_count; } *data_tgt = NULL; static const struct cyaml_schema_value position_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int), }; static const struct cyaml_schema_field animal_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal_s, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, struct animal_s, sound, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_FLAGS("flags", CYAML_FLAG_STRICT | CYAML_FLAG_POINTER, struct animal_s, flags, strings, 4), CYAML_FIELD_INT("value", CYAML_FLAG_OPTIONAL, struct animal_s, value), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), animal_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animal, &animal_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; struct test_cyaml_mem_ctx mem_ctx = { .required = 0, }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } /* * First we load the YAML with the counting allocation function, * to find the number of allocations required to load the document. * This is deterministic. */ cfg.mem_fn = test_cyaml_mem_count_allocs; cfg.mem_ctx = &mem_ctx; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (mem_ctx.required == 0) { return ttest_fail(&tc, "There were no allocations."); } /* Now free what was loaded. */ cyaml_free(config, &top_schema, data_tgt, 0); data_tgt = NULL; /* * Now we load the document multiple times, forcing every possible * allocation to fail. */ cfg.mem_fn = test_cyaml_mem_fail; for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; mem_ctx.fail++) { mem_ctx.current = 0; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_OOM) { return ttest_fail(&tc, cyaml_strerror(err)); } /* Now free what was loaded. */ cyaml_free(config, &top_schema, data_tgt, 0); data_tgt = NULL; } return ttest_pass(&tc); } /** * Test saving, with memory allocation failure at every possible point. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_alloc_oom_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; static const unsigned char yaml[] = "animals:\n" " - kind: cat\n" " sound: meow\n" " position: [ 1, 2, 1]\n" " - kind: snake\n" " sound: hiss\n" " position: [ 3, 1, 0]\n"; struct animal_s { char *kind; char *sound; int *position; }; struct target_struct { struct animal_s **animal; uint32_t animal_count; } *data_tgt = NULL; static const struct cyaml_schema_value position_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field animal_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal_s, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, struct animal_s, sound, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE_FIXED("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 3), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), animal_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animal, &animal_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; struct test_cyaml_mem_ctx mem_ctx = { .required = 0, }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } /* First we load the YAML, so we have something to test saving. */ err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } /* * First we load the YAML with the counting allocation function, * to find the number of allocations required to load the document. * This is deterministic. */ cfg.mem_fn = test_cyaml_mem_count_allocs; cfg.mem_ctx = &mem_ctx; err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data_tgt, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (mem_ctx.required == 0) { return ttest_fail(&tc, "There were no allocations."); } /* Now free what was loaded. */ cyaml_mem(&mem_ctx, buffer, 0); buffer = NULL; len = 0; /* * Now we load the document multiple times, forcing every possible * allocation to fail. */ cfg.mem_fn = test_cyaml_mem_fail; for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; mem_ctx.fail++) { mem_ctx.current = 0; err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data_tgt, 0); if (err != CYAML_ERR_OOM) { return ttest_fail(&tc, cyaml_strerror(err)); } /* Now free what was loaded. */ cyaml_mem(&mem_ctx, buffer, 0); buffer = NULL; len = 0; } return ttest_pass(&tc); } /** * Test loading, with all memory allocation failure at every possible point. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_save_alloc_oom_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; cyaml_config_t cfg = *config; static const unsigned char yaml[] = "animals:\n" " - kind: cat\n" " sound: meow\n" " position: [ 1, 2, 1]\n" " flags:\n" " - first\n" " - second\n" " - third\n" " - fourth\n" " - kind: snake\n" " sound: hiss\n" " position: [ 3, 1, 0]\n" " flags:\n" " - first\n" " - second\n" " - third\n" " - fourth\n"; struct animal_s { char *kind; char *sound; int **position; unsigned position_count; enum test_f *flags; }; struct target_struct { struct animal_s **animal; uint32_t animal_count; } *data_tgt = NULL; static const struct cyaml_schema_value position_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int), }; static const struct cyaml_schema_field animal_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal_s, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, struct animal_s, sound, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_FLAGS("flags", CYAML_FLAG_STRICT | CYAML_FLAG_POINTER, struct animal_s, flags, strings, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), animal_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animal, &animal_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; struct test_cyaml_mem_ctx mem_ctx = { .required = 0, }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } /* First we load the YAML, so we have something to test saving. */ err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } /* * First we load the YAML with the counting allocation function, * to find the number of allocations required to load the document. * This is deterministic. */ cfg.mem_fn = test_cyaml_mem_count_allocs; cfg.mem_ctx = &mem_ctx; err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data_tgt, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (mem_ctx.required == 0) { return ttest_fail(&tc, "There were no allocations."); } /* Now free what was loaded. */ cyaml_mem(&mem_ctx, buffer, 0); buffer = NULL; len = 0; /* * Now we load the document multiple times, forcing every possible * allocation to fail. */ cfg.mem_fn = test_cyaml_mem_fail; for (mem_ctx.fail = 0; mem_ctx.fail < mem_ctx.required; mem_ctx.fail++) { mem_ctx.current = 0; err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data_tgt, 0); if (err != CYAML_ERR_OOM) { return ttest_fail(&tc, cyaml_strerror(err)); } /* Now free what was loaded. */ cyaml_mem(&mem_ctx, buffer, 0); buffer = NULL; len = 0; } return ttest_pass(&tc); } /** * Test loading a flag with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_flag_value_alias( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { unsigned a; unsigned b; }; static const cyaml_strval_t str[] = { { "one", (1 << 0) }, { "two", (1 << 1) }, { "three", (1 << 2) }, { "four", (1 << 3) }, { "five", (1 << 4) }, }; static const unsigned char yaml[] = "a: \n" " - &foo one\n" " - two\n" " - five\n" "b:\n" " - *foo\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("a", CYAML_FLAG_DEFAULT, struct target_struct, a, str, 5), CYAML_FIELD_FLAGS("b", CYAML_FLAG_DEFAULT, struct target_struct, b, str, 5), 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 = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a bitfield with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_bitfield_value_alias_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; static const cyaml_bitdef_t bitvals[] = { { .name = "7", .offset = 0, .bits = 1 }, { .name = "a", .offset = 1, .bits = 2 }, { .name = "b", .offset = 3, .bits = 7 }, { .name = "c", .offset = 10, .bits = 32 }, { .name = "d", .offset = 42, .bits = 8 }, { .name = "e", .offset = 50, .bits = 14 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: &foo 2\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n" "test_bitfield2:\n" " *foo: 1\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n"; struct target_struct { uint64_t test_value_bitfield; uint64_t test_value_bitfield2; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_BITFIELD("test_bitfield2", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield2, bitvals, CYAML_ARRAY_LEN(bitvals)), 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 = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a bitfield with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_bitfield_value_alias_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; static const cyaml_bitdef_t bitvals[] = { { .name = "7", .offset = 0, .bits = 1 }, { .name = "a", .offset = 1, .bits = 2 }, { .name = "b", .offset = 3, .bits = 7 }, { .name = "c", .offset = 10, .bits = 32 }, { .name = "d", .offset = 42, .bits = 8 }, { .name = "e", .offset = 50, .bits = 14 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: &foo 2\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n" "test_bitfield2:\n" " a: *foo\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n"; struct target_struct { uint64_t test_value_bitfield; uint64_t test_value_bitfield2; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_BITFIELD("test_bitfield2", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield2, bitvals, CYAML_ARRAY_LEN(bitvals)), 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 = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a mapping with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_mapping_key_alias( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { char *a; char *b; char *c; char *d; }; static const unsigned char yaml[] = "a: &example b\n" "*example: test\n" "c: meh\n" "d: foo\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct target_struct, b, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("c", CYAML_FLAG_POINTER, struct target_struct, c, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("d", CYAML_FLAG_POINTER, struct target_struct, d, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a mapping with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_mapping_value_alias_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { char *a; char *b; char *c; char *d; }; static const unsigned char yaml[] = "a: 9\n" "b: 90\n" "c: &foo 900\n" "d: *foo\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct target_struct, b, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("c", CYAML_FLAG_POINTER, struct target_struct, c, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("d", CYAML_FLAG_POINTER, struct target_struct, d, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a mapping with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_mapping_value_alias_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { char *a; char *b; char *c; char *d; }; static const unsigned char yaml[] = "a: 9\n" "b: &foo d\n" "c: *d\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct target_struct, b, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("d", CYAML_FLAG_POINTER, struct target_struct, d, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS | CYAML_CFG_IGNORE_UNKNOWN_KEYS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a mapping with an aliased value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_mapping_value_alias_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { char *a; char *b; }; static const unsigned char yaml[] = "a: 9\n" "b: &foo d\n" "c: [a, b, c, *foo]\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct target_struct, b, 0, CYAML_UNLIMITED), CYAML_FIELD_IGNORE("c", CYAML_FLAG_DEFAULT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_NO_ALIAS; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading with an aliased string value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_err_load_invalid_alias( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "test_string_anchor: &foo Hello World!\n" "test_string: *bar\n" "test_int: 9\n"; struct target_struct { char * test_value_string; int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("test_string_anchor", CYAML_FLAG_OPTIONAL), CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading an incomplete alias. * * \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_incomplete_alias( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "anchors:\n" " - &a1 {\n" " a: 777,\n" " b: *a1,\n" " }\n" "test: *a1\n"; struct my_test { int a; char *b; }; struct target_struct { struct my_test test; } *data_tgt = NULL; static const struct cyaml_schema_field inner_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct my_test, a), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct my_test, b, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_MAPPING("test", CYAML_FLAG_DEFAULT, struct target_struct, test, inner_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_ALIAS) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** Structure for log messages checking. */ struct log_check { unsigned string_count_expected; /**< Number of expected log messages. */ const char *const *strings; /**< Array of expected log messages. */ unsigned string_count; /**< Number of log messages captured. */ bool error; /**< Whether a logging error has been found. */ }; /** * Initialise a log check structure for a given log string vector. * * \param[in] lc The log check structure to initialise. * \param[in] strv A string vector containing expected log messages. * \return true on success, false otherwise. */ static inline bool log_check_init( struct log_check *lc, const char *const *strv) { if (lc == NULL) { return false; } lc->string_count_expected = 0; lc->string_count = 0; lc->strings = strv; lc->error = false; while (strv[lc->string_count_expected] != NULL) { lc->string_count_expected++; } return true; } static struct log_check lc; /** * CYAML log function, for checking logs. * * Uses the lc static state. * * \param[in] level Log level of message to log. * \param[in] ctx Client's private logging context. * \param[in] fmt Format string for message to log. * \param[in] args Additional arguments used by fmt. */ static void cyaml_log_check( cyaml_log_t level, void *ctx, const char *fmt, va_list args) { int ret; size_t len; char buffer[128]; (void)(ctx); if (level < CYAML_LOG_ERROR) { /* We only care that critical log messages emerge. */ return; } ret = vsnprintf(buffer, sizeof(buffer), fmt, args); if (ret <= 0) { fprintf(stderr, "TEST ERROR: Logging error\n"); return; } len = (unsigned)ret; if (len >= sizeof(buffer)) { fprintf(stderr, "TEST ERROR: Buffer too small\n"); assert(len < sizeof(buffer)); return; } if (lc.string_count >= lc.string_count_expected) { fprintf(stderr, "ERROR: More log messages than expected\n"); fprintf(stderr, " got: %s", buffer); lc.error |= true; goto out; } if (strcmp(lc.strings[lc.string_count], buffer) != 0) { fprintf(stderr, "ERROR: Unexpected log message\n"); fprintf(stderr, " got: %s", buffer); fprintf(stderr, "expected: %s", lc.strings[lc.string_count]); lc.error |= true; goto out; } out: lc.string_count++; } /** * Check that the logging contains the expected information. * * \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_log( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const char * const expected_log[] = { "Load: Invalid INT value: 'foo'\n", "Load: Backtrace:\n", " in sequence entry '2' (line: 5, column: 26)\n", " in mapping field 'position' (line: 5, column: 17)\n", " in sequence entry '1' (line: 2, column: 5)\n", " in mapping field 'animals' (line: 23, column: 3)\n", NULL }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; cyaml_config_t cfg = *config; static const unsigned char yaml[] = "anchors:\n" " - &a1 {\n" " kind: cat,\n" " sound: meow,\n" " position: &a2 [ 1, &my_value foo, 1],\n" " flags: &a3 [\n" " first,\n" " &a4 second,\n" " third,\n" " fourth,\n" " ]\n" " }\n" " - kind: snake\n" " sound: &a5 hiss\n" " position: &a6 [ 3, 1, 0]\n" " flags: &a7 [\n" " first,\n" " second,\n" " third,\n" " fourth,\n" " ]\n" "animals:\n" " - *a1\n" " - kind: snake\n" " sound: *a5\n" " position: *a6\n" " flags: *a7\n" " value: *my_value\n"; struct animal_s { char *kind; char *sound; int **position; unsigned position_count; enum test_f *flags; int value; }; struct target_struct { struct animal_s **animal; uint32_t animal_count; } *data_tgt = NULL; static const struct cyaml_schema_value position_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int), }; static const struct cyaml_schema_field animal_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal_s, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("sound", CYAML_FLAG_POINTER, struct animal_s, sound, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("position", CYAML_FLAG_POINTER, struct animal_s, position, &position_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_FLAGS("flags", CYAML_FLAG_STRICT | CYAML_FLAG_POINTER, struct animal_s, flags, strings, 4), CYAML_FIELD_INT("value", CYAML_FLAG_OPTIONAL, struct animal_s, value), CYAML_FIELD_END }; static const struct cyaml_schema_value animal_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, **(data_tgt->animal), animal_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animal, &animal_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } log_check_init(&lc, expected_log); cfg.log_fn = cyaml_log_check; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err == CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (lc.error == true) { return ttest_fail(&tc, "Unexpected log message"); } if (lc.string_count_expected > lc.string_count) { return ttest_fail(&tc, "Missing log message(s)"); } return ttest_pass(&tc); } /** * Run the CYAML error unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool errs_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DEFAULT, }; /* Since we expect loads of error logging for these tests, * suppress log output if required log level is greater * than \ref CYAML_LOG_INFO. */ if (log_level > CYAML_LOG_INFO) { config.log_fn = NULL; } ttest_heading(rc, "Bad parameter tests"); pass &= test_err_load_null_data(rc, &config); pass &= test_err_save_null_data(rc, &config); pass &= test_err_load_null_config(rc, &config); pass &= test_err_save_null_config(rc, &config); pass &= test_err_load_null_mem_fn(rc, &config); pass &= test_err_save_null_mem_fn(rc, &config); pass &= test_err_load_null_schema(rc, &config); pass &= test_err_save_null_schema(rc, &config); pass &= test_err_load_schema_top_level_non_pointer(rc, &config); pass &= test_err_save_schema_top_level_non_pointer(rc, &config); pass &= test_err_load_schema_top_level_sequence_no_count(rc, &config); pass &= test_err_load_schema_top_level_not_sequence_count(rc, &config); pass &= test_err_save_schema_top_level_not_sequence_count(rc, &config); ttest_heading(rc, "Bad schema tests"); pass &= test_err_load_schema_bad_type(rc, &config); pass &= test_err_save_schema_bad_type(rc, &config); pass &= test_err_load_schema_bad_bitfield(rc, &config); pass &= test_err_save_schema_bad_bitfield(rc, &config); pass &= test_err_load_schema_string_min_max(rc, &config); pass &= test_err_load_schema_bad_data_size_1(rc, &config); pass &= test_err_load_schema_bad_data_size_2(rc, &config); pass &= test_err_load_schema_bad_data_size_3(rc, &config); pass &= test_err_load_schema_bad_data_size_4(rc, &config); pass &= test_err_load_schema_bad_data_size_5(rc, &config); pass &= test_err_load_schema_bad_data_size_6(rc, &config); pass &= test_err_load_schema_bad_data_size_7(rc, &config); pass &= test_err_load_schema_bad_data_size_8(rc, &config); pass &= test_err_load_schema_bad_data_size_9(rc, &config); pass &= test_err_save_schema_bad_data_size_1(rc, &config); pass &= test_err_save_schema_bad_data_size_2(rc, &config); pass &= test_err_save_schema_bad_data_size_3(rc, &config); pass &= test_err_save_schema_bad_data_size_4(rc, &config); pass &= test_err_save_schema_bad_data_size_5(rc, &config); pass &= test_err_save_schema_bad_data_size_6(rc, &config); pass &= test_err_save_schema_bad_data_size_7(rc, &config); pass &= test_err_save_schema_bad_data_size_8(rc, &config); pass &= test_err_load_schema_sequence_min_max(rc, &config); pass &= test_err_save_schema_sequence_min_max(rc, &config); pass &= test_err_load_schema_bad_data_size_float(rc, &config); pass &= test_err_load_schema_sequence_in_sequence(rc, &config); pass &= test_err_save_schema_sequence_in_sequence(rc, &config); ttest_heading(rc, "YAML / schema mismatch: bad values"); pass &= test_err_load_non_scalar_mapping_key(rc, &config); pass &= test_err_load_schema_invalid_value_uint(rc, &config); pass &= test_err_load_schema_invalid_value_string(rc, &config); pass &= test_err_load_schema_invalid_value_flags_1(rc, &config); pass &= test_err_load_schema_invalid_value_flags_2(rc, &config); pass &= test_err_load_schema_invalid_value_flags_3(rc, &config); pass &= test_err_save_schema_invalid_value_null_ptr(rc, &config); pass &= test_err_load_schema_invalid_value_int_junk(rc, &config); pass &= test_err_load_schema_invalid_value_uint_junk(rc, &config); pass &= test_err_load_schema_invalid_value_flags_junk(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_1(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_2(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_3(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_4(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_5(rc, &config); pass &= test_err_load_schema_invalid_value_bitfield_6(rc, &config); pass &= test_err_load_schema_invalid_value_float_junk(rc, &config); pass &= test_err_load_schema_invalid_value_double_junk(rc, &config); pass &= test_err_load_schema_invalid_value_int_range_1(rc, &config); pass &= test_err_load_schema_invalid_value_int_range_2(rc, &config); pass &= test_err_load_schema_invalid_value_int_range_3(rc, &config); pass &= test_err_load_schema_invalid_value_int_range_4(rc, &config); pass &= test_err_load_schema_invalid_value_int_range_5(rc, &config); pass &= test_err_load_schema_invalid_value_uint_range_1(rc, &config); pass &= test_err_load_schema_invalid_value_uint_range_2(rc, &config); pass &= test_err_load_schema_invalid_value_uint_range_3(rc, &config); pass &= test_err_load_schema_invalid_value_uint_range_4(rc, &config); pass &= test_err_load_schema_invalid_value_uint_range_5(rc, &config); pass &= test_err_load_schema_invalid_value_float_range1(rc, &config); pass &= test_err_load_schema_invalid_value_float_range2(rc, &config); pass &= test_err_load_schema_invalid_value_float_range3(rc, &config); pass &= test_err_load_schema_invalid_value_float_range4(rc, &config); pass &= test_err_load_schema_invalid_value_double_range1(rc, &config); pass &= test_err_load_schema_invalid_value_double_range2(rc, &config); pass &= test_err_load_schema_invalid_value_float_invalid(rc, &config); pass &= test_err_load_schema_invalid_value_double_invalid(rc, &config); ttest_heading(rc, "YAML / schema mismatch: Test integer limits"); pass &= test_err_load_schema_invalid_value_int8_limit_neg(rc, &config); pass &= test_err_load_schema_invalid_value_int8_limit_pos(rc, &config); pass &= test_err_load_schema_invalid_value_int16_limit_neg(rc, &config); pass &= test_err_load_schema_invalid_value_int16_limit_pos(rc, &config); pass &= test_err_load_schema_invalid_value_int32_limit_neg(rc, &config); pass &= test_err_load_schema_invalid_value_int32_limit_pos(rc, &config); pass &= test_err_load_schema_invalid_value_int64_limit_neg(rc, &config); pass &= test_err_load_schema_invalid_value_int64_limit_pos(rc, &config); ttest_heading(rc, "YAML / schema mismatch: string lengths"); pass &= test_err_load_schema_string_min_length(rc, &config); pass &= test_err_load_schema_string_max_length(rc, &config); ttest_heading(rc, "YAML / schema mismatch: mapping fields"); pass &= test_err_load_schema_missing_mapping_field(rc, &config); pass &= test_err_load_schema_unknown_mapping_field(rc, &config); pass &= test_err_load_schema_duplicate_mapping_field(rc, &config); ttest_heading(rc, "YAML / schema mismatch: sequence counts"); pass &= test_err_load_schema_sequence_min_entries(rc, &config); pass &= test_err_load_schema_sequence_max_entries(rc, &config); ttest_heading(rc, "YAML / schema mismatch: bad flags/enum strings"); pass &= test_err_load_schema_flags_mapping(rc, &config); pass &= test_err_load_schema_enum_bad_string(rc, &config); pass &= test_err_load_schema_flags_bad_string(rc, &config); pass &= test_err_save_schema_strict_enum_bad_value(rc, &config); pass &= test_err_load_schema_strict_enum_bad_string(rc, &config); pass &= test_err_save_schema_strict_flags_bad_value(rc, &config); pass &= test_err_load_schema_strict_flags_bad_string(rc, &config); ttest_heading(rc, "YAML / schema mismatch: expected value type tests"); pass &= test_err_load_schema_expect_int_read_seq(rc, &config); pass &= test_err_load_schema_expect_int_read_end_1(rc, &config); pass &= test_err_load_schema_expect_int_read_end_2(rc, &config); pass &= test_err_load_schema_expect_flags_read_scalar(rc, &config); pass &= test_err_load_schema_expect_mapping_read_scalar(rc, &config); pass &= test_err_load_schema_expect_sequence_read_scalar(rc, &config); ttest_heading(rc, "Memory allocation handling tests"); pass &= test_err_load_log(rc, &config); pass &= test_err_free_null(rc, &config); pass &= test_err_load_alloc_oom_1(rc, &config); pass &= test_err_load_alloc_oom_2(rc, &config); pass &= test_err_save_alloc_oom_1(rc, &config); pass &= test_err_save_alloc_oom_2(rc, &config); ttest_heading(rc, "Alias tests"); pass &= test_err_load_invalid_alias(rc, &config); pass &= test_err_load_incomplete_alias(rc, &config); pass &= test_err_load_flag_value_alias(rc, &config); pass &= test_err_load_mapping_key_alias(rc, &config); pass &= test_err_load_mapping_value_alias_1(rc, &config); pass &= test_err_load_mapping_value_alias_2(rc, &config); pass &= test_err_load_mapping_value_alias_3(rc, &config); pass &= test_err_load_bitfield_value_alias_1(rc, &config); pass &= test_err_load_bitfield_value_alias_2(rc, &config); return pass; } libcyaml-1.4.1/test/units/file.c000066400000000000000000000272351443462360400165340ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2021 Michael Drake */ #include #include #include #include #include "ttest.h" #include "test.h" /** * Unit test context data. */ typedef struct test_data { cyaml_data_t **data; unsigned *seq_count; const struct cyaml_config *config; const struct cyaml_schema_value *schema; } test_data_t; /** * Common clean up function to free data loaded by tests. * * \param[in] data The unit test context data. */ static void cyaml_cleanup(void *data) { struct test_data *td = data; unsigned seq_count = 0; if (td->seq_count != NULL) { seq_count = *(td->seq_count); } if (td->data != NULL) { cyaml_free(td->config, td->schema, *(td->data), seq_count); } } /** * Test loading a non-existent file. * * \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_file_load_bad_path( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { char *cakes; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_file("/cyaml/path/shouldn't/exist.yaml", config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_FILE_OPEN) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading a non-existent file. * * \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_file_save_bad_path( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { char *cakes; } *data = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = NULL, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_file("/cyaml/path/shouldn't/exist.yaml", config, &top_schema, data, 0); if (err != CYAML_ERR_FILE_OPEN) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading the basic YAML file. * * \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_file_load_basic( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct animal { char *kind; char **sounds; unsigned sounds_count; }; struct target_struct { struct animal *animals; unsigned animals_count; char **cakes; unsigned cakes_count; } *data_tgt = NULL; static const struct cyaml_schema_value sounds_entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field animal_mapping_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER, struct animal, sounds, &sounds_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value animals_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct animal, animal_mapping_schema), }; static const struct cyaml_schema_value cakes_entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animals, &animals_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER, struct target_struct, cakes, &cakes_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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_file("test/data/basic.yaml", config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading and then saving the basic YAML file. * * \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_file_load_save_basic( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct animal { char *kind; char **sounds; unsigned sounds_count; }; struct target_struct { struct animal *animals; unsigned animals_count; char **cakes; unsigned cakes_count; } *data_tgt = NULL; static const struct cyaml_schema_value sounds_entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field animal_mapping_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER, struct animal, sounds, &sounds_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value animals_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct animal, animal_mapping_schema), }; static const struct cyaml_schema_value cakes_entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animals, &animals_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER, struct target_struct, cakes, &cakes_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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_file("test/data/basic.yaml", config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } err = cyaml_save_file("build/load_save.yaml", config, &top_schema, data_tgt, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test loading the basic YAML file, with a mismatching schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_file_load_basic_invalid( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct animal { char *kind; int *sounds; unsigned sounds_count; }; struct target_struct { struct animal *animals; unsigned animals_count; char **cakes; unsigned cakes_count; } *data_tgt = NULL; static const struct cyaml_schema_value sounds_entry_schema = { /* The data has a string, but we're expecting int here. */ CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field animal_mapping_schema[] = { CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER, struct animal, kind, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER, struct animal, sounds, &sounds_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value animals_entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct animal, animal_mapping_schema), }; static const struct cyaml_schema_value cakes_entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER, struct target_struct, animals, &animals_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER, struct target_struct, cakes, &cakes_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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_file("test/data/basic.yaml", config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_ERR_INVALID_VALUE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test saving to a file when an erro occurs. * * \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_file_save_basic_invalid( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct target_struct { int value; } data = { .value = 9, }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "key", .value = { .type = CYAML_INT, .flags = CYAML_FLAG_DEFAULT, .data_size = 0, }, .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 = { .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_file("build/save.yaml", config, &top_schema, &data, 0); if (err != CYAML_ERR_INVALID_DATA_SIZE) { return ttest_fail(&tc, cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Run the YAML file tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool file_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DEFAULT, }; ttest_heading(rc, "File loading tests"); pass &= test_file_load_basic(rc, &config); pass &= test_file_load_save_basic(rc, &config); /* Since we expect loads of error logging for these tests, * suppress log output if required log level is greater * than \ref CYAML_LOG_INFO. */ if (log_level > CYAML_LOG_INFO) { config.log_fn = NULL; } pass &= test_file_load_bad_path(rc, &config); pass &= test_file_save_bad_path(rc, &config); pass &= test_file_load_basic_invalid(rc, &config); pass &= test_file_save_basic_invalid(rc, &config); return pass; } libcyaml-1.4.1/test/units/free.c000066400000000000000000000074521443462360400165350ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ #include #include #include #include #include "ttest.h" #include "test.h" /** Macro to squash unused variable compiler warnings. */ #define UNUSED(_x) ((void)(_x)) /** * Test cyaml_free with NULL data. * * \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_free_null_data( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_err_t err; struct target_struct { int test_value_int; }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; err = cyaml_free(config, &top_schema, NULL, 0); if (err != CYAML_OK) { return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test cyaml_free with NULL config. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_free_null_config( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; UNUSED(config); err = cyaml_free(NULL, NULL, NULL, 0); if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) { return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test cyaml_free with NULL memory allocation function. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_free_null_mem_fn( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_err_t err; cyaml_config_t cfg = *config; ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; cfg.mem_fn = NULL; err = cyaml_free(&cfg, NULL, NULL, 0); if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) { return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Test cyaml_free with NULL schema. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_free_null_schema( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; err = cyaml_free(config, NULL, NULL, 0); if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) { return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err)); } return ttest_pass(&tc); } /** * Run the CYAML freeing unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool free_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DEFAULT, }; ttest_heading(rc, "Free tests"); pass &= test_free_null_data(rc, &config); pass &= test_free_null_mem_fn(rc, &config); pass &= test_free_null_config(rc, &config); pass &= test_free_null_schema(rc, &config); return pass; } libcyaml-1.4.1/test/units/load.c000066400000000000000000006247541443462360400165450ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ #include #include #include #include #include #include #include "../../src/data.h" #include "ttest.h" #include "test.h" /** Macro to squash unused variable compiler warnings. */ #define UNUSED(_x) ((void)(_x)) /** Helper macro to count bytes of YAML input data. */ #define YAML_LEN(_y) (sizeof(_y) - 1) /** * Unit test context data. */ typedef struct test_data { cyaml_data_t **data; unsigned *seq_count; const struct cyaml_config *config; const struct cyaml_schema_value *schema; } test_data_t; /** * Common clean up function to free data loaded by tests. * * \param[in] data The unit test context data. */ static void cyaml_cleanup(void *data) { struct test_data *td = data; unsigned seq_count = 0; if (td->seq_count != NULL) { seq_count = *(td->seq_count); } if (td->data != NULL) { cyaml_free(td->config, td->schema, *(td->data), seq_count); } } /** * Test loading a positive signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_int_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { int value = 90; static const unsigned char yaml[] = "test_int: 90\n"; struct target_struct { int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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"); } return ttest_pass(&tc); } /** * Test loading a negative signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_int_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { int value = -77; static const unsigned char yaml[] = "test_int: -77\n"; struct target_struct { int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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"); } return ttest_pass(&tc); } /** * Test loading a pointer to a positive signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_int_pos_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { int value = 90; static const unsigned char yaml[] = "test_int: 90\n"; struct target_struct { int *test_value_int; } *data_tgt = NULL; 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_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"); } return ttest_pass(&tc); } /** * Test loading a pointer to a negative signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_int_neg_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { int value = -77; static const unsigned char yaml[] = "test_int: -77\n"; struct target_struct { int *test_value_int; } *data_tgt = NULL; 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_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"); } return ttest_pass(&tc); } /** * Test loading an unsigned integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned value = 9999; static const unsigned char yaml[] = "test_uint: 9999\n"; struct target_struct { unsigned test_value_uint; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_value_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (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_uint != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a pointer to an unsigned integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_uint_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned value = 9999; static const unsigned char yaml[] = "test_uint: 9999\n"; struct target_struct { unsigned *test_value_uint; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT_PTR("test_uint", CYAML_FLAG_POINTER, struct target_struct, test_value_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (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_uint != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a floating point value as a float. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_float( ttest_report_ctx_t *report, const cyaml_config_t *config) { float value = 3.14159f; static const unsigned char yaml[] = "test_fp: 3.14159\n"; struct target_struct { float test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %f, got: %f", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a floating point value as a float. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_float_underflow( ttest_report_ctx_t *report, const cyaml_config_t *config) { float value = 1.55331e-40f; static const unsigned char yaml[] = "test_fp: 1.55331e-40\n"; struct target_struct { float test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %e, got: %e", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a floating point value as a pointer to float. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_float_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { float value = 3.14159f; static const unsigned char yaml[] = "test_fp: 3.14159\n"; struct target_struct { float *test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT_PTR("test_fp", CYAML_FLAG_POINTER, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %lf, got: %lf", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a floating point value as a double. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_double( ttest_report_ctx_t *report, const cyaml_config_t *config) { double value = 3.14159; static const unsigned char yaml[] = "test_fp: 3.14159\n"; struct target_struct { double test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %lf, got: %lf", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a floating point value as a double. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_double_underflow( ttest_report_ctx_t *report, const cyaml_config_t *config) { double value = 1.79769e+308; static const unsigned char yaml[] = "test_fp: 1.79769e+309\n"; struct target_struct { double test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_fp", CYAML_FLAG_DEFAULT, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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; } value *= 10; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %lf, got: %lf", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a floating point value as a pointer to double. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_double_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { double value = 3.14159; static const unsigned char yaml[] = "test_fp: 3.14159\n"; struct target_struct { double *test_value_fp; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT_PTR("test_fp", CYAML_FLAG_POINTER, struct target_struct, test_value_fp), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_fp != value) { return ttest_fail(&tc, "Incorrect value: " "expected: %lf, got: %lf", value, data_tgt->test_value_fp); } return ttest_pass(&tc); } /** * Test loading a boolean value (true). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_bool_true( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool value = true; static const unsigned char yaml[] = "test_bool: true\n"; struct target_struct { unsigned test_value_bool; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_bool != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a boolean value (false). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_bool_false( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool value = false; static const unsigned char yaml[] = "test_bool: false\n"; struct target_struct { unsigned test_value_bool; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_bool != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a pointer to a boolean value (true). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_bool_true_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool value = true; static const unsigned char yaml[] = "test_bool: true\n"; struct target_struct { unsigned *test_value_bool; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL_PTR("test_bool", CYAML_FLAG_POINTER, struct target_struct, test_value_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_bool != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a pointer to a boolean value (false). * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_bool_false_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool value = false; static const unsigned char yaml[] = "test_bool: false\n"; struct target_struct { unsigned *test_value_bool; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL_PTR("test_bool", CYAML_FLAG_POINTER, struct target_struct, test_value_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_bool != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading an enumeration. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_enum( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, } value = TEST_ENUM_SECOND; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "test_enum: second\n"; struct target_struct { enum test_enum test_value_enum; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, struct target_struct, test_value_enum, strings, TEST_ENUM__COUNT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_enum != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a pointer to an enumeration. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_enum_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, } value = TEST_ENUM_SECOND; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "test_enum: second\n"; struct target_struct { enum test_enum *test_value_enum; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM_PTR("test_enum", CYAML_FLAG_POINTER, struct target_struct, test_value_enum, strings, TEST_ENUM__COUNT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_enum != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a sparse enumeration. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_enum_sparse( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST = 3, TEST_ENUM_SECOND = 77, TEST_ENUM_THIRD = 183, TEST_ENUM_FOURTH = 9900, } value = TEST_ENUM_SECOND; static const cyaml_strval_t strings[] = { { "first", TEST_ENUM_FIRST }, { "second", TEST_ENUM_SECOND }, { "third", TEST_ENUM_THIRD }, { "fourth", TEST_ENUM_FOURTH }, }; static const unsigned char yaml[] = "test_enum: second\n"; struct target_struct { enum test_enum test_value_enum; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, struct target_struct, test_value_enum, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_enum != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading an enumeration with numerical fallback. * * \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_enum_fallback( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST = 3, TEST_ENUM_SECOND = 77, TEST_ENUM_THIRD = 183, TEST_ENUM_FOURTH = 9900, } value = TEST_ENUM_SECOND; static const cyaml_strval_t strings[] = { { "first", TEST_ENUM_FIRST }, { "second", TEST_ENUM_SECOND }, { "third", TEST_ENUM_THIRD }, { "fourth", TEST_ENUM_FOURTH }, }; static const unsigned char yaml[] = "test_enum: 77\n"; struct target_struct { enum test_enum test_value_enum; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, struct target_struct, test_value_enum, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_enum != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a string to a character array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *value = "Hello World!"; static const unsigned char yaml[] = "test_string: Hello World!\n"; struct target_struct { char test_value_string[50]; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING("test_string", CYAML_FLAG_DEFAULT, struct target_struct, test_value_string, 0), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, value) != 0) { fprintf(stderr, "expected: %s\n", value); for (unsigned i = 0; i < strlen(value) + 1; i++) { fprintf(stderr, "%2.2x ", value[i]); } fprintf(stderr, "\n"); fprintf(stderr, " got: %s\n", data_tgt->test_value_string); for (unsigned i = 0; i < sizeof(data_tgt->test_value_string); i++) { fprintf(stderr, "%2.2x ", value[i]); } fprintf(stderr, "\n"); return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a string to an allocated char pointer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *value = "null"; static const unsigned char yaml[] = "test_string: null\n"; struct target_struct { char * test_value_string; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, value) != 0) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading an empty string to an allocated char pointer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_string_ptr_empty( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *value = ""; static const unsigned char yaml[] = "test_string:\n"; struct target_struct { char * test_value_string; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, value) != 0) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a null string to an allocated nullable char pointer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_string_ptr_null_str( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *value = NULL; static const unsigned char yaml[] = "test_string: null\n"; struct target_struct { char * test_value_string; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER_NULL_STR, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_string != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading an empty string to an allocated nullable char pointer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_string_ptr_null_empty( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *value = NULL; static const unsigned char yaml[] = "test_string:\n"; struct target_struct { char * test_value_string; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER_NULL, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_string != value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading an ignored value with descendants. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_ignore_deep( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "ignore:\n" " foo: bar\n" " bar:\n" " - 1\n" " - 2\n"; struct target_struct { bool foo; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("ignore", CYAML_FLAG_DEFAULT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 an ignored value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_ignore_scalar( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "ignore: foo\n"; struct target_struct { bool foo; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("ignore", CYAML_FLAG_DEFAULT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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. * * \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_flags( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char yaml[] = "test_flags:\n" " - second\n" " - fifth\n" " - 1024\n"; struct target_struct { enum test_flags test_value_flags; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, struct target_struct, test_value_flags, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_flags != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_flags); } return ttest_pass(&tc); } /** * Test loading a pointer to a flag word. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_flags_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char yaml[] = "test_flags:\n" " - second\n" " - fifth\n" " - 1024\n"; struct target_struct { enum test_flags *test_value_flags; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS_PTR("test_flags", CYAML_FLAG_POINTER, struct target_struct, test_value_flags, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_flags != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_flags); } return ttest_pass(&tc); } /** * Test loading an empty flag word. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_flags_empty( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), } value = TEST_FLAGS_NONE; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char yaml[] = "test_flags: []\n"; struct target_struct { enum test_flags test_value_flags; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, struct target_struct, test_value_flags, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_flags != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_flags); } return ttest_pass(&tc); } /** * Test loading a sparse flag word. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_flags_sparse( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 4), TEST_FLAGS_THIRD = (1 << 7), TEST_FLAGS_FOURTH = (1 << 11), TEST_FLAGS_FIFTH = (1 << 14), TEST_FLAGS_SIXTH = (1 << 20), } value = TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char yaml[] = "test_flags:\n" " - second\n" " - fifth\n"; struct target_struct { enum test_flags test_value_flags; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, struct target_struct, test_value_flags, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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_flags != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_flags); } return ttest_pass(&tc); } /** * Test loading a bitfield. * * \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_bitfield( ttest_report_ctx_t *report, const cyaml_config_t *config) { uint64_t value = 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 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: 0x7\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n"; struct target_struct { uint64_t test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), 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_bitfield != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_bitfield); } return ttest_pass(&tc); } /** * Test loading a pointer to a bitfield. * * \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_bitfield_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { uint64_t value = 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 }, }; static const unsigned char yaml[] = "test_bitfield:\n" " a: 0x7\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n"; struct target_struct { uint64_t *test_value_bitfield; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD_PTR("test_bitfield", CYAML_FLAG_POINTER, struct target_struct, test_value_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), 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_bitfield != value) { return ttest_fail(&tc, "Incorrect value: " "expected: 0x%x, got: 0x%x\n", value, data_tgt->test_value_bitfield); } return ttest_pass(&tc); } /** * Test loading a mapping, to an internal structure. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } value; static const unsigned char yaml[] = "mapping:\n" " a: 123\n" " b: 9999\n"; struct target_struct { struct value_s test_value_mapping; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_MAPPING("mapping", CYAML_FLAG_DEFAULT, struct target_struct, test_value_mapping, test_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; memset(&value, 0, sizeof(value)); value.a = 123; value.b = 9999; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (memcmp(&data_tgt->test_value_mapping, &value, sizeof(value)) != 0) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a mapping, to an allocated structure. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } value; static const unsigned char yaml[] = "mapping:\n" " a: 123\n" " b: 9999\n"; struct target_struct { struct value_s *test_value_mapping; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_MAPPING_PTR("mapping", CYAML_FLAG_POINTER, struct target_struct, test_value_mapping, test_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; memset(&value, 0, sizeof(value)); value.a = 123; value.b = 9999; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (memcmp(data_tgt->test_value_mapping, &value, sizeof(value)) != 0) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a sequence of integers into an int[]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of enums into an enum test_enum[]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_enum( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, } ref[] = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "sequence:\n" " - first\n" " - second\n" " - third\n"; struct target_struct { enum test_enum seq[CYAML_ARRAY_LEN(ref)]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, *(data_tgt->seq), strings, TEST_ENUM__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of unsigned integers into an unsigned[]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; static const unsigned char yaml[] = "sequence:\n" " - 99999\n" " - 99998\n" " - 99997\n" " - 99996\n" " - 99995\n" " - 99994\n"; struct target_struct { unsigned seq[CYAML_ARRAY_LEN(ref)]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of boolean values into an bool[]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_bool( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool ref[] = { true, false, true, false, true, false, true, false }; static const unsigned char yaml[] = "sequence:\n" " - true\n" " - false\n" " - yes\n" " - no\n" " - enable\n" " - disable\n" " - 1\n" " - 0\n"; struct target_struct { bool seq[CYAML_ARRAY_LEN(ref)]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of flag sequences into an array of flag words. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_flags( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), } ref[3] = { TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, TEST_FLAGS_FIRST, TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH }; #define TEST_FLAGS__COUNT 6 static const cyaml_strval_t strings[TEST_FLAGS__COUNT] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "sequence:\n" " - - second\n" " - fifth\n" " - 1024\n" " - - first\n" " - - fourth\n" " - sixth\n"; struct target_struct { enum test_flags seq[3]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data_tgt->seq), strings, TEST_FLAGS__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(data_tgt->seq)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of strings into an array of char[7]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *ref[] = { "This", "is", "merely", "a", "test", }; static const unsigned char yaml[] = "sequence:\n" " - This\n" " - is\n" " - merely\n" " - a\n" " - test\n"; struct target_struct { char seq[5][7]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data_tgt->seq), 0, 6), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (strcmp(data_tgt->seq[i], ref[i]) != 0) { return ttest_fail(&tc, "Incorrect value (i=%u)", i); } } return ttest_pass(&tc); } /** * Test loading a sequence of strings into an array of allocated strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *ref[] = { "This", "is", "merely", "a", "test", }; static const unsigned char yaml[] = "sequence:\n" " - This\n" " - is\n" " - merely\n" " - a\n" " - test\n"; struct target_struct { char *seq[5]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data_tgt->seq), 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (strcmp(data_tgt->seq[i], ref[i]) != 0) { return ttest_fail(&tc, "Incorrect value (i=%u)", i); } } return ttest_pass(&tc); } /** * Test loading a sequence of mappings into an array of structures. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } ref[3]; static const unsigned char yaml[] = "sequence:\n" " - a: 123\n" " b: 9999\n" " - a: 4000\n" " b: 62000\n" " - a: 1\n" " b: 765\n"; struct target_struct { struct value_s seq[3]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(data_tgt->seq)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; memset(ref, 0, sizeof(ref)); ref[0].a = 123; ref[0].b = 9999; ref[1].a = 4000; ref[1].b = 62000; ref[2].a = 1; ref[2].b = 765; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } if (memcmp(data_tgt->seq, ref, sizeof(ref)) != 0) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading a sequence of mappings into an array of pointers to structs. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } ref[3] = { { .a = 123, .b = 9999, }, { .a = 4000, .b = 62000, }, { .a = 1, .b = 765, }, }; static const unsigned char yaml[] = "sequence:\n" " - a: 123\n" " b: 9999\n" " - a: 4000\n" " b: 62000\n" " - a: 1\n" " b: 765\n"; struct target_struct { struct value_s *seq[3]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(data_tgt->seq)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (ref[i].a != data_tgt->seq[i]->a) { return ttest_fail(&tc, "Incorrect value"); } if (ref[i].b != data_tgt->seq[i]->b) { return ttest_fail(&tc, "Incorrect value"); } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of int into int[4][3]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_sequence_fixed_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int seq[4][3]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data_tgt->seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, **(data_tgt->seq), &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j][i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j][i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of int into int*[4]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_sequence_fixed_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int *seq[4]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data_tgt->seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_POINTER, **(data_tgt->seq), &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j][i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j][i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of int into one-dimensional int[]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_sequence_fixed_flat_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int seq[12]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, int, &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_DEFAULT, .data_size = sizeof(int[3]), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, } }, .data_offset = offsetof(struct target_struct, seq), .count_size = sizeof(data_tgt->seq_count), .count_offset = offsetof(struct target_struct, seq_count), }, CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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)); } /* Note: count is count of entries of the outer sequence entries, * so, 4, not 12. */ if (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j * CYAML_ARRAY_LEN(*ref) + i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test loading a sequence of integers to allocated int* array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of enums to allocated enum test_enum* array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_enum( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, } ref[] = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char yaml[] = "sequence:\n" " - first\n" " - second\n" " - third\n"; struct target_struct { enum test_enum *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, *(data_tgt->seq), strings, TEST_ENUM__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of unsigned integers to allocated unsigned* array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; static const unsigned char yaml[] = "sequence:\n" " - 99999\n" " - 99998\n" " - 99997\n" " - 99996\n" " - 99995\n" " - 99994\n"; struct target_struct { unsigned *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of boolean values to allocated bool* array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_bool( ttest_report_ctx_t *report, const cyaml_config_t *config) { bool ref[] = { true, false, true, false, true, false, true, false }; static const unsigned char yaml[] = "sequence:\n" " - true\n" " - false\n" " - yes\n" " - no\n" " - enable\n" " - disable\n" " - 1\n" " - 0\n"; struct target_struct { bool *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of flag sequences to allocated flag words array. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_flags( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), } ref[3] = { TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, TEST_FLAGS_FIRST, TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH }; #define TEST_FLAGS__COUNT 6 static const cyaml_strval_t strings[TEST_FLAGS__COUNT] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, { "fifth", (1 << 4) }, { "sixth", (1 << 5) }, }; static const unsigned char yaml[] = "sequence:\n" " - - second\n" " - fifth\n" " - 1024\n" " - - first\n" " - - fourth\n" " - sixth\n"; struct target_struct { enum test_flags *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data_tgt->seq), strings, TEST_FLAGS__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of strings to allocated array of char[7]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *ref[] = { "This", "is", "merely", "a", "test", }; static const unsigned char yaml[] = "sequence:\n" " - This\n" " - is\n" " - merely\n" " - a\n" " - test\n"; struct target_struct { char (*seq)[7]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data_tgt->seq), 0, 6), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (strcmp(data_tgt->seq[i], ref[i]) != 0) { return ttest_fail(&tc, "Incorrect value (i=%u)", i); } } return ttest_pass(&tc); } /** * Test loading a sequence of strings to allocated array of * allocated strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *ref[] = { "This", "is", "merely", "a", "test", }; static const unsigned char yaml[] = "sequence:\n" " - This\n" " - is\n" " - merely\n" " - a\n" " - test\n"; struct target_struct { char **seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data_tgt->seq), 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (strcmp(data_tgt->seq[i], ref[i]) != 0) { return ttest_fail(&tc, "Incorrect value (i=%u)", i); } } return ttest_pass(&tc); } /** * Test loading a sequence of mappings to allocated array mapping structs. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } ref[3] = { { .a = 123, .b = 9999, }, { .a = 4000, .b = 62000, }, { .a = 1, .b = 765, }, }; static const unsigned char yaml[] = "sequence:\n" " - a: 123\n" " b: 9999\n" " - a: 4000\n" " b: 62000\n" " - a: 1\n" " b: 765\n"; struct target_struct { struct value_s *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (ref[i].a != data_tgt->seq[i].a) { return ttest_fail(&tc, "Incorrect value"); } if (ref[i].b != data_tgt->seq[i].b) { return ttest_fail(&tc, "Incorrect value"); } } return ttest_pass(&tc); } /** * Test loading a sequence of mappings to allocated array of pointers to * mapping structs. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } ref[3] = { { .a = 123, .b = 9999, }, { .a = 4000, .b = 62000, }, { .a = 1, .b = 765, }, }; static const unsigned char yaml[] = "sequence:\n" " - a: 123\n" " b: 9999\n" " - a: 4000\n" " b: 62000\n" " - a: 1\n" " b: 765\n"; struct target_struct { struct value_s **seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (ref[i].a != data_tgt->seq[i]->a) { return ttest_fail(&tc, "Incorrect value"); } if (ref[i].b != data_tgt->seq[i]->b) { return ttest_fail(&tc, "Incorrect value"); } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of integers to allocated array * of int[3]. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_sequence_fixed_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int (*seq)[3]; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, int, &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j][i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j][i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of integers to allocated array * of allocated arrays of integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_sequence_fixed_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int **seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_POINTER, int, &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j][i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j][i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test loading a sequence of sequences of integers a one-dimensional allocated * array of integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_ptr_sequence_fixed_flat_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char yaml[] = "sequence:\n" " - [ 1, 2, 3 ]\n" " - [ 4, 5, 6 ]\n" " - [ 7, 8, 9 ]\n" " - [ 10, 11, 12 ]\n"; struct target_struct { int *seq; uint32_t seq_count; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, int, &entry_schema_int, CYAML_ARRAY_LEN(*ref)), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(int[3]), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, } }, .data_offset = offsetof(struct target_struct, seq), .count_size = sizeof(data_tgt->seq_count), .count_offset = offsetof(struct target_struct, seq_count), }, CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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)); } /* Note: count is count of entries of the outer sequence entries, * so, 4, not 12. */ if (CYAML_ARRAY_LEN(ref) != data_tgt->seq_count) { return ttest_fail(&tc, "Incorrect sequence count: " "expected %u, got %u", CYAML_ARRAY_LEN(ref), data_tgt->seq_count); } for (unsigned j = 0; j < CYAML_ARRAY_LEN(ref); j++) { for (unsigned i = 0; i < CYAML_ARRAY_LEN(*ref); i++) { if (data_tgt->seq[j * CYAML_ARRAY_LEN(*ref) + i] != ref[j][i]) { return ttest_fail(&tc, "Incorrect value " "(i=%u, j=%u): " "got: %i, expected: %i", i, j, data_tgt->seq[j * CYAML_ARRAY_LEN(*ref) + i], ref[j][i]); } } } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int8_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = 127; static const unsigned char yaml[] = "val: 127\n"; struct target_struct { int8_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi8", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int8_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = -128; static const unsigned char yaml[] = "val: -128\n"; struct target_struct { int8_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi8", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int16_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = -32768; static const unsigned char yaml[] = "val: -32768\n"; struct target_struct { int16_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi16", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int16_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = 32767; static const unsigned char yaml[] = "val: 32767\n"; struct target_struct { int16_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi16", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int32_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = -2147483648; static const unsigned char yaml[] = "val: -2147483648\n"; struct target_struct { int32_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi32", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int32_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = 2147483647; static const unsigned char yaml[] = "val: 2147483647\n"; struct target_struct { int32_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi32", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int64_limit_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = -9223372036854775807ll; static const unsigned char yaml[] = "val: -9223372036854775807\n"; struct target_struct { int64_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi64", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test integer limit. * * \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_int64_limit_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { int64_t value = 9223372036854775807; static const unsigned char yaml[] = "val: 9223372036854775807\n"; struct target_struct { int64_t val; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("val", CYAML_FLAG_DEFAULT, struct target_struct, val), 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->val != value) { return ttest_fail(&tc, "Incorrect value " "(got %"PRIi64", expecting %"PRIi64")", data_tgt->val, value); } return ttest_pass(&tc); } /** * Test loading sequence of pointers to integer values with NULLs. * * \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_sequence_null_str_values_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const int expected[] = { 777, 6, 5, 4, 3, 2, 1, 0, }; static const bool expected_nulls[] = { false, false, false, true, false, false, true, false, }; static const unsigned char yaml[] = "- 777\n" "- 6\n" "- 5\n" "- ~\n" "- 3\n" "- 2\n" "- null\n" "- 0\n"; int **value = NULL; unsigned count = 0; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER_NULL_STR, **value) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, *value, &entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (count != CYAML_ARRAY_LEN(expected)) { return ttest_fail(&tc, "Unexpected sequence count."); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(expected); i ++) { if (expected_nulls[i]) { if (value[i] != NULL) { return ttest_fail(&tc, "Expected NULL."); } continue; } else { if (value[i] == NULL) { return ttest_fail(&tc, "Unexpected NULL."); } if ((*(value)[i]) != expected[i]) { return ttest_fail(&tc, "Bad value."); } } } return ttest_pass(&tc); } /** * Test loading sequence of pointers to integer values with NULLs. * * \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_sequence_null_values_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const int expected[] = { 777, 6, 5, 4, 3, 2, 1, 0, }; static const bool expected_nulls[] = { false, false, false, true, false, false, true, false, }; static const unsigned char yaml[] = "- 777\n" "- 6\n" "- 5\n" "- \n" "- 3\n" "- 2\n" "- \n" "- 0\n"; int **value = NULL; unsigned count = 0; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER_NULL, **value) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, *value, &entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (count != CYAML_ARRAY_LEN(expected)) { return ttest_fail(&tc, "Unexpected sequence count."); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(expected); i ++) { if (expected_nulls[i]) { if (value[i] != NULL) { return ttest_fail(&tc, "Expected NULL."); } continue; } else { if (value[i] == NULL) { return ttest_fail(&tc, "Unexpected NULL."); } if ((*(value)[i]) != expected[i]) { return ttest_fail(&tc, "Bad value."); } } } return ttest_pass(&tc); } /** * Test loading sequence of pointers to integer values with NULLs. * * \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_sequence_null_str_values_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned int expected[] = { 7, 6, 5555, 4, 3, 2, 1, 0, }; static const bool expected_nulls[] = { false, true, false, true, false, false, true, false, }; static const unsigned char yaml[] = "- 7\n" "- \n" "- 5555\n" "- Null\n" "- 3\n" "- 2\n" "- NULL\n" "- 0\n"; unsigned int **value = NULL; unsigned count = 0; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_POINTER_NULL_STR, **value) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, *value, &entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (count != CYAML_ARRAY_LEN(expected)) { return ttest_fail(&tc, "Unexpected sequence count."); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(expected); i ++) { if (expected_nulls[i]) { if (value[i] != NULL) { return ttest_fail(&tc, "Expected NULL."); } continue; } else { if (value[i] == NULL) { return ttest_fail(&tc, "Unexpected NULL."); } if ((*(value)[i]) != expected[i]) { return ttest_fail(&tc, "Bad value."); } } } return ttest_pass(&tc); } /** * Test loading sequence of pointers to mapping values with NULLs. * * \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_sequence_null_str_values_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct entry_value { int a; int b; }; static const struct entry_value expected[] = { { .a = 7 }, { .a = 6 }, { .a = 5555 }, { .a = 4 }, { .a = 3 }, { .b = 2 }, { .a = 1 }, { .a = 0 }, }; static const bool expected_nulls[] = { false, true, false, true, false, false, true, false, }; static const unsigned char yaml[] = "- a: 7\n" "- \n" "- a: 5555\n" "- Null\n" "- a: 3\n" "- b: 2\n" "- NULL\n" "- a: 0\n"; struct entry_value **value = NULL; unsigned count = 0; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_OPTIONAL, struct entry_value, a), CYAML_FIELD_INT("b", CYAML_FLAG_OPTIONAL, struct entry_value, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER_NULL_STR, **value, mapping_schema) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, *value, &entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (count != CYAML_ARRAY_LEN(expected)) { return ttest_fail(&tc, "Unexpected sequence count."); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(expected); i ++) { if (expected_nulls[i]) { if (value[i] != NULL) { return ttest_fail(&tc, "Expected NULL."); } continue; } else { if (value[i] == NULL) { return ttest_fail(&tc, "Unexpected NULL."); } if (memcmp(&(*(value)[i]), &expected[i], sizeof(**value)) != 0) { return ttest_fail(&tc, "Bad value."); } } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 1 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[1]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 2 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[2]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 3 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[3]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 4 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_4( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[4]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 5 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_5( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[5]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 6 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_6( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[6]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 7 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_7( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[7]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading a sequence of integers with 8 byte sequence count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_entry_sequence_count_8( ttest_report_ctx_t *report, const cyaml_config_t *config) { int ref[] = { 1, 1, 2, 3, 5, 8 }; static const unsigned char yaml[] = "sequence:\n" " - 1\n" " - 1\n" " - 2\n" " - 3\n" " - 5\n" " - 8\n"; struct target_struct { int seq[CYAML_ARRAY_LEN(ref)]; uint8_t seq_count[8]; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data_tgt->seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != cyaml_data_read(sizeof(data_tgt->seq_count), data_tgt->seq_count, &err)) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->seq[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->seq[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading with schema with scalar top level type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_schema_top_level_scalar( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "7\n"; int *value = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER, int) }; test_data_t td = { .data = (cyaml_data_t **) &value, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &value, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } if (*value != 7) { return ttest_fail(&tc, "Bad value."); } return ttest_pass(&tc); } /** * Test loading with schema with string top level type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_schema_top_level_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "Hello\n"; char *value = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, int, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &value, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } if (strcmp(value, "Hello") != 0) { return ttest_fail(&tc, "Bad value."); } return ttest_pass(&tc); } /** * Test loading with schema with sequence_fixed top level type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_schema_top_level_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "- 7\n" "- 6\n" "- 5\n"; int *value = NULL; unsigned count = 0; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, CYAML_UNLIMITED) }; test_data_t td = { .data = (cyaml_data_t **) &value, .seq_count = &count, .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 **) &value, &count); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (count != 3) { return ttest_fail(&tc, "Unexpected sequence count."); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } if ((value[0] != 7) && (value[1] != 6) && (value[2] != 5)) { return ttest_fail(&tc, "Bad value."); } return ttest_pass(&tc); } /** * Test loading with schema with sequence_fixed top level type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_schema_top_level_sequence_fixed( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char yaml[] = "- 7\n" "- 6\n" "- 5\n"; int *value = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE_FIXED(CYAML_FLAG_POINTER, int, &entry_schema, 3) }; test_data_t td = { .data = (cyaml_data_t **) &value, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema, (cyaml_data_t **) &value, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (value == NULL) { return ttest_fail(&tc, "Data NULL on success."); } if ((value[0] != 7) && (value[1] != 6) && (value[2] != 5)) { return ttest_fail(&tc, "Bad value."); } return ttest_pass(&tc); } /** * Test loading a stream with more than one document. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_multiple_documents_ignored( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; } data = { .a = 9, }; static const unsigned char yaml[] = "a: 9\n" "---\n" "b: foo\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } return ttest_pass(&tc); } /** * Test loading a mapping multiple fields. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_with_multiple_fields( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "a: 9\n" "b: 90\n" "c: 900\n" "d: 9000\n" "e: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("c", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("d", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("e", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with optional fields. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_with_optional_fields( ttest_report_ctx_t *report, const cyaml_config_t *config) { long values[] = { 4, 3, 2, 1 }; const struct target_struct { char *a; char b[10]; int c; long d[4]; long *e; unsigned e_count; char *f; char *g; char h[10]; int i; long j[4]; long *k; unsigned k_count; bool l; } data = { .a = (char *) "Hello", .b = "World!", .c = 0, .d = { 0, 0, 0, 0 }, .e = values, .f = (char *) "Required!", .g = NULL, .h = "\0", .i = 9876, .j = { 1, 2, 3, 4 }, .k = NULL, .l = false, }; static const unsigned char yaml[] = "a: Hello\n" "b: World!\n" "e: [ 4, 3, 2, 1 ]\n" "f: Required!\n" "i: 9876\n" "j: [ 1, 2, 3, 4 ]\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_value sequence_entry = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, sizeof(long)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING("b", CYAML_FLAG_OPTIONAL, struct target_struct, b, 0), CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, struct target_struct, c), CYAML_FIELD_SEQUENCE_FIXED("d", CYAML_FLAG_OPTIONAL, struct target_struct, d, &sequence_entry, 4), CYAML_FIELD_SEQUENCE("e", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct target_struct, e, &sequence_entry, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("f", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct target_struct, f, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("g", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct target_struct, g, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING("h", CYAML_FLAG_OPTIONAL, struct target_struct, h, 0), CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, struct target_struct, i), CYAML_FIELD_SEQUENCE_FIXED("j", CYAML_FLAG_OPTIONAL, struct target_struct, j, &sequence_entry, 4), CYAML_FIELD_SEQUENCE("k", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL, struct target_struct, k, &sequence_entry, 0, CYAML_UNLIMITED), CYAML_FIELD_BOOL("l", CYAML_FLAG_OPTIONAL, struct target_struct, l), 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 (strcmp(data_tgt->a, data.a) != 0) { return ttest_fail(&tc, "Incorrect value for entry a: " "Expected: %s, got: %s", data.a, data_tgt->a); } if (strcmp(data_tgt->b, data.b) != 0) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } for (unsigned i = 0; i < 4; i++) { if (data_tgt->d[i] != data.d[i]) { return ttest_fail(&tc, "Incorrect value for entry d"); } } for (unsigned i = 0; i < 4; i++) { if (data_tgt->e[i] != data.e[i]) { return ttest_fail(&tc, "Incorrect value for entry e " "Index: %u: Expected: %ld, got: %ld", i, data.e[i], data_tgt->e[i]); } } if (strcmp(data_tgt->f, data.f) != 0) { return ttest_fail(&tc, "Incorrect value for entry f: " "Expected: %s, got: %s", data.f, data_tgt->f); } if (data_tgt->g != data.g) { return ttest_fail(&tc, "Incorrect value for entry g: " "Expected: %s, got: %s", data.g, data_tgt->g); } if (strcmp(data_tgt->h, data.h) != 0) { return ttest_fail(&tc, "Incorrect value for entry h"); } if (data_tgt->i != data.i) { return ttest_fail(&tc, "Incorrect value for entry i"); } for (unsigned i = 0; i < 4; i++) { if (data_tgt->j[i] != data.j[i]) { return ttest_fail(&tc, "Incorrect value for entry j"); } } if (data_tgt->k != data.k) { return ttest_fail(&tc, "Incorrect value for entry k"); } if (data_tgt->l != data.l) { return ttest_fail(&tc, "Incorrect value for entry l"); } return ttest_pass(&tc); } /** * Test loading a mapping with only optional fields. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_only_optional_fields( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { int c; int i; }; static const unsigned char yaml[] = "\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("c", CYAML_FLAG_OPTIONAL, struct target_struct, c), CYAML_FIELD_INT("i", CYAML_FLAG_OPTIONAL, struct target_struct, i), 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 != NULL) { return ttest_fail(&tc, "Shouldn't have allocated anything"); } return ttest_pass(&tc); } /** * Test loading a mapping with only optional fields. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_without_any_fields( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { int i; }; static const unsigned char yaml[] = "{}\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_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 == NULL) { return ttest_fail(&tc, "Should have allocated empty structure"); } if (data_tgt->i != 0) { return ttest_fail(&tc, "Value should be initialised to 0"); } return ttest_pass(&tc); } /** * Test loading a mapping with unknown keys ignored by config. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_ignored_unknown_keys( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { short b; int c; long d; long long e; } data = { .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "a: 9\n" "b: 90\n" "c: 900\n" "d: 9000\n" "e: 90000\n" "f: 900000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("c", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("d", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("e", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } cfg.flags |= CYAML_CFG_IGNORE_UNKNOWN_KEYS; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with unknown keys ignored by config. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_warn_ignored_keys( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { short b; int c; long d; long long e; } data = { .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "a: 9\n" "b: 90\n" "c: 900\n" "d: 9000\n" "e: 90000\n" "f: 900000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("c", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("d", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("e", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } cfg.flags |= CYAML_CFG_IGNORE_UNKNOWN_KEYS | CYAML_CFG_IGNORED_KEY_WARNING; err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a sequence with max size 4, and only 2 entries in YAML. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_sequence_without_max_entries( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { const char *seq[4]; unsigned seq_count; } data = { .seq = { "1", "2", NULL, NULL }, .seq_count = 2, }; static const unsigned char yaml[] = "seq: [ 1, 2 ]\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_value sequence_entry = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char *, 0, CYAML_UNLIMITED) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("seq", CYAML_FLAG_OPTIONAL, struct target_struct, seq, &sequence_entry, 0, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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->seq_count != 2) { return ttest_fail(&tc, "Incorrect sequence entry count"); } for (unsigned i = 0; i < 4; i++) { if ((data_tgt->seq[i] == NULL) != (data.seq[i] == NULL)) { return ttest_fail(&tc, "Incorrect value for entry %u", i); } if (data_tgt->seq[i] != NULL) { if (strcmp(data_tgt->seq[i], data.seq[i]) != 0) { return ttest_fail(&tc, "Incorrect value for entry %u", i); } } } return ttest_pass(&tc); } /** * Test loading without a logging function. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_no_log( ttest_report_ctx_t *report, const cyaml_config_t *config) { cyaml_config_t cfg = *config; struct target_struct { const char *seq[4]; unsigned seq_count; } data = { .seq = { "1", "2", NULL, NULL }, .seq_count = 2, }; static const unsigned char yaml[] = "seq: [ 1, 2 ]\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_value sequence_entry = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char *, 0, CYAML_UNLIMITED) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("seq", CYAML_FLAG_OPTIONAL, struct target_struct, seq, &sequence_entry, 0, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.log_fn = NULL; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->seq_count != 2) { return ttest_fail(&tc, "Incorrect sequence entry count"); } for (unsigned i = 0; i < 4; i++) { if ((data_tgt->seq[i] == NULL) != (data.seq[i] == NULL)) { return ttest_fail(&tc, "Incorrect value for entry %u", i); } if (data_tgt->seq[i] != NULL) { if (strcmp(data_tgt->seq[i], data.seq[i]) != 0) { return ttest_fail(&tc, "Incorrect value for entry %u", i); } } } return ttest_pass(&tc); } /** * Test loading with duplicate ignored mapping fields. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_duplicate_ignored( ttest_report_ctx_t *report, const cyaml_config_t *config) { int value = 90; static const unsigned char yaml[] = "ignore: 90\n" "ignore: 90\n" "test_int: 90\n"; struct target_struct { int test_value_int; } *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_IGNORE("ignore", CYAML_FLAG_DEFAULT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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"); } return ttest_pass(&tc); } /** * Test loading a sequence with arbitrary C struct member name for entry count. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_schema_sequence_entry_count_member( ttest_report_ctx_t *report, const cyaml_config_t *config) { unsigned ref[] = { 99999, 99998, 99997, 99996, 99995, 99994 }; static const unsigned char yaml[] = "sequence:\n" " - 99999\n" " - 99998\n" " - 99997\n" " - 99996\n" " - 99995\n" " - 99994\n"; struct target_struct { unsigned *entries; uint32_t n_entries; } *data_tgt = NULL; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data_tgt->entries)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE_COUNT("sequence", CYAML_FLAG_POINTER, struct target_struct, entries, n_entries, &entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (CYAML_ARRAY_LEN(ref) != data_tgt->n_entries) { return ttest_fail(&tc, "Incorrect sequence count"); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(ref); i++) { if (data_tgt->entries[i] != ref[i]) { return ttest_fail(&tc, "Incorrect value (i=%u): " "got: %i, expected: %i", i, data_tgt->entries[i], ref[i]); } } return ttest_pass(&tc); } /** * Test loading an enum with case insensitive string matching configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_enum_insensitive( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, } ref = TEST_ENUM_SECOND; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { { "first", TEST_ENUM_FIRST }, { "second", TEST_ENUM_SECOND }, { "third", TEST_ENUM_THIRD }, }; static const unsigned char yaml[] = "SECOND\n"; enum test_enum *data_tgt = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_ENUM(CYAML_FLAG_POINTER, *data_tgt, strings, TEST_ENUM__COUNT), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (*data_tgt != ref) { return ttest_fail(&tc, "Incorrect value for enum"); } return ttest_pass(&tc); } /** * Test loading a flags value with case insensitive string matching configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_flags_insensitive( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_FLAG_FIRST = (1 << 1), TEST_FLAG_SECOND = (1 << 3), TEST_FLAG_THIRD = (1 << 5), } ref = TEST_FLAG_FIRST | TEST_FLAG_THIRD; static const cyaml_strval_t strings[] = { { "first", TEST_FLAG_FIRST }, { "second", TEST_FLAG_SECOND }, { "third", TEST_FLAG_THIRD }, }; static const unsigned char yaml[] = "- First\n" "- Third\n"; enum test_enum *data_tgt = NULL; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_FLAGS(CYAML_FLAG_POINTER, *data_tgt, strings, CYAML_ARRAY_LEN(strings)), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (*data_tgt != ref) { return ttest_fail(&tc, "Incorrect value for enum"); } return ttest_pass(&tc); } /** * Test loading a mapping with case insensitive string matching configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_fields_cfg_insensitive_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "Lollipop: 9\n" "Squiggle: 90\n" "Unicorns: 900\n" "Cheerful: 9000\n" "LibCYAML: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("lollipop", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_INT("squiggle", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("unicorns", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("cheerful", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("libcyaml", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with case insensitive string matching configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_fields_cfg_insensitive_2( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "Plinth: 9\n" "..Cusp?: 90\n" "..Cusp!: 900\n" "Bleat: 9000\n" "Foo~-|Bar: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("plinth", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_INT("..cusp?", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("..cusp!", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("bleat", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("foO~-|baR", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with case insensitive string matching configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_fields_cfg_insensitive_3( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "Pling: 9\n" "Plin: 90\n" "Pli: 900\n" "Pl: 9000\n" "P: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("plin", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("pli", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("pl", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("p", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with case sensitive string matching for value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_fields_value_sensitive_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "pling: 9\n" "PLing: 90\n" "PLINg: 900\n" "pliNG: 9000\n" "PLING: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_INT("PLing", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("PLINg", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("pliNG", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("PLING", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING( CYAML_FLAG_POINTER | CYAML_FLAG_CASE_SENSITIVE, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading a mapping with case insensitive string matching for value. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_mapping_fields_value_insensitive_1( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct target_struct { signed char a; short b; int c; long d; long long e; } data = { .a = 9, .b = 90, .c = 900, .d = 9000, .e = 90000, }; static const unsigned char yaml[] = "Pling: 9\n" "Plin: 90\n" "Pli: 900\n" "Pl: 9000\n" "P: 90000\n"; struct target_struct *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("plin", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_INT("pli", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_INT("pl", CYAML_FLAG_DEFAULT, struct target_struct, d), CYAML_FIELD_INT("p", CYAML_FLAG_DEFAULT, struct target_struct, e), CYAML_FIELD_INT("pling", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING( CYAML_FLAG_POINTER | CYAML_FLAG_CASE_INSENSITIVE, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = &cfg, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_CASE_INSENSITIVE; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_load_data(yaml, YAML_LEN(yaml), &cfg, &top_schema, (cyaml_data_t **) &data_tgt, NULL); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (data_tgt->a != data.a) { return ttest_fail(&tc, "Incorrect value for entry a"); } if (data_tgt->b != data.b) { return ttest_fail(&tc, "Incorrect value for entry b"); } if (data_tgt->c != data.c) { return ttest_fail(&tc, "Incorrect value for entry c"); } if (data_tgt->d != data.d) { return ttest_fail(&tc, "Incorrect value for entry d"); } if (data_tgt->e != data.e) { return ttest_fail(&tc, "Incorrect value for entry e"); } return ttest_pass(&tc); } /** * Test loading with an unused anchor. * * \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_unused_anchor( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *string_value = "Hello World!"; const int int_value = 9; static const unsigned char yaml[] = "test_string: &foo Hello World!\n" "test_int: 9\n"; struct target_struct { char * test_value_string; int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, string_value) != 0) { return ttest_fail(&tc, "Incorrect value"); } if (data_tgt->test_value_int != int_value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading with an aliased string 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_anchor_scalar_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *string_value = "Hello World!"; const int int_value = 9; static const unsigned char yaml[] = "test_int_anchor: &foo 9\n" "test_string: Hello World!\n" "test_int: *foo\n"; struct target_struct { char * test_value_string; int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("test_int_anchor", CYAML_FLAG_OPTIONAL), CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, string_value) != 0) { return ttest_fail(&tc, "Incorrect value"); } if (data_tgt->test_value_int != int_value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading with an aliased string 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_anchor_scalar_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *string_value = "Hello World!"; const int int_value = 9; static const unsigned char yaml[] = "test_string_anchor: &foo Hello World!\n" "test_string: *foo\n" "test_int: 9\n"; struct target_struct { char * test_value_string; int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("test_string_anchor", CYAML_FLAG_OPTIONAL), CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_value_string, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string, string_value) != 0) { return ttest_fail(&tc, "Incorrect value"); } if (data_tgt->test_value_int != int_value) { return ttest_fail(&tc, "Incorrect value"); } return ttest_pass(&tc); } /** * Test loading multiple anchored and alisased scalars. * * \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_anchor_multiple_scalars( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *string_value1 = "Hello Me!"; const char *string_value2 = "Hello World!"; const int int_value = 99; static const unsigned char yaml[] = "anchors:\n" " - &a1 Hello World!\n" " - &a2 Hello Me!\n" " - &a3 99\n" "test_string1: *a2\n" "test_int: *a3\n" "test_string2: *a1\n"; struct target_struct { char * test_value_string1; char * test_value_string2; int test_value_int; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_STRING_PTR("test_string1", CYAML_FLAG_POINTER, struct target_struct, test_value_string1, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("test_string2", CYAML_FLAG_POINTER, struct target_struct, test_value_string2, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_value_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; 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 (strcmp(data_tgt->test_value_string1, string_value1) != 0) { return ttest_fail(&tc, "Incorrect value: " "expected: %s, got: %s", string_value1, data_tgt->test_value_string1); } if (strcmp(data_tgt->test_value_string2, string_value2) != 0) { return ttest_fail(&tc, "Incorrect value: " "expected: %s, got: %s", string_value2, data_tgt->test_value_string2); } if (data_tgt->test_value_int != int_value) { return ttest_fail(&tc, "Incorrect value: " "expected: %i, got: %i", int_value, data_tgt->test_value_int); } return ttest_pass(&tc); } /** * Test loading an aliased mapping. * * \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_anchor_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *test_a = "Hello Me!"; const int test_b = 777; static const unsigned char yaml[] = "anchors:\n" " - &a2 Hello Me!\n" " - &a1 {\n" " a: *a2,\n" " b: 777,\n" " }\n" "test: *a1\n"; struct my_test { int b; char *a; }; struct target_struct { struct my_test test; } *data_tgt = NULL; static const struct cyaml_schema_field inner_mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct my_test, a, 0, CYAML_UNLIMITED), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct my_test, b), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_MAPPING("test", CYAML_FLAG_DEFAULT, struct target_struct, test, inner_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .config = config, .schema = &top_schema, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_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 (strcmp(data_tgt->test.a, test_a) != 0) { return ttest_fail(&tc, "Incorrect value: " "expected: %s, got: %s", test_a, data_tgt->test.a); } if (data_tgt->test.b != test_b) { return ttest_fail(&tc, "Incorrect value: " "expected: %i, got: %i", test_b, data_tgt->test.b); } return ttest_pass(&tc); } /** * Test loading an aliased sequence. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_load_anchor_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { const int test_a[] = { 1, 22, 333, 4444 }; static const unsigned char yaml[] = "anchors:\n" " - &a1 [\n" " 1,\n" " 22,\n" " 333,\n" " 4444,\n" " ]\n" "test: *a1\n"; struct target_struct { int *a; unsigned a_count; } *data_tgt = NULL; static const struct cyaml_schema_value sequence_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_SEQUENCE("test", CYAML_FLAG_POINTER, struct target_struct, a, &sequence_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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->a_count != CYAML_ARRAY_LEN(test_a)) { return ttest_fail(&tc, "Incorrect value: " "expected: %u, got: %u", data_tgt->a_count, CYAML_ARRAY_LEN(test_a)); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(test_a); i++) { if (data_tgt->a[i] != test_a[i]) { return ttest_fail(&tc, "Incorrect value: " "expected: %i, got: %i", data_tgt->a[i], test_a[i]); } } return ttest_pass(&tc); } /** * Test loading with anchors within anchors, etc. * * \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_anchor_deep_mapping_sequence( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *test_a = "Hello Me!"; const int test_b[] = { 1, 22, 333, 4444 }; static const unsigned char yaml[] = "anchors:\n" " - &a1 Hello Me!\n" " - &a2 {\n" " a: *a1,\n" " b: &a3 [ 1, 22, 333, 4444 ],\n" " c: *a1,\n" " d: *a3,\n" " }\n" "test_a: *a2\n" "test_b: *a3\n"; struct my_test { char *a; int *b; unsigned b_count; char *c; int *d; unsigned d_count; }; struct target_struct { struct my_test test_a; int *test_b; unsigned test_b_count; } *data_tgt = NULL; static const struct cyaml_schema_value sequence_entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_field inner_mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct my_test, a, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("b", CYAML_FLAG_POINTER, struct my_test, b, &sequence_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("c", CYAML_FLAG_POINTER, struct my_test, c, 0, CYAML_UNLIMITED), CYAML_FIELD_SEQUENCE("d", CYAML_FLAG_POINTER, struct my_test, d, &sequence_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_IGNORE("anchors", CYAML_FLAG_OPTIONAL), CYAML_FIELD_MAPPING("test_a", CYAML_FLAG_DEFAULT, struct target_struct, test_a, inner_mapping_schema), CYAML_FIELD_SEQUENCE("test_b", CYAML_FLAG_POINTER, struct target_struct, test_b, &sequence_entry_schema, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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 (strcmp(data_tgt->test_a.a, test_a) != 0) { return ttest_fail(&tc, "Incorrect value: " "expected: %s, got: %s", test_a, data_tgt->test_a.a); } if (strcmp(data_tgt->test_a.c, test_a) != 0) { return ttest_fail(&tc, "Incorrect value: " "expected: %s, got: %s", test_a, data_tgt->test_a.c); } if (data_tgt->test_b_count != CYAML_ARRAY_LEN(test_b)) { return ttest_fail(&tc, "Incorrect value: " "expected: %u, got: %u", data_tgt->test_b_count, CYAML_ARRAY_LEN(test_b)); } for (unsigned i = 0; i < CYAML_ARRAY_LEN(test_b); i++) { if (data_tgt->test_b[i] != test_b[i]) { return ttest_fail(&tc, "Incorrect value: " "expected: %i, got: %i", data_tgt->test_b[i], test_b[i]); } } return ttest_pass(&tc); } /** * Test loading when an anchor is updated. * * The newest definition should be used. * * \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_anchor_updated_anchor( ttest_report_ctx_t *report, const cyaml_config_t *config) { const char *string_value1 = "Hello Me!"; const char *string_value2 = "Hello World!"; static const unsigned char yaml[] = "a: &a1 Hello Me!\n" "b: *a1\n" "c: &a1 Hello World!\n" "d: *a1\n"; struct target_struct { char *a; char *b; char *c; char *d; } *data_tgt = NULL; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("a", CYAML_FLAG_POINTER, struct target_struct, a, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("b", CYAML_FLAG_POINTER, struct target_struct, b, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("c", CYAML_FLAG_POINTER, struct target_struct, c, 0, CYAML_UNLIMITED), CYAML_FIELD_STRING_PTR("d", CYAML_FLAG_POINTER, struct target_struct, d, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; test_data_t td = { .data = (cyaml_data_t **) &data_tgt, .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 (strcmp(data_tgt->a, string_value1) != 0) { return ttest_fail(&tc, "Incorrect value (a): " "expected: %s, got: %s", string_value1, data_tgt->a); } if (strcmp(data_tgt->b, string_value1) != 0) { return ttest_fail(&tc, "Incorrect value (b): " "expected: %s, got: %s", string_value1, data_tgt->b); } if (strcmp(data_tgt->c, string_value2) != 0) { return ttest_fail(&tc, "Incorrect value (c): " "expected: %s, got: %s", string_value2, data_tgt->c); } if (strcmp(data_tgt->d, string_value2) != 0) { return ttest_fail(&tc, "Incorrect value (d): " "expected: %s, got: %s", string_value2, data_tgt->d); } return ttest_pass(&tc); } /** * Run the YAML loading unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool load_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DEFAULT, }; ttest_heading(rc, "Load single entry mapping tests: simple types"); pass &= test_load_mapping_entry_enum(rc, &config); pass &= test_load_mapping_entry_uint(rc, &config); pass &= test_load_mapping_entry_float(rc, &config); pass &= test_load_mapping_entry_double(rc, &config); pass &= test_load_mapping_entry_string(rc, &config); pass &= test_load_mapping_entry_int_pos(rc, &config); pass &= test_load_mapping_entry_int_neg(rc, &config); pass &= test_load_mapping_entry_enum_ptr(rc, &config); pass &= test_load_mapping_entry_uint_ptr(rc, &config); pass &= test_load_mapping_entry_float_ptr(rc, &config); pass &= test_load_mapping_entry_bool_true(rc, &config); pass &= test_load_mapping_entry_bool_false(rc, &config); pass &= test_load_mapping_entry_double_ptr(rc, &config); pass &= test_load_mapping_entry_string_ptr(rc, &config); pass &= test_load_mapping_entry_int_pos_ptr(rc, &config); pass &= test_load_mapping_entry_int_neg_ptr(rc, &config); pass &= test_load_mapping_entry_enum_sparse(rc, &config); pass &= test_load_mapping_entry_ignore_deep(rc, &config); pass &= test_load_mapping_entry_ignore_scalar(rc, &config); pass &= test_load_mapping_entry_enum_fallback(rc, &config); pass &= test_load_mapping_entry_bool_true_ptr(rc, &config); pass &= test_load_mapping_entry_bool_false_ptr(rc, &config); pass &= test_load_mapping_entry_float_underflow(rc, &config); pass &= test_load_mapping_entry_double_underflow(rc, &config); 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); ttest_heading(rc, "Load single entry mapping tests: complex types"); pass &= test_load_mapping_entry_flags(rc, &config); pass &= test_load_mapping_entry_mapping(rc, &config); pass &= test_load_mapping_entry_bitfield(rc, &config); pass &= test_load_mapping_entry_flags_ptr(rc, &config); pass &= test_load_mapping_entry_mapping_ptr(rc, &config); pass &= test_load_mapping_entry_flags_empty(rc, &config); pass &= test_load_mapping_entry_bitfield_ptr(rc, &config); pass &= test_load_mapping_entry_flags_sparse(rc, &config); ttest_heading(rc, "Load single entry mapping tests: sequences"); pass &= test_load_mapping_entry_sequence_int(rc, &config); pass &= test_load_mapping_entry_sequence_enum(rc, &config); pass &= test_load_mapping_entry_sequence_uint(rc, &config); pass &= test_load_mapping_entry_sequence_bool(rc, &config); pass &= test_load_mapping_entry_sequence_flags(rc, &config); pass &= test_load_mapping_entry_sequence_string(rc, &config); pass &= test_load_mapping_entry_sequence_mapping(rc, &config); pass &= test_load_mapping_entry_sequence_string_ptr(rc, &config); pass &= test_load_mapping_entry_sequence_mapping_ptr(rc, &config); pass &= test_load_mapping_entry_sequence_sequence_fixed_int(rc, &config); pass &= test_load_mapping_entry_sequence_sequence_fixed_ptr_int(rc, &config); pass &= test_load_mapping_entry_sequence_sequence_fixed_flat_int(rc, &config); ttest_heading(rc, "Load single entry mapping tests: ptr sequences"); pass &= test_load_mapping_entry_sequence_ptr_int(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_enum(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_uint(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_bool(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_flags(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_string(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_mapping(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_string_ptr(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_mapping_ptr(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_sequence_fixed_int(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_sequence_fixed_ptr_int(rc, &config); pass &= test_load_mapping_entry_sequence_ptr_sequence_fixed_flat_int(rc, &config); ttest_heading(rc, "Test integer limits"); pass &= test_load_mapping_entry_int8_limit_neg(rc, &config); pass &= test_load_mapping_entry_int8_limit_pos(rc, &config); pass &= test_load_mapping_entry_int16_limit_neg(rc, &config); pass &= test_load_mapping_entry_int16_limit_pos(rc, &config); pass &= test_load_mapping_entry_int32_limit_neg(rc, &config); pass &= test_load_mapping_entry_int32_limit_pos(rc, &config); pass &= test_load_mapping_entry_int64_limit_neg(rc, &config); pass &= test_load_mapping_entry_int64_limit_pos(rc, &config); ttest_heading(rc, "Load tests: ptr sequence with null values"); pass &= test_load_sequence_null_values_int(rc, &config); pass &= test_load_sequence_null_str_values_int(rc, &config); pass &= test_load_sequence_null_str_values_uint(rc, &config); pass &= test_load_sequence_null_str_values_mapping(rc, &config); ttest_heading(rc, "Load tests: sequence count sizes"); pass &= test_load_mapping_entry_sequence_count_1(rc, &config); pass &= test_load_mapping_entry_sequence_count_2(rc, &config); pass &= test_load_mapping_entry_sequence_count_3(rc, &config); pass &= test_load_mapping_entry_sequence_count_4(rc, &config); pass &= test_load_mapping_entry_sequence_count_5(rc, &config); pass &= test_load_mapping_entry_sequence_count_6(rc, &config); pass &= test_load_mapping_entry_sequence_count_7(rc, &config); pass &= test_load_mapping_entry_sequence_count_8(rc, &config); ttest_heading(rc, "Load tests: various"); pass &= test_load_no_log(rc, &config); pass &= test_load_duplicate_ignored(rc, &config); pass &= test_load_schema_top_level_scalar(rc, &config); pass &= test_load_schema_top_level_string(rc, &config); pass &= test_load_schema_top_level_sequence(rc, &config); pass &= test_load_mapping_warn_ignored_keys(rc, &config); pass &= test_load_multiple_documents_ignored(rc, &config); pass &= test_load_mapping_without_any_fields(rc, &config); pass &= test_load_mapping_with_multiple_fields(rc, &config); pass &= test_load_mapping_with_optional_fields(rc, &config); pass &= test_load_mapping_only_optional_fields(rc, &config); pass &= test_load_mapping_ignored_unknown_keys(rc, &config); pass &= test_load_sequence_without_max_entries(rc, &config); pass &= test_load_schema_top_level_sequence_fixed(rc, &config); pass &= test_load_schema_sequence_entry_count_member(rc, &config); ttest_heading(rc, "Load tests: case sensitivity"); pass &= test_load_enum_insensitive(rc, &config); pass &= test_load_flags_insensitive(rc, &config); pass &= test_load_mapping_fields_cfg_insensitive_1(rc, &config); pass &= test_load_mapping_fields_cfg_insensitive_2(rc, &config); pass &= test_load_mapping_fields_cfg_insensitive_3(rc, &config); pass &= test_load_mapping_fields_value_sensitive_1(rc, &config); pass &= test_load_mapping_fields_value_insensitive_1(rc, &config); ttest_heading(rc, "Load tests: anchors and aliases (scalars)"); pass &= test_load_unused_anchor(rc, &config); pass &= test_load_anchor_scalar_int(rc, &config); pass &= test_load_anchor_scalar_string(rc, &config); pass &= test_load_anchor_multiple_scalars(rc, &config); ttest_heading(rc, "Load tests: anchors and aliases (non scalars)"); pass &= test_load_anchor_mapping(rc, &config); pass &= test_load_anchor_sequence(rc, &config); pass &= test_load_anchor_deep_mapping_sequence(rc, &config); ttest_heading(rc, "Load tests: anchors and aliases (edge cases)"); pass &= test_load_anchor_updated_anchor(rc, &config); return pass; } libcyaml-1.4.1/test/units/save.c000066400000000000000000003340751443462360400165560ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2021 Michael Drake */ #include #include #include #include #include #include "../../src/data.h" #include "ttest.h" #include "test.h" /** Macro to squash unused variable compiler warnings. */ #define UNUSED(_x) ((void)(_x)) /** Helper macro to count bytes of YAML input data. */ #define YAML_LEN(_y) (sizeof(_y) - 1) /** * Unit test context data. */ typedef struct test_data { char **buffer; const struct cyaml_config *config; } test_data_t; /** * Common clean up function to free data allocated by tests. * * \param[in] data The unit test context data. */ static void cyaml_cleanup(void *data) { struct test_data *td = data; if (td->config->mem_fn != NULL && td->buffer != NULL) { td->config->mem_fn(td->config->mem_ctx, *(td->buffer), 0); } } /** * Test saving an unsigned integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_uint: 555\n" "...\n"; static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a float. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_float( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_float: 3.14\n" "...\n"; static const struct target_struct { float test_float; } data = { .test_float = 3.14f, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_float", CYAML_FLAG_DEFAULT, struct target_struct, test_float), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a double. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_double( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_float: 3.1400000000000001e+00\n" "...\n"; static const struct target_struct { double test_float; } data = { .test_float = 3.14, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLOAT("test_float", CYAML_FLAG_DEFAULT, struct target_struct, test_float), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string. * * \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_save_mapping_entry_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_string: This is a test, of sorts.\n" "...\n"; static const struct target_struct { char test_string[32]; } data = { .test_string = "This is a test, of sorts.", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING("test_string", CYAML_FLAG_DEFAULT, struct target_struct, test_string, 0), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a positive signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_int_pos( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_int: 90\n" "...\n"; static const struct target_struct { int test_int; } data = { .test_int = 90, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a negative signed integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_int_neg( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_int: -14\n" "...\n"; static const struct target_struct { int test_int; } data = { .test_int = -14, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a negative signed 64-bit integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_int_64( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_int: -9223372036854775800\n" "...\n"; static const struct target_struct { int64_t test_int; } data = { .test_int = -9223372036854775800, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT, struct target_struct, test_int), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a boolean. * * \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_save_mapping_entry_bool_true( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_bool: true\n" "...\n"; static const struct target_struct { bool test_bool; } data = { .test_bool = true, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, struct target_struct, test_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a boolean. * * \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_save_mapping_entry_bool_false( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_bool: false\n" "...\n"; static const struct target_struct { bool test_bool; } data = { .test_bool = false, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BOOL("test_bool", CYAML_FLAG_DEFAULT, struct target_struct, test_bool), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving 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_save_mapping_entry_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_string: This is a test, of sorts.\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test, of sorts.", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("test_string", CYAML_FLAG_POINTER, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a strict enum. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_enum_strict( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_e { FIRST, SECOND, THIRD, FOURTH, COUNT }; static const cyaml_strval_t strings[COUNT] = { { "first", 0 }, { "second", 1 }, { "third", 2 }, { "fourth", 3 }, }; static const unsigned char ref[] = "---\n" "test_enum: third\n" "...\n"; static const struct target_struct { enum test_e test_enum; } data = { .test_enum = THIRD, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_STRICT, struct target_struct, test_enum, strings, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a numerical enum. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_enum_number( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_e { FIRST, SECOND, THIRD, FOURTH }; static const cyaml_strval_t strings[] = { { "first", 0 }, { "second", 1 }, { "third", 2 }, { "fourth", 3 }, }; static const unsigned char ref[] = "---\n" "test_enum: 99\n" "...\n"; static const struct target_struct { enum test_e test_enum; } data = { .test_enum = 99, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_DEFAULT, struct target_struct, test_enum, strings, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sparse, unordered enum. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_enum_sparse( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_e { FIRST = -33, SECOND = 1, THIRD = 256, FOURTH = 3, FIFTH = 999, }; static const cyaml_strval_t strings[] = { { "first", FIRST }, { "second", SECOND }, { "third", THIRD }, { "fourth", FOURTH }, { "fifth", FIFTH }, }; static const unsigned char ref[] = "---\n" "test_enum: third\n" "...\n"; static const struct target_struct { enum test_e test_enum; } data = { .test_enum = THIRD, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_ENUM("test_enum", CYAML_FLAG_STRICT, struct target_struct, test_enum, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string pointer with plain style. * * \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_save_mapping_entry_string_ptr_plain( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "str: This is a test!!\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test!!", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("str", CYAML_FLAG_POINTER | CYAML_FLAG_SCALAR_PLAIN, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string pointer with single quote style. * * \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_save_mapping_entry_string_ptr_single( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "str: 'This is a test!!'\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test!!", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("str", CYAML_FLAG_POINTER | CYAML_FLAG_SCALAR_QUOTE_SINGLE, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string pointer with double quote style. * * \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_save_mapping_entry_string_ptr_double( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "str: \"This is a test!!\"\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test!!", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("str", CYAML_FLAG_POINTER | CYAML_FLAG_SCALAR_QUOTE_DOUBLE, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string pointer with folded style. * * \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_save_mapping_entry_string_ptr_folded( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "str: >-\n" " This is a test!!\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test!!", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("str", CYAML_FLAG_POINTER | CYAML_FLAG_SCALAR_FOLDED, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a string pointer with literal style. * * \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_save_mapping_entry_string_ptr_literal( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "str: |-\n" " This is a test!!\n" "...\n"; static const struct target_struct { const char *test_string; } data = { .test_string = "This is a test!!", }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_STRING_PTR("str", CYAML_FLAG_POINTER | CYAML_FLAG_SCALAR_LITERAL, struct target_struct, test_string, 0, CYAML_UNLIMITED), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a mapping. * * \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_save_mapping_entry_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; }; static const unsigned char ref[] = "---\n" "mapping:\n" " a: 123\n" " b: 9999\n" "...\n"; static const struct target_struct { struct value_s test_mapping; } data = { .test_mapping = { .a = 123, .b = 9999 }, }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_MAPPING("mapping", CYAML_FLAG_DEFAULT, struct target_struct, test_mapping, test_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving 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_save_mapping_entry_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; } value = { .a = 123, .b = 9999 }; static const unsigned char ref[] = "---\n" "mapping:\n" " a: 123\n" " b: 9999\n" "...\n"; const struct target_struct { struct value_s *test_mapping; } data = { .test_mapping = &value, }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_MAPPING_PTR("mapping", CYAML_FLAG_POINTER, struct target_struct, test_mapping, test_mapping_schema), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a strict flags 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_save_mapping_entry_flags_strict( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; static const unsigned char ref[] = "---\n" "test_flags:\n" "- first\n" "- fourth\n" "...\n"; static const struct target_struct { enum test_f test_flags; } data = { .test_flags = FIRST | FOURTH, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_STRICT, struct target_struct, test_flags, strings, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a numerical flags 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_save_mapping_entry_flags_number( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 0), SECOND = (1 << 1), THIRD = (1 << 2), FOURTH = (1 << 3), }; static const cyaml_strval_t strings[] = { { "first", (1 << 0) }, { "second", (1 << 1) }, { "third", (1 << 2) }, { "fourth", (1 << 3) }, }; static const unsigned char ref[] = "---\n" "test_flags:\n" "- first\n" "- fourth\n" "- 1024\n" "...\n"; static const struct target_struct { enum test_f test_flags; } data = { .test_flags = FIRST | FOURTH | 1024, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, struct target_struct, test_flags, strings, 4), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sparse flags 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_save_mapping_entry_flags_sparse( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_f { NONE = 0, FIRST = (1 << 3), SECOND = (1 << 9), THIRD = (1 << 10), FOURTH = (1 << 21), }; static const cyaml_strval_t strings[] = { { "none", NONE }, { "first", FIRST }, { "second", SECOND }, { "third", THIRD }, { "fourth", FOURTH }, }; static const unsigned char ref[] = "---\n" "test_flags:\n" "- first\n" "- fourth\n" "...\n"; static const struct target_struct { enum test_f test_flags; } data = { .test_flags = FIRST | FOURTH, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_FLAGS("test_flags", CYAML_FLAG_DEFAULT, struct target_struct, test_flags, strings, CYAML_ARRAY_LEN(strings)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a bitfield 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_save_mapping_entry_bitfield( ttest_report_ctx_t *report, const cyaml_config_t *config) { 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 }, }; static const unsigned char ref[] = "---\n" "test_bitfield:\n" " a: 0x7\n" " b: 0x7f\n" " c: 0xffffffff\n" " d: 0xff\n" " e: 0x3fff\n" "...\n"; static const struct target_struct { uint64_t test_bitfield; } data = { .test_bitfield = 0xFFFFFFFFFFFFFFFFu, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sparse bitfield 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_save_mapping_entry_bitfield_sparse( ttest_report_ctx_t *report, const cyaml_config_t *config) { 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 }, }; static const unsigned char ref[] = "---\n" "test_bitfield:\n" " a: 0x7\n" " b: 0x7f\n" " e: 0x3fff\n" "...\n"; static const struct target_struct { uint64_t test_bitfield; } data = { .test_bitfield = ( 0x7llu << 0) | ( 0x7Fllu << 3) | (0x3FFFllu << 50), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_BITFIELD("test_bitfield", CYAML_FLAG_DEFAULT, struct target_struct, test_bitfield, bitvals, CYAML_ARRAY_LEN(bitvals)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- 1\n" "- 1\n" "- 2\n" "- 3\n" "- 5\n" "- 8\n" "...\n"; static const struct target_struct { int seq[6]; uint32_t seq_count; } data = { .seq = { 1, 1, 2, 3, 5, 8 }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of unsigned integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- 1\n" "- 1\n" "- 2\n" "- 3\n" "- 5\n" "- 8\n" "...\n"; static const struct target_struct { unsigned seq[6]; uint32_t seq_count; } data = { .seq = { 1, 1, 2, 3, 5, 8 }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of enums. * * \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_save_mapping_entry_sequence_enum( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- first\n" "- second\n" "- third\n" "...\n"; static const struct target_struct { enum test_enum seq[3]; uint32_t seq_count; } data = { .seq = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, *(data.seq), strings, TEST_ENUM__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of boolean values. * * \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_save_mapping_entry_sequence_bool( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- true\n" "- false\n" "- true\n" "- false\n" "- true\n" "- false\n" "- true\n" "- false\n" "...\n"; static const struct target_struct { bool seq[8]; uint32_t seq_count; } data = { .seq = { true, false, true, false, true, false, true, false }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of flags. * * \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_save_mapping_entry_sequence_flags( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- - second\n" " - fifth\n" " - 1024\n" "- - first\n" "- - fourth\n" " - sixth\n" "...\n"; static const struct target_struct { enum test_flags seq[3]; uint32_t seq_count; } data = { .seq = { TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, TEST_FLAGS_FIRST, TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data.seq), strings, CYAML_ARRAY_LEN(strings)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- This\n" "- is\n" "- merely\n" "- a\n" "- test\n" "...\n"; static const struct target_struct { char seq[5][7]; uint32_t seq_count; } data = { .seq = { { 'T', 'h', 'i', 's'}, { 'i', 's', }, { 'm', 'e', 'r', 'e', 'l', 'y', }, { 'a', }, { 't', 'e', 's', 't', }, }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data.seq), 0, 6), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- This\n" "- is\n" "- merely\n" "- a\n" "- test\n" "...\n"; static const struct target_struct { char *seq[5]; uint32_t seq_count; } data = { .seq = { (char *) "This", (char *) "is", (char *) "merely", (char *) "a", (char *) "test", }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data.seq), 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of mappings. * * \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_save_mapping_entry_sequence_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; }; static const unsigned char ref[] = "---\n" "sequence:\n" "- a: 123\n" " b: 9999\n" "- a: 4000\n" " b: 62000\n" "- a: 1\n" " b: 765\n" "...\n"; static const struct target_struct { struct value_s seq[3]; uint32_t seq_count; } data = { .seq = { [0] = { .a = 123, .b = 9999 }, [1] = { .a = 4000, .b = 62000 }, [2] = { .a = 1, .b = 765 }, }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of mappings pointers. * * \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_save_mapping_entry_sequence_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct value_s { short a; long b; } v[3] = { { .a = 123, .b = 9999, }, { .a = 4000, .b = 62000, }, { .a = 1, .b = 765, }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- a: 123\n" " b: 9999\n" "- a: 4000\n" " b: 62000\n" "- a: 1\n" " b: 765\n" "...\n"; static const struct target_struct { const struct value_s *seq[3]; uint32_t seq_count; } data = { .seq = { [0] = &v[0], [1] = &v[1], [2] = &v[2], }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of fixed-length sequences. * * \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_save_mapping_entry_sequence_sequence_fixed_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const struct target_struct { int seq[4][3]; uint32_t seq_count; } data = { .seq = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data.seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, **(data.seq), &entry_schema_int, CYAML_ARRAY_LEN(*data.seq)) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of fixed-length sequences pointers. * * \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_save_mapping_entry_sequence_sequence_fixed_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const int v[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const struct target_struct { const int *seq[4]; uint32_t seq_count; } data = { .seq = { v[0], v[1], v[2], v[3], }, .seq_count = CYAML_ARRAY_LEN(data.seq), }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data.seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_POINTER, **(data.seq), &entry_schema_int, CYAML_ARRAY_LEN(*v)) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_DEFAULT, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a flattened sequence of fixed-length sequences. * * \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_save_mapping_entry_sequence_sequence_fixed_flat_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const struct target_struct { int seq[12]; uint32_t seq_count; } data = { .seq = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, }, /* Note: count is count of entries of the outer sequence * entries, so, 4, not 12. */ .seq_count = CYAML_ARRAY_LEN(data.seq) / 3, }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, int, &entry_schema_int, 3), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_DEFAULT, .data_size = sizeof(int[3]), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, } }, .data_offset = offsetof(struct target_struct, seq), .count_size = sizeof(data.seq_count), .count_offset = offsetof(struct target_struct, seq_count), }, CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- 1\n" "- 1\n" "- 2\n" "- 3\n" "- 5\n" "- 8\n" "...\n"; static const int seq[6] = { 1, 1, 2, 3, 5, 8 }; static const struct target_struct { const int *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of unsigned integers. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_ptr_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- 1\n" "- 1\n" "- 2\n" "- 3\n" "- 5\n" "- 8\n" "...\n"; static const unsigned seq[6] = { 1, 1, 2, 3, 5, 8 }; static const struct target_struct { const unsigned *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_UINT(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of enums. * * \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_save_mapping_entry_sequence_ptr_enum( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_enum { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD, TEST_ENUM__COUNT, }; static const cyaml_strval_t strings[TEST_ENUM__COUNT] = { [TEST_ENUM_FIRST] = { "first", 0 }, [TEST_ENUM_SECOND] = { "second", 1 }, [TEST_ENUM_THIRD] = { "third", 2 }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- first\n" "- second\n" "- third\n" "...\n"; static const enum test_enum seq[3] = { TEST_ENUM_FIRST, TEST_ENUM_SECOND, TEST_ENUM_THIRD }; static const struct target_struct { const enum test_enum *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_ENUM(CYAML_FLAG_DEFAULT, *(data.seq), strings, TEST_ENUM__COUNT), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of boolean values. * * \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_save_mapping_entry_sequence_ptr_bool( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- true\n" "- false\n" "- true\n" "- false\n" "- true\n" "- false\n" "- true\n" "- false\n" "...\n"; static const bool seq[8] = { true, false, true, false, true, false, true, false }; static const struct target_struct { const bool *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_BOOL(CYAML_FLAG_DEFAULT, *(data.seq)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of flags. * * \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_save_mapping_entry_sequence_ptr_flags( ttest_report_ctx_t *report, const cyaml_config_t *config) { enum test_flags { TEST_FLAGS_NONE = 0, TEST_FLAGS_FIRST = (1 << 0), TEST_FLAGS_SECOND = (1 << 1), TEST_FLAGS_THIRD = (1 << 2), TEST_FLAGS_FOURTH = (1 << 3), TEST_FLAGS_FIFTH = (1 << 4), TEST_FLAGS_SIXTH = (1 << 5), }; static const cyaml_strval_t strings[] = { { "none", TEST_FLAGS_NONE }, { "first", TEST_FLAGS_FIRST }, { "second", TEST_FLAGS_SECOND }, { "third", TEST_FLAGS_THIRD }, { "fourth", TEST_FLAGS_FOURTH }, { "fifth", TEST_FLAGS_FIFTH }, { "sixth", TEST_FLAGS_SIXTH }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- - second\n" " - fifth\n" " - 1024\n" "- - first\n" "- - fourth\n" " - sixth\n" "...\n"; static const enum test_flags seq[3] = { TEST_FLAGS_SECOND | TEST_FLAGS_FIFTH | 1024, TEST_FLAGS_FIRST, TEST_FLAGS_FOURTH | TEST_FLAGS_SIXTH, }; static const struct target_struct { const enum test_flags *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_FLAGS(CYAML_FLAG_DEFAULT, *(data.seq), strings, CYAML_ARRAY_LEN(strings)), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_ptr_string( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- This\n" "- is\n" "- merely\n" "- a\n" "- test\n" "...\n"; static const char seq[][7] = { "This", "is", "merely", "a", "test", }; static const struct target_struct { const char (*seq)[7]; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_DEFAULT, *(data.seq), 0, 6), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of strings. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_sequence_ptr_string_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- This\n" "- is\n" "- merely\n" "- a\n" "- test\n" "...\n"; static const char *seq[] = { "This", "is", "merely", "a", "test", }; static const struct target_struct { const char **seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_STRING(CYAML_FLAG_POINTER, *(data.seq), 0, CYAML_UNLIMITED), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of mappings. * * \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_save_mapping_entry_sequence_ptr_mapping( ttest_report_ctx_t *report, const cyaml_config_t *config) { struct value_s { short a; long b; }; static const unsigned char ref[] = "---\n" "sequence:\n" "- a: 123\n" " b: 9999\n" "- a: 4000\n" " b: 62000\n" "- a: 1\n" " b: 765\n" "...\n"; static const struct value_s seq[3] = { [0] = { .a = 123, .b = 9999 }, [1] = { .a = 4000, .b = 62000 }, [2] = { .a = 1, .b = 765 }, }; static const struct target_struct { const struct value_s *seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of mappings pointers. * * \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_save_mapping_entry_sequence_ptr_mapping_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const struct value_s { short a; long b; } v[3] = { { .a = 123, .b = 9999, }, { .a = 4000, .b = 62000, }, { .a = 1, .b = 765, }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- a: 123\n" " b: 9999\n" "- a: 4000\n" " b: 62000\n" "- a: 1\n" " b: 765\n" "...\n"; static const struct value_s *seq[3] = { [0] = &v[0], [1] = &v[1], [2] = &v[2], }; static const struct target_struct { const struct value_s **seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_field test_mapping_schema[] = { CYAML_FIELD_INT("a", CYAML_FLAG_DEFAULT, struct value_s, a), CYAML_FIELD_INT("b", CYAML_FLAG_DEFAULT, struct value_s, b), CYAML_FIELD_END }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct value_s, test_mapping_schema), }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of fixed-length sequences. * * \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_save_mapping_entry_sequence_ptr_sequence_fixed_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const int seq[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const struct target_struct { const int (*seq)[3]; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data.seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, **(data.seq), &entry_schema_int, CYAML_ARRAY_LEN(*data.seq)) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence of fixed-length sequences pointers. * * \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_save_mapping_entry_sequence_ptr_sequence_fixed_ptr_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const int v[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, { 10, 11, 12 }, }; static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const int *seq[] = { v[0], v[1], v[2], v[3], }; static const struct target_struct { const int **seq; uint32_t seq_count; } data = { .seq = seq, .seq_count = CYAML_ARRAY_LEN(seq), }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, **(data.seq)), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_POINTER, **(data.seq), &entry_schema_int, CYAML_ARRAY_LEN(*v)) }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_SEQUENCE("sequence", CYAML_FLAG_POINTER, struct target_struct, seq, &entry_schema, 0, CYAML_ARRAY_LEN(ref)), CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a flattened sequence of fixed-length sequences. * * \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_save_mapping_entry_sequence_ptr_sequence_fixed_flat_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "sequence:\n" "- - 1\n" " - 2\n" " - 3\n" "- - 4\n" " - 5\n" " - 6\n" "- - 7\n" " - 8\n" " - 9\n" "- - 10\n" " - 11\n" " - 12\n" "...\n"; static const int seq[12] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, }; static const struct target_struct { const int *seq; uint32_t seq_count; } data = { .seq = seq, /* Note: count is count of entries of the outer sequence * entries, so, 4, not 12. */ .seq_count = CYAML_ARRAY_LEN(seq) / 3, }; static const struct cyaml_schema_value entry_schema_int = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int), }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_SEQUENCE_FIXED( CYAML_FLAG_DEFAULT, int, &entry_schema_int, 3), }; static const struct cyaml_schema_field mapping_schema[] = { { .key = "sequence", .value = { .type = CYAML_SEQUENCE, .flags = CYAML_FLAG_POINTER, .data_size = sizeof(int[3]), .sequence = { .entry = &entry_schema, .min = 0, .max = CYAML_UNLIMITED, } }, .data_offset = offsetof(struct target_struct, seq), .count_size = sizeof(data.seq_count), .count_offset = offsetof(struct target_struct, seq_count), }, CYAML_FIELD_END, }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving optional mapping field. * * \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_save_mapping_entry_optional_uint( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_uint: 555\n" "...\n"; static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_OPTIONAL, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving optional mapping field with pointer 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_save_mapping_entry_optional_uint_ptr( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_uint: 555\n" "...\n"; static unsigned test_uint = 555; static const struct target_struct { unsigned *test_uint; } data = { .test_uint = &test_uint, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT_PTR("test_uint", CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving optional mapping field with NULL pointer 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_save_mapping_entry_optional_uint_ptr_null( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "--- {}\n" "...\n"; static const struct target_struct { unsigned *test_uint; } data = { .test_uint = NULL, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT_PTR("test_uint", CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving an unsigned integer. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_mapping_entry_ignored( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "test_uint: 555\n" "...\n"; static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_uint), CYAML_FIELD_IGNORE("foo", CYAML_FLAG_DEFAULT), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence containing a NULL 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_save_sequence_null_values_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref1[] = "---\n" "- 7\n" "- 6\n" "- 5\n" "- \n" "- 3\n" "- 2\n" "- \n" "- 0\n" "...\n"; /* As of libyaml 0.2.5, trailing spaces are not emitted. */ static const unsigned char ref2[] = "---\n" "- 7\n" "- 6\n" "- 5\n" "-\n" "- 3\n" "- 2\n" "-\n" "- 0\n" "...\n"; static const int d[] = { 7, 6, 5, 4, 3, 2, 1, 0 }; static const int *data[] = { d + 0, d + 1, d + 2, NULL, d + 4, d + 5, NULL, d + 7, }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER_NULL, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, 3), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_STYLE_BLOCK; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data, CYAML_ARRAY_LEN(data)); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if ((len != YAML_LEN(ref1) || memcmp(ref1, buffer, len) != 0) && (len != YAML_LEN(ref2) || memcmp(ref2, buffer, len) != 0)) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref1), YAML_LEN(ref1), ref1, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence containing a NULL 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_save_sequence_null_str_values_int( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "- 7\n" "- 6\n" "- 5\n" "- null\n" "- 3\n" "- 2\n" "- 1\n" "- 0\n" "...\n"; static const int d[] = { 7, 6, 5, 4, 3, 2, 1, 0 }; static const int *data[] = { d + 0, d + 1, d + 2, NULL, d + 4, d + 5, d + 6, d + 7, }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_POINTER_NULL_STR, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, 3), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_STYLE_BLOCK; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data, CYAML_ARRAY_LEN(data)); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving with schema with sequence_fixed top level type. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_schema_top_level_sequence_fixed( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "- 7\n" "- 6\n" "- 5\n" "...\n"; int data[3] = { 7, 6, 5 }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE_FIXED(CYAML_FLAG_POINTER, int, &entry_schema, 3) }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence with flow style configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_sequence_config_flow_style( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "--- [7, 6, 5]\n" "...\n"; int data[3] = { 7, 6, 5 }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, 3), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_STYLE_FLOW; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data, 3); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a sequence with block style configured. * * \param[in] report The test report context. * \param[in] config The CYAML config to use for the test. * \return true if test passes, false otherwise. */ static bool test_save_sequence_config_block_style( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "- 7\n" "- 6\n" "- 5\n" "...\n"; int data[3] = { 7, 6, 5 }; static const struct cyaml_schema_value entry_schema = { CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int) }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_SEQUENCE(CYAML_FLAG_POINTER, int, &entry_schema, 0, 3), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; cfg.flags |= CYAML_CFG_STYLE_BLOCK; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, data, 3); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a mapping with flow style 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_save_mapping_value_flow_style( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const char ref[] = "--- {a: 555, b: 99, c: 7}\n" "...\n"; static const struct target_struct { unsigned a; unsigned b; unsigned c; } data = { .a = 555, .b = 99, .c = 7, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_UINT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_UINT("c", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER | CYAML_FLAG_FLOW, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; cyaml_config_t cfg = *config; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving a mapping with block style 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_save_mapping_value_block_style( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "---\n" "a: 555\n" "b: 99\n" "c: 7\n" "...\n"; static const struct target_struct { unsigned a; unsigned b; unsigned c; } data = { .a = 555, .b = 99, .c = 7, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("a", CYAML_FLAG_DEFAULT, struct target_struct, a), CYAML_FIELD_UINT("b", CYAML_FLAG_DEFAULT, struct target_struct, b), CYAML_FIELD_UINT("c", CYAML_FLAG_DEFAULT, struct target_struct, c), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER | CYAML_FLAG_BLOCK, struct target_struct, mapping_schema), }; char *buffer = NULL; size_t len = 0; test_data_t td = { .buffer = &buffer, .config = config, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } err = cyaml_save_data(&buffer, &len, config, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Test saving without document delimiters. * * \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_save_no_document_delimiters( ttest_report_ctx_t *report, const cyaml_config_t *config) { static const unsigned char ref[] = "test_uint: 555\n"; static const struct target_struct { unsigned test_uint; } data = { .test_uint = 555, }; static const struct cyaml_schema_field mapping_schema[] = { CYAML_FIELD_UINT("test_uint", CYAML_FLAG_DEFAULT, struct target_struct, test_uint), CYAML_FIELD_END }; static const struct cyaml_schema_value top_schema = { CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct target_struct, mapping_schema), }; cyaml_config_t cfg = *config; char *buffer; size_t len; test_data_t td = { .buffer = &buffer, .config = &cfg, }; cyaml_err_t err; ttest_ctx_t tc; if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) { return true; } cfg.flags &= ~((unsigned)CYAML_CFG_DOCUMENT_DELIM); err = cyaml_save_data(&buffer, &len, &cfg, &top_schema, &data, 0); if (err != CYAML_OK) { return ttest_fail(&tc, cyaml_strerror(err)); } if (len != YAML_LEN(ref) || memcmp(ref, buffer, len) != 0) { return ttest_fail(&tc, "Bad data:\n" "EXPECTED (%zu):\n\n%.*s\n\n" "GOT (%zu):\n\n%.*s\n", YAML_LEN(ref), YAML_LEN(ref), ref, len, len, buffer); } return ttest_pass(&tc); } /** * Run the YAML saving unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool save_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DOCUMENT_DELIM, }; ttest_heading(rc, "Save single entry mapping tests: simple types"); pass &= test_save_mapping_entry_uint(rc, &config); pass &= test_save_mapping_entry_float(rc, &config); pass &= test_save_mapping_entry_double(rc, &config); pass &= test_save_mapping_entry_string(rc, &config); pass &= test_save_mapping_entry_int_64(rc, &config); pass &= test_save_mapping_entry_int_pos(rc, &config); pass &= test_save_mapping_entry_int_neg(rc, &config); pass &= test_save_mapping_entry_bool_true(rc, &config); pass &= test_save_mapping_entry_bool_false(rc, &config); pass &= test_save_mapping_entry_string_ptr(rc, &config); pass &= test_save_mapping_entry_enum_strict(rc, &config); pass &= test_save_mapping_entry_enum_number(rc, &config); pass &= test_save_mapping_entry_enum_sparse(rc, &config); ttest_heading(rc, "Save single entry mapping tests: scalar style"); pass &= test_save_mapping_entry_string_ptr_plain(rc, &config); pass &= test_save_mapping_entry_string_ptr_single(rc, &config); pass &= test_save_mapping_entry_string_ptr_double(rc, &config); pass &= test_save_mapping_entry_string_ptr_folded(rc, &config); pass &= test_save_mapping_entry_string_ptr_literal(rc, &config); ttest_heading(rc, "Save single entry mapping tests: complex types"); pass &= test_save_mapping_entry_mapping(rc, &config); pass &= test_save_mapping_entry_bitfield(rc, &config); pass &= test_save_mapping_entry_mapping_ptr(rc, &config); pass &= test_save_mapping_entry_flags_strict(rc, &config); pass &= test_save_mapping_entry_flags_number(rc, &config); pass &= test_save_mapping_entry_flags_sparse(rc, &config); pass &= test_save_mapping_entry_bitfield_sparse(rc, &config); ttest_heading(rc, "Save single entry mapping tests: sequences"); pass &= test_save_mapping_entry_sequence_int(rc, &config); pass &= test_save_mapping_entry_sequence_uint(rc, &config); pass &= test_save_mapping_entry_sequence_enum(rc, &config); pass &= test_save_mapping_entry_sequence_bool(rc, &config); pass &= test_save_mapping_entry_sequence_flags(rc, &config); pass &= test_save_mapping_entry_sequence_string(rc, &config); pass &= test_save_mapping_entry_sequence_mapping(rc, &config); pass &= test_save_mapping_entry_sequence_string_ptr(rc, &config); pass &= test_save_mapping_entry_sequence_mapping_ptr(rc, &config); pass &= test_save_mapping_entry_sequence_sequence_fixed_int(rc, &config); pass &= test_save_mapping_entry_sequence_sequence_fixed_ptr_int(rc, &config); pass &= test_save_mapping_entry_sequence_sequence_fixed_flat_int(rc, &config); ttest_heading(rc, "Save single entry mapping tests: ptr sequences"); pass &= test_save_mapping_entry_sequence_ptr_int(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_enum(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_uint(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_bool(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_flags(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_string(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_mapping(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_string_ptr(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_mapping_ptr(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_sequence_fixed_int(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_sequence_fixed_ptr_int(rc, &config); pass &= test_save_mapping_entry_sequence_ptr_sequence_fixed_flat_int(rc, &config); ttest_heading(rc, "Save tests: ptr sequence with null values"); pass &= test_save_sequence_null_values_int(rc, &config); pass &= test_save_sequence_null_str_values_int(rc, &config); ttest_heading(rc, "Save tests: optional mapping fields"); pass &= test_save_mapping_entry_optional_uint(rc, &config); pass &= test_save_mapping_entry_optional_uint_ptr(rc, &config); pass &= test_save_mapping_entry_optional_uint_ptr_null(rc, &config); ttest_heading(rc, "Save tests: various"); pass &= test_save_mapping_entry_ignored(rc, &config); pass &= test_save_no_document_delimiters(rc, &config); pass &= test_save_mapping_value_flow_style(rc, &config); pass &= test_save_mapping_value_block_style(rc, &config); pass &= test_save_sequence_config_flow_style(rc, &config); pass &= test_save_sequence_config_block_style(rc, &config); pass &= test_save_schema_top_level_sequence_fixed(rc, &config); return pass; } libcyaml-1.4.1/test/units/test.c000066400000000000000000000034721443462360400165710ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2017-2021 Michael Drake */ #include #include #include #include #include #include #include "ttest.h" #include "test.h" /** * Print program usage * * \param[in] prog_name Name of program. */ static void usage(const char *prog_name) { fprintf(stderr, "Usage: %s [-q|-v|-d]\n", prog_name); } /** * Main entry point from OS. * * \param[in] argc Argument count. * \param[in] argv Vector of string arguments. * \return Program return code. */ int main(int argc, char *argv[]) { bool pass = true; bool quiet = false; ttest_report_ctx_t rc; const char *test_list = NULL; cyaml_log_fn_t log_fn = cyaml_log; cyaml_log_t log_level = CYAML_LOG_ERROR; enum { ARG_PROG_NAME, ARG_VERBOSE, ARG_TEST_LIST, ARG__COUNT, }; if (argc > ARG__COUNT) { usage(argv[ARG_PROG_NAME]); return EXIT_FAILURE; } for (int i = 1; i < argc; i++) { if ((strcmp(argv[i], "-q") == 0) || (strcmp(argv[i], "--quiet") == 0)) { quiet = true; log_fn = NULL; } else if ((strcmp(argv[i], "-v") == 0) || (strcmp(argv[i], "--verbose") == 0)) { log_level = CYAML_LOG_INFO; } else if ((strcmp(argv[i], "-d") == 0) || (strcmp(argv[i], "--debug") == 0)) { log_level = CYAML_LOG_DEBUG; } else { test_list = argv[i]; } } rc = ttest_init(test_list, quiet); pass &= utf8_tests(&rc, log_level, log_fn); pass &= util_tests(&rc, log_level, log_fn); pass &= free_tests(&rc, log_level, log_fn); pass &= load_tests(&rc, log_level, log_fn); pass &= errs_tests(&rc, log_level, log_fn); pass &= file_tests(&rc, log_level, log_fn); pass &= save_tests(&rc, log_level, log_fn); ttest_report(&rc); return (pass) ? EXIT_SUCCESS : EXIT_FAILURE; } libcyaml-1.4.1/test/units/test.h000066400000000000000000000017211443462360400165710ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2021 Michael Drake */ #ifndef TEST_H #define TEST_H /** In load.c */ extern bool load_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In file.c */ extern bool file_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In free.c */ extern bool free_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In utf8.c */ extern bool utf8_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In util.c */ extern bool util_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In errs.c */ extern bool errs_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); /** In save.c */ extern bool save_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn); #endif libcyaml-1.4.1/test/units/ttest.h000066400000000000000000000155041443462360400167610ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2013-2019 Michael Drake */ #ifndef TTEST_H #define TTEST_H #include /** * Test cleanup client callback. * * The caller passes this callback function to ttest_start, and it is called * in whichever tlsa-test function finishes the test (ttest_pass, ttest_fail, * or ttest_todo). * * \param[in] Client data to clean up. */ typedef void (*ttest_cleanup_fn)(void *data); /** * Internal tlsa-test report context. * * Clients should not touch this directly, it is only exposed in this * header because tlsa-test is arranged to be header-only, for convenience * of utilisation. */ typedef struct ttest_report_ctx { /** Space/comma separated list of tests to run. */ const char *test_list; size_t test_list_len; bool quiet; /**< Whether to print only the report summary. */ unsigned tests; /**< Number of tests started. */ unsigned todo; /**< Number of tests marked as unimplemented. */ unsigned passed; /**< Number of tests passed. */ } ttest_report_ctx_t; /** * Internal tlsa-test test context. * * Clients should not touch this directly, it is only exposed in this * header because tlsa-test is arranged to be header-only, for convenience * of utilisation. */ typedef struct ttest_ctx { ttest_report_ctx_t *report; /**< The tlsa-test report context. */ const char *name; /**< The unit test name. */ ttest_cleanup_fn cleanup; /**< Client's unit test cleanup function. */ void *cleanup_data; /**< Client's unit test cleanup context. */ } ttest_ctx_t; /** * Initialise a tlsa-test report context. * * \param[in] test_list Space/comma separated list of tests to run. * \param[in] quiet Whether report should be a minimal summary. * \return initialised tlsa-test report context. */ static inline ttest_report_ctx_t ttest_init( const char *test_list, bool quiet) { ttest_report_ctx_t rc = { .test_list = test_list, .test_list_len = 0, .quiet = quiet, }; if (test_list != NULL) { rc.test_list_len = strlen(test_list); } return rc; } /** * Determine whether test of given name should be run. * * \param[in] report The tlsa-test report context. * \param[in] name The name of the test to consider. * \return true if test should be run, false otherwise. */ static inline bool ttest__run_test( ttest_report_ctx_t *report, const char *name) { size_t len; size_t pos = 0; size_t name_len; if (report->test_list == NULL || report->test_list_len == 0) { return true; } name_len = strlen(name); while (pos < report->test_list_len) { /* Skip commas and spaces. */ pos += strspn(report->test_list + pos, ", "); len = strcspn(report->test_list + pos, ", "); if (len == name_len) { if (memcmp(report->test_list + pos, name, len) == 0) { return true; } } pos += len; } return false; } /** * Start a unit test. * * The when complete, the test must be competed by calling either ttest_pass, * ttest_fail, or ttest_todo. * * \param[in] report The tlsa-test report context. * \param[in] name Name for this unit test. * \param[in] cleanup Cleanup function to call on test completion. * \param[in] cleanup_data Pointer to client cleanup context. * \param[out] test_ctx_out Returns the unit test context on success. * The test context must be passed to the test * completion function. * \return true if the test should be started, false otherwise. */ static inline bool ttest_start( ttest_report_ctx_t *report, const char *name, ttest_cleanup_fn cleanup, void *cleanup_data, ttest_ctx_t *test_ctx_out) { ttest_ctx_t tc = { .name = name, .report = report, .cleanup = cleanup, .cleanup_data = cleanup_data, }; if (!ttest__run_test(report, name)) { return false; } report->tests++; *test_ctx_out = tc; return true; } /** * Pass a unit test. * * This function competes a unit test. It should be called when a test passes. * This function always returns true. * * \param[in] tc Unit test context, returned by ttest_start. * \return true. */ static inline bool ttest_pass( const ttest_ctx_t *tc) { assert(tc != NULL); assert(tc->report != NULL); tc->report->passed++; if (tc->cleanup != NULL) { tc->cleanup(tc->cleanup_data); } if (tc->report->quiet == false) { fprintf(stderr, " PASS: %s\n", tc->name); } return true; } /** * Fail a unit test. * * This function competes a unit test. It should be called when a test fails. * This function always returns false. Prints the test result. * * \param[in] tc Unit test context, returned by ttest_start. * \param[in] reason Format string to explain reason for test failure. * \param[in] ... Additional arguments for formatted rendering. * \return false. */ static inline bool ttest_fail( const ttest_ctx_t *tc, const char *reason, ...) { va_list args; assert(tc != NULL); assert(tc->report != NULL); fprintf(stderr, " FAIL: %s (", tc->name); va_start(args, reason); vfprintf(stderr, reason, args); va_end(args); fprintf(stderr, ")\n"); /* Cleanup after printing result, in case `reason` refers to cleaned up * memory. */ if (tc->cleanup != NULL) { tc->cleanup(tc->cleanup_data); } return false; } /** * Make a unit test as unimplemented. * * This function competes a unit test. Should be called on unimplemented tests. * This function always returns true. * * \param[in] tc Unit test context, returned by ttest_start. * \return true. */ static inline bool ttest_todo( const ttest_ctx_t *tc) { assert(tc != NULL); assert(tc->report != NULL); tc->report->todo++; if (tc->cleanup != NULL) { tc->cleanup(tc->cleanup_data); } if (tc->report->quiet == false) { fprintf(stderr, " TODO: %s\n", tc->name); } return true; } /** * Print a visual divider in the test output. */ static inline void ttest_divider(void) { fprintf(stderr, "========================================" "========================================\n"); } /** * Print a test heading. * * \param[in] tr The tlsa-test report context. * \param[in] heading The heading to print. */ static inline void ttest_heading( const ttest_report_ctx_t *tr, const char *heading) { if (tr->test_list == NULL || tr->test_list_len == 0) { if (!tr->quiet) { ttest_divider(); fprintf(stderr, "TEST: %s\n", heading); ttest_divider(); } } } /** * Print the test report summary. * * \param[in] tr The tlsa-test report context. */ static inline void ttest_report( const ttest_report_ctx_t *tr) { ttest_divider(); if (tr->todo > 0) { fprintf(stderr, "TODO: %u test%s unimplemented.\n", tr->todo, (tr->todo > 1) ? "s" : ""); } fprintf(stderr, "%s: %u of %u tests passed.\n", (tr->passed == tr->tests - tr->todo) ? "PASS" : "FAIL", tr->passed, tr->tests - tr->todo); ttest_divider(); } #endif libcyaml-1.4.1/test/units/utf8.c000066400000000000000000000155501443462360400165000ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2021 Michael Drake */ #include #include #include #include #include "../../src/utf8.h" #include "ttest.h" #include "test.h" /** Helper macro to squash unused variable warnings. */ #define UNUSED(_x) ((void)(_x)) /** Helper macro to get the length of string string literals. */ #define SLEN(_s) (CYAML_ARRAY_LEN(_s) - 1) /** * Test utf-8 decoding. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_utf8_get_codepoint( ttest_report_ctx_t *report) { static const struct tests { unsigned c; const char *s; unsigned l; } t[] = { { 0xfffd, "\ufffd", SLEN("\ufffd") }, { 0xfffd, "\xC1\x9C", SLEN("\xC1\x9C") }, { 0x1f638, u8"😸", SLEN(u8"😸") }, { 0xfffd, u8"😸", 0 }, { 0xfffd, u8"😸", 5 }, }; bool pass = true; for (unsigned i = 0; i < CYAML_ARRAY_LEN(t); i++) { unsigned l; unsigned c; ttest_ctx_t tc; char name[sizeof(__func__) + 32]; sprintf(name, "%s_%u", __func__, i); if (!ttest_start(report, name, NULL, NULL, &tc)) { continue; } l = t[i].l; c = cyaml_utf8_get_codepoint((uint8_t *)t[i].s, &l); if (c != t[i].c) { pass &= ttest_fail(&tc, "Incorrect codepoint for %s " "(expecting %4.4x, got %4.4x)", t[i].s, t[i].c, c); continue; } pass &= ttest_pass(&tc); } return pass; } /** * Test comparing the same strings. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_utf8_strcmp_same( ttest_report_ctx_t *report) { const char *strings[] = { "Simple", "test", "This is a LONGER string, if you see what I mean.", "29087 lsdkfj ", u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß", u8"àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ", u8"¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿", "\xc3\0", u8"αβγδε", u8"¯\\_(ツ)_/¯", "\xfa", u8"😸", }; bool pass = true; for (unsigned i = 0; i < CYAML_ARRAY_LEN(strings); i++) { ttest_ctx_t tc; char name[sizeof(__func__) + 32]; sprintf(name, "%s_%u", __func__, i); if (!ttest_start(report, name, NULL, NULL, &tc)) { continue; } if (cyaml_utf8_casecmp(strings[i], strings[i]) != 0) { pass &= ttest_fail(&tc, "Failed to match: %s", strings[i]); continue; } pass &= ttest_pass(&tc); } return pass; } /** * Test comparing strings that match. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_utf8_strcmp_matches( ttest_report_ctx_t *report) { static const struct string_pairs { const char *a; const char *b; } pairs[] = { { "", "" }, { "This is a TEST", "this is A test" }, { u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ", u8"àáâãäåæçèéêëìíîïðñòóôõö" }, { u8"ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞ", u8"āăąćĉċčďđēĕėęěĝğ" }, { u8"ijĵķĸĺļľ", u8"IJĴĶĸĹĻĽ" }, { u8"ŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶ", u8"ŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷ" }, { u8"űųŵŷŸźżž", u8"űųŵŷÿŹŻŽ" }, { u8"ƂƄ ơƣƥ", u8"ƃƅ ƠƢƤ" }, { u8"ǞǠǢǤǦǨǪǬǮ", u8"ǟǡǣǥǧǩǫǭǯ" }, { u8"ǸǺǼǾȀȂȄȆȈȊȌȎȐȒȔȖȘȚȜȞ", u8"ǹǻǽǿȁȃȅȇȉȋȍȏȑȓȕȗșțȝȟ" }, { u8"ȢȤȦȨȪȬȮȰȲ", u8"ȣȥȧȩȫȭȯȱȳ" }, { u8"ɇɉɋɍɏ", u8"ɆɈɊɌɎ" }, { u8"ƯźżžƳƵ", u8"ưŹŻŽƴƶ" }, { u8"ǍǏǑǓǕǗǙǛ", u8"ǎǐǒǔǖǘǚǜ" }, { u8"\u0178", u8"\u00ff" }, { u8"\u0187", u8"\u0188" }, { u8"\u018b", u8"\u018c" }, { u8"\u018e", u8"\u01dd" }, { u8"\u0191", u8"\u0192" }, { u8"\u0198", u8"\u0199" }, { u8"\u01a7", u8"\u01a8" }, { u8"\u01ac", u8"\u01ad" }, { u8"\u01af", u8"\u01b0" }, { u8"\u01b7", u8"\u0292" }, { u8"\u01b8", u8"\u01b9" }, { u8"\u01bc", u8"\u01bd" }, { u8"\u01c4", u8"\u01c6" }, { u8"\u01c5", u8"\u01c6" }, { u8"\u01c7", u8"\u01c9" }, { u8"\u01c8", u8"\u01c9" }, { u8"\u01ca", u8"\u01cc" }, { u8"\u01cb", u8"\u01cc" }, { u8"\u01f1", u8"\u01f3" }, { u8"\u01f2", u8"\u01f3" }, { u8"\u01f4", u8"\u01f5" }, { u8"\u01f7", u8"\u01bf" }, { u8"\u0220", u8"\u019e" }, { u8"\u023b", u8"\u023c" }, { u8"\u023d", u8"\u019a" }, { u8"\u0241", u8"\u0242" }, { u8"\u0243", u8"\u0180" }, { "\xF0\x9F\x98\xB8", "\xF0\x9F\x98\xB8" }, { "\xF0\x00\x98\xB8", "\xF0\x00\x98\xB8" }, { "\xF0\x9F\x00\xB8", "\xF0\x9F\x00\xB8" }, { "\xF0\x9F\x98\x00", "\xF0\x9F\x98\x00" }, { "\xE2\x9F\x9A", "\xE2\x9F\x9A" }, { "\xE2\x00\x9A", "\xE2\x00\x9A" }, { "\xE2\x9F\x00", "\xE2\x9F\x00" }, { "A\xc2""C", "A\xc2""C" }, { "A\xc2""C", u8"A\ufffdC" }, { u8"A\ufffdC", "A\xc2""C" }, }; bool pass = true; for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) { ttest_ctx_t tc; char name[sizeof(__func__) + 32]; sprintf(name, "%s_%u", __func__, i); if (!ttest_start(report, name, NULL, NULL, &tc)) { continue; } if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) != 0) { pass &= ttest_fail(&tc, "Failed to match strings: " "%s and %s", pairs[i].a, pairs[i].b); continue; } pass &= ttest_pass(&tc); } return pass; } /** * Test comparing strings that match. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_utf8_strcmp_mismatches( ttest_report_ctx_t *report) { static const struct string_pairs { const char *a; const char *b; } pairs[] = { { "Invalid", "\xfa" }, { "Cat", u8"😸" }, { "cat", u8"😸" }, { "1 cat", u8"😸" }, { "[cat]", u8"😸" }, { "Ü cat", u8"😸" }, { "Ü cat", u8"😸" }, { "\\", "\xC1\x9C" }, }; bool pass = true; for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) { ttest_ctx_t tc; char name[sizeof(__func__) + 32]; sprintf(name, "%s_%u", __func__, i); if (!ttest_start(report, name, NULL, NULL, &tc)) { continue; } if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) == 0) { pass &= ttest_fail(&tc, "Failed to detect mismatch: " "%s and %s", pairs[i].a, pairs[i].b); continue; } pass &= ttest_pass(&tc); } return pass; } /** * Run the CYAML util unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool utf8_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; UNUSED(log_level); UNUSED(log_fn); ttest_heading(rc, "UTF-8 tests: Codepoint composition"); pass &= test_utf8_get_codepoint(rc); ttest_heading(rc, "UTF-8 tests: String comparison"); pass &= test_utf8_strcmp_same(rc); pass &= test_utf8_strcmp_matches(rc); pass &= test_utf8_strcmp_mismatches(rc); return pass; } libcyaml-1.4.1/test/units/util.c000066400000000000000000000154041443462360400165650ustar00rootroot00000000000000/* * SPDX-License-Identifier: ISC * * Copyright (C) 2018-2021 Michael Drake */ #include #include #include #include #include #include "../../src/mem.h" #include "../../src/util.h" #include "ttest.h" #include "test.h" /** * Test cyaml memory functions. * * \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_util_memory_funcs( ttest_report_ctx_t *report, const cyaml_config_t *config) { ttest_ctx_t tc; unsigned char *mem, *tmp; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; /* Create test allocation */ mem = cyaml__alloc(config, 0xff, true); if (mem == NULL) { return ttest_fail(&tc, "Memory allocation failed."); } /* Check allocation was zeroed. */ for (unsigned i = 0; i < 0x7f; i++) { if (mem[i] != 0) { return ttest_fail(&tc, "Allocation not cleaned."); } } /* Set our own known values */ for (unsigned i = 0; i < 0xff; i++) { mem[i] = 0xff; } /* Shrink allocation */ tmp = cyaml__realloc(config, mem, 0xff, 0x7f, true); if (tmp == NULL) { return ttest_fail(&tc, "Allocation shrink failed."); } mem = tmp; /* Check for our own values. */ for (unsigned i = 0; i < 0x7f; i++) { if (mem[i] != 0xff) { return ttest_fail(&tc, "Data trampled by realloc."); } } /* Free test allocation. */ cyaml__free(config, mem); return ttest_pass(&tc); } /** * Test cyaml memory functions. * * \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_util_strdup( ttest_report_ctx_t *report, const cyaml_config_t *config) { ttest_ctx_t tc; const char *orig = "Hello!"; char *copy; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; /* Create test allocation */ copy = cyaml__strdup(config, orig, NULL); if (copy == NULL) { return ttest_fail(&tc, "Memory allocation failed."); } /* Confirm string duplication. */ if (strcmp(orig, copy) != 0) { return ttest_fail(&tc, "String not copied correctly."); } /* Free test allocation. */ cyaml__free(config, copy); return ttest_pass(&tc); } /** * Test cyaml memory functions. * * \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_util_strdup_len( ttest_report_ctx_t *report, const cyaml_config_t *config) { ttest_ctx_t tc; const char *orig = "Hello!"; char *copy; size_t len; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; /* Create test allocation */ copy = cyaml__strdup(config, orig, &len); if (copy == NULL) { return ttest_fail(&tc, "Memory allocation failed."); } /* Confirm string duplication. */ if (strcmp(orig, copy) != 0) { return ttest_fail(&tc, "String not copied correctly."); } /* Confirm string length. */ if (strlen(orig) != len) { return ttest_fail(&tc, "Bad string length."); } /* Free test allocation. */ cyaml__free(config, copy); return ttest_pass(&tc); } /** * Test invalid state machine state. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_util_state_invalid( ttest_report_ctx_t *report) { ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT), "") != 0) { return ttest_fail(&tc, "Wrong string for invalid state."); } if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT + 1), "") != 0) { return ttest_fail(&tc, "Wrong string for invalid state."); } return ttest_pass(&tc); } /** * Test CYAML_OK error code. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_util_err_success_zero( ttest_report_ctx_t *report) { ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; if (CYAML_OK != 0) { return ttest_fail(&tc, "CYAML_OK value not zero"); } return ttest_pass(&tc); } /** * Test valid cyaml_strerror strings. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_util_err_strings_valid( ttest_report_ctx_t *report) { ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; if (cyaml_strerror(CYAML_OK) == NULL) { return ttest_fail(&tc, "CYAML_OK string is NULL"); } if (strcmp(cyaml_strerror(CYAML_OK), "Success") != 0) { return ttest_fail(&tc, "CYAML_OK string not 'Success'"); } for (unsigned i = 1; i <= CYAML_ERR__COUNT; i++) { if (cyaml_strerror(i) == NULL) { return ttest_fail(&tc, "Error code %u string is NULL", i); } for (unsigned j = 0; j < i; j++) { if (strcmp(cyaml_strerror(j), cyaml_strerror(i)) == 0) { return ttest_fail(&tc, "Two error codes " "have same string (%u and %u)", i, j); } } } return ttest_pass(&tc); } /** * Test invalid cyaml_strerror strings. * * \param[in] report The test report context. * \return true if test passes, false otherwise. */ static bool test_util_err_strings_invalid( ttest_report_ctx_t *report) { ttest_ctx_t tc; if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true; if (strcmp(cyaml_strerror(CYAML_ERR__COUNT), "Invalid error code") != 0) { return ttest_fail(&tc, "CYAML_ERR__COUNT string not " "'Invalid error code'"); } if (strcmp(cyaml_strerror(CYAML_ERR__COUNT + 1), "Invalid error code") != 0) { return ttest_fail(&tc, "CYAML_ERR__COUNT + 1 string not " "'Invalid error code'"); } if (strcmp(cyaml_strerror((cyaml_err_t)-1), "Invalid error code") != 0) { return ttest_fail(&tc, "-1 string not 'Invalid error code'"); } return ttest_pass(&tc); } /** * Run the CYAML util unit tests. * * \param[in] rc The ttest report context. * \param[in] log_level CYAML log level. * \param[in] log_fn CYAML logging function, or NULL. * \return true iff all unit tests pass, otherwise false. */ bool util_tests( ttest_report_ctx_t *rc, cyaml_log_t log_level, cyaml_log_fn_t log_fn) { bool pass = true; cyaml_config_t config = { .log_fn = log_fn, .mem_fn = cyaml_mem, .log_level = log_level, .flags = CYAML_CFG_DEFAULT, }; ttest_heading(rc, "Memory utility functions"); pass &= test_util_strdup(rc, &config); pass &= test_util_strdup_len(rc, &config); pass &= test_util_memory_funcs(rc, &config); ttest_heading(rc, "Error code tests"); pass &= test_util_state_invalid(rc); pass &= test_util_err_success_zero(rc); pass &= test_util_err_strings_valid(rc); pass &= test_util_err_strings_invalid(rc); return pass; }