diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a34739d3..640bb1ec3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: options: "-DENABLE_TESTS=ON", packager: "sudo apt-get", # no expect because stdout seems to be redirected - packages: "libcmocka-dev libxxhash-dev", + packages: "libcmocka-dev libxxhash-dev libcbor-dev", snaps: "", build-cmd: "make" } @@ -36,7 +36,7 @@ jobs: cc: "clang", options: "-DENABLE_TESTS=ON", packager: "sudo apt-get", - packages: "libcmocka-dev libxxhash-dev", + packages: "libcmocka-dev libxxhash-dev libcbor-dev", snaps: "", build-cmd: "make" } @@ -47,7 +47,7 @@ jobs: cc: "gcc", options: "", packager: "sudo apt-get", - packages: "libcmocka-dev libxxhash-dev valgrind", + packages: "libcmocka-dev libxxhash-dev libcbor-dev valgrind", snaps: "", build-cmd: "make" } @@ -59,7 +59,7 @@ jobs: options: "", packager: "sudo apt-get", # no valgrind because it does not support DWARF5 yet generated by clang 14 - packages: "libcmocka-dev libxxhash-dev", + packages: "libcmocka-dev libxxhash-dev libcbor-dev", snaps: "", build-cmd: "make" } @@ -70,7 +70,7 @@ jobs: cc: "clang", options: "-DENABLE_TESTS=ON -DPATH_EXPECT=", packager: "brew", - packages: "cmocka xxhash tcl-tk", + packages: "cmocka xxhash tcl-tk libcbor", snaps: "", build-cmd: "make" } @@ -81,7 +81,7 @@ jobs: cc: "clang", options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_TESTS=ON -DENABLE_VALGRIND_TESTS=OFF", packager: "sudo apt-get", - packages: "libcmocka-dev libxxhash-dev", + packages: "libcmocka-dev libxxhash-dev libcbor-dev", snaps: "", build-cmd: "make" } @@ -282,7 +282,7 @@ jobs: prepare: | echo "Installing dependencies..." pkg update -f - pkg install -y cmake cmocka xxhash bash git pcre2 + pkg install -y cmake cmocka xxhash bash git pcre2 libcbor run: | set -e @@ -308,7 +308,7 @@ jobs: i386/ubuntu \ /bin/bash -c " \ apt-get update -q && \ - apt-get install -y -q git cmake gcc libcmocka-dev libxxhash-dev libpcre2-dev && \ + apt-get install -y -q git cmake gcc libcmocka-dev libxxhash-dev libpcre2-dev libcbor-dev && \ mkdir build && cd build && \ cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=ON .. && \ make && \ @@ -338,7 +338,7 @@ jobs: # install dependencies inside the emulated container install: | apt-get update -q -y - apt-get install -q -y git cmake gcc libcmocka-dev libxxhash-dev libpcre2-dev + apt-get install -q -y git cmake gcc libcmocka-dev libxxhash-dev libpcre2-dev libcbor-dev # run the build run: | diff --git a/src/lcbor.c b/src/lcbor.c index e8e432b6f..8997cce79 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -50,6 +50,8 @@ void lycbor_ctx_free(struct lycbor_ctx *cborctx) { if (cborctx) { + ly_log_location_revert(0, 0, 1); + if (cborctx->cbor_data) { cbor_decref(&cborctx->cbor_data); } diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 8beeb75f0..ac0579d6c 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -663,7 +663,6 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st LY_ERR rc = LY_SUCCESS, r; enum cbor_type type; const char *expected; - ly_bool in_parent = 0; const char *name, *prefix = NULL; size_t name_len, prefix_len = 0; struct lys_module *mod; @@ -693,7 +692,8 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st next_entry: instance++; if (!node || (node->schema != prev->schema)) { - LOGVAL(ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); + LOGVAL(ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", + instance, prev->schema->module->name, prev->schema->name); rc = LY_EVALID; goto cleanup; } @@ -726,7 +726,6 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st case LYS_NOTIF: case LYS_ACTION: case LYS_RPC: - in_parent = 1; expected = "@/object"; LY_CHECK_GOTO(type != CBOR_TYPE_MAP, representation_error); break; @@ -862,11 +861,9 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st * @param[in,out] first_p Pointer to the first sibling. * @param[in,out] node_p Pointer to the node to insert. * @param[in] last If set, insert at the end. - * @param[in] ext Extension instance. */ static void -lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last, - struct lysc_ext_instance *ext) +lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last) { if (!*node_p) { return; @@ -998,7 +995,7 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le /* continue with next instance */ assert(*node_p); lydcbor_maintain_children(parent, first_p, node_p, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT); LOG_LOCBACK(0); @@ -1303,20 +1300,22 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str if (lydcbor_is_null(handle[0])) { /* [null] case */ - goto cleanup; + str_val = strdup(""); + str_len = 0; } } + if (!str_val) { + if ((type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && (type != CBOR_TYPE_NEGINT) && + (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) { + rc = LY_ENOT; + goto cleanup; + } - if ((type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && (type != CBOR_TYPE_NEGINT) && - (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) { - rc = LY_ENOT; - goto cleanup; + /* create terminal node */ + r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } - /* create terminal node */ - r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, str_val, str_len * 8, NULL, LY_VALUE_CBOR, NULL, type_hints, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1478,6 +1477,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* parse opaq */ r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, value_item, first_p, &node); free(key_str); + key_str = NULL; LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } } else { @@ -1532,7 +1532,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); lydcbor_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT); } break; case LYS_LEAF: @@ -1568,7 +1568,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* finally connect the parsed node */ lydcbor_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT); } /* success */ @@ -1654,7 +1654,7 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) { + if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && *first_p && (*first_p)->next) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); diff --git a/src/parser_data.h b/src/parser_data.h index 7c00a7ad7..3af394ca5 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -274,7 +274,8 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char * @param[in] data Pointer to the memory buffer containing the data to parse. * @param[in] data_len Length of the memory buffer. * @param[in] format Data format (e.g., XML, JSON, LYD_LYB). - * @param[in] options Parsing options, see @ref dataparseroptions. + * @param[in] parse_options Options for parser, see @ref dataparseroptions. + * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions. * @param[in] ctx_node Optional context node for parsing (can be NULL). * @param[out] tree Pointer to the resulting data tree (set on success). * @return LY_ERR value indicating success or error reason. diff --git a/src/parser_internal.h b/src/parser_internal.h index 204b3a2c3..deab929f6 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -70,7 +70,7 @@ typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx); /** * @brief Internal (common) context for YANG data parsers. * - * Covers ::lyd_xml_ctx, ::lyd_json_ctx and ::lyd_lyb_ctx. + * Covers ::lyd_xml_ctx, ::lyd_json_ctx, ::lyd_lyb_ctx and ::lyd_cbor_ctx. */ struct lyd_ctx { uint32_t parse_opts; /**< various @ref dataparseroptions. */ @@ -196,9 +196,9 @@ struct lyd_cbor_ctx { /* callbacks */ lyd_ctx_free_clb free; /**< destructor */ + struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ struct ly_set ext_node; /**< set of nodes with extension instances to validate */ - struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */ }; diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c index 860ca7e12..8d16e87d1 100644 --- a/src/plugins_types/instanceid.c +++ b/src/plugins_types/instanceid.c @@ -269,7 +269,11 @@ lyplg_type_print_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_v { char *ret; - if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) { + switch (format) { + case LY_VALUE_CANON: + case LY_VALUE_JSON: + case LY_VALUE_LYB: + case LY_VALUE_CBOR: if (dynamic) { *dynamic = 0; } @@ -277,6 +281,8 @@ lyplg_type_print_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_v *value_size_bits = strlen(value->_canonical) * 8; } return value->_canonical; + default: + break; } /* print the value in the specific format */ diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c index 112679721..18171a143 100644 --- a/src/plugins_types/xpath1.0.c +++ b/src/plugins_types/xpath1.0.c @@ -468,7 +468,7 @@ lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value /* LY_VALUE_STR_NS should never be transformed */ if ((val->format == LY_VALUE_STR_NS) || (format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || - (format == LY_VALUE_LYB)) { + (format == LY_VALUE_LYB) || (format == LY_VALUE_CBOR)) { /* canonical */ if (dynamic) { *dynamic = 0; diff --git a/src/printer_cbor.c b/src/printer_cbor.c index 1fd9a33a0..fe11441ad 100644 --- a/src/printer_cbor.c +++ b/src/printer_cbor.c @@ -909,9 +909,6 @@ cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_ *array_p = cbor_new_indefinite_array(); LY_CHECK_RET(!*array_p, LY_EMEM); - key = cbor_print_member_name(pctx, node, 0); - LY_CHECK_ERR_RET(!key, cbor_decref(array_p), LY_EMEM); - LY_CHECK_RET(cbor_print_array_open(pctx, node)); } @@ -942,6 +939,7 @@ cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_ } if (cbor_print_array_is_last_inst(pctx, node)) { + key = cbor_print_member_name(pctx, node, 0); /* add completed array to parent map */ if (key) { struct cbor_pair pair = { @@ -956,6 +954,9 @@ cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_ cbor_decref(&pair.value); return LY_EMEM; } + } else { + cbor_decref(array_p); + return LY_EMEM; } cbor_print_array_close(pctx); *array_p = NULL; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d93fae715..b92420ed4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,9 @@ function(ly_add_utest) # Set common attributes of all tests set_target_properties(${TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS}) + if (CBOR_FOUND) + target_link_libraries(${TEST_NAME} ${CBOR_LIBRARY}) + endif() if (NOT WIN32) target_link_libraries(${TEST_NAME} m) else() diff --git a/tests/modules/yang/cbor-test.yang b/tests/modules/yang/cbor-test.yang new file mode 100644 index 000000000..af653caa7 --- /dev/null +++ b/tests/modules/yang/cbor-test.yang @@ -0,0 +1,232 @@ +module cbor-test { + yang-version 1.1; + namespace "urn:cbor:test"; + prefix ct; + + import ietf-yang-schema-mount { + prefix yangmnt; + } + + revision 2026-07-01; + + identity iface; + identity ethernet { + base iface; + } + + container cont-root { + leaf lf-root-str { + type string; + } + + container services { + yangmnt:mount-point "services"; + } + } + + /* ===== Basic scalar types ===== */ + + leaf lf-str { + type string; + } + + leaf lf-bool { + type boolean; + } + + leaf lf-empty { + type empty; + } + + leaf lf-bin { + type binary; + } + + leaf lf-dec64 { + type decimal64 { + fraction-digits 2; + } + } + + /* ===== Signed integers ===== */ + + leaf lf-intg8 { + type int8; + } + + leaf lf-intg16 { + type int16; + } + + leaf lf-intg32 { + type int32; + } + + leaf lf-intg64 { + type int64; + } + + /* ===== Unsigned integers ===== */ + + leaf lf-uintg8 { + type uint8; + } + + leaf lf-uintg16 { + type uint16; + } + + leaf lf-uintg32 { + type uint32; + } + + leaf lf-uintg64 { + type uint64; + } + + /* ===== Complex built-in types ===== */ + + leaf lf-enum { + type enumeration { + enum red; + enum green; + enum blue; + } + } + + leaf lf-bits { + type bits { + bit up; + bit running; + bit testing; + } + } + + leaf lf-idenref { + type identityref { + base iface; + } + } + + leaf lf-inst-id { + type instance-identifier; + } + + leaf lf-union { + type union { + type uint32; + type string; + type boolean; + } + } + + leaf lf-lfref { + type leafref { + path "../lf-str"; + } + } + + /* ===== Leaf-lists ===== */ + + leaf-list ll-str { + type string; + } + + leaf-list ll-intg32 { + type int32; + } + + /* ===== Presence container ===== */ + + container cont-presence { + presence "Presence container."; + + leaf lf-def-str { + type string; + default "abc"; + } + } + + /* ===== List ===== */ + + list list-user { + key "key-id"; + + leaf key-id { + type uint32; + } + + leaf lf-name { + type string; + } + } + + /* ===== Choice / Case ===== */ + + choice choice-transport { + + case case-tcp { + + leaf lf-tcp-port { + type uint16; + } + } + + case case-unix { + + leaf lf-unix-socket { + type string; + } + } + } + + /* ===== Anydata / Anyxml ===== */ + + anydata anydata-payload; + + anyxml anyxml-payload; + + /* ===== RPC ===== */ + + rpc rpc-reset { + + input { + + leaf lf1 { + type boolean; + } + } + + output { + + leaf lf2 { + type string; + } + } + } + + /* ===== Notification ===== */ + + notification notif-alarm { + + leaf lf-severity { + type enumeration { + enum info; + enum warning; + enum critical; + } + } + + leaf lf-message { + type string; + } + } + + /* ===== Augment ===== */ + + augment "/ct:cont-root" { + leaf lf-augmented-str { + type string; + } + } +} \ No newline at end of file diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt index 9e2047b2c..a63f1d5d6 100644 --- a/tests/perf/CMakeLists.txt +++ b/tests/perf/CMakeLists.txt @@ -6,6 +6,10 @@ set(format_sources add_executable(ly_perf ${CMAKE_CURRENT_SOURCE_DIR}/perf.c $) set_target_properties(ly_perf PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") target_link_libraries(ly_perf ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS}) +if (CBOR_FOUND) + target_link_libraries(ly_perf ${CBOR_LIBRARY}) +endif() + if(XXHASH_FOUND) target_link_libraries(ly_perf ${XXHASH_LIBRARY}) endif() diff --git a/tests/style/CMakeLists.txt b/tests/style/CMakeLists.txt index cf7c97b88..243afb9da 100644 --- a/tests/style/CMakeLists.txt +++ b/tests/style/CMakeLists.txt @@ -9,6 +9,10 @@ endif() add_executable(cpp_compat cpp_compat.c $) target_include_directories(cpp_compat BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(cpp_compat ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS} m) +if (CBOR_FOUND) + target_link_libraries(cpp_compat ${CBOR_LIBRARY}) +endif() + if(XXHASH_FOUND) target_link_libraries(cpp_compat ${XXHASH_LIBRARY}) endif() diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt index 37ae7296c..a77d60d75 100644 --- a/tests/utests/CMakeLists.txt +++ b/tests/utests/CMakeLists.txt @@ -71,6 +71,9 @@ ly_add_utest(NAME lyb SOURCES data/test_lyb.c) ly_add_utest(NAME validation SOURCES data/test_validation.c) ly_add_utest(NAME merge SOURCES data/test_merge.c) ly_add_utest(NAME diff SOURCES data/test_diff.c) +if(CBOR_FOUND) + ly_add_utest(NAME cbor SOURCES data/test_cbor.c) +endif() ly_add_utest(NAME metadata SOURCES extensions/test_metadata.c) ly_add_utest(NAME nacm SOURCES extensions/test_nacm.c) diff --git a/tests/utests/data/test_cbor.c b/tests/utests/data/test_cbor.c new file mode 100644 index 000000000..e43dc5374 --- /dev/null +++ b/tests/utests/data/test_cbor.c @@ -0,0 +1,306 @@ +/** + * @file test_cbor.c + * @author Juraj Budai + * @brief Cmocka tests for CBOR data format. + * + * Copyright (c) 2026 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "in_internal.h" +#include "parser_data.h" +#include "printer_data.h" + +void **glob_state; + +static int +setup(void **state) +{ + UTEST_SETUP; + glob_state = state; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "cbor-test", NULL, NULL)); + + return 0; +} + +struct test_case { + const char *name; + const char *json; +}; + +static void +test_node(void **state) +{ + struct lyd_node *tree; + char *buffer, *json; + struct ly_out *out; + size_t i; + + const struct test_case tests[] = { + {"lf-str", "{\"cbor-test:lf-str\":\"example string\"}"}, + {"lf-bool", "{\"cbor-test:lf-bool\":true}"}, + {"lf-empty", "{\"cbor-test:lf-empty\":[null]}"}, + {"lf-bin", "{\"cbor-test:lf-bin\":\"dGVzdA==\"}"}, + {"lf-dec64", "{\"cbor-test:lf-dec64\":\"12.34\"}"}, + + {"lf-intg8", "{\"cbor-test:lf-intg8\":-128}"}, + {"lf-intg16", "{\"cbor-test:lf-intg16\":-32768}"}, + {"lf-intg32", "{\"cbor-test:lf-intg32\":-2147483648}"}, + {"lf-intg64", "{\"cbor-test:lf-intg64\":\"-9223372036854775808\"}"}, + + {"lf-uintg8", "{\"cbor-test:lf-uintg8\":255}"}, + {"lf-uintg16", "{\"cbor-test:lf-uintg16\":65535}"}, + {"lf-uintg32", "{\"cbor-test:lf-uintg32\":4294967295}"}, + {"lf-uintg64", "{\"cbor-test:lf-uintg64\":\"18446744073709551615\"}"}, + + {"lf-enum", "{\"cbor-test:lf-enum\":\"green\"}"}, + {"lf-bits", "{\"cbor-test:lf-bits\":\"up running\"}"}, + {"lf-idenref", "{\"cbor-test:lf-idenref\":\"ethernet\"}"}, + { + "lf-inst-id", + "{" + "\"cbor-test:cont-root\":{\"lf-root-str\":\"example string\"}," + "\"cbor-test:lf-inst-id\":\"/cbor-test:cont-root/lf-root-str\"" + "}" + }, + {"lf-union-s", "{\"cbor-test:lf-union\":\"text\"}"}, + {"lf-union-b", "{\"cbor-test:lf-union\":false}"}, + { + "lf-lfref", + "{" + "\"cbor-test:lf-str\":\"referenced string\"," + "\"cbor-test:lf-lfref\":\"referenced string\"" + "}" + }, + { + "augment", + "{" + "\"cbor-test:cont-root\":{" + "\"lf-root-str\":\"example string\"," + "\"lf-augmented-str\":\"augmented string\"" + "}" + "}" + }, + {"anydata", "{\"cbor-test:anydata-payload\":{\"nested-key\":\"value\"}}"}, + {"anyxml", "{\"cbor-test:anyxml-payload\":{\"xml-element\":\"text\"}}"}, + {"ll-str", "{\"cbor-test:ll-str\":[\"val1\",\"val2\",\"val3\",\"val4\"]}"}, + {"ll-intg32", "{\"cbor-test:ll-intg32\":[-30,10,20]}"}, + {"list-one", "{\"cbor-test:list-user\":[{\"key-id\":1,\"lf-name\":\"Alice\"}]}"}, + {"list-multi", "{\"cbor-test:list-user\":[{\"key-id\":1,\"lf-name\":\"Alice\"},{\"key-id\":2,\"lf-name\":\"Bob\"}]}"}, + {"choice-tcp", "{\"cbor-test:lf-tcp-port\":8080}"}, + {"choice-unix", "{\"cbor-test:lf-unix-socket\":\"/var/run/test.sock\"}"}, + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + CHECK_PARSE_LYD_PARAM(tests[i].json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buffer, 0, &out)); + assert_int_equal(LY_SUCCESS, lyd_print_all(out, tree, LYD_CBOR, 0)); + + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem_len(((struct utest_context *)*state)->ctx, buffer, + ly_out_printed(out), LYD_CBOR, LYD_PARSE_ONLY, 0, &tree)); + + ly_out_free(out, NULL, 0); + free(buffer); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&json, tree, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_SIBLINGS)); + assert_string_equal(json, tests[i].json); + + free(json); + lyd_free_all(tree); + } +} + +static void +test_operations(void **state) +{ + struct lyd_node *tree; + char *buffer, *json; + struct ly_out *out; + struct ly_in *in; + size_t i; + + const struct test_case tests[] = { + {"rpc", "{\"cbor-test:rpc-reset\":{\"lf1\":true}}"}, + {"notification", "{\"cbor-test:notif-alarm\":{\"lf-severity\":\"warning\",\"lf-message\":\"Something\"}}"}, + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + enum lyd_type type = (strcmp(tests[i].name, "rpc") == 0) ? LYD_TYPE_RPC_YANG : LYD_TYPE_NOTIF_YANG; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(tests[i].json, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(((struct utest_context *)*state)->ctx, NULL, in, LYD_JSON, type, 0, &tree, NULL)); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buffer, 0, &out)); + assert_int_equal(LY_SUCCESS, lyd_print_all(out, tree, LYD_CBOR, 0)); + + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(buffer, &in)); + in->length = ly_out_printed(out); + assert_int_equal(LY_SUCCESS, lyd_parse_op(((struct utest_context *)*state)->ctx, NULL, in, LYD_CBOR, type, 0, &tree, NULL)); + ly_in_free(in, 0); + + ly_out_free(out, NULL, 0); + free(buffer); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&json, tree, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_SIBLINGS)); + assert_string_equal(json, tests[i].json); + + free(json); + lyd_free_all(tree); + } +} + +static LY_ERR +test_ext_data_clb(const struct lysc_ext_instance *ext, const struct lyd_node *UNUSED(parent), void *user_data, + void **ext_data, ly_bool *ext_data_free) +{ + void **state = glob_state; + struct lyd_node *data = NULL; + const struct lys_module *sm_mod; + + (void)ext; + + if (user_data) { + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, NULL); + CHECK_PARSE_LYD_PARAM(user_data, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + sm_mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-schema-mount"); + assert_int_equal(LY_SUCCESS, lyd_validate_module(&data, sm_mod, 0, NULL)); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, user_data); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +static void +test_schema_mount(void **state) +{ + struct lyd_node *tree = NULL; + char *cbor_buffer = NULL, *xml_buffer = NULL; + const char *ext_data, *input_xml; + struct ly_out *out_cbor = NULL; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "base-mod", NULL, NULL)); + + ext_data = + "" + " " + " mount-set" + " " + " base-mod" + " urn:example:base-mod" + " " + " " + " " + " mount-schema" + " mount-set" + " " + " " + " ds:running" + " mount-schema" + " " + " 1" + "" + "" + " " + " cbor-test" + " " + " " + " " + ""; + + input_xml = + "\n" + " Hello\n" + " \n" + " Mounted value\n" + " \n" + "\n"; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, (void *)ext_data); + + CHECK_PARSE_LYD_PARAM(input_xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&cbor_buffer, 0, &out_cbor)); + assert_int_equal(LY_SUCCESS, lyd_print_all(out_cbor, tree, LYD_CBOR, 0)); + + lyd_free_all(tree); + tree = NULL; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem_len(UTEST_LYCTX, cbor_buffer, + ly_out_printed(out_cbor), LYD_CBOR, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &tree)); + + ly_out_free(out_cbor, NULL, 0); + free(cbor_buffer); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&xml_buffer, tree, LYD_XML, LYD_PRINT_SIBLINGS)); + assert_string_equal(input_xml, xml_buffer); + + free(xml_buffer); + lyd_free_all(tree); +} + +static void +test_opaque(void **state) +{ + struct lyd_node *tree; + char *buffer, *json; + const char *input_json; + struct ly_out *out; + + input_json = + "{" + "\"cbor-test:cont-root\":{" + "\"lf-root-str\":\"Something\"," + "\"nonexisting-element\":\"Random value\"" + "}" + "}"; + + CHECK_PARSE_LYD_PARAM(input_json, LYD_JSON, LYD_PARSE_ONLY | LYD_PARSE_OPAQ, 0, LY_SUCCESS, tree); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buffer, 0, &out)); + assert_int_equal(LY_SUCCESS, lyd_print_all(out, tree, LYD_CBOR, 0)); + + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem_len(((struct utest_context *)*state)->ctx, buffer, + ly_out_printed(out), LYD_CBOR, LYD_PARSE_ONLY | LYD_PARSE_OPAQ, 0, &tree)); + + ly_out_free(out, NULL, 0); + free(buffer); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&json, tree, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_SIBLINGS)); + assert_string_equal(json, input_json); + + free(json); + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_node, setup), + UTEST(test_operations, setup), + UTEST(test_schema_mount, setup), + UTEST(test_opaque, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}