diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-23 09:41:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-23 11:00:43 +0000 |
commit | d0b1bae8c5c70c5d06f3dcecc450a75e7f7cb5af (patch) | |
tree | 7ea7c5e622a5d7c9c989057a1eca8954c4d7fefb /tests | |
parent | Initial commit. (diff) | |
download | libyang3-d0b1bae8c5c70c5d06f3dcecc450a75e7f7cb5af.tar.xz libyang3-d0b1bae8c5c70c5d06f3dcecc450a75e7f7cb5af.zip |
Adding upstream version 3.1.0+dfsg.upstream/3.1.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
302 files changed, 60028 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..259ef34 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,55 @@ +# Correct RPATH usage on OS X +set(CMAKE_MACOSX_RPATH TRUE) + +configure_file("${PROJECT_SOURCE_DIR}/tests/tests_config.h.in" "${PROJECT_BINARY_DIR}/tests/tests_config.h" ESCAPE_QUOTES @ONLY) +include_directories(SYSTEM ${CMOCKA_INCLUDE_DIR}) +include_directories(${PROJECT_BINARY_DIR}/tests/) + +function(ly_add_utest) + cmake_parse_arguments(ADDTEST "" "NAME;WRAP" "SOURCES" ${ARGN}) + set(TEST_NAME utest_${ADDTEST_NAME}) + + foreach(TEST_SOURCE ${ADDTEST_SOURCES}) + list(APPEND TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SOURCE}) + endforeach() + + add_executable(${TEST_NAME} ${TEST_SOURCES} $<TARGET_OBJECTS:yangobj>) + target_compile_definitions(${TEST_NAME} PRIVATE LIBYANG_BUILD) + + # 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 (NOT WIN32) + target_link_libraries(${TEST_NAME} m) + else() + target_link_libraries(${TEST_NAME} ${COMPAT_WIN_LIBRARIES}) + endif() + if (NOT APPLE) + if (ADDTEST_WRAP) + set_target_properties(${TEST_NAME} PROPERTIES LINK_FLAGS "${ADDTEST_WRAP}") + endif() + endif() + + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "MALLOC_CHECK_=3") + + if(ENABLE_VALGRIND_TESTS) + add_test(${TEST_NAME}_valgrind valgrind --leak-check=full --show-leak-kinds=all --suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp --error-exitcode=1 ${CMAKE_BINARY_DIR}/tests/${TEST_NAME}) + endif() +endfunction() + +if(ENABLE_TESTS) + add_subdirectory(plugins) + add_subdirectory(utests) + if(NOT WIN32) + add_subdirectory(style) + add_subdirectory(fuzz) + endif() + add_subdirectory(yanglint) + add_subdirectory(yangre) +endif() +if(ENABLE_PERF_TESTS) + add_subdirectory(perf) +endif() + +set(format_sources ${format_sources} PARENT_SCOPE) diff --git a/tests/cstr.sh b/tests/cstr.sh new file mode 100755 index 0000000..e672bc1 --- /dev/null +++ b/tests/cstr.sh @@ -0,0 +1,503 @@ +#!/bin/bash + +# @file cstr.sh +# @author Adam Piecek <piecek@cesnet.cz> +# @brief Helper script for creating tests that converts yang and c strings. +# +# Copyright (c) 2020 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 + +#---------- Global variables ---------- + +script_name="$(basename $0)" + +# -i <FILE> +target_file="" +target_text="" + +# -l NUM +linenum_flag="" +linenum_default=1 +linenum=$linenum_default + +# -v +verbose="" + +# -y <EXE> +yanglint_flag="" +yanglint_exe="yanglint -f yang" +yanglint_run="" + +# <FILE> +input_text="" +input_file="" +input_ext="" + +tmpfile="" + +#---------- Basic functions ---------- + +function usage(){ +echo "\ +Usage: +$script_name [<INPUT>] [OPTION]... + +Examples: +From the \"test.c\" file, c-string is converted from line 20 to a yang schema. +$script_name test.c -l 20 +The yang scheme is converted from the \"a.yang\" file to c-string and inserted into the \"test.c\" file on line 30. +$script_name a.yang -i test.c -l 30 + +Note: +If <INPUT> missing, read standard input. Press ctrl-d to end the entry. +The c-string is statement starting with character (\") and ending with string (\";). +This script creates a temporary file in the \"/tmp\" directory with the file name <yang_module_name>.yang, +which it eventually deletes when it finishes its run. +" +echo "\ +OPTION: +-c, --combinations # Print a help table with all important parameter combinations and a usage comment. +# +-i, --insert=FILE # Insert c-string to the FILE. + # Option is valid only if <INPUT> is in .yang format and option \"-l\" is set. + # Warning: make sure you have a file backed up so you can undo the change. + # Don't forget to reload the file in your editor to see the changes. +# +-l, --line=NUM # If <INPUT> is in .c format: find c-string on the NUM line. + # If <INPUT> is in .yang format: insert c-string on the NUM-th line. + # If <INPUT> is from standard input, then the parameter is always $linenum_default + # regardless of the value of the --line parameter. +# +-v, --verbose # Print debug messages. +# +-y, --yanglint=EXEC # Run yang schema formatting by \"EXEC\". + # Default value is \"./$yanglint_exe </tmp/TEMPORARY_FILE>\", + # but if the local directory does not contain yanglint, + # then yanglint is taken from PATH. + # Note that parameters must also be entered, eg \"pyang -f tree\". +" | column -t -s "#" +} + +function combinations(){ +echo "\ +Abbreviations: c -> .c format file, y -> .yang format file +All important combinations of parameters are: ( -y, -v parameters are ommitted) +" +echo "\ +<c> -i -l # Not allowed. +<c> -i # Not allowed. +<c> -l # Find c-string on line -l in file/stdin <c>, convert it to .yang and print the result. +<c> # Get c-string on line $linenum_default in file/stdin <c>, convert it to .yang and print the result. +<y> -i -l # Get yang schema from file/stdin <y>, convert it to .c format and insert result to the -i file on line -l. +<y> -i # Get yang schema from file/stdin <y>, convert it to .c format and insert result to the -i file on line $linenum_default. +<y> -l # Not allowed. +<y> # Get yang schema from file/stdin <y>, convert it to .c format and print the result. +" | column -t -s "#" +} + +function print_verbose() +{ + if [ -n "$verbose" ] && [ -n "$1" ]; then echo "Verbose: $1" ; fi +} + +function exit_if_error() +{ + if [ $? != 0 ]; then + if [ -n "$1" ]; then + echo "$1" >&2 + fi + exit 1 + fi +} + +function print_error_then_exit() +{ + echo "$1" >&2 + exit 1 +} + +function fill_input() { + # check if argument is empty + if [ -z "$1" ]; then + # read from stdin + while IFS= read -r line; do + if [ -z $input_text ] ; then + input_text="$line" + else + input_text="$input_text\n$line" + fi + done + # substitute string \n with newline + input_text="$(echo -e "$input_text")" + print_verbose "Text is loaded from stdin" + else + # check if file exists and is a regular file + if [ -f "$1" ]; then + # <INPUT> is readable file + input_file="$1" + if [ -r $input_file ]; then + # read from file is possible + input_text=$(cat "$input_file") + print_verbose "Text is loaded from \"$input_file\" file" + else + print_error_then_exit "Error: cannot read \"$input_file\"" + fi + else + print_error_then_exit "Error: file \"$1\" cannot open" + fi + fi + # set input_text + # set input_file if file name was entered +} + + +#---------- Getopt ---------- + +# options may be followed by one colon to indicate they have a required argument +options=$(getopt -o ci:hl:vy: -l combinations,insert,help,line:,verbose,yanglint: --name "$0" -- "$@") +exit_if_error "Failed to parse options...exiting." + +eval set -- "$options" + +# extract options and their arguments into variables. +while true ; do + case "$1" in + -c | --combinations ) + combinations + exit 0 + ;; + -i | --insert ) + target_file="$2" + shift 2 + ;; + -h | --help ) + usage + exit 0 + ;; + -l | --line ) + linenum_flag="true" + linenum="$2" + shift 2 + ;; + -v | --verbose ) + verbose="true" + shift + ;; + -y | --yanglint) + yanglint_flag="true" + yanglint_exe="$2" + shift 2 + ;; + -- ) + # set input_text + # set input_file if file name was entered + fill_input $2 + break + ;; + -* ) + usage + print_error_then_exit "$0: error - unrecognized option $1" + ;; + *) + print_error_then_exit "Internal error!" + ;; + esac +done + +#---------- Functions for checking parameters ---------- + +function get_one_line() +{ + local text_with_more_lines="$1" + local linenum="$2" + echo "$(echo "$text_with_more_lines" | sed "${linenum}q;d")" +} + +function recognize_format() +{ + local text="$1" + local linenum="$2" + local line=$(get_one_line "$text" "$linenum") + local matched_chars=$(expr match "$line" "^\s*\"") + if [ "$matched_chars" == "0" ]; then + echo "yang" + else + echo "c" + fi +} + +#---------- Check parameters ---------- + +# check -y +exe_name=$(echo "$yanglint_exe" | awk '{print $1;}') +if [ -n "$yanglint_flag" ]; then + # try user's exe + command -v "$exe_name" > /dev/null 2>&1 + if [ $? != 0 ] ; then + command -v "./$exe_name" > /dev/null 2>&1 + exit_if_error "Error: cannot find exe \"$exe_name\"" + yanglint_run="./$yanglint_exe" + else + yanglint_run="$yanglint_exe" + fi + print_verbose "Using user's EXE \"$exe_name\"" +else + # try in exe current directory + command -v "./$exe_name" > /dev/null 2>&1 + if [ $? == 0 ] ; then + print_verbose "Using default \"$exe_name\" in current directory" + yanglint_run="./$yanglint_exe" + else + # try PATH's exe + command -v "$exe_name" > /dev/null 2>&1 + exit_if_error "Error: \"$exe_name\" wasn't found in the current directory nor is installed" + print_verbose "Using default \"$exe_name\" from PATH" + yanglint_run="$yanglint_exe" + fi +fi +# yanglint_run must be set +yanglint_run="$yanglint_run"" " # add space due to input filename + +# check <INPUT> +# expected that input_text has string +if [ -n "$input_file" ]; then + # get suffix of the <INPUT> file + input_ext=${input_file##*.} +else + # <INPUT> is from stdin + input_ext=$(recognize_format "$input_text" "1") +fi +print_verbose "<INPUT> is in \"$input_ext\" format" +# input_ext must be set + +# check -i +if [ -n "$target_file" ]; then + # if target_file is writeable + if [ -w $target_file ]; then + print_verbose "target_file $target_file is writeable" + else + print_error_then_exit "Error: cannot insert text to file \"$target_file\"" + fi + # if <INPUT> is yang then -l must be set + if [ "$input_ext" == "yang" ] && [ -n "$linenum_flag" ]; then + print_verbose "-i option is valid" + else + print_error_then_exit "Error: Option -i is valid only if <INPUT> is in .yang format and option \"-l\" is set." + fi + target_text=$(cat "$target_file") +fi +# target_text must be set + +# check -l +if [ -n "$linenum_flag" ]; then + + if [ -z "$input_file" ]; then + # reading <INPUT> from stdin + print_verbose "-l option is ignored because <INPUT> is from stdin." + linenum=$linenum_default + else + if [ "$linenum" -lt "0" ]; then + print_error_then_exit "Error: only positive numbers in --line option are valid" + fi + if [ "$input_ext" == "yang" ]; then + if [ -z "$target_file" ]; then + print_error_then_exit "Error: Option -l with <INPUT> format yang is valid only if option -i is set too." + fi + text4linenum="$target_text" + else + text4linenum="$input_text" + fi + # check if linenum is not too big + lines_count=$(echo "$text4linenum" | wc -l) + if [ "$linenum" -gt "$lines_count" ]; then + print_error_then_exit "Error: number in --line option is too big" + fi + print_verbose "-l option is valid" + fi +else + print_verbose "-l option is not set" + # rest of restrictions must be checked in option -i +fi + +#---------- Formatting text ---------- + +# warning: do not call this function in subshell $(formatting_yang_text) +function formatting_yang_text() +{ + # parameters: modify global variable input_text, read yanglint_run, read tmpfile + echo "$input_text" > "$tmpfile" + # check if <INPUT> is valid yang file, store only stderr to variable + yanglint_output=$(eval ""$yanglint_run" "$tmpfile"" 2>&1) # do not add local + local yanglint_retval="$?" + if [ "$yanglint_retval" != "0" ]; then + print_verbose "$yanglint_output" + fi + $(exit $yanglint_retval) + exit_if_error "Error: yang-schema in is not valid." + input_text="$yanglint_output" +} + +#---------- Main functions ---------- + +# called from main run +function cstring2yang() +{ + local ret + local input_text="$1" + local linenum="$2" + # extract statement from c language file from specific line + ret=$(echo "$input_text" | + awk -v linenum="$linenum" ' + NR >= linenum {lineflag=1; print} + /;\s*$/ {if(lineflag == 1) exit;} + ') + # substitute special characters - for example \" + ret=$(printf "$ret") + # remove everything before first " and remove last " + ret=$(echo "$ret" | grep -oP '"\K.*' | sed 's/"\s*$//') + # but last line is not right due to "; at the end of line + # so get last line and remove "; + lastline=$(echo "$ret" | tail -n1 | sed -n 's/";\s*$//p') + # get everything before last line + ret=$(echo "$ret" | head -n -1) + # concatenate result + ret=$ret$lastline + echo "$ret" +} + +# called from main run +function yang2cstring() +{ + local ret + local input_text="$1" + # backslashing character " + ret=${input_text//\"/\\\"} + ret=$(echo "$ret" | awk -v s="\"" -v e="\\\n\"" '{print s$0e}') + ret="$ret"";" + echo "$ret" +} + +# called from --Create temporary file-- +function get_yang_module_name() +{ + local text="$1" + local linenum="$2" + local input_ext="$3" + if [ "$input_ext" == "yang" ]; then + # module name search on line 1 in .yang file + linenum=1 + fi + # else get module name on line $linenum in .c file + echo "$(echo "$text" | awk -v linenum="$linenum" 'NR >= linenum' | grep -oP -m 1 "\s*module\s+\K\S+")" +} + +# called from tabs2spaces_in_line and insert_indentation +function number2spaces() +{ + local number="$1" + # return string containing <number> spaces + echo "$(printf ' %.0s' $(seq 1 $number))" +} + +# called from count_indentation_in_line function +function tabs2spaces_in_line() +{ + local text="$1" + local linenum="$2" + local tabinspaces="$(number2spaces 4)" # 1 tab = 4 spaces + local line="$(get_one_line "$text" "$linenum")" + echo "$(echo "$line" | sed "s/\t/$tabinspaces/")" +} + +# called from main run +function count_indentation_in_line() +{ + local text="$1" + local linenum="$2" + local line="$(tabs2spaces_in_line "$1" "$2")" + echo "$(expr match "$line" "^ *")" +} + +# called from main run +function insert_indentation() +{ + local text="$1" + local number_of_spaces="$2" # from count_indentation_in_line + local spaces="$(number2spaces "$number_of_spaces")" + echo "$(echo "$text" | sed -e "s/^/$spaces/")" +} + +# called from main run +function insert_text2file() +{ + local text="$1" + local linenum="$2" + local filename="$3" + + linenum=$((linenum + 1)) + awk -i inplace -v text="$text" -v linenum="$linenum" 'NR == linenum {print text} 1' $filename +} + +#---------- Create temporary file ---------- + +module_name=$(get_yang_module_name "$input_text" "$linenum" "$input_ext") +if [ -z "$module_name" ]; then + print_error_then_exit "Error: module name not found" +fi +tmpfile="/tmp/""$module_name"".yang" +touch "$tmpfile" +exit_if_error "Error: error while creating temporary file" +# delete temporary file after script end +trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT +exit_if_error "Error: trap return error" + +#---------- Main run ---------- + +# print new line for clarity +if [ -z "$input_file" ] && [ -z "$target_file" ]; then + echo "" +fi + +if [ "$input_ext" == "yang" ]; then + if [ -z "$target_file" ]; then + # Options: (<y> -l, <y>, <y-stdin> -l, <y-stdin>) + print_verbose "Print c-string to output" + formatting_yang_text + echo "$(yang2cstring "$input_text")" + else + # Options: (<y-stdin> -i -l, <y> -i -l, <y> -i) + print_verbose "Insert c-string to target_file" + + # formatting and converting + formatting_yang_text + inserted_text="$(yang2cstring "$input_text")" + # add extra backslash + inserted_text=${inserted_text//\\/\\\\} + + # indentation + indentation="$(count_indentation_in_line "$target_text" "$linenum")" + print_verbose "indentation is: $indentation" + inserted_text="$(insert_indentation "$inserted_text" "$indentation")" + + # inserting to file + insert_text2file "$inserted_text" "$linenum" "$target_file" + echo "Done" + fi +elif [ "$input_ext" == "c" ] || [ "$input_ext" == "h" ]; then + # Options: (<c-stdin> -l, <c-stdin>, <c> -l, <c>) + print_verbose "Print yang to output or from file <c> print yang to output" + output="$(cstring2yang "$input_text" "$linenum")" + # "input_text" is input and output parameter for formatting_yang_text + input_text="$output" + formatting_yang_text + echo "$input_text" +else + print_error_then_exit "Error: format \"$input_ext\" is not supported" +fi + +exit 0 diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt new file mode 100644 index 0000000..3da61e1 --- /dev/null +++ b/tests/fuzz/CMakeLists.txt @@ -0,0 +1,28 @@ +if(ENABLE_FUZZ_TARGETS) + set(fuzz_targets lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json yang_parse_module) + + if(FUZZER STREQUAL "AFL") + foreach(target_name IN LISTS fuzz_targets) + add_executable(${target_name}_fuzz_harness ${target_name}.c main.c) + target_link_libraries(${target_name}_fuzz_harness yang) + endforeach() + elseif() + foreach(target_name IN LISTS fuzz_targets) + add_executable(${target_name}_fuzz_harness ${target_name}.c) + set_source_files_properties(${target_name}.c PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer") + target_link_libraries(${target_name}_fuzz_harness yang "-fsanitize=fuzzer") + endforeach() + endif() +endif() + +if(ENABLE_TESTS) + add_executable(fuzz_regression_test fuzz_regression_test.c) + set(fuzz_regression_tests lys_parse_mem lyd_parse_mem_xml lyd_parse_mem_json) + foreach(target_name IN LISTS fuzz_regression_tests) + file(COPY ${CMAKE_SOURCE_DIR}/tests/fuzz/corpus/${target_name} DESTINATION ${CMAKE_BINARY_DIR}/tests/fuzz/) + add_executable(regress_fuzz_${target_name} ${target_name}.c main.c) + set_target_properties(regress_fuzz_${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests/fuzz/${target_name}") + target_link_libraries(regress_fuzz_${target_name} yang) + add_test(NAME regress_fuzz_${target_name} COMMAND fuzz_regression_test regress_fuzz_${target_name} . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tests/fuzz/${target_name}) + endforeach() +endif() diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md new file mode 100644 index 0000000..d0abf4d --- /dev/null +++ b/tests/fuzz/README.md @@ -0,0 +1,64 @@ +# FUZZING +This directory contains a collection of fuzz harnesses, which are designed to +be used with [AFL](http://lcamtuf.coredump.cx/afl/) and [LibFuzzer](https://llvm.org/docs/LibFuzzer.html) +fuzzers. The harnesses should also be easily reusable with other similar fuzzers. + +Two asciinema examples are available, one for LibFuzzer: +https://asciinema.org/a/311035 +and one for AFL: +https://asciinema.org/a/311060 + +To build the fuzz targets, the ENABLE_FUZZ_TARGETS option has to be enabled. +The FUZZER option specifies which fuzzer to use, currently only AFL and LibFuzzer +are supported, with AFL being the default. LibFuzzer is based on the same +principles that AFL works with, but has a different implementation of the fuzzing engine +and is integrated with UBSAN by default, while AFL lacks official integration with UBSAN. + +To use the harnesses with AFL, one of AFL's compilers should be used. +For example the AFL clang-fast compiler can be used with the cmake option shown below. +It is recommended to set the build type to Release, since otherwise the fuzzer will +detect failed asserts as crashes. +If LibFuzzer is used, clang has to be used, as gcc doesn't support -fsanitize=fuzzer. + +``` +$ cmake -DENABLE_FUZZ_TARGETS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=path_to_afl/afl-clang-fast .. +``` + +After that the programs can be built by running make: +``` +$ make +``` + +The target executables will then be available in tests/fuzz of the build directory that was used. + +The libyang yang test files available in the `tests/modules/` subdirectory can be used as initial +test cases for fuzzing targets that receive YANG models, like the lys_parse_mem_fuzz_harness. However, a smaller corpus of YANG models should probably +be used, as larger models decrease execution speed. A good place to start would be to collect +small YANG files, each of which uses only a single YANG feature. + +The files that will be used as starting test cases should be copied into a single directory. Those files should then be minimized by using afl-cmin and afl-tmin. + +To increase the speed of fuzzing, the test cases and AFL output files should be stored on a temporary RAM disk. +If a new fuzz target is used, AFL persistent mode should be used. More about persistent mode can be read in the official AFL documentation. + +When all of the above is done the fuzzing process can begin. AFL supports running multiple instances of the fuzzer, which can speed up the +process on multi core CPUs. The first fuzz instance should be started in master mode, and the other instances in slave mode. +The total number of instances should usually be equal to the number of cores. + +Below is an example of running 2 instances. The -i flag specifies the testcase input directory, and the -o file specifies the directory the fuzzer will use for output. +``` +afl-fuzz -i minimised_testcases/ -o syncdir/ -M fuzzer1 -- libyang/build/tests/fuzz/lyd_parse_mem_fuzz_harness +afl-fuzz -i minimised_testcases/ -o syncdir/ -S fuzzer2 -- libyang/build/tests/fuzz/lyd_parse_mem_fuzz_harness +``` + +To fuzz with LibFuzzer, at the most basic level, everything that is required is +to run the compiled fuzz target. +However, running the target like that invokes the harness with only one job +on a single core, with no starting inputs. +Multiple jobs running on separate cores should be used, with a starting input corpus. +The options are described in the official LibFuzzer documentation (https://llvm.org/docs/LibFuzzer.html). + +## Fuzzing corpus and regression testing +The `tests/fuzz/corpus` directory contains subdirectories for every fuzz target. Those subdirectories contain a collection of previous inputs that were found by fuzzing and caused visible issues or crashes. Every input file is named after the issue or pull request where it was originally reported. When a new issue is discovered, the input causing the issue should be added to the appropriate directory. + +These input files are then used by the fuzz_regression_test test which sends the corpus into the corresponding fuzz harness, to test whether any of the files crash and cause regressions. diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull11438 b/tests/fuzz/corpus/lyd_parse_mem_json/pull11438 new file mode 100644 index 0000000..d4722b2 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull11438 @@ -0,0 +1 @@ +{"0R:0::809e-47,-689e-47,-689e-489e-47":[809e-47,-689e-47,-689e-4709e-47,-689e-47,-689e-489e-47":[809e-47,-689e-47,-689e-47647,-688Je7,-6889e647,-688Je7,-6889e-47"
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1203 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1203 new file mode 100644 index 0000000..b732c50 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1203 @@ -0,0 +1,12 @@ +
+
+
+
+
+
+
+
+
+
+
+ diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1269 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269 new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269 @@ -0,0 +1 @@ +[] diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2 new file mode 100644 index 0000000..9f553e6 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1269_2 @@ -0,0 +1 @@ +[[], []] diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1280 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1280 new file mode 100644 index 0000000..0b15336 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1280 @@ -0,0 +1 @@ +"\u1
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number new file mode 100644 index 0000000..cbbee10 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_number @@ -0,0 +1 @@ +{"200 -11-10T23:00:00Z": 1E+2} diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings new file mode 100644 index 0000000..9e93b42 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1347_strings @@ -0,0 +1 @@ +{"200 -11-10T23:00:00Z": "hello w\rld"}, diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1348 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1348 new file mode 100644 index 0000000..5759231 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1348 @@ -0,0 +1 @@ + "a\tHt\\\\HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH(HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\\\\\\\\\" diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1460 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460 new file mode 100644 index 0000000..66bc72f --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460 @@ -0,0 +1 @@ +"viøonisionp\u\
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2 new file mode 100644 index 0000000..1df63c8 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1460_2 @@ -0,0 +1 @@ +"viøonisionp\uGAAA" diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1571 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1571 new file mode 100644 index 0000000..965dad8 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1571 @@ -0,0 +1 @@ +{"@types:uint32":{"typus:@uint32":1,"typus:@uint32":2,"typJs:uint32":3}} diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1585 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1585 new file mode 100644 index 0000000..e9c1d5d --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1585 @@ -0,0 +1 @@ +{"@types:uint32":{"@":{"ns:int32":{"a":[1 diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1626 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1626 new file mode 100644 index 0000000..d8e0d57 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1626 @@ -0,0 +1 @@ +{"@types:uint32":{"@":{">:1,":9,"\\\\\\\\\\:2,":8,":3,":7,":-402 diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1693 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1693 new file mode 100644 index 0000000..db6d1a4 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1693 @@ -0,0 +1 @@ +{"types:cont":{"":"","":{}}}
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1 new file mode 100644 index 0000000..e411daa --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_1 @@ -0,0 +1 @@ +{"types:cont":{"leaflt30,1,10,2]xxnt32":1,"types:uint":1,"types:uinis2":922337203685477}}
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2 b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2 new file mode 100644 index 0000000..623be41 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_json/pull1696_2 @@ -0,0 +1 @@ +{"types:cont":{"leaflt30,1,GGGGGGGGGGGGGGGGGGGGGGGGGGGGG10,2]xxnt32":1,"types:ui~t":1,"types:uinis2":922337203685477}}
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074 new file mode 100644 index 0000000..c1195cb --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1074 @@ -0,0 +1,4 @@ +<a xmlns="ns"> +<b>x</b> +<c xml:id="D">1</c> +</a> diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131 Binary files differnew file mode 100644 index 0000000..5cc30ac --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1131 diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132 new file mode 100644 index 0000000..174fa95 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132 @@ -0,0 +1 @@ +<dnc a="E@V(#iC<doc>�</ddoc>�/doc>
oc>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2 new file mode 100644 index 0000000..68ee778 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_2 @@ -0,0 +1,119 @@ +<?xmF-8"?> +<?xmlp://www.stoa.org/epidoc/schema/latest/tei-epidoc.rng" schematypens="http://relaxng.org/ns/structure/1.0"?> +<TEI xmlns="http://www.tei-c.oŠg/nel href=(&#38;#38) or with a general entity (&amp;test/"> + +<!-- Start: not-wf/sa --> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-001" + URI="not-wf/sa/001.xml" SECTIONS="3.1 [41]"> + Attribute values must start with attribute names, not "?". </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-002" + URI="not-wf/sa/002.xml" SECTIONS="2.3 [4]"> + Names may not start with "."; it's not a Letter. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-003" + URI="not-wf/sa/003.xml" SECTIONS="2.6 [16]"> + Processing Instruction target name is required.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-004" + URI="not-wf/sa/004.xml" SECTIONS="2.6 [16]"> + SGML-ism: processing instructions end in '?>' not '>'. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-005" + URI="not-wf/sa/005.xml" SECTIONS="2.6 [16]"> + Processing instructions end in '?>' not '?'. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-006" + URI="not-wf/sa/006.xml" SECTIONS="2.5 [16]"> + XML comments may not contain "--" </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-007" + URI="not-wf/sa/007.xml" SECTIONS="4.1 [68]"> + General entity references have no whitespace after the + entity name and before the semicolon. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-008" + URI="not-wf/sa/008.xml" SECTIONS="2.3 [5]"> + Entity references must include names, which don't begin + with '.' (it's not a Letter or other name start character). </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-009" + URI="not-wf/sa/009.xml" SECTIONS="4.1 [66]"> + Character references may have only decimal or numeric strings.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-010" + URI="not-wf/sa/010.xml" SECTIONS="4.1 [68]"> + Ampersand may only appear as part of a general entity reference.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-011" + URI="not-wf/sa/011.xml" SECTIONS="3.1 [41]"> + SGML-ism: attribute values must be explicitly assigned a + value, it can't act as a boolean toggle. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-012" + URI="not-wf/sa/012.xml" SECTIONS="2.3 [10]"> + SGML-ism: attribute values must be quoted in all cases. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-013" + URI="not-wf/sa/013.xml" SECTIONS="2.3 [10]"> + The quotes on both ends of an attribute value must match. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-014" + URI="not-wf/sa/014.xml" SECTIONS="2.3 [10]"> + Attribute valueF-8"?> +<?xmlp://www.stoa.org/epidoc/schema/latest/tei-epidoc.rng" schematypens="http://relaxng.org/ns/structure/1.0"?> +<TEI xmlns="http://www.tei-c.oŠg/nel href=(&#38;#38) or with a general entity (&amp;test/"> + +<!-- Start: not-wf/sa --> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-001" + URI="not-wf/sa/001.xml" SECTIONS="3.1 [41]"> + Attribute values must start with attribute names, not "?". </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-002" + URI="not-wf/sa/002.xml" SECTIONS="2.3 [4]"> + Names may not start with "."; it's not a Letter. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-003" + URI="not-wf/sa/003.xml" SECTIONS="2.6 [16]"> + Processing Instruction target name is required.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-004" + URI="not-wf/sa/004.xml" SECTIONS="2.6 [16]"> + SGML-ism: processing instructions end in '?>' not '>'. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-005" + URI="not-wf/sa/005.xml" SECTIONS="2.6 [16]"> + Processing instructions end in '?>s may not contain literal '<' characters. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-015" + URI="not-wf/sa/015.xml" SECTIONS="3.1 [41]"> + Attribute values need a value, not just an equals sign. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-016" + URI="not-wf/sa/016.xml" SECTIONS="3.1 [41]"> + Attribute values need an associated name.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-017" + URI="not-wf/sa/017.xml" SECTIONS="2.7 [18]"> + CDATA sections need a terminating ']]>'. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-018" + URI="not-wf/sa/018.xml" SECTIONS="2.7 [19]"> + CDATA sections begin with a literal '<![CDATA[', no space.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-019" + URI="not-wf/sa/019.xml" SECTIONS="3.1 [42]"> + End tags may not be abbreviated as '</>'.</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-020" + URI="not-wf/sa/020.xml" SECTIONS="2.3 [10]"> + Attribute values may not contain literal '&' + characters except as part of an entity reference. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-021" + URI="not-wf/sa/021.xml" SECTIONS="2.3 [10]"> + Attribute values may not contain literal '&' + characters except as part of an entity reference. 3/TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-022" + URI="not-wf/sa/022.xml" SECTIONS="4.1 [66]"> + Character references end with semicolons, always!</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-023" + URI="not-wf/sa/023.xml" SECTIONS="2.3 [5]"> + Digits are not valid name start characters. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-024" + URI="not-wf/sa/024.xml" SECTIONS="2.3 [5]"> + Digits are not valid name start characters. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-025" + URI="not-wf/sa/025.xml" SECTIONS="2.4 [14]"> + Text may not contain a literal ']]>' sequence. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sz-026" + URI="not-wf/sa/026.xml" SECTIONS="2.4 [14]"> + Text may not contain a literal ']]>' sequence. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-027" + URI="not-wf/sa/027.xml" SECTIONS="2.5 [15]"> + Comments must be terminated with "-->".</TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-028" + URI="not-wf/sa/028.xml" SECTIONS="2.6 [16]"> + Processing instructions must end with '?>'. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-029" + URI="not-wf/sa/029.xml" SECTIONS="2.4 [14]"> + Text may not contain a literal ']]>' sequence. </TEST> +<TEST TYPE="not-wf" ENTITIES="none" ID="not-wf-sa-030" + URI="not-wf/sa/030.xml" SECTIONS="2.2 [2]"> + A form feed is not a legal XML character. </TEST> diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3 b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3 new file mode 100644 index 0000000..914b233 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/issue1132_3 @@ -0,0 +1,18 @@ +<?xml ve?> +<?xml-modelþhref="http://structure/1.0"ture/1.0"?> +<TEI xmlns="http://www.tei-c.oŠg/nel hres=(&#38;#38) or with a eneral entityk(&amp3).</p>" > +]> +.0"?> +<TEI xmlns="http://www.tei-c.oŠY/nel hres=(&#38;#38) oramp;amp3)ntityk(&amp3).</p>" > +]> +.0"?> +<TEI xmlns="htœœœœœœœtp://www.tei-c.oŠY/nel hres=(&#38;#38) oramp;amp3).</p>" > +]> +.0"?> +<TEI xmlns="http://www.tei-c///////////////////////////////////////////////////////!!!!!!!!!!!!!!/////////////////////////////////////////////////////////////////////////.oŠY/nel hres=(&#38;#38) or with aematypen"http://relaxng.org/ns/structure/1<?xmF-8"?> +<?xml-mode" schematypens="http://relaxng.org/ns/structure/laxng.org/ns/structure/1<?xmF-8"?O +<?xml-mode" schematypens="http:laxng.org/ns/strucÖure/1.0"?> +<TEI xmlns="http://www.tei-c.fŠg/neQ hres(&#with a genepal entityk(&amp;).</p>" > +]> +.0"?> +<TEI xmlns="http://www.tei-c.oŠY/nel hr Sntityk1111111111111111111111111111>&e
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1 Binary files differnew file mode 100644 index 0000000..51bb241 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_1 diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2 new file mode 100644 index 0000000..174fa95 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1129_2 @@ -0,0 +1 @@ +<dnc a="E@V(#iC<doc>�</ddoc>�/doc>
oc>
diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529 new file mode 100644 index 0000000..4fd305f --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1529 @@ -0,0 +1 @@ +<enums w=''B:s=''xmlns='urn:tests:types'
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537 new file mode 100644 index 0000000..4e5141a --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1537 @@ -0,0 +1 @@ +<str xmlns='urn:tests:types'>'Ó<
\ No newline at end of file diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562 b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562 new file mode 100644 index 0000000..2c64095 --- /dev/null +++ b/tests/fuzz/corpus/lyd_parse_mem_xml/pull1562 @@ -0,0 +1 @@ +<un1 xmlns='urn:tests:types' /=t> diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1004.yang b/tests/fuzz/corpus/lys_parse_mem/issue1004.yang new file mode 100644 index 0000000..76479d2 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1004.yang @@ -0,0 +1,10 @@ +module a { + yang-version 1.1; + namespace "a"; + prefix a; + + leaf-list A { + type pt8; + default 0; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1025.yang b/tests/fuzz/corpus/lys_parse_mem/issue1025.yang new file mode 100644 index 0000000..94d78f2 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1025.yang @@ -0,0 +1,16 @@ +module a { + yang-version 1.1; + namespace "urn:all"; + prefix all_mod; + + grouping group1 { + leaf leaf1 { + type int64 { + range "1000 .. 50000" { + error:message + "Spec"; + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1027.yang b/tests/fuzz/corpus/lys_parse_mem/issue1027.yang new file mode 100644 index 0000000..2356615 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1027.yang @@ -0,0 +1,9 @@ +module d{ + namespace ""; + prefix d; + leaf f { + type string; + must ":e"; + default ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1040.yang b/tests/fuzz/corpus/lys_parse_mem/issue1040.yang new file mode 100644 index 0000000..3641d27 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1040.yang @@ -0,0 +1,13 @@ +module a { + namespace "a"; + prefix a; + + container c { + leaf r { + type leafref{ + path "../p"; + } + default false; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1041.yang b/tests/fuzz/corpus/lys_parse_mem/issue1041.yang new file mode 100644 index 0000000..16c6d87 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1041.yang @@ -0,0 +1,34 @@ +module foo { + namespace foo; + prefix foo; + yang-version 1.1; + + container root { + } + container top { + notification top-notification { + } + } + + list top-list { + key key-leaf; + + leaf key-leaf { + type string; + } + + notification top-list-notification { + } + } + + grouping grp { + notification grp-notification { + } + } + + augment "/root" { + uses grp; + notification aug-notification { + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang new file mode 100644 index 0000000..d6b323d --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_base-yang-types.yang @@ -0,0 +1,9 @@ +module issue1042_base-yang-types { + yang-version 1.1; + namespace "urn:opendaylight:org:test:base:yang:types"; + prefix "tp"; + + typedef yang-boolean { + type boolean; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang new file mode 100644 index 0000000..f8fe6a6 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider-b.yang @@ -0,0 +1,13 @@ +module issue1042_test-type-provider-b { + yang-version 1.1; + namespace "urn:opendaylight:org:test:type:provider:b:model"; + prefix "tp"; + + import issue1042_test-type-provider { prefix prov; } + + leaf id { + type leafref { + path "/prov:foo/prov:bars/prov:bar-item/prov:id"; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang new file mode 100644 index 0000000..467e23b --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1042_test-type-provider.yang @@ -0,0 +1,13 @@ +module issue1042_test-type-provider { + yang-version 1.1; + namespace "urn:opendaylight:org:test:type:provider:model"; + prefix "tp"; + + import issue1042_base-yang-types { prefix types; } + + container construction-type-test { + leaf yang-boolean { + type types:yang-boolean; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue1043.yang b/tests/fuzz/corpus/lys_parse_mem/issue1043.yang new file mode 100644 index 0000000..950e92d --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue1043.yang @@ -0,0 +1,31 @@ +module SUPf-entity { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-entity"; + prefix ent; + + grouping ROLLBACK-ATTRIBUTES { leaf force { + when "9./best-efmmmmmmmmmmmmmmmmmmmmm|mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmfort = 'falsq'" { + } + type boolean; + default "false"; + } + leaf best-effort { + when ".</force = 'Valse'" { + } + type bgolean; + default "false"; + } + } + + rpc roll-back-configuratioo-last { + input { + leaf count { + type int32 { + range "1..100"; } + mandatory true; + } + uses ROLLBACK-ATTRIBUTES; + } + } +} + diff --git a/tests/fuzz/corpus/lys_parse_mem/issue722.yang b/tests/fuzz/corpus/lys_parse_mem/issue722.yang new file mode 100644 index 0000000..4dcf047 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue722.yang @@ -0,0 +1,16 @@ +module mod6 { + prefix abc; + namespace "http://www.example.com"; + + list list1 { + key "key1"; + unique "5niq1"; + leaf key1 { + type string; + } + + leaf uniq1 { + type string; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue723.yang b/tests/fuzz/corpus/lys_parse_mem/issue723.yang new file mode 100644 index 0000000..a2cbacc --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue723.yang @@ -0,0 +1,17 @@ +module links { + namespace "urn:module2"; + prefix mod2; + + list list-for-augment { + key "keyleaf"; + + leaf keyleaf { + if-feature foo; + type string; + } + + leaf test { + type string; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue724.yang b/tests/fuzz/corpus/lys_parse_mem/issue724.yang new file mode 100644 index 0000000..f4c37c4 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue724.yang @@ -0,0 +1,22 @@ +module mod1 { + namespace "urn:all"; + prefix av; + yang-version 1.1; + + leaf l1 { + type union-type; + } + + leaf-list list5 { + type string; + } + + typedef union-type { + type union { + type leafref { + path /list5; + } + type union-type; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue728.yang b/tests/fuzz/corpus/lys_parse_mem/issue728.yang new file mode 100644 index 0000000..6e67951 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue728.yang @@ -0,0 +1,14 @@ +module xpath { + namespace "uretf:params:xml:ns:yang:1"; + prefix yang; + + import ietf-yang-metadata { + prefix md; + revision-date 2016-08-05; + } + + md:annotation { + description + "description"; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue733.yang b/tests/fuzz/corpus/lys_parse_mem/issue733.yang new file mode 100644 index 0000000..f807697 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue733.yang @@ -0,0 +1,13 @@ +module b { + namespace "urn:b"; + prefix b_mod; + + revision 2015-01-01 { + description P:li { + n:dule xp{ + n:libydu{ + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue734.yang b/tests/fuzz/corpus/lys_parse_mem/issue734.yang new file mode 100644 index 0000000..7cd568f --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue734.yang @@ -0,0 +1,17 @@ +module x { + namespace "urn:lin:b-vev"; + prefix b_dev_mod; + + deviation /b_r-leaf { + deviate add { + unique "uniq1 cont2/uniq2 cont2/uniq3" { + d:annotmeration { + enum:first; + enum last; + enum before; + enum after; + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue735.yang b/tests/fuzz/corpus/lys_parse_mem/issue735.yang new file mode 100644 index 0000000..67d7dd3 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue735.yang @@ -0,0 +1,17 @@ +module links { + yang-version 1.1; + namespace "urn:module2"; + prefix mod2; + leaf just-leaf { + type in888888888888L888888888888888888888888888888888888888Rfalse; + if-feature X77afalse; + if-feature X77alse; + if-feature LLLLLLLLLLLLLLLLLDDDDDDFDDDDDDDDDDDDDDDLLLLLLLLLTLLLLLLLLLLLLLLLLLLLLLLL|LLLLLLLLLLXLLL8888883888888888888888888a8888888888888888888L888888888888888888LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL8888883888888888888888888a8888888888888888888L888888888888888888888888888888888888888Rfalse; + if-feature X77afalse; + if-feature X77alse; + if-feature LLLLLLLLLLLLLLDDDDDDDDDDD888888888888888888888Rfalse; + if-feature X77afalse; + if-feature H77alse; + if-feature LLLLLLLLLLLLLLDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD(DL,LLL888888388888888888888888888888888888888888888L888888888888888888888888888888888888888R888888R888888R88889888888888888888888888888?8888ean; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue739.yang b/tests/fuzz/corpus/lys_parse_mem/issue739.yang new file mode 100644 index 0000000..33d48ab --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue739.yang @@ -0,0 +1,11 @@ +module ietf-datastores { + yang-version 1.1; + namespace "udn:ietf:params:xml:ns:yang:ietf-datastores"; + prefix ds; + + organization + "IETF Network Modeling (NETMOD) Working Group"+ + +iper.net> +} + diff --git a/tests/fuzz/corpus/lys_parse_mem/issue740.yang b/tests/fuzz/corpus/lys_parse_mem/issue740.yang new file mode 100644 index 0000000..41e3050 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue740.yang @@ -0,0 +1,14 @@ +module xpath-1.1 { + namespace "urn:xpath-1.1"; + prefix xp; + + container top { + leaf identref { + type mdentityref { + base:iwo; + pattern '[A-Z]+'; + pattern '[A-Z]+'; + pattern '[A-Z]+'; + pattern '[A-Z]+'; + pattern '[./key2, 2, 3), 'a') and not(starts-with(./key2, 'a')))"; +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue741.yang b/tests/fuzz/corpus/lys_parse_mem/issue741.yang new file mode 100644 index 0000000..685174c --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue741.yang @@ -0,0 +1,16 @@ +module mod6 { + prefix adc; + namespace "http://www.example.com"; + + grouping g { + list ll { + leaf:date { + type string; + } + } + } + + container ccc { + uses g; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue742.yang b/tests/fuzz/corpus/lys_parse_mem/issue742.yang new file mode 100644 index 0000000..0e94299 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue742.yang @@ -0,0 +1,15 @@ +module links { + yang-version 1.1; + namespace "urn:mo:1"; + prefix yang; + + import ietf-yang-metadata { + prefix md; + revision-date 2016-08-05; + } + + md:annotation value { + reference "RFC7950 section 7.7.9."; + description; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue769.yang b/tests/fuzz/corpus/lys_parse_mem/issue769.yang new file mode 100644 index 0000000..a5bcfa0 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue769.yang @@ -0,0 +1,31 @@ +module mod6 { + + prefix abc; + namespace "http://www.example.c; + yang-version 1.1; + + container cont1 { + //x" { + + } + + augment "/aug-cont" { + list list2 { + key "key2"; + leaf key2 { + type string; + } + } + notification nn { + typedef Mt { + type string { + length "1..255"; + } + } + + container log { + grouping g { + notification nn { + type j2an; + } + } diff --git a/tests/fuzz/corpus/lys_parse_mem/issue771.yang b/tests/fuzz/corpus/lys_parse_mem/issue771.yang Binary files differnew file mode 100644 index 0000000..dbcf22c --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue771.yang diff --git a/tests/fuzz/corpus/lys_parse_mem/issue772.yang b/tests/fuzz/corpus/lys_parse_mem/issue772.yang new file mode 100644 index 0000000..83e7f34 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue772.yang @@ -0,0 +1,54 @@ +module all { + yang-version 1.1; + namespace "urn:all"; + prefix all_mod; + + grouping t1 { + uses group1 { + } + + leaf leaf12 { + type bits { + bit flag0 { + position 0; + if-feature "feat1"; + } + bit flag1; + bit flag2 { + position 2; + } + + bit flag3 { + position 3; + } + } + default "flag0 flag3"; + } + + list list1 { + key "leaf18"; + unique "leaf1--------------------------------------------------- leaf leaT18 { + type string; + } + + + action act1 { + input ons on thg leaf"; + leaf leaf30 { + type string; + } + } + } + + augment "/cont1" { + leaf leaf17 { + type ideZtityref { + base all_imp:iden44; + } + must "../leaf17 = 'all_imp:iden } + + action act1 { + t5'"; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue773.yang b/tests/fuzz/corpus/lys_parse_mem/issue773.yang Binary files differnew file mode 100644 index 0000000..fcd1403 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue773.yang diff --git a/tests/fuzz/corpus/lys_parse_mem/issue774.yang b/tests/fuzz/corpus/lys_parse_mem/issue774.yang new file mode 100644 index 0000000..086d018 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue774.yang @@ -0,0 +1,55 @@ +module state-lists { + yang-version 1.1; + namespace "urn:state-lists"; + prefix sl; + + container cont { + config false; + grouping group1 { + leaf leaf3 { + type tdef2 { + length "3..9 | 30..40"; + pattern "[ac + }*"; + } + + units "none"; + default "aaa"; + } + + typedef tdef2 { + type string { + length "2..17 | 20..50"; + pattern "[ab]*"; + } + } + + container cont1 { + uses group1 { + if-feature "feat2"; + refine "leaf1" { + if-feature "feat3"; + must "24 - 4 = number('20')"; + default "25"; + config true; + mandatory false; + description "dsc"; + reference "none"; + } + } + + leaf leaf4 { + type int64 { + range "1000 .. 50000" { + error-message + "Special e + } + ."; + } + } + } + + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue777.yang b/tests/fuzz/corpus/lys_parse_mem/issue777.yang new file mode 100644 index 0000000..21bb436 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue777.yang @@ -0,0 +1,16 @@ +module m0d0 { + prefix a0c; + namespace ¢0000000000000000000000"; + + list list0 { +key "key1"; + unique "0n000"; + leaf key1 { + type string; + } + + leaf uniq0 { + type string; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue780.yang b/tests/fuzz/corpus/lys_parse_mem/issue780.yang Binary files differnew file mode 100644 index 0000000..2e9ba1e --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue780.yang diff --git a/tests/fuzz/corpus/lys_parse_mem/issue788.yang b/tests/fuzz/corpus/lys_parse_mem/issue788.yang new file mode 100644 index 0000000..9804c02 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue788.yang @@ -0,0 +1,8 @@ +module d00000000 { + namespace "n"; + prefix d; + leaf l1 { + type string; + when "/l0{k='when']"; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue789.yang b/tests/fuzz/corpus/lys_parse_mem/issue789.yang new file mode 100644 index 0000000..1ec8ae7 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue789.yang @@ -0,0 +1,10 @@ +module m { + prefix p; + namespace "n"; + grouping g { + } + + grouping s { + uses g; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue791.yang b/tests/fuzz/corpus/lys_parse_mem/issue791.yang new file mode 100644 index 0000000..d2568b7 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue791.yang @@ -0,0 +1,3 @@ +module m { + include "" +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang b/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang new file mode 100644 index 0000000..8303f87 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue791_2.yang @@ -0,0 +1,13 @@ +module m { + namespace "n"; + prefix p; + + container c { + leaf trg-bits { + type bits { + bit b1; + bit ""; + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue795.yang b/tests/fuzz/corpus/lys_parse_mem/issue795.yang new file mode 100644 index 0000000..f33e321 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue795.yang @@ -0,0 +1,7 @@ +module m { + prefix p; + namespace "n"; + list l { + must ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue804.yang b/tests/fuzz/corpus/lys_parse_mem/issue804.yang new file mode 100644 index 0000000..1578b7e --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue804.yang @@ -0,0 +1,7 @@ +module m { + prefix p; + namespace n; + list l { + if-feature 0(; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue805.yang b/tests/fuzz/corpus/lys_parse_mem/issue805.yang new file mode 100644 index 0000000..45ee5f1 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue805.yang @@ -0,0 +1,7 @@ +module d{ + namespace n; + prefix p; + list l { + when ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue807.yang b/tests/fuzz/corpus/lys_parse_mem/issue807.yang new file mode 100644 index 0000000..0493a79 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue807.yang @@ -0,0 +1,9 @@ +module d{ + namespace ""; + prefix d; + leaf f { + type string; + must "0e"; + default ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue826.yang b/tests/fuzz/corpus/lys_parse_mem/issue826.yang new file mode 100644 index 0000000..ffd0778 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue826.yang @@ -0,0 +1,12 @@ +module mod6 { + + prefix abc; + namespace "ht/www.example.c; +-versin~ 1.1 containerLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLre lse; +if-feature DDDDDDDDDDDD,DDLLLLLLLLLTL)ont1 { //x" {} augment "/auDDDDDDDDDDDDDDLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr-----------------------------------------------------------------------------------------------------------------------------------------------LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLau" { + container c { uses egroup1 { + if-feature "feat2"; if-feature lse; + +} } + } +}
\ No newline at end of file diff --git a/tests/fuzz/corpus/lys_parse_mem/issue827.yang b/tests/fuzz/corpus/lys_parse_mem/issue827.yang new file mode 100644 index 0000000..c5637f9 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue827.yang @@ -0,0 +1,10 @@ +module eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeod { + yang-version 1.1; + namespace "urn:all"; + prefix p; + + container cond1 { } + + grouping group1 { + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue872.yang b/tests/fuzz/corpus/lys_parse_mem/issue872.yang new file mode 100644 index 0000000..27decd5 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue872.yang @@ -0,0 +1,7 @@ +module d{ +namespace "";prefix d; + leaf f{ + type w0iiiiiiiiiiiiiiiiiiiiiiiiiiiii0000; + default ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue874.yang b/tests/fuzz/corpus/lys_parse_mem/issue874.yang new file mode 100644 index 0000000..c42be25 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue874.yang @@ -0,0 +1,28 @@ +module o00 { prefix c; namespace "00t000000w0000p00000 +00n000e0000n00000 + + 0cANG m0dule de0in0s an 'exten0ion' s0atemns + for defining 0etadat0 an0Copyri0ht (0) 2016 IE00 T0uct and th0 persons identifi4.0 of the IETF Tru0t0s Le0a0 P0ovi0i00s + Relatin0 t0 IE0F D0cu0e0 of RFC 7 (/tru0te0.ietf0org0license-info0. + + Th0s ve00io0 of thi0 YA0G mod0le i0 pa't of RFC 78 (http:/0www-e0itor.!rg/info/0fc0902); see the 0FC i000lf + f0r fodule, i ";revision 2016-08-05{ +description +"Initial revision."; +reference "RFC 7952: Defining and 0sin0 0etada0a with YANG"; +} + +extension annotation{ +argument name; +description "This extension allows f0r defietadat0tadata an00tation0 in + YAN0 modules. 0he 0sion."; +reference "RFC 7952: Defining and 0sin0 0etada0a with YANG"; +} + +extension annotation{ +argument name; +description " YAN0 modules. 0he 0sion."; +reference "RFC 7952: Defining and 0sin0 0etada0a with YANG"; +} + +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue970.yang b/tests/fuzz/corpus/lys_parse_mem/issue970.yang new file mode 100644 index 0000000..18df054 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue970.yang @@ -0,0 +1,18 @@ +module p{ + namespace ""; + prefix p; + + container ports{ + list port { + key name; + leaf name{ + type string;} + } + } + augment "/ports/port" { + when "0</*=0"; + leaf i { + type int32; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue973.yang b/tests/fuzz/corpus/lys_parse_mem/issue973.yang new file mode 100644 index 0000000..9da8f00 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue973.yang @@ -0,0 +1,10 @@ +module p{ + namespace ""; + prefix p; + + leaf mgmt-interface { + type leafref { + path ""; + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue975.yang b/tests/fuzz/corpus/lys_parse_mem/issue975.yang new file mode 100644 index 0000000..d0a91a0 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue975.yang @@ -0,0 +1,28 @@ +module example-ietf-interfaces { + yang-version 1.1; + + namespace "urn:ietf:params:xml:ns:yang:example-ietf-interfaces"; + + prefix if; + import ietf-yang-types { + prefix yang; + } + + container interfaces-state { + config false; + list interface { + key "name"; + leaf name { + type string; + } + container statistics { + leaf in-broadcast-pkts { + when "derived-from(if:type, 'ianaifp:multicast')" { + } + + type yang:counter64; + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang b/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang new file mode 100644 index 0000000..670a13b --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue976_a.yang @@ -0,0 +1,12 @@ +module a{ + yang-version 1.1; + namespace "ns1"; + prefix a; + + import issue976_b{ + prefix acl; + } + + augment "/acl:acls/acl:acl/acl:aces/acl:ace/acl:matches" { + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang b/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang new file mode 100644 index 0000000..ee0b621 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue976_b.yang @@ -0,0 +1,32 @@ +module issue976_b { + yang-version 1.1; + namespace "ns2"; + prefix acl; + + container acls { + list acl { + key "name"; + leaf name { + type string; + } + container aces { + list ace { + key "name"; + leaf name { + type string { + length "1..64"; + } + } + container matches { + leaf egress-interface { + type if:interface-ref; + } + leaf ingress-interface { + type if:interface-ref; + } + } + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang b/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang new file mode 100644 index 0000000..1fe355c --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue979_a.yang @@ -0,0 +1,41 @@ +module a { + namespace "a"; + prefix a; + + import b{ + prefix b; + } + + typedef HexOffset { + type string; + } + + grouping group { + container action { + config false; + container register { + config false; + list location { + key "location"; + config false; + leaf location { + type string; + } + b:action "write" { + input { + leaf reg-addr { + type HexOffset; + mandatory true; + } + } + output { + leaf result { + type string; + } + } + } + } + } + } + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang b/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang new file mode 100644 index 0000000..7f5f43e --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/issue979_b.yang @@ -0,0 +1,13 @@ +module b { + namespace "b"; + prefix b; + + extension action { + argument name { + b:arg-type { + type b:identifier; + } + } + } +} + diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1524.yang b/tests/fuzz/corpus/lys_parse_mem/pull1524.yang new file mode 100644 index 0000000..c39fc92 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/pull1524.yang @@ -0,0 +1 @@ +module ''+'c
\ No newline at end of file diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1568.yang b/tests/fuzz/corpus/lys_parse_mem/pull1568.yang new file mode 100644 index 0000000..29b6c2d --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/pull1568.yang @@ -0,0 +1,4 @@ +module +f{grouping +s{list +ó €¡ym{
\ No newline at end of file diff --git a/tests/fuzz/corpus/lys_parse_mem/pull1592.yang b/tests/fuzz/corpus/lys_parse_mem/pull1592.yang new file mode 100644 index 0000000..e722cd8 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/pull1592.yang @@ -0,0 +1,67 @@ +module + + + + + + +nn + + + +{revision +"2016-08-05{ +aaimod:uhGGaaaaaaaaa;aaaiiiiiGGiiiircodimoaa;aaai_od;aaiGGiiiin + + + +{revisd:umGG{{{{{{{{{{{{{{{{{{{;{{{{{{aaaaaadkaaaaaaaaaiiiiiimiiiiiiiGGiiiiimodimod:umGGaaaaaaaaa;aaaimod:umGUaaaaaaaaa;aaaiiii[GGiiiiimodimod:umGGaaaaaaaaa;amodule +odpmduLepd{iiiiiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iciiiiiiiihiiiiiiiiiGGiiiiimod:u-------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iciiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm-----------------------------------iiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm------------------------------------------------------------------------------------------------------------------------------------------------------------QQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm-----------------------------------iiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{hiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmQQmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiimiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------------------------------------------------------------------------------------------------------------------------mmmmmmmmmmmmmmmmmmmmmmmm-------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564249382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{niiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iii{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmmmmmó «mmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +module + + + + +󌋄 +////// +rpsubmodule + + + + + + + +/// + + + +1n-esudmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmzmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQQmmmmmó ¯mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ulmduLed{{{{{{{{{{{{{{{{iii{{{{{{{{{{iiiimod:ul{{{{{{iimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmmm{{{{{{{{mmQQQQQQQQQQQQQQQQQQQQGiiiii0od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiamod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm--------------------------------------------------------mmmmmmmmmmmmmm{{{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii0od:ul{{k{{{siiiiGGiiiii:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmrmmm---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------j----mmmmmmmmm{{{{dpmduLed{{{{{{QQQQQQQQQQQQQQQQQQQmmmmm{{dpmduLed{piiiiiiiiiiiihiiiiiiiiiGGiiiii215833472564209382772827879od:ul{{k{{{siiiiGGiiiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiimod:uln{{{{iiiiiiiiihiiiiiiiiiGGiiiiimodó €³:ul{{{{{{{{{{{{{{{{{{{{iiiimod:ul{{{{{{{{{{{{{{{{{{{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm:mmmmmmmm{{{{dpmduLed{{{{{{{{{{{{{{{{iiiimod:{{iiiim{{mmmmmmmmmmmmmmmmmmmmmmmmmm5833472564209382772827879od:ul{{k{{{siii
\ No newline at end of file diff --git a/tests/fuzz/corpus/lys_parse_mem/pull958.yang b/tests/fuzz/corpus/lys_parse_mem/pull958.yang new file mode 100644 index 0000000..9df4b76 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/pull958.yang @@ -0,0 +1,8 @@ +module m { + prefix p; + namespace " + + list l { + must ""; + } +} diff --git a/tests/fuzz/corpus/lys_parse_mem/pull960.yang b/tests/fuzz/corpus/lys_parse_mem/pull960.yang new file mode 100644 index 0000000..2356615 --- /dev/null +++ b/tests/fuzz/corpus/lys_parse_mem/pull960.yang @@ -0,0 +1,9 @@ +module d{ + namespace ""; + prefix d; + leaf f { + type string; + must ":e"; + default ""; + } +} diff --git a/tests/fuzz/fuzz_regression_test.c b/tests/fuzz/fuzz_regression_test.c new file mode 100644 index 0000000..736cc52 --- /dev/null +++ b/tests/fuzz/fuzz_regression_test.c @@ -0,0 +1,70 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include<fcntl.h> + +int main(int argc, char **argv) +{ + DIR *d; + struct dirent *dir; + pid_t p = 0; + int input_fd = 0; + int status = 0; + int rc = 0; + struct stat path_stat; + + if (argc != 3) { + fprintf(stderr, "invalid number of arguments. Call like this ./fuzz_regression_test fuzz_harness corpus_dir\n"); + return EXIT_FAILURE; + } + + d = opendir(argv[2]); + if (!d) { + fprintf(stderr, "error opening dir %s\n", argv[2]); + return EXIT_FAILURE; + } + + while ((dir = readdir(d)) != NULL) { + stat(dir->d_name, &path_stat); + if (!S_ISREG(path_stat.st_mode)) { + continue; + } + + p = fork(); + if (p == -1) { + fprintf(stderr, "fork failed\n"); + return EXIT_FAILURE; + } else if (p == 0) { + input_fd = open(dir->d_name, O_RDONLY); + if (input_fd == -1) { + fprintf(stderr, "error opening input file %s\n", dir->d_name); + return EXIT_FAILURE; + } + + dup2(input_fd, STDIN_FILENO); + execl(argv[1], argv[1], NULL); + return EXIT_SUCCESS; + } + + rc = waitpid(p, &status, 0); + if (rc == -1) { + fprintf(stderr, "waitpid failed\n"); + return EXIT_FAILURE; + } + + if (!WIFEXITED(status)) { + fprintf(stderr, "test %s - %s failed\n", argv[1], dir->d_name); + return EXIT_FAILURE; + } + + printf("test %s - %s successful\n", argv[1], dir->d_name); + } + + closedir(d); + + return EXIT_SUCCESS; +} diff --git a/tests/fuzz/lyd_parse_mem_json.c b/tests/fuzz/lyd_parse_mem_json.c new file mode 100644 index 0000000..0acad34 --- /dev/null +++ b/tests/fuzz/lyd_parse_mem_json.c @@ -0,0 +1,86 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "libyang.h" + +int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len) +{ + struct ly_ctx *ctx = NULL; + static bool log = false; + const char *schema_a = + "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;" + "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;}" + "identity fast-ethernet {base ethernet;}}"; + const char *schema_b = + "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}" + "feature f; identity gigabit-ethernet { base defs:ethernet;}" + "container cont {leaf leaftarget {type empty;}" + "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}" + "leaf-list leaflisttarget {type uint8; max-elements 5;}}" + "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}" + "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}" + "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}" + "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}" + "leaf-list leaflisttarget {type string;}" + "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}" + "leaf binary-norestr {type binary;}" + "leaf int8 {type int8 {range 10..20;}}" + "leaf uint8 {type uint8 {range 150..200;}}" + "leaf int16 {type int16 {range -20..-10;}}" + "leaf uint16 {type uint16 {range 150..200;}}" + "leaf int32 {type int32;}" + "leaf uint32 {type uint32;}" + "leaf int64 {type int64;}" + "leaf uint64 {type uint64;}" + "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}" + "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}" + "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}" + "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}" + "leaf str {type string {length 8..10; pattern '[a-z ]*';}}" + "leaf str-norestr {type string;}" + "leaf str-utf8 {type string{length 2..5; pattern '€*';}}" + "leaf bool {type boolean;}" + "leaf empty {type empty;}" + "leaf ident {type identityref {base defs:interface-type;}}" + "leaf inst {type instance-identifier {require-instance true;}}" + "leaf inst-noreq {type instance-identifier {require-instance false;}}" + "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}" + "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}" + "leaf un1 {type union {" + "type leafref {path /int8; require-instance true;}" + "type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }" + "type string {length 1..20;}}}}"; + char *data = NULL; + struct lyd_node *tree = NULL; + LY_ERR err; + + if (!log) { + ly_log_options(0); + log = true; + } + + err = ly_ctx_new(NULL, 0, &ctx); + if (err != LY_SUCCESS) { + fprintf(stderr, "Failed to create context\n"); + exit(EXIT_FAILURE); + } + + lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL); + lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL); + + data = malloc(len + 1); + if (data == NULL) { + return 0; + } + memcpy(data, buf, len); + data[len] = 0; + + lyd_parse_data_mem(ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree); + lyd_free_all(tree); + ly_ctx_destroy(ctx); + + free(data); + + return 0; +} diff --git a/tests/fuzz/lyd_parse_mem_xml.c b/tests/fuzz/lyd_parse_mem_xml.c new file mode 100644 index 0000000..5574f8d --- /dev/null +++ b/tests/fuzz/lyd_parse_mem_xml.c @@ -0,0 +1,86 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "libyang.h" + +int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len) +{ + struct ly_ctx *ctx = NULL; + static bool log = false; + const char *schema_a = + "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;" + "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;}" + "identity fast-ethernet {base ethernet;}}"; + const char *schema_b = + "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}" + "feature f; identity gigabit-ethernet { base defs:ethernet;}" + "container cont {leaf leaftarget {type empty;}" + "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}" + "leaf-list leaflisttarget {type uint8; max-elements 5;}}" + "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}" + "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}" + "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}" + "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}" + "leaf-list leaflisttarget {type string;}" + "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}" + "leaf binary-norestr {type binary;}" + "leaf int8 {type int8 {range 10..20;}}" + "leaf uint8 {type uint8 {range 150..200;}}" + "leaf int16 {type int16 {range -20..-10;}}" + "leaf uint16 {type uint16 {range 150..200;}}" + "leaf int32 {type int32;}" + "leaf uint32 {type uint32;}" + "leaf int64 {type int64;}" + "leaf uint64 {type uint64;}" + "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}" + "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}" + "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}" + "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}" + "leaf str {type string {length 8..10; pattern '[a-z ]*';}}" + "leaf str-norestr {type string;}" + "leaf str-utf8 {type string{length 2..5; pattern '€*';}}" + "leaf bool {type boolean;}" + "leaf empty {type empty;}" + "leaf ident {type identityref {base defs:interface-type;}}" + "leaf inst {type instance-identifier {require-instance true;}}" + "leaf inst-noreq {type instance-identifier {require-instance false;}}" + "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}" + "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}" + "leaf un1 {type union {" + "type leafref {path /int8; require-instance true;}" + "type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }" + "type string {length 1..20;}}}}"; + char *data = NULL; + struct lyd_node *tree = NULL; + LY_ERR err; + + if (!log) { + ly_log_options(0); + log = true; + } + + err = ly_ctx_new(NULL, 0, &ctx); + if (err != LY_SUCCESS) { + fprintf(stderr, "Failed to create context\n"); + exit(EXIT_FAILURE); + } + + lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL); + lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL); + + data = malloc(len + 1); + if (data == NULL) { + return 0; + } + memcpy(data, buf, len); + data[len] = 0; + + lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree); + lyd_free_all(tree); + ly_ctx_destroy(ctx); + + free(data); + + return 0; +} diff --git a/tests/fuzz/lys_parse_mem.c b/tests/fuzz/lys_parse_mem.c new file mode 100644 index 0000000..02e0a62 --- /dev/null +++ b/tests/fuzz/lys_parse_mem.c @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "libyang.h" + +int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len) +{ + struct ly_ctx *ctx = NULL; + static bool log = false; + char *data = NULL; + LY_ERR err; + + if (!log) { + ly_log_options(0); + log = true; + } + + err = ly_ctx_new(NULL, 0, &ctx); + if (err != LY_SUCCESS) { + fprintf(stderr, "Failed to create context\n"); + exit(EXIT_FAILURE); + } + + data = malloc(len + 1); + if (data == NULL) { + return 0; + } + + memcpy(data, buf, len); + data[len] = 0; + + lys_parse_mem(ctx, data, LYS_IN_YANG, NULL); + ly_ctx_destroy(ctx); + free(data); + return 0; +} diff --git a/tests/fuzz/main.c b/tests/fuzz/main.c new file mode 100644 index 0000000..a4b64b5 --- /dev/null +++ b/tests/fuzz/main.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len); + +#ifdef __AFL_COMPILER + +int main(void) { + int ret; + uint8_t buf[64 * 1024]; + +#ifdef __AFL_LOOP + while (__AFL_LOOP(10000)) +#endif + { + ret = fread(buf, 1, sizeof(buf), stdin); + if (ret < 0) { + return 0; + } + + LLVMFuzzerTestOneInput(buf, ret); + + } + + return 0; +} + +#else + +int +main(void) +{ + int ret; + uint8_t buf[64 * 1024]; + + ret = fread(buf, 1, sizeof(buf), stdin); + if (ret < 0) { + return 0; + } + + LLVMFuzzerTestOneInput(buf, ret); + + return 0; +} + +#endif /* __AFL_COMPILER */ diff --git a/tests/fuzz/yang_parse_module.c b/tests/fuzz/yang_parse_module.c new file mode 100644 index 0000000..f420966 --- /dev/null +++ b/tests/fuzz/yang_parse_module.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "libyang.h" + +int LLVMFuzzerTestOneInput(uint8_t const *buf, size_t len) +{ + struct lys_module *mod; + uint8_t *data = NULL; + struct ly_ctx *ctx = NULL; + static bool log = false; + LY_ERR err; + + if (!log) { + ly_log_options(0); + log = true; + } + + err = ly_ctx_new(NULL, 0, &ctx); + if (err != LY_SUCCESS) { + fprintf(stderr, "Failed to create new context\n"); + return 0; + } + + data = malloc(len + 1); + if (data == NULL) { + fprintf(stderr, "Out of memory\n"); + return 0; + } + memcpy(data, buf, len); + data[len] = 0; + + lys_parse_mem(ctx, (const char *)data, LYS_IN_YANG, &mod); + + free(data); + ly_ctx_destroy(ctx); + return 0; +} diff --git a/tests/ld.supp b/tests/ld.supp new file mode 100644 index 0000000..e501a3b --- /dev/null +++ b/tests/ld.supp @@ -0,0 +1,8 @@ +{ + dl's thread-local data + Memcheck:Leak + match-leak-kinds: reachable + fun:calloc + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 +} diff --git a/tests/modules/yang/iana-if-type@2014-05-08.yang b/tests/modules/yang/iana-if-type@2014-05-08.yang new file mode 100644 index 0000000..5dd8219 --- /dev/null +++ b/tests/modules/yang/iana-if-type@2014-05-08.yang @@ -0,0 +1,1547 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + import ietf-interfaces { + prefix if; + } + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + <mailto:iana@iana.org>"; + description + "This YANG module defines YANG identities for IANA-registered + interface types. + + This YANG module is maintained by IANA and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Requests for new values should be made to IANA via + email (iana@iana.org). + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC 7224; + see the RFC itself for full legal notices."; + reference + "IANA 'ifType definitions' registry. + <http://www.iana.org/assignments/smi-numbers>"; + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7224: IANA Interface Type YANG Module"; + } + + identity iana-interface-type { + base if:interface-type; + description + "This identity is used as a base for all interface types + defined in the 'ifType definitions' registry."; + } + + + + + + + identity other { + base iana-interface-type; + } + identity regular1822 { + base iana-interface-type; + } + identity hdh1822 { + base iana-interface-type; + } + identity ddnX25 { + base iana-interface-type; + } + identity rfc877x25 { + base iana-interface-type; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + identity ethernetCsmacd { + base iana-interface-type; + description + "For all Ethernet-like interfaces, regardless of speed, + as per RFC 3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88023Csmacd { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88024TokenBus { + base iana-interface-type; + } + identity iso88025TokenRing { + base iana-interface-type; + } + identity iso88026Man { + base iana-interface-type; + } + identity starLan { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity proteon10Mbit { + base iana-interface-type; + } + identity proteon80Mbit { + base iana-interface-type; + } + identity hyperchannel { + base iana-interface-type; + } + identity fddi { + base iana-interface-type; + reference + "RFC 1512 - FDDI Management Information Base"; + } + identity lapb { + base iana-interface-type; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + identity sdlc { + base iana-interface-type; + } + identity ds1 { + base iana-interface-type; + description + "DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + identity e1 { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + + + identity basicISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity primaryISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity propPointToPointSerial { + base iana-interface-type; + description + "Proprietary serial."; + } + identity ppp { + base iana-interface-type; + } + identity softwareLoopback { + base iana-interface-type; + } + identity eon { + base iana-interface-type; + description + "CLNP over IP."; + } + identity ethernet3Mbit { + base iana-interface-type; + } + identity nsip { + base iana-interface-type; + description + "XNS over IP."; + } + identity slip { + base iana-interface-type; + description + "Generic SLIP."; + } + identity ultra { + base iana-interface-type; + description + "Ultra Technologies."; + } + identity ds3 { + base iana-interface-type; + description + "DS3-MIB."; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + identity sip { + base iana-interface-type; + description + "SMDS, coffee."; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + identity frameRelay { + base iana-interface-type; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + identity rs232 { + base iana-interface-type; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + identity para { + base iana-interface-type; + description + "Parallel-port."; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + identity arcnet { + base iana-interface-type; + description + "ARCnet."; + } + identity arcnetPlus { + base iana-interface-type; + description + "ARCnet Plus."; + } + + + + identity atm { + base iana-interface-type; + description + "ATM cells."; + } + identity miox25 { + base iana-interface-type; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + identity sonet { + base iana-interface-type; + description + "SONET or SDH."; + } + identity x25ple { + base iana-interface-type; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + identity iso88022llc { + base iana-interface-type; + } + identity localTalk { + base iana-interface-type; + } + identity smdsDxi { + base iana-interface-type; + } + identity frameRelayService { + base iana-interface-type; + description + "FRNETSERV-MIB."; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + identity v35 { + base iana-interface-type; + } + identity hssi { + base iana-interface-type; + } + identity hippi { + base iana-interface-type; + } + + identity modem { + base iana-interface-type; + description + "Generic modem."; + } + identity aal5 { + base iana-interface-type; + description + "AAL5 over ATM."; + } + identity sonetPath { + base iana-interface-type; + } + identity sonetVT { + base iana-interface-type; + } + identity smdsIcip { + base iana-interface-type; + description + "SMDS InterCarrier Interface."; + } + identity propVirtual { + base iana-interface-type; + description + "Proprietary virtual/internal."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity propMultiplexor { + base iana-interface-type; + description + "Proprietary multiplexing."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity ieee80212 { + base iana-interface-type; + description + "100BaseVG."; + } + identity fibreChannel { + base iana-interface-type; + description + "Fibre Channel."; + } + + + + identity hippiInterface { + base iana-interface-type; + description + "HIPPI interfaces."; + } + identity frameRelayInterconnect { + base iana-interface-type; + status obsolete; + description + "Obsolete; use either + frameRelay(32) or frameRelayService(44)."; + } + identity aflane8023 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.3."; + } + identity aflane8025 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.5."; + } + identity cctEmul { + base iana-interface-type; + description + "ATM Emulated circuit."; + } + identity fastEther { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity isdn { + base iana-interface-type; + description + "ISDN and X.25."; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + + + + identity v11 { + base iana-interface-type; + description + "CCITT V.11/X.21."; + } + identity v36 { + base iana-interface-type; + description + "CCITT V.36."; + } + identity g703at64k { + base iana-interface-type; + description + "CCITT G703 at 64Kbps."; + } + identity g703at2mb { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + } + identity qllc { + base iana-interface-type; + description + "SNA QLLC."; + } + identity fastEtherFX { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity channel { + base iana-interface-type; + description + "Channel."; + } + identity ieee80211 { + base iana-interface-type; + description + "Radio spread spectrum."; + } + identity ibm370parChan { + base iana-interface-type; + description + "IBM System 360/370 OEMI Channel."; + } + identity escon { + base iana-interface-type; + description + "IBM Enterprise Systems Connection."; + } + identity dlsw { + base iana-interface-type; + description + "Data Link Switching."; + } + identity isdns { + base iana-interface-type; + description + "ISDN S/T interface."; + } + identity isdnu { + base iana-interface-type; + description + "ISDN U interface."; + } + identity lapd { + base iana-interface-type; + description + "Link Access Protocol D."; + } + identity ipSwitch { + base iana-interface-type; + description + "IP Switching Objects."; + } + identity rsrb { + base iana-interface-type; + description + "Remote Source Route Bridging."; + } + identity atmLogical { + base iana-interface-type; + description + "ATM Logical Port."; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + identity ds0 { + base iana-interface-type; + description + "Digital Signal Level 0."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity ds0Bundle { + base iana-interface-type; + description + "Group of ds0s on the same ds1."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity bsc { + base iana-interface-type; + description + "Bisynchronous Protocol."; + } + identity async { + base iana-interface-type; + description + "Asynchronous Protocol."; + } + identity cnr { + base iana-interface-type; + description + "Combat Net Radio."; + } + identity iso88025Dtr { + base iana-interface-type; + description + "ISO 802.5r DTR."; + } + identity eplrs { + base iana-interface-type; + description + "Ext Pos Loc Report Sys."; + } + identity arap { + base iana-interface-type; + description + "Appletalk Remote Access Protocol."; + } + identity propCnls { + base iana-interface-type; + description + "Proprietary Connectionless Protocol."; + } + identity hostPad { + base iana-interface-type; + description + "CCITT-ITU X.29 PAD Protocol."; + } + identity termPad { + base iana-interface-type; + description + "CCITT-ITU X.3 PAD Facility."; + } + identity frameRelayMPI { + base iana-interface-type; + description + "Multiproto Interconnect over FR."; + } + identity x213 { + base iana-interface-type; + description + "CCITT-ITU X213."; + } + identity adsl { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop."; + } + identity radsl { + base iana-interface-type; + description + "Rate-Adapt. Digital Subscriber Loop."; + } + identity sdsl { + base iana-interface-type; + description + "Symmetric Digital Subscriber Loop."; + } + identity vdsl { + base iana-interface-type; + description + "Very H-Speed Digital Subscrib. Loop."; + } + identity iso88025CRFPInt { + base iana-interface-type; + description + "ISO 802.5 CRFP."; + } + identity myrinet { + base iana-interface-type; + description + "Myricom Myrinet."; + } + identity voiceEM { + base iana-interface-type; + description + "Voice recEive and transMit."; + } + identity voiceFXO { + base iana-interface-type; + description + "Voice Foreign Exchange Office."; + } + identity voiceFXS { + base iana-interface-type; + description + "Voice Foreign Exchange Station."; + } + identity voiceEncap { + base iana-interface-type; + description + "Voice encapsulation."; + } + identity voiceOverIp { + base iana-interface-type; + description + "Voice over IP encapsulation."; + } + identity atmDxi { + base iana-interface-type; + description + "ATM DXI."; + } + identity atmFuni { + base iana-interface-type; + description + "ATM FUNI."; + } + identity atmIma { + base iana-interface-type; + description + "ATM IMA."; + } + identity pppMultilinkBundle { + base iana-interface-type; + description + "PPP Multilink Bundle."; + } + identity ipOverCdlc { + base iana-interface-type; + description + "IBM ipOverCdlc."; + } + identity ipOverClaw { + base iana-interface-type; + description + "IBM Common Link Access to Workstn."; + } + identity stackToStack { + base iana-interface-type; + description + "IBM stackToStack."; + } + identity virtualIpAddress { + base iana-interface-type; + description + "IBM VIPA."; + } + identity mpc { + base iana-interface-type; + description + "IBM multi-protocol channel support."; + } + identity ipOverAtm { + base iana-interface-type; + description + "IBM ipOverAtm."; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + identity iso88025Fiber { + base iana-interface-type; + description + "ISO 802.5j Fiber Token Ring."; + } + identity tdlc { + base iana-interface-type; + description + "IBM twinaxial data link control."; + } + identity gigabitEthernet { + base iana-interface-type; + status deprecated; + + + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity hdlc { + base iana-interface-type; + description + "HDLC."; + } + identity lapf { + base iana-interface-type; + description + "LAP F."; + } + identity v37 { + base iana-interface-type; + description + "V.37."; + } + identity x25mlp { + base iana-interface-type; + description + "Multi-Link Protocol."; + } + identity x25huntGroup { + base iana-interface-type; + description + "X25 Hunt Group."; + } + identity transpHdlc { + base iana-interface-type; + description + "Transp HDLC."; + } + identity interleave { + base iana-interface-type; + description + "Interleave channel."; + } + identity fast { + base iana-interface-type; + description + "Fast channel."; + } + + identity ip { + base iana-interface-type; + description + "IP (for APPN HPR in IP networks)."; + } + identity docsCableMaclayer { + base iana-interface-type; + description + "CATV Mac Layer."; + } + identity docsCableDownstream { + base iana-interface-type; + description + "CATV Downstream interface."; + } + identity docsCableUpstream { + base iana-interface-type; + description + "CATV Upstream interface."; + } + identity a12MppSwitch { + base iana-interface-type; + description + "Avalon Parallel Processor."; + } + identity tunnel { + base iana-interface-type; + description + "Encapsulation interface."; + } + identity coffee { + base iana-interface-type; + description + "Coffee pot."; + reference + "RFC 2325 - Coffee MIB"; + } + identity ces { + base iana-interface-type; + description + "Circuit Emulation Service."; + } + identity atmSubInterface { + base iana-interface-type; + description + "ATM Sub Interface."; + } + + identity l2vlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using 802.1Q."; + } + identity l3ipvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IP."; + } + identity l3ipxvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IPX."; + } + identity digitalPowerline { + base iana-interface-type; + description + "IP over Power Lines."; + } + identity mediaMailOverIp { + base iana-interface-type; + description + "Multimedia Mail over IP."; + } + identity dtm { + base iana-interface-type; + description + "Dynamic synchronous Transfer Mode."; + } + identity dcn { + base iana-interface-type; + description + "Data Communications Network."; + } + identity ipForward { + base iana-interface-type; + description + "IP Forwarding Interface."; + } + identity msdsl { + base iana-interface-type; + description + "Multi-rate Symmetric DSL."; + } + identity ieee1394 { + base iana-interface-type; + + description + "IEEE1394 High Performance Serial Bus."; + } + identity if-gsn { + base iana-interface-type; + description + "HIPPI-6400."; + } + identity dvbRccMacLayer { + base iana-interface-type; + description + "DVB-RCC MAC Layer."; + } + identity dvbRccDownstream { + base iana-interface-type; + description + "DVB-RCC Downstream Channel."; + } + identity dvbRccUpstream { + base iana-interface-type; + description + "DVB-RCC Upstream Channel."; + } + identity atmVirtual { + base iana-interface-type; + description + "ATM Virtual Interface."; + } + identity mplsTunnel { + base iana-interface-type; + description + "MPLS Tunnel Virtual Interface."; + } + identity srp { + base iana-interface-type; + description + "Spatial Reuse Protocol."; + } + identity voiceOverAtm { + base iana-interface-type; + description + "Voice over ATM."; + } + identity voiceOverFrameRelay { + base iana-interface-type; + description + "Voice Over Frame Relay."; + } + identity idsl { + base iana-interface-type; + description + "Digital Subscriber Loop over ISDN."; + } + identity compositeLink { + base iana-interface-type; + description + "Avici Composite Link Interface."; + } + identity ss7SigLink { + base iana-interface-type; + description + "SS7 Signaling Link."; + } + identity propWirelessP2P { + base iana-interface-type; + description + "Prop. P2P wireless interface."; + } + identity frForward { + base iana-interface-type; + description + "Frame Forward Interface."; + } + identity rfc1483 { + base iana-interface-type; + description + "Multiprotocol over ATM AAL5."; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + identity usb { + base iana-interface-type; + description + "USB Interface."; + } + identity ieee8023adLag { + base iana-interface-type; + description + "IEEE 802.3ad Link Aggregate."; + } + identity bgppolicyaccounting { + base iana-interface-type; + description + "BGP Policy Accounting."; + } + identity frf16MfrBundle { + base iana-interface-type; + description + "FRF.16 Multilink Frame Relay."; + } + identity h323Gatekeeper { + base iana-interface-type; + description + "H323 Gatekeeper."; + } + identity h323Proxy { + base iana-interface-type; + description + "H323 Voice and Video Proxy."; + } + identity mpls { + base iana-interface-type; + description + "MPLS."; + } + identity mfSigLink { + base iana-interface-type; + description + "Multi-frequency signaling link."; + } + identity hdsl2 { + base iana-interface-type; + description + "High Bit-Rate DSL - 2nd generation."; + } + identity shdsl { + base iana-interface-type; + description + "Multirate HDSL2."; + } + identity ds1FDL { + base iana-interface-type; + description + "Facility Data Link (4Kbps) on a DS1."; + } + identity pos { + base iana-interface-type; + description + "Packet over SONET/SDH Interface."; + } + + + + identity dvbAsiIn { + base iana-interface-type; + description + "DVB-ASI Input."; + } + identity dvbAsiOut { + base iana-interface-type; + description + "DVB-ASI Output."; + } + identity plc { + base iana-interface-type; + description + "Power Line Communications."; + } + identity nfas { + base iana-interface-type; + description + "Non-Facility Associated Signaling."; + } + identity tr008 { + base iana-interface-type; + description + "TR008."; + } + identity gr303RDT { + base iana-interface-type; + description + "Remote Digital Terminal."; + } + identity gr303IDT { + base iana-interface-type; + description + "Integrated Digital Terminal."; + } + identity isup { + base iana-interface-type; + description + "ISUP."; + } + identity propDocsWirelessMaclayer { + base iana-interface-type; + description + "Cisco proprietary Maclayer."; + } + + + + identity propDocsWirelessDownstream { + base iana-interface-type; + description + "Cisco proprietary Downstream."; + } + identity propDocsWirelessUpstream { + base iana-interface-type; + description + "Cisco proprietary Upstream."; + } + identity hiperlan2 { + base iana-interface-type; + description + "HIPERLAN Type 2 Radio Interface."; + } + identity propBWAp2Mp { + base iana-interface-type; + description + "PropBroadbandWirelessAccesspt2Multipt (use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated, and ieee80216WMAN(237) should be used + instead)."; + } + identity sonetOverheadChannel { + base iana-interface-type; + description + "SONET Overhead Channel."; + } + identity digitalWrapperOverheadChannel { + base iana-interface-type; + description + "Digital Wrapper."; + } + identity aal2 { + base iana-interface-type; + description + "ATM adaptation layer 2."; + } + identity radioMAC { + base iana-interface-type; + description + "MAC layer over radio links."; + } + identity atmRadio { + base iana-interface-type; + description + "ATM over radio links."; + } + identity imt { + base iana-interface-type; + description + "Inter-Machine Trunks."; + } + identity mvl { + base iana-interface-type; + description + "Multiple Virtual Lines DSL."; + } + identity reachDSL { + base iana-interface-type; + description + "Long Reach DSL."; + } + identity frDlciEndPt { + base iana-interface-type; + description + "Frame Relay DLCI End Point."; + } + identity atmVciEndPt { + base iana-interface-type; + description + "ATM VCI End Point."; + } + identity opticalChannel { + base iana-interface-type; + description + "Optical Channel."; + } + identity opticalTransport { + base iana-interface-type; + description + "Optical Transport."; + } + identity propAtm { + base iana-interface-type; + description + "Proprietary ATM."; + } + identity voiceOverCable { + base iana-interface-type; + description + "Voice Over Cable Interface."; + } + + + + identity infiniband { + base iana-interface-type; + description + "Infiniband."; + } + identity teLink { + base iana-interface-type; + description + "TE Link."; + } + identity q2931 { + base iana-interface-type; + description + "Q.2931."; + } + identity virtualTg { + base iana-interface-type; + description + "Virtual Trunk Group."; + } + identity sipTg { + base iana-interface-type; + description + "SIP Trunk Group."; + } + identity sipSig { + base iana-interface-type; + description + "SIP Signaling."; + } + identity docsCableUpstreamChannel { + base iana-interface-type; + description + "CATV Upstream Channel."; + } + identity econet { + base iana-interface-type; + description + "Acorn Econet."; + } + identity pon155 { + base iana-interface-type; + description + "FSAN 155Mb Symetrical PON interface."; + } + + + + identity pon622 { + base iana-interface-type; + description + "FSAN 622Mb Symetrical PON interface."; + } + identity bridge { + base iana-interface-type; + description + "Transparent bridge interface."; + } + identity linegroup { + base iana-interface-type; + description + "Interface common to multiple lines."; + } + identity voiceEMFGD { + base iana-interface-type; + description + "Voice E&M Feature Group D."; + } + identity voiceFGDEANA { + base iana-interface-type; + description + "Voice FGD Exchange Access North American."; + } + identity voiceDID { + base iana-interface-type; + description + "Voice Direct Inward Dialing."; + } + identity mpegTransport { + base iana-interface-type; + description + "MPEG transport interface."; + } + identity sixToFour { + base iana-interface-type; + status deprecated; + description + "6to4 interface (DEPRECATED)."; + reference + "RFC 4087 - IP Tunnel MIB"; + } + identity gtp { + base iana-interface-type; + description + "GTP (GPRS Tunneling Protocol)."; + } + identity pdnEtherLoop1 { + base iana-interface-type; + description + "Paradyne EtherLoop 1."; + } + identity pdnEtherLoop2 { + base iana-interface-type; + description + "Paradyne EtherLoop 2."; + } + identity opticalChannelGroup { + base iana-interface-type; + description + "Optical Channel Group."; + } + identity homepna { + base iana-interface-type; + description + "HomePNA ITU-T G.989."; + } + identity gfp { + base iana-interface-type; + description + "Generic Framing Procedure (GFP)."; + } + identity ciscoISLvlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using Cisco ISL."; + } + identity actelisMetaLOOP { + base iana-interface-type; + description + "Acteleis proprietary MetaLOOP High Speed Link."; + } + identity fcipLink { + base iana-interface-type; + description + "FCIP Link."; + } + identity rpr { + base iana-interface-type; + description + "Resilient Packet Ring Interface Type."; + } + + + + identity qam { + base iana-interface-type; + description + "RF Qam Interface."; + } + identity lmp { + base iana-interface-type; + description + "Link Management Protocol."; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + identity cblVectaStar { + base iana-interface-type; + description + "Cambridge Broadband Networks Limited VectaStar."; + } + identity docsCableMCmtsDownstream { + base iana-interface-type; + description + "CATV Modular CMTS Downstream Interface."; + } + identity adsl2 { + base iana-interface-type; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)."; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + identity macSecControlledIF { + base iana-interface-type; + description + "MACSecControlled."; + } + identity macSecUncontrolledIF { + base iana-interface-type; + description + "MACSecUncontrolled."; + } + identity aviciOpticalEther { + base iana-interface-type; + description + "Avici Optical Ethernet Aggregate."; + } + identity atmbond { + base iana-interface-type; + description + "atmbond."; + } + identity voiceFGDOS { + base iana-interface-type; + description + "Voice FGD Operator Services."; + } + identity mocaVersion1 { + base iana-interface-type; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA."; + } + identity ieee80216WMAN { + base iana-interface-type; + description + "IEEE 802.16 WMAN interface."; + } + identity adsl2plus { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop Version 2 - + Version 2 Plus and all variants."; + } + identity dvbRcsMacLayer { + base iana-interface-type; + description + "DVB-RCS MAC Layer."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbTdm { + base iana-interface-type; + description + "DVB Satellite TDM."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbRcsTdma { + base iana-interface-type; + description + "DVB-RCS TDMA."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity x86Laps { + base iana-interface-type; + description + "LAPS based on ITU-T X.86/Y.1323."; + } + identity wwanPP { + base iana-interface-type; + description + "3GPP WWAN."; + } + identity wwanPP2 { + base iana-interface-type; + description + "3GPP2 WWAN."; + } + identity voiceEBS { + base iana-interface-type; + description + "Voice P-phone EBS physical interface."; + } + identity ifPwType { + base iana-interface-type; + description + "Pseudowire interface type."; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)"; + } + identity ilan { + base iana-interface-type; + description + "Internal LAN on a bridge per IEEE 802.1ap."; + } + identity pip { + base iana-interface-type; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB."; + } + identity aluELP { + base iana-interface-type; + description + "Alcatel-Lucent Ethernet Link Protection."; + } + identity gpon { + base iana-interface-type; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948."; + } + identity vdsl2 { + base iana-interface-type; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)."; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + identity capwapDot11Profile { + base iana-interface-type; + description + "WLAN Profile Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapDot11Bss { + base iana-interface-type; + description + "WLAN BSS Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapWtpVirtualRadio { + base iana-interface-type; + description + "WTP Virtual Radio Interface."; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + identity bits { + base iana-interface-type; + description + "bitsport."; + } + identity docsCableUpstreamRfPort { + base iana-interface-type; + description + "DOCSIS CATV Upstream RF Port."; + } + + + identity cableDownstreamRfPort { + base iana-interface-type; + description + "CATV downstream RF Port."; + } + identity vmwareVirtualNic { + base iana-interface-type; + description + "VMware Virtual Network Interface."; + } + identity ieee802154 { + base iana-interface-type; + description + "IEEE 802.15.4 WPAN interface."; + reference + "IEEE 802.15.4-2006"; + } + identity otnOdu { + base iana-interface-type; + description + "OTN Optical Data Unit."; + } + identity otnOtu { + base iana-interface-type; + description + "OTN Optical channel Transport Unit."; + } + identity ifVfiType { + base iana-interface-type; + description + "VPLS Forwarding Instance Interface Type."; + } + identity g9981 { + base iana-interface-type; + description + "G.998.1 bonded interface."; + } + identity g9982 { + base iana-interface-type; + description + "G.998.2 bonded interface."; + } + identity g9983 { + base iana-interface-type; + description + "G.998.3 bonded interface."; + } + + identity aluEpon { + base iana-interface-type; + description + "Ethernet Passive Optical Networks (E-PON)."; + } + identity aluEponOnu { + base iana-interface-type; + description + "EPON Optical Network Unit."; + } + identity aluEponPhysicalUni { + base iana-interface-type; + description + "EPON physical User to Network interface."; + } + identity aluEponLogicalLink { + base iana-interface-type; + description + "The emulation of a point-to-point link over the EPON + layer."; + } + identity aluGponOnu { + base iana-interface-type; + description + "GPON Optical Network Unit."; + reference + "ITU-T G.984.2"; + } + identity aluGponPhysicalUni { + base iana-interface-type; + description + "GPON physical User to Network interface."; + reference + "ITU-T G.984.2"; + } + identity vmwareNicTeam { + base iana-interface-type; + description + "VMware NIC Team."; + } +} diff --git a/tests/modules/yang/ietf-interfaces@2014-05-08.yang b/tests/modules/yang/ietf-interfaces@2014-05-08.yang new file mode 100644 index 0000000..ad64425 --- /dev/null +++ b/tests/modules/yang/ietf-interfaces@2014-05-08.yang @@ -0,0 +1,725 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: Thomas Nadeau + <mailto:tnadeau@lucidvision.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 7223; see + the RFC itself for full legal notices."; + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Configuration data nodes + */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request if the implementation does not support + pre-provisioning of interfaces or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation to + + decide whether to modify this single leaf in 'startup' or + perform an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + /* + * Operational state data nodes + */ + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + + + + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + + + + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + + + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + + + + + + + + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + + + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + + + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + + + + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + + + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} diff --git a/tests/modules/yang/ietf-ip@2014-06-16.yang b/tests/modules/yang/ietf-ip@2014-06-16.yang new file mode 100644 index 0000000..1499120 --- /dev/null +++ b/tests/modules/yang/ietf-ip@2014-06-16.yang @@ -0,0 +1,758 @@ +module ietf-ip { + + namespace "urn:ietf:params:xml:ns:yang:ietf-ip"; + prefix ip; + + import ietf-interfaces { + prefix if; + } + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: Thomas Nadeau + <mailto:tnadeau@lucidvision.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + + + + + + + + + + description + "This module contains a collection of YANG definitions for + configuring IP implementations. + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 7277; see + the RFC itself for full legal notices."; + + revision 2014-06-16 { + description + "Initial revision."; + reference + "RFC 7277: A YANG Data Model for IP Management"; + } + + /* + + * Features + */ + + feature ipv4-non-contiguous-netmasks { + description + "Indicates support for configuring non-contiguous + subnet masks."; + } + + feature ipv6-privacy-autoconf { + description + "Indicates support for Privacy Extensions for Stateless Address + Autoconfiguration in IPv6."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6"; + } + + + + + + /* + * Typedefs + */ + + typedef ip-address-origin { + type enumeration { + enum other { + description + "None of the following."; + } + enum static { + description + "Indicates that the address has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum dhcp { + description + "Indicates an address that has been assigned to this + system by a DHCP server."; + } + enum link-layer { + description + "Indicates an address created by IPv6 stateless + autoconfiguration that embeds a link-layer address in its + interface identifier."; + } + enum random { + description + "Indicates an address chosen by the system at + + random, e.g., an IPv4 address within 169.254/16, an + RFC 4941 temporary address, or an RFC 7217 semantically + opaque address."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + RFC 7217: A Method for Generating Semantically Opaque + Interface Identifiers with IPv6 Stateless + Address Autoconfiguration (SLAAC)"; + } + } + description + "The origin of an address."; + } + + + + typedef neighbor-origin { + type enumeration { + enum other { + description + "None of the following."; + } + enum static { + description + "Indicates that the mapping has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum dynamic { + description + "Indicates that the mapping has been dynamically resolved + using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery + protocol."; + } + } + description + "The origin of a neighbor entry."; + } + + /* + * Configuration data nodes + */ + + augment "/if:interfaces/if:interface" { + description + "Parameters for configuring IP on interfaces. + + If an interface is not capable of running IP, the server + must not allow the client to configure these parameters."; + + container ipv4 { + presence + "Enables IPv4 unless the 'enabled' leaf + (which defaults to 'true') is set to 'false'"; + description + "Parameters for the IPv4 address family."; + + + + + + + + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv4 is enabled or disabled on this + interface. When IPv4 is enabled, this interface is + connected to an IPv4 stack, and the interface can send + and receive IPv4 packets."; + } + leaf forwarding { + type boolean; + default false; + description + "Controls IPv4 packet forwarding of datagrams received by, + but not addressed to, this interface. IPv4 routers + forward datagrams. IPv4 hosts do not (except those + source-routed via the host)."; + } + leaf mtu { + type uint16 { + range "68..max"; + } + units octets; + description + "The size, in octets, of the largest IPv4 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 791: Internet Protocol"; + } + list address { + key "ip"; + description + "The list of configured IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address on the interface."; + } + + + + choice subnet { + mandatory true; + description + "The subnet can be specified as a prefix-length, or, + if the server supports non-contiguous netmasks, as + a netmask."; + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the subnet prefix."; + } + leaf netmask { + if-feature ipv4-non-contiguous-netmasks; + type yang:dotted-quad; + description + "The subnet specified as a netmask."; + } + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv4 addresses to + link-layer addresses. + + Entries in this list are used as static entries in the + ARP Cache."; + reference + "RFC 826: An Ethernet Address Resolution Protocol"; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + } + + + container ipv6 { + presence + "Enables IPv6 unless the 'enabled' leaf + (which defaults to 'true') is set to 'false'"; + description + "Parameters for the IPv6 address family."; + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv6 is enabled or disabled on this + interface. When IPv6 is enabled, this interface is + connected to an IPv6 stack, and the interface can send + and receive IPv6 packets."; + } + leaf forwarding { + type boolean; + default false; + description + "Controls IPv6 packet forwarding of datagrams received by, + but not addressed to, this interface. IPv6 routers + forward datagrams. IPv6 hosts do not (except those + source-routed via the host)."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 6.2.1, IsRouter"; + } + leaf mtu { + type uint32 { + range "1280..max"; + } + units octets; + description + "The size, in octets, of the largest IPv6 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + Section 5"; + } + + + list address { + key "ip"; + description + "The list of configured IPv6 addresses on the interface."; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address on the interface."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + mandatory true; + description + "The length of the subnet prefix."; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv6 addresses to + link-layer addresses. + + Entries in this list are used as static entries in the + Neighbor Cache."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + + + + + + leaf dup-addr-detect-transmits { + type uint32; + default 1; + description + "The number of consecutive Neighbor Solicitation messages + sent while performing Duplicate Address Detection on a + tentative address. A value of zero indicates that + Duplicate Address Detection is not performed on + tentative addresses. A value of one indicates a single + transmission with no follow-up retransmissions."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + container autoconf { + description + "Parameters to control the autoconfiguration of IPv6 + addresses, as described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + + leaf create-global-addresses { + type boolean; + default true; + description + "If enabled, the host creates global addresses as + described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration + Section 5.5"; + } + leaf create-temporary-addresses { + if-feature ipv6-privacy-autoconf; + type boolean; + default false; + description + "If enabled, the host creates temporary addresses as + described in RFC 4941."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6"; + } + + + + + + + + leaf temporary-valid-lifetime { + if-feature ipv6-privacy-autoconf; + type uint32; + units "seconds"; + default 604800; + description + "The time period during which the temporary address + is valid."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_VALID_LIFETIME"; + } + leaf temporary-preferred-lifetime { + if-feature ipv6-privacy-autoconf; + type uint32; + units "seconds"; + default 86400; + description + "The time period during which the temporary address is + preferred."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_PREFERRED_LIFETIME"; + } + } + } + } + + /* + * Operational state data nodes + */ + + augment "/if:interfaces-state/if:interface" { + description + "Data nodes for the operational state of IP on interfaces."; + + container ipv4 { + presence "Present if IPv4 is enabled on this interface"; + config false; + description + "Interface-specific parameters for the IPv4 address family."; + + + + + + leaf forwarding { + type boolean; + description + "Indicates whether IPv4 packet forwarding is enabled or + disabled on this interface."; + } + leaf mtu { + type uint16 { + range "68..max"; + } + units octets; + description + "The size, in octets, of the largest IPv4 packet that the + interface will send and receive."; + reference + "RFC 791: Internet Protocol"; + } + list address { + key "ip"; + description + "The list of IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address on the interface."; + } + choice subnet { + description + "The subnet can be specified as a prefix-length, or, + if the server supports non-contiguous netmasks, as + a netmask."; + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the subnet prefix."; + } + leaf netmask { + if-feature ipv4-non-contiguous-netmasks; + type yang:dotted-quad; + description + "The subnet specified as a netmask."; + } + } + + + leaf origin { + type ip-address-origin; + description + "The origin of this address."; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv4 addresses to + link-layer addresses. + + This list represents the ARP Cache."; + reference + "RFC 826: An Ethernet Address Resolution Protocol"; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + description + "The link-layer address of the neighbor node."; + } + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry."; + } + } + + } + + container ipv6 { + presence "Present if IPv6 is enabled on this interface"; + config false; + description + "Parameters for the IPv6 address family."; + + + + + + + + + leaf forwarding { + type boolean; + default false; + description + "Indicates whether IPv6 packet forwarding is enabled or + disabled on this interface."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 6.2.1, IsRouter"; + } + leaf mtu { + type uint32 { + range "1280..max"; + } + units octets; + description + "The size, in octets, of the largest IPv6 packet that the + interface will send and receive."; + reference + "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + Section 5"; + } + list address { + key "ip"; + description + "The list of IPv6 addresses on the interface."; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address on the interface."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + mandatory true; + description + "The length of the subnet prefix."; + } + leaf origin { + type ip-address-origin; + description + "The origin of this address."; + } + + + + leaf status { + type enumeration { + enum preferred { + description + "This is a valid address that can appear as the + destination or source address of a packet."; + } + enum deprecated { + description + "This is a valid but deprecated address that should + no longer be used as a source address in new + communications, but packets addressed to such an + address are processed as expected."; + } + enum invalid { + description + "This isn't a valid address, and it shouldn't appear + as the destination or source address of a packet."; + } + enum inaccessible { + description + "The address is not accessible because the interface + to which this address is assigned is not + operational."; + } + enum unknown { + description + "The status cannot be determined for some reason."; + } + enum tentative { + description + "The uniqueness of the address on the link is being + verified. Addresses in this state should not be + used for general communication and should only be + used to determine the uniqueness of the address."; + } + enum duplicate { + description + "The address has been determined to be non-unique on + the link and so must not be used."; + } + + + + + + + + enum optimistic { + description + "The address is available for use, subject to + restrictions, while its uniqueness on a link is + being verified."; + } + } + description + "The status of an address. Most of the states correspond + to states from the IPv6 Stateless Address + Autoconfiguration protocol."; + reference + "RFC 4293: Management Information Base for the + Internet Protocol (IP) + - IpAddressStatusTC + RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv6 addresses to + link-layer addresses. + + This list represents the Neighbor Cache."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + description + "The link-layer address of the neighbor node."; + } + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry."; + } + leaf is-router { + type empty; + description + "Indicates that the neighbor node acts as a router."; + } + leaf state { + type enumeration { + enum incomplete { + description + "Address resolution is in progress, and the link-layer + address of the neighbor has not yet been + determined."; + } + enum reachable { + description + "Roughly speaking, the neighbor is known to have been + reachable recently (within tens of seconds ago)."; + } + enum stale { + description + "The neighbor is no longer known to be reachable, but + until traffic is sent to the neighbor no attempt + should be made to verify its reachability."; + } + enum delay { + description + "The neighbor is no longer known to be reachable, and + traffic has recently been sent to the neighbor. + Rather than probe the neighbor immediately, however, + delay sending probes for a short while in order to + give upper-layer protocols a chance to provide + reachability confirmation."; + } + enum probe { + description + "The neighbor is no longer known to be reachable, and + unicast Neighbor Solicitation probes are being sent + to verify reachability."; + } + } + description + "The Neighbor Unreachability Detection state of this + entry."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 7.3.2"; + } + } + } + } +} diff --git a/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang new file mode 100644 index 0000000..bf4855f --- /dev/null +++ b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang @@ -0,0 +1,464 @@ +module ietf-netconf-acm { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; + + prefix nacm; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "Network Configuration Access Control Model. + + Copyright (c) 2012 - 2018 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8341; see + the RFC itself for full legal notices."; + + revision "2018-02-14" { + description + "Added support for YANG 1.1 actions and notifications tied to + data nodes. Clarified how NACM extensions can be used by + other data models."; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + revision "2012-02-22" { + description + "Initial version."; + reference + "RFC 6536: Network Configuration Protocol (NETCONF) + Access Control Model"; + } + + /* + * Extension statements + */ + + extension default-deny-write { + description + "Used to indicate that the data model node + represents a sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have write access to the node. An + explicit access control rule is required for all other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-write' extension MAY appear within a data + definition statement. It is ignored otherwise."; + } + + extension default-deny-all { + description + "Used to indicate that the data model node + controls a very sensitive security system parameter. + + If present, the NETCONF server will only allow the designated + 'recovery session' to have read, write, or execute access to + the node. An explicit access control rule is required for all + other users. + + If the NACM module is used, then it must be enabled (i.e., + /nacm/enable-nacm object equals 'true'), or this extension + is ignored. + + The 'default-deny-all' extension MAY appear within a data + definition statement, 'rpc' statement, or 'notification' + statement. It is ignored otherwise."; + } + + /* + * Derived types + */ + + typedef user-name-type { + type string { + length "1..max"; + } + description + "General-purpose username string."; + } + + typedef matchall-string-type { + type string { + pattern '\*'; + } + description + "The string containing a single asterisk '*' is used + to conceptually represent all possible values + for the particular leaf using this data type."; + } + + typedef access-operations-type { + type bits { + bit create { + description + "Any protocol operation that creates a + new data node."; + } + bit read { + description + "Any protocol operation or notification that + returns the value of a data node."; + } + bit update { + description + "Any protocol operation that alters an existing + data node."; + } + bit delete { + description + "Any protocol operation that removes a data node."; + } + bit exec { + description + "Execution access to the specified protocol operation."; + } + } + description + "Access operation."; + } + + typedef group-name-type { + type string { + length "1..max"; + pattern '[^\*].*'; + } + description + "Name of administrative group to which + users can be assigned."; + } + + typedef action-type { + type enumeration { + enum permit { + description + "Requested action is permitted."; + } + enum deny { + description + "Requested action is denied."; + } + } + description + "Action taken by the server when a particular + rule matches."; + } + + typedef node-instance-identifier { + type yang:xpath1.0; + description + "Path expression used to represent a special + data node, action, or notification instance-identifier + string. + + A node-instance-identifier value is an + unrestricted YANG instance-identifier expression. + All the same rules as an instance-identifier apply, + except that predicates for keys are optional. If a key + predicate is missing, then the node-instance-identifier + represents all possible server instances for that key. + + This XML Path Language (XPath) expression is evaluated in the + following context: + + o The set of namespace declarations are those in scope on + the leaf element where this type is used. + + o The set of variable bindings contains one variable, + 'USER', which contains the name of the user of the + current session. + + o The function library is the core function library, but + note that due to the syntax restrictions of an + instance-identifier, no functions are allowed. + + o The context node is the root node in the data tree. + + The accessible tree includes actions and notifications tied + to data nodes."; + } + + /* + * Data definition statements + */ + + container nacm { + nacm:default-deny-all; + + description + "Parameters for NETCONF access control model."; + + leaf enable-nacm { + type boolean; + default "true"; + description + "Enables or disables all NETCONF access control + enforcement. If 'true', then enforcement + is enabled. If 'false', then enforcement + is disabled."; + } + + leaf read-default { + type action-type; + default "permit"; + description + "Controls whether read access is granted if + no appropriate rule is found for a + particular read request."; + } + + leaf write-default { + type action-type; + default "deny"; + description + "Controls whether create, update, or delete access + is granted if no appropriate rule is found for a + particular write request."; + } + + leaf exec-default { + type action-type; + default "permit"; + description + "Controls whether exec access is granted if no appropriate + rule is found for a particular protocol operation request."; + } + + leaf enable-external-groups { + type boolean; + default "true"; + description + "Controls whether the server uses the groups reported by the + NETCONF transport layer when it assigns the user to a set of + NACM groups. If this leaf has the value 'false', any group + names reported by the transport layer are ignored by the + server."; + } + + leaf denied-operations { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request was denied."; + } + + leaf denied-data-writes { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request to alter + a configuration datastore was denied."; + } + + leaf denied-notifications { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that + a notification was dropped for a subscription because + access to the event type was denied."; + } + + container groups { + description + "NETCONF access control groups."; + + list group { + key name; + + description + "One NACM group entry. This list will only contain + configured entries, not any entries learned from + any transport protocols."; + + leaf name { + type group-name-type; + description + "Group name associated with this entry."; + } + + leaf-list user-name { + type user-name-type; + description + "Each entry identifies the username of + a member of the group associated with + this entry."; + } + } + } + + list rule-list { + key name; + ordered-by user; + description + "An ordered collection of access control rules."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule-list."; + } + leaf-list group { + type union { + type matchall-string-type; + type group-name-type; + } + description + "List of administrative groups that will be + assigned the associated access rights + defined by the 'rule' list. + + The string '*' indicates that all groups apply to the + entry."; + } + + list rule { + key name; + ordered-by user; + description + "One access control rule. + + Rules are processed in user-defined order until a match is + found. A rule matches if 'module-name', 'rule-type', and + 'access-operations' match the request. If a rule + matches, the 'action' leaf determines whether or not + access is granted."; + + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule."; + } + + leaf module-name { + type union { + type matchall-string-type; + type string; + } + default "*"; + description + "Name of the module associated with this rule. + + This leaf matches if it has the value '*' or if the + object being accessed is defined in the module with the + specified module name."; + } + choice rule-type { + description + "This choice matches if all leafs present in the rule + match the request. If no leafs are present, the + choice matches all requests."; + case protocol-operation { + leaf rpc-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if + its value equals the requested protocol operation + name."; + } + } + case notification { + leaf notification-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if its + value equals the requested notification name."; + } + } + + case data-node { + leaf path { + type node-instance-identifier; + mandatory true; + description + "Data node instance-identifier associated with the + data node, action, or notification controlled by + this rule. + + Configuration data or state data + instance-identifiers start with a top-level + data node. A complete instance-identifier is + required for this type of path value. + + The special value '/' refers to all possible + datastore contents."; + } + } + } + + leaf access-operations { + type union { + type matchall-string-type; + type access-operations-type; + } + default "*"; + description + "Access operations associated with this rule. + + This leaf matches if it has the value '*' or if the + bit corresponding to the requested operation is set."; + } + + leaf action { + type action-type; + mandatory true; + description + "The access control action associated with the + rule. If a rule has been determined to match a + particular request, then this object is used + to determine whether to permit or deny the + request."; + } + + leaf comment { + type string; + description + "A textual description of the access rule."; + } + } + } + } +} diff --git a/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang b/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang new file mode 100644 index 0000000..31dce50 --- /dev/null +++ b/tests/modules/yang/ietf-netconf-nmda@2019-01-07.yang @@ -0,0 +1,385 @@ + module ietf-netconf-nmda { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda"; + prefix ncds; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-datastores { + prefix ds; + reference + "RFC 8342: Network Management Datastore Architecture + (NMDA)"; + } + import ietf-origin { + prefix or; + reference + "RFC 8342: Network Management Datastore Architecture + (NMDA)"; + } + import ietf-netconf { + prefix nc; + reference + "RFC 6241: Network Configuration Protocol (NETCONF)"; + } + import ietf-netconf-with-defaults { + prefix ncwd; + reference + "RFC 6243: With-defaults Capability for NETCONF"; + } + + organization + "IETF NETCONF Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + + WG List: <mailto:netconf@ietf.org> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Author: Phil Shafer + <mailto:phil@juniper.net> + + Author: Kent Watsen + <mailto:kent+ietf@watsen.net> + + Author: Robert Wilton + <mailto:rwilton@cisco.com>"; + description + "This YANG module defines a set of NETCONF operations to support + the Network Management Datastore Architecture (NMDA). + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here. + + Copyright (c) 2019 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Simplified BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8526; see + the RFC itself for full legal notices."; + + revision 2019-01-07 { + description + "Initial revision."; + reference + "RFC 8526: NETCONF Extensions to Support the Network Management + Datastore Architecture"; + } + + feature origin { + description + "Indicates that the server supports the 'origin' annotation."; + reference + "RFC 8342: Network Management Datastore Architecture (NMDA)"; + } + + feature with-defaults { + description + "NETCONF :with-defaults capability. If the server advertises + the :with-defaults capability for a session, then this + feature must also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference + "RFC 6243: With-defaults Capability for NETCONF, Section 4; and + RFC 8526: NETCONF Extensions to Support the Network Management + Datastore Architecture, Section 3.1.1.2"; + } + + rpc get-data { + description + "Retrieve data from an NMDA datastore. The content returned + by get-data must satisfy all filters, i.e., the filter + criteria are logically ANDed. + + Any ancestor nodes (including list keys) of nodes selected by + the filters are included in the response. + + The 'with-origin' parameter is only valid for an operational + datastore. If 'with-origin' is used with an invalid + datastore, then the server MUST return an <rpc-error> element + with an <error-tag> value of 'invalid-value'. + + The 'with-defaults' parameter only applies to the operational + datastore if the NETCONF :with-defaults and + :with-operational-defaults capabilities are both advertised. + If the 'with-defaults' parameter is present in a request for + which it is not supported, then the server MUST return an + <rpc-error> element with an <error-tag> value of + 'invalid-value'."; + input { + leaf datastore { + type ds:datastore-ref; + mandatory true; + description + "Datastore from which to retrieve data. + + If the datastore is not supported by the server, then the + server MUST return an <rpc-error> element with an + <error-tag> value of 'invalid-value'."; + } + choice filter-spec { + description + "The content filter specification for this request."; + anydata subtree-filter { + description + "This parameter identifies the portions of the + target datastore to retrieve."; + reference + "RFC 6241: Network Configuration Protocol (NETCONF), + Section 6"; + } + leaf xpath-filter { + if-feature "nc:xpath"; + type yang:xpath1.0; + description + "This parameter contains an XPath expression identifying + the portions of the target datastore to retrieve. + + If the expression returns a node-set, all nodes in the + node-set are selected by the filter. Otherwise, if the + expression does not return a node-set, then the + <get-data> operation fails. + + The expression is evaluated in the following XPath + context: + + o The set of namespace declarations are those in + scope on the 'xpath-filter' leaf element. + + o The set of variable bindings is empty. + + o The function library is the core function library, + and the XPath functions are defined in Section 10 + of RFC 7950. + + o The context node is the root node of the target + datastore."; + } + } + leaf config-filter { + type boolean; + description + "Filter for nodes with the given value for their 'config' + property. When this leaf is set to 'true', only 'config + true' nodes are selected, and when set to 'false', only + 'config false' nodes are selected. If this leaf is not + present, no nodes are filtered."; + } + choice origin-filters { + when 'derived-from-or-self(datastore, "ds:operational")'; + if-feature "origin"; + description + "Filters configuration nodes based on the 'origin' + annotation. Configuration nodes that do not have an + 'origin' annotation are treated as if they have the + 'origin' annotation 'or:unknown'. + + System state nodes are not affected by origin-filters and + thus not filtered. Note that system state nodes can be + filtered with the 'config-filter' leaf."; + + leaf-list origin-filter { + type or:origin-ref; + description + "Filter based on the 'origin' annotation. A + configuration node matches the filter if its 'origin' + annotation is derived from or equal to any of the given + filter values."; + } + leaf-list negated-origin-filter { + type or:origin-ref; + description + "Filter based on the 'origin' annotation. A + configuration node matches the filter if its 'origin' + annotation is neither derived from nor equal to any of + the given filter values."; + } + } + leaf max-depth { + type union { + type uint16 { + range "1..65535"; + } + type enumeration { + enum unbounded { + description + "All descendant nodes are included."; + } + } + } + default "unbounded"; + description + "For each node selected by the filters, this parameter + selects how many conceptual subtree levels should be + returned in the reply. If the depth is 1, the reply + includes just the selected nodes but no children. If the + depth is 'unbounded', all descendant nodes are included."; + } + leaf with-origin { + when 'derived-from-or-self(../datastore, "ds:operational")'; + if-feature "origin"; + type empty; + description + "If this parameter is present, the server will return + the 'origin' annotation for the nodes that have one."; + } + uses ncwd:with-defaults-parameters { + if-feature "with-defaults"; + } + } + output { + anydata data { + description + "Copy of the source datastore subset that matched + the filter criteria (if any). An empty data + container indicates that the request did not + produce any results."; + } + } + } + + rpc edit-data { + description + "Edit data in an NMDA datastore. + + If an error condition occurs such that an error severity + <rpc-error> element is generated, the server will stop + processing the <edit-data> operation and restore the + specified configuration to its complete state at + the start of this <edit-data> operation."; + input { + leaf datastore { + type ds:datastore-ref; + mandatory true; + description + "Datastore that is the target of the <edit-data> operation. + + If the target datastore is not writable, or is not + supported by the server, then the server MUST return an + <rpc-error> element with an <error-tag> value of + 'invalid-value'."; + } + leaf default-operation { + type enumeration { + enum merge { + description + "The default operation is merge."; + } + enum replace { + description + "The default operation is replace."; + } + enum none { + description + "There is no default operation."; + } + } + default "merge"; + description + "The default operation to use."; + } + choice edit-content { + mandatory true; + description + "The content for the edit operation."; + anydata config { + description + "Inline config content."; + } + leaf url { + if-feature "nc:url"; + type inet:uri; + description + "URL-based config content."; + } + } + } + } + + /* + * Augment the <lock> and <unlock> operations with a + * "datastore" parameter. + */ + + augment "/nc:lock/nc:input/nc:target/nc:config-target" { + description + "Add NMDA datastore as target."; + leaf datastore { + type ds:datastore-ref; + description + "Datastore to lock. + + The <lock> operation is only supported on writable + datastores. + + If the <lock> operation is not supported by the server on + the specified target datastore, then the server MUST return + an <rpc-error> element with an <error-tag> value of + 'invalid-value'."; + } + } + + augment "/nc:unlock/nc:input/nc:target/nc:config-target" { + description + "Add NMDA datastore as target."; + leaf datastore { + type ds:datastore-ref; + description + "Datastore to unlock. + + The <unlock> operation is only supported on writable + datastores. + + If the <unlock> operation is not supported by the server on + the specified target datastore, then the server MUST return + an <rpc-error> element with an <error-tag> value of + 'invalid-value'."; + } + } + + /* + * Augment the <validate> operation with a + * "datastore" parameter. + */ + + augment "/nc:validate/nc:input/nc:source/nc:config-source" { + description + "Add NMDA datastore as source."; + leaf datastore { + type ds:datastore-ref; + description + "Datastore to validate. + + The <validate> operation is supported only on configuration + datastores. + + If the <validate> operation is not supported by the server + on the specified target datastore, then the server MUST + return an <rpc-error> element with an <error-tag> value of + 'invalid-value'."; + } + } + } diff --git a/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang new file mode 100644 index 0000000..e19d2b3 --- /dev/null +++ b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang @@ -0,0 +1,140 @@ +module ietf-netconf-with-defaults { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"; + + prefix ncwd; + + import ietf-netconf { prefix nc; } + + organization + "IETF NETCONF (Network Configuration Protocol) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netconf/> + + WG List: <netconf@ietf.org> + + WG Chair: Bert Wijnen + <bertietf@bwijnen.net> + + WG Chair: Mehmet Ersue + <mehmet.ersue@nsn.com> + + Editor: Andy Bierman + <andy.bierman@brocade.com> + + Editor: Balazs Lengyel + <balazs.lengyel@ericsson.com>"; + + description + "This module defines an extension to the NETCONF protocol + that allows the NETCONF client to control how default + values are handled by the server in particular NETCONF + operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6243; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial version."; + reference + "RFC 6243: With-defaults Capability for NETCONF"; + } + + typedef with-defaults-mode { + description + "Possible modes to report default data."; + reference + "RFC 6243; Section 3."; + type enumeration { + enum report-all { + description + "All default data is reported."; + reference + "RFC 6243; Section 3.1"; + } + enum report-all-tagged { + description + "All default data is reported. + Any nodes considered to be default data + will contain a 'default' XML attribute, + set to 'true' or '1'."; + reference + "RFC 6243; Section 3.4"; + } + enum trim { + description + "Values are not reported if they contain the default."; + reference + "RFC 6243; Section 3.2"; + } + enum explicit { + description + "Report values that contain the definition of + explicitly set data."; + reference + "RFC 6243; Section 3.3"; + } + } + } + + grouping with-defaults-parameters { + description + "Contains the <with-defaults> parameter for control + of defaults in NETCONF retrieval operations."; + + leaf with-defaults { + description + "The explicit defaults processing mode requested."; + reference + "RFC 6243; Section 4.5.1"; + + type with-defaults-mode; + } + } + + // extending the get-config operation + augment /nc:get-config/nc:input { + description + "Adds the <with-defaults> parameter to the + input of the NETCONF <get-config> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the get operation + augment /nc:get/nc:input { + description + "Adds the <with-defaults> parameter to + the input of the NETCONF <get> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the copy-config operation + augment /nc:copy-config/nc:input { + description + "Adds the <with-defaults> parameter to + the input of the NETCONF <copy-config> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + +} diff --git a/tests/modules/yang/ietf-netconf@2011-06-01.yang b/tests/modules/yang/ietf-netconf@2011-06-01.yang new file mode 100644 index 0000000..6449421 --- /dev/null +++ b/tests/modules/yang/ietf-netconf@2011-06-01.yang @@ -0,0 +1,934 @@ +module ietf-netconf { + + // the namespace for NETCONF XML definitions is unchanged + // from RFC 4741, which this document replaces + namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; + + prefix nc; + + import ietf-inet-types { + prefix inet; + } + + import ietf-netconf-acm { prefix nacm; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netconf/> + WG List: <netconf@ietf.org> + + WG Chair: Bert Wijnen + <bertietf@bwijnen.net> + + WG Chair: Mehmet Ersue + <mehmet.ersue@nsn.com> + + Editor: Martin Bjorklund + <mbj@tail-f.com> + + Editor: Juergen Schoenwaelder + <j.schoenwaelder@jacobs-university.de> + + Editor: Andy Bierman + <andy.bierman@brocade.com>"; + description + "NETCONF Protocol Data Types and Protocol Operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6241; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial revision; + 2013-09-29: Updated to include NACM attributes, + as specified in RFC 6536: sec 3.2.5 and 3.2.8"; + reference + "RFC 6241: Network Configuration Protocol"; + } + + extension get-filter-element-attributes { + description + "If this extension is present within an 'anyxml' + statement named 'filter', which must be conceptually + defined within the RPC input section for the <get> + and <get-config> protocol operations, then the + following unqualified XML attribute is supported + within the <filter> element, within a <get> or + <get-config> protocol operation: + + type : optional attribute with allowed + value strings 'subtree' and 'xpath'. + If missing, the default value is 'subtree'. + + If the 'xpath' feature is supported, then the + following unqualified XML attribute is + also supported: + + select: optional attribute containing a + string representing an XPath expression. + The 'type' attribute must be equal to 'xpath' + if this attribute is present."; + } + + // NETCONF capabilities defined as features + feature writable-running { + description + "NETCONF :writable-running capability; + If the server advertises the :writable-running + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.2"; + } + + feature candidate { + description + "NETCONF :candidate capability; + If the server advertises the :candidate + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.3"; + } + + feature confirmed-commit { + if-feature candidate; + description + "NETCONF :confirmed-commit:1.1 capability; + If the server advertises the :confirmed-commit:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + + reference "RFC 6241, Section 8.4"; + } + + feature rollback-on-error { + description + "NETCONF :rollback-on-error capability; + If the server advertises the :rollback-on-error + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.5"; + } + + feature validate { + description + "NETCONF :validate:1.1 capability; + If the server advertises the :validate:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.6"; + } + + feature startup { + description + "NETCONF :startup capability; + If the server advertises the :startup + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.7"; + } + + feature url { + description + "NETCONF :url capability; + If the server advertises the :url + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.8"; + } + + feature xpath { + description + "NETCONF :xpath capability; + If the server advertises the :xpath + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.9"; + } + + // NETCONF Simple Types + + typedef session-id-type { + type uint32 { + range "1..max"; + } + description + "NETCONF Session Id"; + } + + typedef session-id-or-zero-type { + type uint32; + description + "NETCONF Session Id or Zero to indicate none"; + } + typedef error-tag-type { + type enumeration { + enum in-use { + description + "The request requires a resource that + already is in use."; + } + enum invalid-value { + description + "The request specifies an unacceptable value for one + or more parameters."; + } + enum too-big { + description + "The request or response (that would be generated) is + too large for the implementation to handle."; + } + enum missing-attribute { + description + "An expected attribute is missing."; + } + enum bad-attribute { + description + "An attribute value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-attribute { + description + "An unexpected attribute is present."; + } + enum missing-element { + description + "An expected element is missing."; + } + enum bad-element { + description + "An element value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-element { + description + "An unexpected element is present."; + } + enum unknown-namespace { + description + "An unexpected namespace is present."; + } + enum access-denied { + description + "Access to the requested protocol operation or + data model is denied because authorization failed."; + } + enum lock-denied { + description + "Access to the requested lock is denied because the + lock is currently held by another entity."; + } + enum resource-denied { + description + "Request could not be completed because of + insufficient resources."; + } + enum rollback-failed { + description + "Request to roll back some configuration change (via + rollback-on-error or <discard-changes> operations) + was not completed for some reason."; + + } + enum data-exists { + description + "Request could not be completed because the relevant + data model content already exists. For example, + a 'create' operation was attempted on data that + already exists."; + } + enum data-missing { + description + "Request could not be completed because the relevant + data model content does not exist. For example, + a 'delete' operation was attempted on + data that does not exist."; + } + enum operation-not-supported { + description + "Request could not be completed because the requested + operation is not supported by this implementation."; + } + enum operation-failed { + description + "Request could not be completed because the requested + operation failed for some reason not covered by + any other error condition."; + } + enum partial-operation { + description + "This error-tag is obsolete, and SHOULD NOT be sent + by servers conforming to this document."; + } + enum malformed-message { + description + "A message could not be handled because it failed to + be parsed correctly. For example, the message is not + well-formed XML or it uses an invalid character set."; + } + } + description "NETCONF Error Tag"; + reference "RFC 6241, Appendix A"; + } + + typedef error-severity-type { + type enumeration { + enum error { + description "Error severity"; + } + enum warning { + description "Warning severity"; + } + } + description "NETCONF Error Severity"; + reference "RFC 6241, Section 4.3"; + } + + typedef edit-operation-type { + type enumeration { + enum merge { + description + "The configuration data identified by the + element containing this attribute is merged + with the configuration at the corresponding + level in the configuration datastore identified + by the target parameter."; + } + enum replace { + description + "The configuration data identified by the element + containing this attribute replaces any related + configuration in the configuration datastore + identified by the target parameter. If no such + configuration data exists in the configuration + datastore, it is created. Unlike a + <copy-config> operation, which replaces the + entire target configuration, only the configuration + actually present in the config parameter is affected."; + } + enum create { + description + "The configuration data identified by the element + containing this attribute is added to the + configuration if and only if the configuration + data does not already exist in the configuration + datastore. If the configuration data exists, an + <rpc-error> element is returned with an + <error-tag> value of 'data-exists'."; + } + enum delete { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if and only if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, an <rpc-error> element is returned with + an <error-tag> value of 'data-missing'."; + } + enum remove { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, the 'remove' operation is silently ignored + by the server."; + } + } + default "merge"; + description "NETCONF 'operation' attribute values"; + reference "RFC 6241, Section 7.2"; + } + + // NETCONF Standard Protocol Operations + + rpc get-config { + description + "Retrieve all or part of a specified configuration."; + + reference "RFC 6241, Section 7.1"; + + input { + container source { + description + "Particular configuration to retrieve."; + + choice config-source { + mandatory true; + description + "The configuration to retrieve."; + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source. + This is optional-to-implement on the server because + not all servers will support filtering for this + datastore."; + } + } + } + + anyxml filter { + description + "Subtree or XPath filter to use."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the source datastore subset that matched + the filter criteria (if any). An empty data container + indicates that the request did not produce any results."; + } + } + } + + rpc edit-config { + description + "The <edit-config> operation loads all or part of a specified + configuration to the specified target configuration."; + + reference "RFC 6241, Section 7.2"; + + input { + container target { + description + "Particular configuration to edit."; + + choice config-target { + mandatory true; + description + "The configuration target."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config source."; + } + } + } + + leaf default-operation { + type enumeration { + enum merge { + description + "The default operation is merge."; + } + enum replace { + description + "The default operation is replace."; + } + enum none { + description + "There is no default operation."; + } + } + default "merge"; + description + "The default operation to use."; + } + + leaf test-option { + if-feature validate; + type enumeration { + enum test-then-set { + description + "The server will test and then set if no errors."; + } + enum set { + description + "The server will set without a test first."; + } + + enum test-only { + description + "The server will only test and not set, even + if there are no errors."; + } + } + default "test-then-set"; + description + "The test option to use."; + } + + leaf error-option { + type enumeration { + enum stop-on-error { + description + "The server will stop on errors."; + } + enum continue-on-error { + description + "The server may continue on errors."; + } + enum rollback-on-error { + description + "The server will roll back on errors. + This value can only be used if the 'rollback-on-error' + feature is supported."; + } + } + default "stop-on-error"; + description + "The error option to use."; + } + + choice edit-content { + mandatory true; + description + "The content for the edit operation."; + + anyxml config { + description + "Inline Config content."; + } + leaf url { + if-feature url; + type inet:uri; + description + "URL-based config content."; + } + } + } + } + + rpc copy-config { + description + "Create or replace an entire configuration datastore with the + contents of another complete configuration datastore."; + + reference "RFC 6241, Section 7.3"; + + input { + container target { + description + "Particular configuration to copy to."; + + choice config-target { + mandatory true; + description + "The configuration target of the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config target. + This is optional-to-implement on the server."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + + container source { + description + "Particular configuration to copy from."; + + choice config-source { + mandatory true; + description + "The configuration source for the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: <config> element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } + + rpc delete-config { + nacm:default-deny-all; + description + "Delete a configuration datastore."; + + reference "RFC 6241, Section 7.4"; + + input { + container target { + description + "Particular configuration to delete."; + + choice config-target { + mandatory true; + description + "The configuration target to delete."; + + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + } + } + + rpc lock { + description + "The lock operation allows the client to lock the configuration + system of a device."; + + reference "RFC 6241, Section 7.5"; + + input { + container target { + description + "Particular configuration to lock."; + + choice config-target { + mandatory true; + description + "The configuration target to lock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc unlock { + description + "The unlock operation is used to release a configuration lock, + previously obtained with the 'lock' operation."; + + reference "RFC 6241, Section 7.6"; + + input { + container target { + description + "Particular configuration to unlock."; + + choice config-target { + mandatory true; + description + "The configuration target to unlock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc get { + description + "Retrieve running configuration and device state information."; + + reference "RFC 6241, Section 7.7"; + + input { + anyxml filter { + description + "This parameter specifies the portion of the system + configuration and state data to retrieve."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the running datastore subset and/or state + data that matched the filter criteria (if any). + An empty data container indicates that the request did not + produce any results."; + } + } + } + + rpc close-session { + description + "Request graceful termination of a NETCONF session."; + + reference "RFC 6241, Section 7.8"; + } + + rpc kill-session { + nacm:default-deny-all; + description + "Force the termination of a NETCONF session."; + + reference "RFC 6241, Section 7.9"; + + input { + leaf session-id { + type session-id-type; + mandatory true; + description + "Particular session to kill."; + } + } + } + + rpc commit { + if-feature candidate; + + description + "Commit the candidate configuration as the device's new + current configuration."; + + reference "RFC 6241, Section 8.3.4.1"; + + input { + leaf confirmed { + if-feature confirmed-commit; + type empty; + description + "Requests a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf confirm-timeout { + if-feature confirmed-commit; + type uint32 { + range "1..max"; + } + units "seconds"; + default "600"; // 10 minutes + description + "The timeout interval for a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist { + if-feature confirmed-commit; + type string; + description + "This parameter is used to make a confirmed commit + persistent. A persistent confirmed commit is not aborted + if the NETCONF session terminates. The only way to abort + a persistent confirmed commit is to let the timer expire, + or to use the <cancel-commit> operation. + + The value of this parameter is a token that must be given + in the 'persist-id' parameter of <commit> or + <cancel-commit> operations in order to confirm or cancel + the persistent confirmed commit. + + The token should be a random string."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist-id { + if-feature confirmed-commit; + type string; + description + "This parameter is given in order to commit a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the <commit> operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + reference "RFC 6241, Section 8.3.4.1"; + } + + } + } + + rpc discard-changes { + if-feature candidate; + + description + "Revert the candidate configuration to the current + running configuration."; + reference "RFC 6241, Section 8.3.4.2"; + } + + rpc cancel-commit { + if-feature confirmed-commit; + description + "This operation is used to cancel an ongoing confirmed commit. + If the confirmed commit is persistent, the parameter + 'persist-id' must be given, and it must match the value of the + 'persist' parameter."; + reference "RFC 6241, Section 8.4.4.1"; + + input { + leaf persist-id { + type string; + description + "This parameter is given in order to cancel a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the <commit> operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + } + } + } + + rpc validate { + if-feature validate; + + description + "Validates the contents of the specified configuration."; + + reference "RFC 6241, Section 8.6.4.1"; + + input { + container source { + description + "Particular configuration to validate."; + + choice config-source { + mandatory true; + description + "The configuration source to validate."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: <config> element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } + +} diff --git a/tests/modules/yang/ietf-origin@2018-02-14.yang b/tests/modules/yang/ietf-origin@2018-02-14.yang new file mode 100644 index 0000000..3080c91 --- /dev/null +++ b/tests/modules/yang/ietf-origin@2018-02-14.yang @@ -0,0 +1,147 @@ +module ietf-origin { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-origin"; + prefix or; + + import ietf-yang-metadata { + prefix md; + } + + organization + "IETF Network Modeling (NETMOD) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netmod/> + + WG List: <mailto:netmod@ietf.org> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Author: Phil Shafer + <mailto:phil@juniper.net> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net> + + Author: Rob Wilton + <rwilton@cisco.com>"; + + description + "This YANG module defines an 'origin' metadata annotation and a + set of identities for the origin value. + + Copyright (c) 2018 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Simplified BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8342 + (https://www.rfc-editor.org/info/rfc8342); see the RFC itself + for full legal notices."; + + revision 2018-02-14 { + description + "Initial revision."; + reference + "RFC 8342: Network Management Datastore Architecture (NMDA)"; + } + + /* + * Identities + */ + + identity origin { + description + "Abstract base identity for the origin annotation."; + } + + identity intended { + base origin; + description + "Denotes configuration from the intended configuration + datastore."; + } + + identity dynamic { + base origin; + description + "Denotes configuration from a dynamic configuration + datastore."; + } + + identity system { + base origin; + description + "Denotes configuration originated by the system itself. + + Examples of system configuration include applied configuration + for an always-existing loopback interface, or interface + configuration that is auto-created due to the hardware + currently present in the device."; + } + + identity learned { + base origin; + description + "Denotes configuration learned from protocol interactions with + other devices, instead of via either the intended + configuration datastore or any dynamic configuration + datastore. + + Examples of protocols that provide learned configuration + include link-layer negotiations, routing protocols, and + DHCP."; + } + + identity default { + base origin; + description + "Denotes configuration that does not have a configured or + learned value but has a default value in use. Covers both + values defined in a 'default' statement and values defined + via an explanation in a 'description' statement."; + } + + identity unknown { + base origin; + description + "Denotes configuration for which the system cannot identify the + origin."; + } + + /* + * Type definitions + */ + + typedef origin-ref { + type identityref { + base origin; + } + description + "An origin identity reference."; + } + + /* + * Metadata annotations + */ + + md:annotation origin { + type origin-ref; + description + "The 'origin' annotation can be present on any configuration + data node in the operational state datastore. It specifies + from where the node originated. If not specified for a given + configuration data node, then the origin is the same as the + origin of its parent node in the data tree. The origin for + any top-level configuration data nodes must be specified."; + } +} diff --git a/tests/modules/yang/ietf-restconf@2017-01-26.yang b/tests/modules/yang/ietf-restconf@2017-01-26.yang new file mode 100644 index 0000000..b47455b --- /dev/null +++ b/tests/modules/yang/ietf-restconf@2017-01-26.yang @@ -0,0 +1,278 @@ +module ietf-restconf { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "rc"; + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains conceptual YANG specifications + for basic RESTCONF media type definitions used in + RESTCONF protocol messages. + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The 'restconf-media-type' YANG extension statement + provides a normative syntax for XML and JSON + message-encoding purposes. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + extension yang-data { + argument name { + yin-element true; + } + description + "This extension is used to specify a YANG data template that + represents conceptual data defined in YANG. It is + intended to describe hierarchical data independent of + protocol context or specific message-encoding format. + Data definition statements within a yang-data extension + specify the generic syntax for the specific YANG data + template, whose name is the argument of the 'yang-data' + extension statement. + + Note that this extension does not define a media type. + A specification using this extension MUST specify the + message-encoding rules, including the content media type. + + The mandatory 'name' parameter value identifies the YANG + data template that is being defined. It contains the + template name. + + This extension is ignored unless it appears as a top-level + statement. It MUST contain data definition statements + that result in exactly one container data node definition. + An instance of a YANG data template can thus be translated + into an XML instance document, whose top-level element + corresponds to the top-level container. + The module name and namespace values for the YANG module using + the extension statement are assigned to instance document data + conforming to the data definition statements within + this extension. + + The substatements of this extension MUST follow the + 'data-def-stmt' rule in the YANG ABNF. + + The XPath document root is the extension statement itself, + such that the child nodes of the document root are + represented by the data-def-stmt substatements within + this extension. This conceptual document is the context + for the following YANG statements: + + - must-stmt + - when-stmt + - path-stmt + - min-elements-stmt + - max-elements-stmt + - mandatory-stmt + - unique-stmt + - ordered-by + - instance-identifier data type + + The following data-def-stmt substatements are constrained + when used within a 'yang-data' extension statement. + + - The list-stmt is not required to have a key-stmt defined. + - The if-feature-stmt is ignored if present. + - The config-stmt is ignored if present. + - The available identity values for any 'identityref' + leaf or leaf-list nodes are limited to the module + containing this extension statement and the modules + imported into that module. + "; + } + + rc:yang-data yang-errors { + uses errors; + } + + rc:yang-data yang-api { + uses restconf; + } + + grouping errors { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch error report within a response message."; + + container errors { + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference + "RFC 6241, Section 4.3."; + + leaf error-type { + type enumeration { + enum transport { + description + "The transport layer."; + } + enum rpc { + description + "The rpc or notification layer."; + } + enum protocol { + description + "The protocol operation layer."; + } + enum application { + description + "The server application layer."; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error-tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error-tag."; + } + + leaf error-path { + type instance-identifier; + description + "The YANG instance identifier associated + with the error node."; + } + + leaf error-message { + type string; + description + "A message describing the error."; + } + + anydata error-info { + description + "This anydata value MUST represent a container with + zero or more data nodes representing additional + error information."; + } + } + } + } + + grouping restconf { + description + "Conceptual grouping representing the RESTCONF + root resource."; + + container restconf { + description + "Conceptual container representing the RESTCONF + root resource."; + + container data { + description + "Container representing the datastore resource. + Represents the conceptual root of all state data + and configuration data supported by the server. + The child nodes of this container can be any data + resources that are defined as top-level data nodes + from the YANG modules advertised by the server in + the 'ietf-yang-library' module."; + } + + container operations { + description + "Container for all operation resources. + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG 'rpc' statement. + + For example, the 'system-restart' RPC operation defined + in the 'ietf-system' module would be represented as + an empty leaf in the 'ietf-system' namespace. This is + a conceptual leaf and will not actually be found in + the module: + + module ietf-system { + leaf system-reset { + type empty; + } + } + + To invoke the 'system-restart' RPC operation: + + POST /restconf/operations/ietf-system:system-restart + + To discover the RPC operations supported by the server: + + GET /restconf/operations + + In XML, the YANG module namespace identifies the module: + + <system-restart + xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/> + + In JSON, the YANG module name identifies the module: + + { 'ietf-system:system-restart' : [null] } + "; + } + leaf yang-library-version { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + config false; + mandatory true; + description + "Identifies the revision date of the 'ietf-yang-library' + module that is implemented by this RESTCONF server. + Indicates the year, month, and day in YYYY-MM-DD + numeric format."; + } + } + } + +} diff --git a/tests/modules/yang/notifications@2008-07-14.yang b/tests/modules/yang/notifications@2008-07-14.yang new file mode 100644 index 0000000..b696f39 --- /dev/null +++ b/tests/modules/yang/notifications@2008-07-14.yang @@ -0,0 +1,95 @@ +module notifications { + + namespace "urn:ietf:params:xml:ns:netconf:notification:1.0"; + prefix "ncEvent"; + + import ietf-yang-types { prefix yang; } + + organization + "IETF NETCONF WG"; + + contact + "netconf@ops.ietf.org"; + + description + "Conversion of the 'ncEvent' XSD in the + NETCONF Notifications RFC."; + + reference + "RFC 5277."; + + revision 2008-07-14 { + description "RFC 5277 version."; + } + + typedef streamNameType { + description + "The name of an event stream."; + type string; + } + + rpc create-subscription { + description + "The command to create a notification subscription. It + takes as argument the name of the notification stream + and filter. Both of those options limit the content of + the subscription. In addition, there are two time-related + parameters, startTime and stopTime, which can be used to + select the time interval of interest to the notification + replay feature."; + + input { + leaf stream { + description + "An optional parameter that indicates which stream of events + is of interest. If not present, then events in the default + NETCONF stream will be sent."; + type streamNameType; + default "NETCONF"; + } + + anyxml filter { + description + "An optional parameter that indicates which subset of all + possible events is of interest. The format of this + parameter is the same as that of the filter parameter + in the NETCONF protocol operations. If not present, + all events not precluded by other parameters will + be sent."; + } + + leaf startTime { + description + "A parameter used to trigger the replay feature and + indicates that the replay should start at the time + specified. If start time is not present, this is not a + replay subscription."; + type yang:date-and-time; + } + + leaf stopTime { + // must ". >= ../startTime"; + description + "An optional parameter used with the optional replay + feature to indicate the newest notifications of + interest. If stop time is not present, the notifications + will continue until the subscription is terminated. + Must be used with startTime."; + type yang:date-and-time; + } + } + } + + /*container notification { + description "internal struct to start a notification"; + config false; + + leaf eventTime { + mandatory true; + type yang:date-and-time; + } + + // eventType and any data content goes here + }*/ +} + diff --git a/tests/modules/yang/sm-extension.yang b/tests/modules/yang/sm-extension.yang new file mode 100644 index 0000000..63d164a --- /dev/null +++ b/tests/modules/yang/sm-extension.yang @@ -0,0 +1,17 @@ +module sm-extension { + yang-version 1.1; + namespace "urn:sm-ext"; + prefix "sm-ext"; + + list tlist { + key "name"; + leaf name { + type uint32; + } + } + container tcont { + leaf tleaf { + type uint32; + } + } +} diff --git a/tests/modules/yang/sm-mod.yang b/tests/modules/yang/sm-mod.yang new file mode 100644 index 0000000..8ac834e --- /dev/null +++ b/tests/modules/yang/sm-mod.yang @@ -0,0 +1,9 @@ +module sm-mod { + yang-version 1.1; + namespace "urn:sm-mod"; + prefix "sm-mod"; + + import sm-modp { + prefix smp; + } +} diff --git a/tests/modules/yang/sm-modp.yang b/tests/modules/yang/sm-modp.yang new file mode 100644 index 0000000..33f45b4 --- /dev/null +++ b/tests/modules/yang/sm-modp.yang @@ -0,0 +1,24 @@ +module sm-modp { + yang-version 1.1; + namespace "urn:sm-modp"; + prefix "sm-modp"; + + import ietf-yang-schema-mount { + prefix yangmnt; + } + + revision 2017-01-26; + + container ncmp { + yangmnt:mount-point "root"; + } + + container not-compiled { + leaf first { + type string; + } + leaf second { + type string; + } + } +} diff --git a/tests/modules/yang/sm-rpcnotif.yang b/tests/modules/yang/sm-rpcnotif.yang new file mode 100644 index 0000000..fda5442 --- /dev/null +++ b/tests/modules/yang/sm-rpcnotif.yang @@ -0,0 +1,25 @@ +module sm-rpcnotif { + yang-version 1.1; + namespace "urn:rpcnotif"; + prefix "smrn"; + + container cont { + notification cn; + action cr { + input { + leaf in { + type string; + } + } + output { + leaf out { + type string; + } + } + } + } + rpc r1; + rpc r2; + notification n1; + notification n2; +} diff --git a/tests/modules/yang/sm.yang b/tests/modules/yang/sm.yang new file mode 100644 index 0000000..3da9c9a --- /dev/null +++ b/tests/modules/yang/sm.yang @@ -0,0 +1,39 @@ +module sm { + yang-version 1.1; + namespace "urn:sm"; + prefix "sm"; + + import ietf-yang-schema-mount { + prefix yangmnt; + } + import ietf-interfaces { + prefix if; + } + + container root { + yangmnt:mount-point "root"; + } + container root2 { + yangmnt:mount-point "root"; + } + container root3 { + list ls { + key name; + leaf name { + type string; + } + yangmnt:mount-point "mnt-root"; + } + } + leaf target { + type string; + } + + augment /if:interfaces/if:interface { + leaf sm-name { + type leafref { + path "/sm:target"; + } + } + } +} diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt new file mode 100644 index 0000000..ec1dfb5 --- /dev/null +++ b/tests/perf/CMakeLists.txt @@ -0,0 +1,14 @@ +set(format_sources + ${format_sources} + ${CMAKE_CURRENT_SOURCE_DIR}/perf.c + PARENT_SCOPE) + +add_executable(ly_perf ${CMAKE_CURRENT_SOURCE_DIR}/perf.c $<TARGET_OBJECTS:yangobj>) +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 (NOT WIN32) + target_link_libraries(ly_perf m) +endif() + +add_test(NAME ly_perf_1000 COMMAND ly_perf 1000 10) +add_test(NAME ly_perf_100000 COMMAND ly_perf 100000 3) diff --git a/tests/perf/perf.c b/tests/perf/perf.c new file mode 100644 index 0000000..da1eb93 --- /dev/null +++ b/tests/perf/perf.c @@ -0,0 +1,849 @@ +/** + * @file perf.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief performance tests + * + * Copyright (c) 2021 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 _GNU_SOURCE + +#include <assert.h> +#include <inttypes.h> +#include <stdlib.h> +#include <sys/time.h> +#include <time.h> + +#include "libyang.h" +#include "tests_config.h" + +#ifdef HAVE_CALLGRIND +# include <valgrind/callgrind.h> +#endif + +#define TEMP_FILE "perf_tmp" + +/** + * @brief Test state structure. + */ +struct test_state { + const struct lys_module *mod; + uint32_t count; + struct lyd_node *data1; + struct lyd_node *data2; +}; + +typedef LY_ERR (*setup_cb)(const struct lys_module *mod, uint32_t count, struct test_state *state); + +typedef LY_ERR (*test_cb)(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end); + +/** + * @brief Single test structure. + */ +struct test { + const char *name; + setup_cb setup; + test_cb test; +}; + +/** + * @brief Get current time as timespec. + * + * @param[out] ts Timespect to fill. + */ +static void +time_get(struct timespec *ts) +{ +#ifdef CLOCK_MONOTONIC_RAW + clock_gettime(CLOCK_MONOTONIC_RAW, ts); +#elif defined (CLOCK_MONOTONIC) + clock_gettime(CLOCK_MONOTONIC, ts); +#elif defined (CLOCK_REALTIME) + /* no monotonic clock available, return realtime */ + clock_gettime(CLOCK_REALTIME, ts); +#else + int rc; + struct timeval tv; + + gettimeofday(&tv, NULL); + ts->tv_sec = (time_t)tv.tv_sec; + ts->tv_nsec = 1000L * (long)tv.tv_usec; +#endif +} + +/** + * @brief Get the difference of 2 timespecs in microseconds. + * + * @param[in] ts1 Smaller (older) timespec. + * @param[in] ts2 Larger (later) timespec. + * @return Difference of timespecs in usec. + */ +static uint64_t +time_diff(const struct timespec *ts1, const struct timespec *ts2) +{ + uint64_t usec_diff = 0; + int64_t nsec_diff; + + assert(ts1->tv_sec <= ts2->tv_sec); + + /* seconds diff */ + usec_diff += (ts2->tv_sec - ts1->tv_sec) * 1000000; + + /* nanoseconds diff */ + nsec_diff = ts2->tv_nsec - ts1->tv_nsec; + usec_diff += nsec_diff ? nsec_diff / 1000 : 0; + + return usec_diff; +} + +/** + * @brief Create data tree with list instances. + * + * @param[in] mod Module of the top-level node. + * @param[in] offset Starting offset of the identifier number values. + * @param[in] count Number of list instances to create, with increasing identifier numbers. + * @param[out] data Created data. + * @return LY_ERR value. + */ +static LY_ERR +create_list_inst(const struct lys_module *mod, uint32_t offset, uint32_t count, struct lyd_node **data) +{ + LY_ERR ret; + uint32_t i; + char k1_val[32], k2_val[32], l_val[32], lfl_val[32]; + struct lyd_node *list; + + if ((ret = lyd_new_inner(NULL, mod, "cont", 0, data))) { + return ret; + } + + for (i = 0; i < count; ++i) { + sprintf(k1_val, "%" PRIu32, i + offset); + sprintf(k2_val, "str%" PRIu32, i + offset); + sprintf(l_val, "l%" PRIu32, i + offset); + + if ((ret = lyd_new_list(*data, NULL, "lst", 0, &list, k1_val, k2_val))) { + return ret; + } + if ((ret = lyd_new_term(list, NULL, "l", l_val, 0, NULL))) { + return ret; + } + } + + /* Last list contains a "lfl" leaf-list with @p count terms. */ + for (i = 0; i < count; ++i) { + sprintf(lfl_val, "%" PRIu32, i + offset); + if ((ret = lyd_new_term(list, NULL, "lfl", lfl_val, 0, NULL))) { + return ret; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Execute a test. + * + * @param[in] setup Setup callback to call once. + * @param[in] test Test callback. + * @param[in] name Name of the test. + * @param[in] mod Module of testing data. + * @param[in] count Count of list instances, size of the testing data set. + * @param[in] tries Number of (re)tries of the test to get more accurate measurements. + * @return LY_ERR value. + */ +static LY_ERR +exec_test(setup_cb setup, test_cb test, const char *name, const struct lys_module *mod, uint32_t count, uint32_t tries) +{ + LY_ERR ret; + struct timespec ts_start, ts_end; + struct test_state state = {0}; + const uint32_t name_fixed_len = 38; + char str[name_fixed_len + 1]; + uint32_t i, printed; + uint64_t time_usec = 0; + + /* print test start */ + printed = sprintf(str, "| %s ", name); + while (printed + 2 < name_fixed_len) { + printed += sprintf(str + printed, "."); + } + if (printed + 1 < name_fixed_len) { + printed += sprintf(str + printed, " "); + } + sprintf(str + printed, "|"); + fputs(str, stdout); + fflush(stdout); + + /* setup */ + if ((ret = setup(mod, count, &state))) { + return ret; + } + + /* test */ + for (i = 0; i < tries; ++i) { + if ((ret = test(&state, &ts_start, &ts_end))) { + return ret; + } + time_usec += time_diff(&ts_start, &ts_end); + } + time_usec /= tries; + + /* teardown */ + lyd_free_siblings(state.data1); + lyd_free_siblings(state.data2); + + /* print time */ + printf(" %" PRIu64 ".%06" PRIu64 " s |\n", time_usec / 1000000, time_usec % 1000000); + + return LY_SUCCESS; +} + +static void +TEST_START(struct timespec *ts) +{ + time_get(ts); + +#ifdef HAVE_CALLGRIND + CALLGRIND_START_INSTRUMENTATION; +#endif +} + +static void +TEST_END(struct timespec *ts) +{ + time_get(ts); + +#ifdef HAVE_CALLGRIND + CALLGRIND_STOP_INSTRUMENTATION; +#endif +} + +/* TEST SETUP */ +static LY_ERR +setup_basic(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + state->mod = mod; + state->count = count; + + return LY_SUCCESS; +} + +static LY_ERR +setup_data_single_tree(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + state->mod = mod; + state->count = count; + + return create_list_inst(mod, 0, count, &state->data1); +} + +static LY_ERR +setup_data_same_trees(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + LY_ERR ret; + + state->mod = mod; + state->count = count; + + if ((ret = create_list_inst(mod, 0, count, &state->data1))) { + return ret; + } + if ((ret = create_list_inst(mod, 0, count, &state->data2))) { + return ret; + } + + return LY_SUCCESS; +} + +static LY_ERR +setup_data_no_same_trees(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + LY_ERR ret; + + state->mod = mod; + state->count = count; + + if ((ret = create_list_inst(mod, 0, count, &state->data1))) { + return ret; + } + if ((ret = create_list_inst(mod, count, count, &state->data2))) { + return ret; + } + + return LY_SUCCESS; +} + +static LY_ERR +setup_data_empty_and_full_trees(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + LY_ERR ret; + + state->mod = mod; + state->count = count; + + if ((ret = create_list_inst(mod, 0, 0, &state->data1))) { + return ret; + } + if ((ret = create_list_inst(mod, 0, count, &state->data2))) { + return ret; + } + + return LY_SUCCESS; +} + +static LY_ERR +setup_data_offset_tree(const struct lys_module *mod, uint32_t count, struct test_state *state) +{ + LY_ERR ret; + + state->mod = mod; + state->count = count; + + if ((ret = create_list_inst(mod, count, count, &state->data2))) { + return ret; + } + + return LY_SUCCESS; +} + +/* TEST CB */ +static LY_ERR +test_create_new_text(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data = NULL; + + TEST_START(ts_start); + + if ((r = create_list_inst(state->mod, 0, state->count, &data))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(data); + + return LY_SUCCESS; +} + +static LY_ERR +test_create_new_bin(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data = NULL; + uint32_t i, k2_len, l_len; + char k2_val[32], l_val[32]; + struct lyd_node *list; + + TEST_START(ts_start); + + if ((r = lyd_new_inner(NULL, state->mod, "cont", 0, &data))) { + return r; + } + + for (i = 0; i < state->count; ++i) { + k2_len = sprintf(k2_val, "str%" PRIu32, i); + l_len = sprintf(l_val, "l%" PRIu32, i); + + if ((r = lyd_new_list(data, NULL, "lst", LYD_NEW_VAL_BIN, &list, &i, sizeof i, k2_val, k2_len))) { + return r; + } + if ((r = lyd_new_term_bin(list, NULL, "l", l_val, l_len, 0, NULL))) { + return r; + } + } + + TEST_END(ts_end); + + lyd_free_siblings(data); + + return LY_SUCCESS; +} + +static LY_ERR +test_create_path(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data = NULL; + uint32_t i; + char path[64], l_val[32]; + + TEST_START(ts_start); + + if ((r = lyd_new_inner(NULL, state->mod, "cont", 0, &data))) { + return r; + } + + for (i = 0; i < state->count; ++i) { + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", i, i); + sprintf(l_val, "l%" PRIu32, i); + + if ((r = lyd_new_path(data, NULL, path, l_val, 0, NULL))) { + return r; + } + } + + TEST_END(ts_end); + + lyd_free_siblings(data); + + return LY_SUCCESS; +} + +static LY_ERR +test_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + + TEST_START(ts_start); + + if ((r = lyd_validate_all(&state->data1, NULL, LYD_VALIDATE_PRESENT, NULL))) { + return r; + } + + TEST_END(ts_end); + + return LY_SUCCESS; +} + +static LY_ERR +_test_parse(struct test_state *state, LYD_FORMAT format, ly_bool use_file, uint32_t print_options, uint32_t parse_options, + uint32_t validate_options, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *data = NULL; + char *buf = NULL; + struct ly_in *in = NULL; + + if (use_file) { + if ((ret = lyd_print_path(TEMP_FILE, state->data1, format, print_options))) { + goto cleanup; + } + if ((ret = ly_in_new_filepath(TEMP_FILE, 0, &in))) { + goto cleanup; + } + } else { + if ((ret = lyd_print_mem(&buf, state->data1, format, print_options))) { + goto cleanup; + } + if ((ret = ly_in_new_memory(buf, &in))) { + goto cleanup; + } + } + + TEST_START(ts_start); + + if ((ret = lyd_parse_data(state->mod->ctx, NULL, in, format, parse_options, validate_options, &data))) { + goto cleanup; + } + + TEST_END(ts_end); + +cleanup: + free(buf); + ly_in_free(in, 0); + lyd_free_siblings(data); + return ret; +} + +static LY_ERR +test_parse_xml_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_XML, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end); +} + +static LY_ERR +test_parse_xml_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_XML, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, + ts_start, ts_end); +} + +static LY_ERR +test_parse_xml_file_no_validate_format(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_XML, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end); +} + +static LY_ERR +test_parse_json_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_JSON, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end); +} + +static LY_ERR +test_parse_json_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_JSON, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, + ts_start, ts_end); +} + +static LY_ERR +test_parse_json_file_no_validate_format(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_JSON, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end); +} + +static LY_ERR +test_parse_lyb_mem_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_LYB, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, ts_start, ts_end); +} + +static LY_ERR +test_parse_lyb_mem_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_LYB, 0, LYD_PRINT_SHRINK, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, + ts_start, ts_end); +} + +static LY_ERR +test_parse_lyb_file_no_validate(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_parse(state, LYD_LYB, 1, 0, LYD_PARSE_STRICT | LYD_PARSE_ONLY | LYD_PARSE_ORDERED, 0, ts_start, ts_end); +} + +static LY_ERR +_test_print(struct test_state *state, LYD_FORMAT format, uint32_t print_options, struct timespec *ts_start, + struct timespec *ts_end) +{ + LY_ERR ret = LY_SUCCESS; + char *buf = NULL; + + TEST_START(ts_start); + + if ((ret = lyd_print_mem(&buf, state->data1, format, print_options))) { + goto cleanup; + } + + TEST_END(ts_end); + +cleanup: + free(buf); + return ret; +} + +static LY_ERR +test_print_xml(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_print(state, LYD_XML, LYD_PRINT_SHRINK, ts_start, ts_end); +} + +static LY_ERR +test_print_json(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_print(state, LYD_JSON, LYD_PRINT_SHRINK, ts_start, ts_end); +} + +static LY_ERR +test_print_lyb(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + return _test_print(state, LYD_LYB, LYD_PRINT_SHRINK, ts_start, ts_end); +} + +static LY_ERR +test_dup(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data; + + TEST_START(ts_start); + + if ((r = lyd_dup_siblings(state->data1, NULL, LYD_DUP_RECURSIVE, &data))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(data); + + return LY_SUCCESS; +} + +static LY_ERR +test_dup_siblings_to_empty(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + + TEST_START(ts_start); + + if ((r = lyd_dup_siblings(lyd_child(state->data2), (struct lyd_node_inner *)state->data1, 0, NULL))) { + return r; + } + + TEST_END(ts_end); + + return LY_SUCCESS; +} + +static LY_ERR +test_free(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data; + + if ((r = create_list_inst(state->mod, 0, state->count, &data))) { + return r; + } + + TEST_START(ts_start); + + lyd_free_siblings(data); + + TEST_END(ts_end); + + return LY_SUCCESS; +} + +static LY_ERR +test_xpath_find(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct ly_set *set; + char path[64]; + + sprintf(path, "/perf:cont/lst[k1=%" PRIu32 " and k2='str%" PRIu32 "']", state->count / 2, state->count / 2); + + TEST_START(ts_start); + + if ((r = lyd_find_xpath(state->data1, path, &set))) { + return r; + } + + TEST_END(ts_end); + + ly_set_free(set, NULL); + + return LY_SUCCESS; +} + +static LY_ERR +test_xpath_find_hash(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct ly_set *set; + char path[64]; + + sprintf(path, "/perf:cont/lst[k1=%" PRIu32 "][k2='str%" PRIu32 "']", state->count / 2, state->count / 2); + + TEST_START(ts_start); + + if ((r = lyd_find_xpath(state->data1, path, &set))) { + return r; + } + + TEST_END(ts_end); + + ly_set_free(set, NULL); + + return LY_SUCCESS; +} + +static LY_ERR +test_compare_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + + TEST_START(ts_start); + + if ((r = lyd_compare_siblings(state->data1, state->data2, LYD_COMPARE_FULL_RECURSION))) { + return r; + } + + TEST_END(ts_end); + + return LY_SUCCESS; +} + +static LY_ERR +test_diff_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *diff; + + TEST_START(ts_start); + + if ((r = lyd_diff_siblings(state->data1, state->data2, 0, &diff))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(diff); + + return LY_SUCCESS; +} + +static LY_ERR +test_diff_no_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *diff; + + TEST_START(ts_start); + + if ((r = lyd_diff_siblings(state->data1, state->data2, 0, &diff))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(diff); + + return LY_SUCCESS; +} + +static LY_ERR +test_merge_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + + TEST_START(ts_start); + + if ((r = lyd_merge_siblings(&state->data1, state->data2, 0))) { + return r; + } + + TEST_END(ts_end); + + return LY_SUCCESS; +} + +static LY_ERR +test_merge_no_same(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data1; + + if ((r = create_list_inst(state->mod, 0, state->count, &data1))) { + return r; + } + + TEST_START(ts_start); + + if ((r = lyd_merge_siblings(&data1, state->data2, 0))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(data1); + + return LY_SUCCESS; +} + +static LY_ERR +test_merge_no_same_destruct(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + LY_ERR r; + struct lyd_node *data1, *data2; + + if ((r = create_list_inst(state->mod, 0, state->count, &data1))) { + return r; + } + if ((r = create_list_inst(state->mod, state->count, state->count, &data2))) { + return r; + } + + TEST_START(ts_start); + + if ((r = lyd_merge_siblings(&data1, data2, LYD_MERGE_DESTRUCT))) { + return r; + } + + TEST_END(ts_end); + + lyd_free_siblings(data1); + + return LY_SUCCESS; +} + +struct test tests[] = { + {"create new text", setup_basic, test_create_new_text}, + {"create new bin", setup_basic, test_create_new_bin}, + {"create path", setup_basic, test_create_path}, + {"validate", setup_data_single_tree, test_validate}, + {"parse xml mem validate", setup_data_single_tree, test_parse_xml_mem_validate}, + {"parse xml mem no validate", setup_data_single_tree, test_parse_xml_mem_no_validate}, + {"parse xml file no validate format", setup_data_single_tree, test_parse_xml_file_no_validate_format}, + {"parse json mem validate", setup_data_single_tree, test_parse_json_mem_validate}, + {"parse json mem no validate", setup_data_single_tree, test_parse_json_mem_no_validate}, + {"parse json file no validate format", setup_data_single_tree, test_parse_json_file_no_validate_format}, + {"parse lyb mem validate", setup_data_single_tree, test_parse_lyb_mem_validate}, + {"parse lyb mem no validate", setup_data_single_tree, test_parse_lyb_mem_no_validate}, + {"parse lyb file no validate", setup_data_single_tree, test_parse_lyb_file_no_validate}, + {"print xml", setup_data_single_tree, test_print_xml}, + {"print json", setup_data_single_tree, test_print_json}, + {"print lyb", setup_data_single_tree, test_print_lyb}, + {"dup", setup_data_single_tree, test_dup}, + {"dup_siblings_to_empty", setup_data_empty_and_full_trees, test_dup_siblings_to_empty}, + {"free", setup_basic, test_free}, + {"xpath find", setup_data_single_tree, test_xpath_find}, + {"xpath find hash", setup_data_single_tree, test_xpath_find_hash}, + {"compare same", setup_data_same_trees, test_compare_same}, + {"diff same", setup_data_same_trees, test_diff_same}, + {"diff no same", setup_data_no_same_trees, test_diff_no_same}, + {"merge same", setup_data_same_trees, test_merge_same}, + {"merge no same", setup_data_offset_tree, test_merge_no_same}, + {"merge no same destruct", setup_basic, test_merge_no_same_destruct}, +}; + +int +main(int argc, char **argv) +{ + LY_ERR ret = LY_SUCCESS; + struct ly_ctx *ctx = NULL; + const struct lys_module *mod; + uint32_t i, count, tries; + + if (argc < 3) { + fprintf(stderr, "Usage:\n%s list-instance-count test-tries\n\n", argv[0]); + return LY_EINVAL; + } + + count = atoi(argv[1]); + if (!count) { + fprintf(stderr, "Invalid count \"%s\".\n", argv[1]); + return LY_EINVAL; + } + + tries = atoi(argv[2]); + if (!tries) { + fprintf(stderr, "Invalid tries \"%s\".\n", argv[2]); + return LY_EINVAL; + } + + printf("\nly_perf:\n\tdata set size: %" PRIu32 "\n\teach test executed: %" PRIu32 " %s\n\n", count, tries, + (tries > 1) ? "times" : "time"); + + /* create context */ + if ((ret = ly_ctx_new(TESTS_SRC "/perf", 0, &ctx))) { + goto cleanup; + } + + /* load modules */ + if (!(mod = ly_ctx_load_module(ctx, "perf", NULL, NULL))) { + ret = LY_ENOTFOUND; + goto cleanup; + } + + /* tests */ + for (i = 0; i < (sizeof tests / sizeof(struct test)); ++i) { + if ((ret = exec_test(tests[i].setup, tests[i].test, tests[i].name, mod, count, tries))) { + goto cleanup; + } + } + + printf("\n"); + +cleanup: + ly_ctx_destroy(ctx); + return ret; +} diff --git a/tests/perf/perf.yang b/tests/perf/perf.yang new file mode 100644 index 0000000..0560270 --- /dev/null +++ b/tests/perf/perf.yang @@ -0,0 +1,27 @@ +module perf { + yang-version 1.1; + namespace "urn:sysrepo:tests:perf"; + prefix p; + + container cont { + list lst { + key "k1 k2"; + + leaf k1 { + type uint32; + } + + leaf k2 { + type string; + } + + leaf l { + type string; + } + + leaf-list lfl { + type uint32; + } + } + } +} diff --git a/tests/plugins/CMakeLists.txt b/tests/plugins/CMakeLists.txt new file mode 100644 index 0000000..1e21824 --- /dev/null +++ b/tests/plugins/CMakeLists.txt @@ -0,0 +1,19 @@ +include(CMakeParseArguments) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +function(ly_add_plugin) + cmake_parse_arguments(ADDPLUGIN "" "NAME" "SOURCES" ${ARGN}) + set(PLUGIN_NAME plugin_${ADDPLUGIN_NAME}) + + foreach(PLUGIN_SOURCE ${ADDPLUGIN_SOURCES}) + list(APPEND PLUGIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_SOURCE}) + endforeach() + + add_library(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES}) + set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "") + target_link_libraries(${PLUGIN_NAME} yang) +endfunction(ly_add_plugin) + +ly_add_plugin(NAME invalid SOURCES invalid.c) +ly_add_plugin(NAME simple SOURCES simple.c) diff --git a/tests/plugins/invalid.c b/tests/plugins/invalid.c new file mode 100644 index 0000000..000ccb4 --- /dev/null +++ b/tests/plugins/invalid.c @@ -0,0 +1,41 @@ +/** + * @file invalid.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief Invalid testing plugins implementation + * + * Copyright (c) 2021 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 + */ + +#include <stdint.h> + +#include "libyang.h" +#include "plugins_exts.h" +#include "plugins_types.h" + +/* + * EXTENSION PLUGIN + */ + +/** + * @brief Instead of plugin description, only the API version is declared. + * + * Here should be LY_PLUGINS_EXTENSIONS used. + */ +uint32_t plugins_extensions_apiver__ = LYPLG_EXT_API_VERSION; + +/* + * TYPE PLUGIN + */ + +/** + * @brief Instead of plugin description, only the API version is declared. + * + * Here should be LYPLG_TYPES used. + */ +uint32_t plugins_types_apiver__ = LYPLG_TYPE_API_VERSION; diff --git a/tests/plugins/simple.c b/tests/plugins/simple.c new file mode 100644 index 0000000..3595c76 --- /dev/null +++ b/tests/plugins/simple.c @@ -0,0 +1,92 @@ +/** + * @file simple.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief Simple testing plugins implementation + * + * Copyright (c) 2021 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 + */ + +#include <stdlib.h> + +#include "libyang.h" +#include "plugins_exts.h" +#include "plugins_types.h" + +/* + * EXTENSION PLUGIN + */ + +/** + * @brief Compile simple extension instances. + * + * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile. + */ +static LY_ERR +hint_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *extp, struct lysc_ext_instance *ext) +{ + /* check that the extension is instantiated at an allowed place - data node */ + if (!(ext->parent_stmt & LY_STMT_DATA_NODE_MASK)) { + lyplg_ext_compile_log(cctx, ext, LY_LLWRN, 0, + "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.", + extp->name, lyplg_ext_stmt2str(ext->parent_stmt)); + return LY_ENOT; + } + + return LY_SUCCESS; +} + +/** + * @brief Plugin descriptions for the test extensions + */ +LYPLG_EXTENSIONS = { + { + .module = "libyang-plugins-simple", + .revision = NULL, + .name = "hint", + + .plugin.id = "ly2 simple test v1", + .plugin.parse = NULL, + .plugin.compile = hint_compile, + .plugin.printer_info = NULL, + .plugin.node = NULL, + .plugin.snode = NULL, + .plugin.validate = NULL, + .plugin.pfree = NULL, + .plugin.cfree = NULL + }, + {0} /* terminating zeroed item */ +}; + +/* + * TYPE PLUGIN + */ + +/** + * @brief Plugin information for note (string) type implementation. + * + * Everything is just the same as for built-in string. + */ +LYPLG_TYPES = { + { + .module = "libyang-plugins-simple", + .revision = NULL, + .name = "note", + + .plugin.id = "ly2 simple test v1", + .plugin.store = lyplg_type_store_string, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_simple, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_simple, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 0 + }, + {0} +}; diff --git a/tests/style/CMakeLists.txt b/tests/style/CMakeLists.txt new file mode 100644 index 0000000..93d6049 --- /dev/null +++ b/tests/style/CMakeLists.txt @@ -0,0 +1,12 @@ +add_test(NAME headers + COMMAND ${CMAKE_SOURCE_DIR}/compat/check_includes.sh ${CMAKE_SOURCE_DIR}/src/ ${CMAKE_SOURCE_DIR}/tools/lint/ ${CMAKE_SOURCE_DIR}/tools/re/) + +if (${SOURCE_FORMAT_ENABLED}) + add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check) +endif() + +# just compile +add_executable(cpp_compat cpp_compat.c $<TARGET_OBJECTS:yangobj>) +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) +target_compile_options(cpp_compat PUBLIC "-Werror=c++-compat") diff --git a/tests/style/cpp_compat.c b/tests/style/cpp_compat.c new file mode 100644 index 0000000..28a8ffd --- /dev/null +++ b/tests/style/cpp_compat.c @@ -0,0 +1,95 @@ +/** + * @file cpp_compat.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for C++ compatibility of public headers (macros) + * + * Copyright (c) 2021 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 + */ + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "plugins_exts.h" +#include "plugins_types.h" + +int +main(void) +{ + struct ly_ctx *ctx = NULL; + const struct lys_module *mod; + int *sa = NULL, *item; + struct test_list { + int val; + struct test_list *next; + } *tl = NULL, *tl_item = NULL, *tl_next; + LY_ARRAY_COUNT_TYPE u; + const struct lysc_node *scnode; + struct lyd_node *data = NULL, *next, *elem, *opaq = NULL; + LY_ERR ret = LY_SUCCESS; + + if ((ret = ly_ctx_new(NULL, 0, &ctx))) { + goto cleanup; + } + if (!(mod = ly_ctx_get_module_latest(ctx, "ietf-yang-library"))) { + ret = LY_EINT; + goto cleanup; + } + if ((ret = ly_ctx_get_yanglib_data(ctx, &data, "%u", ly_ctx_get_change_count(ctx)))) { + goto cleanup; + } + if ((ret = lyd_new_opaq(NULL, ctx, "name", "val", NULL, "module", &opaq))) { + goto cleanup; + } + + /* tree_edit.h / tree.h */ + LY_ARRAY_NEW_GOTO(ctx, sa, item, ret, cleanup); + LY_ARRAY_NEW_GOTO(ctx, sa, item, ret, cleanup); + LY_ARRAY_FOR(sa, int, item) {} + LY_ARRAY_FREE(sa); + sa = NULL; + + LY_ARRAY_CREATE_GOTO(ctx, sa, 2, ret, cleanup); + LY_ARRAY_INCREMENT(sa); + LY_ARRAY_INCREMENT(sa); + LY_ARRAY_FOR(sa, u) {} + LY_ARRAY_DECREMENT_FREE(sa); + LY_ARRAY_DECREMENT_FREE(sa); + + LY_LIST_NEW_GOTO(ctx, &tl, tl_item, next, ret, cleanup); + LY_LIST_NEW_GOTO(ctx, &tl, tl_item, next, ret, cleanup); + LY_LIST_FOR(tl, tl_item) {} + LY_LIST_FOR_SAFE(tl, tl_next, tl_item) {} + tl_item = tl->next; + + /* tree_data.h */ + LYD_TREE_DFS_BEGIN(data, elem) { + LYD_TREE_DFS_END(data, elem); + } + LYD_LIST_FOR_INST(data, data->schema, elem) {} + LYD_LIST_FOR_INST_SAFE(data, data->schema, next, elem) {} + (void)LYD_CTX(data); + (void)LYD_NAME(data); + + /* tree_schema.h */ + LYSC_TREE_DFS_BEGIN(mod->compiled->data, scnode) { + LYSC_TREE_DFS_END(mod->compiled->data, scnode); + } + (void)LYSP_MODULE_NAME(mod->parsed); + (void)lysc_is_userordered(data->schema); + (void)lysc_is_key(data->schema); + (void)lysc_is_np_cont(data->schema); + (void)lysc_is_dup_inst_list(data->schema); + +cleanup: + free(tl_item); + free(tl); + lyd_free_tree(opaq); + lyd_free_all(data); + ly_ctx_destroy(ctx); + return ret; +} diff --git a/tests/tests_config.h.in b/tests/tests_config.h.in new file mode 100644 index 0000000..119fb35 --- /dev/null +++ b/tests/tests_config.h.in @@ -0,0 +1,27 @@ +/** + * @file tests_config.h + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief cmocka tests configuration header. + * + * Copyright (c) 2015 - 2021 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 + */ +#ifndef LYTEST_CONFIG_H_ +#define LYTEST_CONFIG_H_ + +#define TESTS_SRC "@CMAKE_CURRENT_SOURCE_DIR@" +#define TESTS_BIN "@CMAKE_CURRENT_BINARY_DIR@" + +#define TESTS_DIR_MODULES_YANG TESTS_SRC"/modules/yang" + +/** + * @brief Macro for support of callgrind header and macros. + */ +#cmakedefine HAVE_CALLGRIND + +#endif /* LYTEST_CONFIG_H_ */ diff --git a/tests/tool_i.tcl b/tests/tool_i.tcl new file mode 100644 index 0000000..d0f3d4b --- /dev/null +++ b/tests/tool_i.tcl @@ -0,0 +1,156 @@ +# @brief Common functions and variables for Tool Under Test (TUT). +# +# The script requires variables: +# TUT_PATH - Assumed absolute path to the directory in which the TUT is located. +# TUT_NAME - TUT name (without path). +# +# The script sets the variables: +# TUT - The path (including the name) of the executable TUT. +# error_prompt - Delimiter on error. +# error_head - Header on error. + +package require Expect + +# Complete the path for Tool Under Test (TUT). For example, on Windows, TUT can be located in the Debug or Release +# subdirectory. Note that Release build takes precedence over Debug. +set conftypes {{} Release Debug} +foreach i $conftypes { + if { [file executable "$TUT_PATH/$i/$TUT_NAME"] || [file executable "$TUT_PATH/$i/$TUT_NAME.exe"] } { + set TUT "$TUT_PATH/$i/$TUT_NAME" + break + } +} +if {![info exists TUT]} { + error "$TUT_NAME executable not found" +} + +# prompt of error message +set error_prompt ">>>" +# the beginning of error message +set error_head "$error_prompt Check-failed" + +# detection on eof and timeout will be on every expect command +expect_after { + eof { + global error_head + error "$error_head unexpected termination" + } timeout { + global error_head + error "$error_head timeout" + } +} + +# Run commands from command line +tcltest::loadTestedCommands + +# namespace of internal functions +namespace eval ly::private {} + +# Send command 'cmd' to the process, then check output string by 'pattern'. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex or an exact string to match. If is not specified, only prompt assumed afterwards. +# It must not contain a prompt. There can be an '$' character at the end of the pattern, in which case the regex +# matches the characters before the prompt. +# Parameter 'opt' can contain: +# -ex has a similar meaning to the expect command. The 'pattern' parameter is used as a simple string +# for exact matching of the output. So 'pattern' is not a regular expression but some characters +# must still be escaped, eg ][. +proc ly_cmd {cmd {pattern ""} {opt ""}} { + global prompt + + send -- "${cmd}\r" + expect -- "${cmd}\r\n" + + if { $pattern eq "" } { + # command without output + expect ^$prompt + return + } + + # definition of an expression that matches failure + set failure_pattern "\r\n${prompt}$" + + if { $opt eq "" && [string index $pattern end] eq "$"} { + # check output by regular expression + # It was explicitly specified how the expression should end. + set pattern [string replace $pattern end end] + expect { + -re "${pattern}\r\n${prompt}$" {} + -re $failure_pattern { + error "unexpected output:\n$expect_out(buffer)" + } + } + } elseif { $opt eq "" } { + # check output by regular expression + expect { + -re "${pattern}.*\r\n${prompt}$" {} + -re $failure_pattern { + error "unexpected output:\n$expect_out(buffer)" + } + } + } elseif { $opt eq "-ex" } { + # check output by exact matching + expect { + -ex "${pattern}\r\n${prompt}" {} + -re $failure_pattern { + error "unexpected output:\n$expect_out(buffer)" + } + } + } else { + global error_head + error "$error_head unrecognized value of parameter 'opt'" + } +} + +# Send command 'cmd' to the process, expect some header and then check output string by 'pattern'. +# This function is useful for checking an error that appears in the form of a header. +# Parameter header is the expected header on the output. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex. It must not contain a prompt. +proc ly_cmd_header {cmd header pattern} { + global prompt + + send -- "${cmd}\r" + expect -- "${cmd}\r\n" + + expect { + -re "$header .*${pattern}.*\r\n${prompt}$" {} + -re "\r\n${prompt}$" { + error "unexpected output:\n$expect_out(buffer)" + } + } +} + +# Whatever is written is sent, output is ignored and then another prompt is expected. +# Parameter cmd is optional and any output is ignored. +proc ly_ignore {{cmd ""}} { + global prompt + + send "${cmd}\r" + expect -re "$prompt$" +} + +# Send a completion request and check if the anchored regex output matches. +proc ly_completion {input output} { + global prompt + + send -- "${input}\t" + # expecting echoing input, output and 10 terminal control characters + expect -re "^${input}\r${prompt}${output}.*\r.*$" +} + +# Send a completion request and check if the anchored regex hint options match. +proc ly_hint {input prev_input hints} { + global prompt + + set output {} + foreach i $hints { + # each element might have some number of spaces and CRLF around it + append output "${i} *(?:\\r\\n)?" + } + + send -- "${input}\t" + # expecting the hints, previous input from which the hints were generated + # and some number of terminal control characters + expect -re "${output}\r${prompt}${prev_input}.*\r.*$" +} diff --git a/tests/tool_ni.tcl b/tests/tool_ni.tcl new file mode 100644 index 0000000..7282d35 --- /dev/null +++ b/tests/tool_ni.tcl @@ -0,0 +1,141 @@ +# @brief Common functions and variables for Tool Under Test (TUT). +# +# The script requires variables: +# TUT_PATH - Assumed absolute path to the directory in which the TUT is located. +# TUT_NAME - TUT name (without path). +# +# The script sets the variables: +# TUT - The path (including the name) of the executable TUT. +# error_prompt - Delimiter on error. +# error_head - Header on error. + +# Complete the path for Tool Under Test (TUT). For example, on Windows, TUT can be located in the Debug or Release +# subdirectory. Note that Release build takes precedence over Debug. +set conftypes {{} Release Debug} +foreach i $conftypes { + if { [file executable "$TUT_PATH/$i/$TUT_NAME"] || [file executable "$TUT_PATH/$i/$TUT_NAME.exe"] } { + set TUT "$TUT_PATH/$i/$TUT_NAME" + break + } +} +if {![info exists TUT]} { + error "$TUT_NAME executable not found" +} + +# prompt of error message +set error_prompt ">>>" +# the beginning of error message +set error_head "$error_prompt Check-failed" + +# Run commands from command line +tcltest::loadTestedCommands + +# namespace of internal functions +namespace eval ly::private { + namespace export * +} + +# Run the process with arguments. +# Parameter cmd is a string with arguments. +# Parameter wrn is a flag. Set to 1 if stderr should be ignored. +# Returns a pair where the first is the return code and the second is the output. +proc ly::private::ly_exec {cmd {wrn ""}} { + global TUT + try { + set results [exec -- $TUT {*}$cmd] + set status 0 + } trap CHILDSTATUS {results options} { + # return code is not 0 + set status [lindex [dict get $options -errorcode] 2] + } trap NONE results { + if { $wrn == 1 } { + set status 0 + } else { + error "return code is 0 but something was written to stderr:\n$results\n" + } + } trap CHILDKILLED {results options} { + set status [lindex [dict get $options -errorcode] 2] + error "process was killed: $status" + } + list $status $results +} + +# Internal function. +# Check the output with pattern. +# Parameter pattern is a regex or an exact string to match. +# Parameter msg is the output to check. +# Parameter 'opt' is optional. If contains '-ex', then the 'pattern' parameter is +# used as a simple string for exact matching of the output. +proc ly::private::output_check {pattern msg {opt ""}} { + if { $opt eq "" } { + expr {![regexp -- $pattern $msg]} + } elseif { $opt eq "-ex" } { + expr {![string equal "$pattern" $msg]} + } else { + global error_head + error "$error_head unrecognized value of parameter 'opt'" + } +} + +# Execute yanglint with arguments and expect success. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex or an exact string to match. +# Parameter 'opt' is optional. If contains '-ex', then the 'pattern' parameter is +# used as a simple string for exact matching of the output. +proc ly_cmd {cmd {pattern ""} {opt ""}} { + namespace import ly::private::* + lassign [ly_exec $cmd] rc msg + if { $rc != 0 } { + error "unexpected return code $rc:\n$msg\n" + } + if { $pattern ne "" && [output_check $pattern $msg $opt] } { + error "unexpected output:\n$msg\n" + } + return +} + +# Execute yanglint with arguments and expect error. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex. +proc ly_cmd_err {cmd pattern} { + namespace import ly::private::* + lassign [ly_exec $cmd] rc msg + if { $rc == 0 } { + error "unexpected return code $rc" + } + if { [output_check $pattern $msg] } { + error "unexpected output:\n$msg\n" + } + return +} + +# Execute yanglint with arguments, expect warning in stderr but success. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex. +proc ly_cmd_wrn {cmd pattern} { + namespace import ly::private::* + lassign [ly_exec $cmd 1] rc msg + if { $rc != 0 } { + error "unexpected return code $rc:\n$msg\n" + } + if { [output_check $pattern $msg] } { + error "unexpected output:\n$msg\n" + } + return +} + +# Check if yanglint supports the specified option. +# Parameter opt is a option to be found. +# Return true if option is found otherwise false. +proc ly_opt_exists {opt} { + namespace import ly::private::* + lassign [ly_exec "--help"] rc msg + if { $rc != 0 } { + error "unexpected return code $rc:\n$msg\n" + } + if { [output_check $opt $msg] } { + return false + } else { + return true + } +} diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt new file mode 100644 index 0000000..46558e8 --- /dev/null +++ b/tests/utests/CMakeLists.txt @@ -0,0 +1,79 @@ + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +set(format_sources + ${format_sources} + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/basic/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/data/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/extensions/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/schema/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/types/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/restriction/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/node/*.c + PARENT_SCOPE) + +ly_add_utest(NAME uint8 SOURCES types/uint8.c) +ly_add_utest(NAME uint16 SOURCES types/uint16.c) +ly_add_utest(NAME uint32 SOURCES types/uint32.c) +ly_add_utest(NAME uint64 SOURCES types/uint64.c) +ly_add_utest(NAME int8 SOURCES types/int8.c) +ly_add_utest(NAME int16 SOURCES types/int16.c) +ly_add_utest(NAME int32 SOURCES types/int32.c) +ly_add_utest(NAME int64 SOURCES types/int64.c) +ly_add_utest(NAME string SOURCES types/string.c) +ly_add_utest(NAME bits SOURCES types/bits.c) +ly_add_utest(NAME binary SOURCES types/binary.c) +ly_add_utest(NAME inet_types SOURCES types/inet_types.c) +ly_add_utest(NAME yang_types SOURCES types/yang_types.c) +ly_add_utest(NAME enumeration SOURCES types/enumeration.c) +ly_add_utest(NAME instanceid SOURCES types/instanceid.c) +ly_add_utest(NAME instanceid_keys SOURCES types/instanceid_keys.c) +ly_add_utest(NAME union SOURCES types/union.c) +ly_add_utest(NAME boolean SOURCES types/boolean.c) +ly_add_utest(NAME decimal64 SOURCES types/decimal64.c) +ly_add_utest(NAME empty SOURCES types/empty.c) +ly_add_utest(NAME identityref SOURCES types/identityref.c) +ly_add_utest(NAME leafref SOURCES types/leafref.c) + +ly_add_utest(NAME range SOURCES restriction/test_range.c) +ly_add_utest(NAME pattern SOURCES restriction/test_pattern.c) + +ly_add_utest(NAME list SOURCES node/list.c) + +ly_add_utest(NAME common SOURCES basic/test_common.c) +ly_add_utest(NAME set SOURCES basic/test_set.c) +ly_add_utest(NAME hash_table SOURCES basic/test_hash_table.c) +ly_add_utest(NAME inout SOURCES basic/test_inout.c) +ly_add_utest(NAME context SOURCES basic/test_context.c) +if(NOT WIN32) +ly_add_utest(NAME plugins SOURCES basic/test_plugins.c) +endif() +ly_add_utest(NAME xml SOURCES basic/test_xml.c) +ly_add_utest(NAME json SOURCES basic/test_json.c) +ly_add_utest(NAME xpath SOURCES basic/test_xpath.c) +ly_add_utest(NAME yanglib SOURCES basic/test_yanglib.c) + +ly_add_utest(NAME schema SOURCES schema/test_schema.c) +ly_add_utest(NAME yang SOURCES schema/test_yang.c) +ly_add_utest(NAME yin SOURCES schema/test_yin.c) +ly_add_utest(NAME tree_schema_compile SOURCES schema/test_tree_schema_compile.c) +ly_add_utest(NAME printer_tree SOURCES schema/test_printer_tree.c) + +ly_add_utest(NAME tree_data SOURCES data/test_tree_data.c) +ly_add_utest(NAME tree_data_sorted SOURCES data/test_tree_data_sorted.c) +ly_add_utest(NAME new SOURCES data/test_new.c) +ly_add_utest(NAME parser_xml SOURCES data/test_parser_xml.c) +ly_add_utest(NAME printer_xml SOURCES data/test_printer_xml.c) +ly_add_utest(NAME printer_json SOURCES data/test_printer_json.c) +ly_add_utest(NAME parser_json SOURCES data/test_parser_json.c) +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) + +ly_add_utest(NAME metadata SOURCES extensions/test_metadata.c) +ly_add_utest(NAME nacm SOURCES extensions/test_nacm.c) +ly_add_utest(NAME yangdata SOURCES extensions/test_yangdata.c) +ly_add_utest(NAME schema_mount SOURCES extensions/test_schema_mount.c) +ly_add_utest(NAME structure SOURCES extensions/test_structure.c) diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c new file mode 100644 index 0000000..36269e7 --- /dev/null +++ b/tests/utests/basic/test_common.c @@ -0,0 +1,416 @@ +/** + * @file test_common.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from common.c + * + * Copyright (c) 2018 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 "ly_common.h" + +static void +test_utf8(void **UNUSED(state)) +{ + char buf[5] = {0}; + const char *str = buf; + unsigned int c; + size_t len; + + /* test invalid UTF-8 characters in lyxml_getutf8 + * - https://en.wikipedia.org/wiki/UTF-8 */ + buf[0] = (char)0x04; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[0] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xc0; + buf[1] = (char)0x00; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xe0; + buf[1] = (char)0x00; + buf[2] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xf0; + buf[1] = (char)0x00; + buf[2] = (char)0x80; + buf[3] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); +} + +static void +test_parse_int(void **UNUSED(state)) +{ + const char *str; + int64_t i = 500; + + str = "10"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, 10); + + /* leading zeros are allowed, trailing whitespaces are allowed */ + str = "000\n\t "; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, 0); + + /* negative value */ + str = "-10"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, -10); + + /* non-NULL terminated string */ + str = "+5sometext"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, 2, -10, 10, 10, &i)); + assert_int_equal(i, 5); + + /* out of bounds value */ + str = "11"; + assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + str = "-11"; + assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + /* NaN */ + str = "zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + /* mixing number with text */ + str = "10zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + str = "10 zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); +} + +static void +test_parse_uint(void **UNUSED(state)) +{ + const char *str; + uint64_t u = 500; + + str = "10"; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u)); + assert_int_equal(u, 10); + + /* leading zeros are allowed, trailing whitespaces are allowed */ + str = "000\n\t "; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u)); + assert_int_equal(u, 0); + /* non-NULL terminated string */ + str = "+5sometext"; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, 2, 10, 10, &u)); + assert_int_equal(u, 5); + + /* out of bounds value */ + str = "11"; + assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), 10, 10, &u)); + str = "-1"; + assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), (uint64_t)-1, 10, &u)); + + /* NaN */ + str = "zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); + + /* mixing number with text */ + str = "10zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); + + str = "10 zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); +} + +static void +test_parse_nodeid(void **UNUSED(state)) +{ + const char *str; + const char *prefix, *name; + size_t prefix_len, name_len; + + str = "123"; + assert_int_equal(LY_EINVAL, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + + str = "a12_-.!"; + assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + assert_null(prefix); + assert_int_equal(0, prefix_len); + assert_non_null(name); + assert_int_equal(6, name_len); + assert_int_equal(0, strncmp("a12_-.", name, name_len)); + assert_string_equal("!", str); + + str = "a12_-.:_b2 xxx"; + assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + assert_non_null(prefix); + assert_int_equal(6, prefix_len); + assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len)); + assert_non_null(name); + assert_int_equal(3, name_len); + assert_int_equal(0, strncmp("_b2", name, name_len)); + assert_string_equal(" xxx", str); +} + +static void +test_parse_instance_predicate(void **UNUSED(state)) +{ + const char *str, *errmsg; + const char *prefix, *id, *value; + size_t prefix_len, id_len, value_len; + + str = "[ex:name='fred']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_string_equal(prefix, "ex:name='fred']"); + assert_int_equal(prefix_len, 2); + assert_string_equal(id, "name='fred']"); + assert_int_equal(id_len, 4); + assert_string_equal(value, "fred']"); + assert_int_equal(value_len, 4); + + str = "[ex:ip = \"[192.0.2.1]\"][ex:port='80']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, "[ex:port='80']"); + assert_string_equal(prefix, "ex:ip = \"[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(prefix_len, 2); + assert_string_equal(id, "ip = \"[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(id_len, 2); + assert_string_equal(value, "[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(value_len, 11); + + str = "[. = 'blowfish-cbc']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_null(prefix); + assert_int_equal(prefix_len, 0); + assert_string_equal(id, ". = 'blowfish-cbc']"); + assert_int_equal(id_len, 1); + assert_string_equal(value, "blowfish-cbc']"); + assert_int_equal(value_len, 12); + + str = "[ 3 ]"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_null(prefix); + assert_int_equal(prefix_len, 0); + assert_null(id); + assert_int_equal(id_len, 0); + assert_string_equal(value, "3 ]"); + assert_int_equal(value_len, 1); + + /* invalid predicates */ + /* position must be positive integer */ + str = "[0]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "The position predicate cannot be zero."); + str = "[-1]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Invalid instance predicate format (negative position or invalid node-identifier)."); + + /* invalid node-identifier */ + str = "[$node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Invalid node-identifier."); + str = "[.node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Unexpected character instead of '=' in leaf-list-predicate."); + str = "[13node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (pos) is not terminated by \']\' character."); + + str = "[ex:node]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Unexpected character instead of '=' in key-predicate."); + + str = "[ex:node= value]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "String value is not quoted."); + + str = "[ex:node='value\"]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Value is not terminated quoted-string."); + + str = "[ex:node='value ]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Value is not terminated quoted-string."); + + str = "[ex:node=\"value\"[3]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (key-predicate) is not terminated by \']\' character."); + str = "[.=\"value\"[3]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (leaf-list-predicate) is not terminated by \']\' character."); + + /* the limit of the string is too short, it ends one character earlier */ + str = "[ex:node='value']"; + assert_int_equal(LY_EINVAL, ly_parse_instance_predicate(&str, strlen(str) - 1, LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate is incomplete."); +} + +static void +test_value_prefix_next(void **UNUSED(state)) +{ + const char *next; + ly_bool is_prefix; + uint32_t bytes; + + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(NULL, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(0, bytes); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next("", NULL, &bytes, &is_prefix, &next)); + assert_int_equal(0, bytes); + + /* prefix */ + next = "pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix */ + next = "node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "::::"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "//a/:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "//a//"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* prefix, prefix */ + next = "pref1:pref2:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_string_equal(next, "pref2:"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* prefix, no-prefix */ + next = "pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix, prefix */ + next = "/pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(1, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix, prefix */ + next = "//pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(2, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix, prefix, no-prefix */ + next = "/pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(1, bytes); + assert_string_equal(next, "pref:node"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* prefix, no-prefix, prefix */ + next = "pref:node pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node pref:"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* prefix, no-prefix, prefix, no-prefix */ + next = "pref:node /pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node /pref:node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(6, bytes); + assert_string_equal(next, "pref:node"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_utf8), + UTEST(test_parse_int), + UTEST(test_parse_uint), + UTEST(test_parse_nodeid), + UTEST(test_parse_instance_predicate), + UTEST(test_value_prefix_next), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c new file mode 100644 index 0000000..7feb65f --- /dev/null +++ b/tests/utests/basic/test_context.c @@ -0,0 +1,1103 @@ +/** + * @file test_context.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from context.c + * + * Copyright (c) 2018 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 "context.h" +#include "in.h" +#include "ly_common.h" +#include "schema_compile.h" +#include "tests_config.h" +#include "tree_schema_internal.h" + +#ifdef _WIN32 + +static void +slashes_to_backslashes(char *path) +{ + while ((path = strchr(path, '/'))) { + *path++ = '\\'; + } +} + +static void +test_searchdirs(void **state) +{ + const char * const *list; + char *path1 = strdup(TESTS_BIN "/utests"); + char *path2 = strdup(TESTS_SRC); + + slashes_to_backslashes(path1); + slashes_to_backslashes(path2); + + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_searchdir())."); + assert_null(ly_ctx_get_searchdirs(NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_searchdirs())."); + assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_searchdir())."); + + /* correct path */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]); + + /* duplicated paths */ + assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]); + + /* another path */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path2)); + assert_int_equal(2, UTEST_LYCTX->search_paths.count); + assert_string_equal(path2, UTEST_LYCTX->search_paths.objs[1]); + + /* get searchpaths */ + list = ly_ctx_get_searchdirs(UTEST_LYCTX); + assert_non_null(list); + assert_string_equal(path1, list[0]); + assert_string_equal(path2, list[1]); + assert_null(list[2]); + + /* removing searchpaths */ + /* nonexisting */ + assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile")); + CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL, 0); + + /* first */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_not_equal(path1, list[0]); + + /* second */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path2)); + assert_int_equal(0, UTEST_LYCTX->search_paths.count); + + free(path1); + free(path2); +} + +#else + +static void +test_searchdirs(void **state) +{ + const char * const *list; + + /* invalid arguments */ + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_searchdir())."); + assert_null(ly_ctx_get_searchdirs(NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_searchdirs())."); + assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_searchdir())."); + + /* readable and executable, but not a directory */ + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utest_context")); + CHECK_LOG_CTX("Given search directory \""TESTS_BIN "/utest_context\" is not a directory.", NULL, 0); + /* not existing */ + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, "/nonexistingfile")); + CHECK_LOG_CTX("Unable to use search directory \"/nonexistingfile\" (No such file or directory).", NULL, 0); + + /* ly_set_add() fails */ + /* no change */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, NULL)); + + /* correct path */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests")); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]); + + /* duplicated paths */ + assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests")); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]); + + /* another paths - add 8 to fill the initial buffer of the searchpaths list */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/CMakeFiles")); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../src")); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../CMakeModules")); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../doc")); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC)); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN)); + assert_int_equal(7, UTEST_LYCTX->search_paths.count); + + /* get searchpaths */ + list = ly_ctx_get_searchdirs(UTEST_LYCTX); + assert_non_null(list); + assert_string_equal(TESTS_BIN "/utests", list[0]); + assert_string_equal(TESTS_BIN "/CMakeFiles", list[1]); + assert_string_equal(TESTS_SRC, list[5]); + assert_string_equal(TESTS_BIN, list[6]); + assert_null(list[7]); + + /* removing searchpaths */ + /* nonexisting */ + assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile")); + CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL, 0); + /* first */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN "/utests")); + assert_string_not_equal(TESTS_BIN "/utests", list[0]); + assert_int_equal(6, UTEST_LYCTX->search_paths.count); + /* middle */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_SRC)); + assert_int_equal(5, UTEST_LYCTX->search_paths.count); + /* last */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN)); + assert_int_equal(4, UTEST_LYCTX->search_paths.count); + /* all */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL)); + assert_int_equal(0, UTEST_LYCTX->search_paths.count); + + /* again - no change */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL)); + + /* cleanup */ + ly_ctx_destroy(UTEST_LYCTX); + + /* test searchdir list in ly_ctx_new() */ + assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &UTEST_LYCTX)); + CHECK_LOG_LASTMSG("Unable to use search directory \"/nonexistingfile\" (No such file or directory)."); + assert_int_equal(LY_SUCCESS, + ly_ctx_new(TESTS_SRC PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_SRC, + LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + assert_int_equal(2, UTEST_LYCTX->search_paths.count); + assert_string_equal(TESTS_SRC, UTEST_LYCTX->search_paths.objs[0]); + assert_string_equal(TESTS_BIN, UTEST_LYCTX->search_paths.objs[1]); +} + +#endif + +static void +test_options(void **state) +{ + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0xffff, &UTEST_LYCTX)); + + /* invalid arguments */ + assert_int_equal(0, ly_ctx_get_options(NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_options())."); + + assert_int_equal(LY_EINVAL, ly_ctx_set_options(NULL, 0)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_options())."); + assert_int_equal(LY_EINVAL, ly_ctx_unset_options(NULL, 0)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_options())."); + + /* unset */ + /* LY_CTX_ALL_IMPLEMENTED */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED); + + /* LY_CTX_REF_IMPLEMENTED */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED); + + /* LY_CTX_DISABLE_SEARCHDIRS */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS); + + /* LY_CTX_DISABLE_SEARCHDIR_CWD */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD); + + /* LY_CTX_PREFER_SEARCHDIRS */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS); + + /* LY_CTX_LEAFREF_EXTENDED */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED); + + /* LY_CTX_LEAFREF_LINKING */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + + /* LY_CTX_BUILTIN_PLUGINS_ONLY */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_BUILTIN_PLUGINS_ONLY); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_BUILTIN_PLUGINS_ONLY)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_BUILTIN_PLUGINS_ONLY); + + assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX)); + + /* set back */ + /* LY_CTX_ALL_IMPLEMENTED */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED); + + /* LY_CTX_REF_IMPLEMENTED */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED); + + /* LY_CTX_DISABLE_SEARCHDIRS */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS); + + /* LY_CTX_DISABLE_SEARCHDIR_CWD */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD); + + /* LY_CTX_PREFER_SEARCHDIRS */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS); + + /* LY_CTX_LEAFREF_EXTENDED */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED); + + /* LY_CTX_LEAFREF_LINKING */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + + /* LY_CTX_BUILTIN_PLUGINS_ONLY */ + assert_int_equal(LY_EINVAL, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_BUILTIN_PLUGINS_ONLY)); + CHECK_LOG_CTX("Invalid argument option (LY_CTX_BUILTIN_PLUGINS_ONLY can be set only when creating a new context) (ly_ctx_set_options()).", NULL, 0); + + assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX)); +} + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_models(void **state) +{ + struct ly_in *in; + const char *str; + struct lys_module *mod1, *mod2; + struct lys_glob_unres unres = {0}; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + + /* invalid arguments */ + assert_int_equal(0, ly_ctx_get_change_count(NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_change_count())."); + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + assert_int_equal(UTEST_LYCTX->change_count, ly_ctx_get_change_count(UTEST_LYCTX)); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Invalid schema input format.", NULL, 0); + + /* import callback */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)(str = "test")); + assert_ptr_equal(test_imp_clb, UTEST_LYCTX->imp_clb); + assert_ptr_equal(str, UTEST_LYCTX->imp_clb_data); + assert_ptr_equal(test_imp_clb, ly_ctx_get_module_imp_clb(UTEST_LYCTX, (void **)&str)); + assert_string_equal("test", str); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL); + assert_null(UTEST_LYCTX->imp_clb); + assert_null(UTEST_LYCTX->imp_clb_data); + + /* name collision of module and submodule */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}"); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0); + CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", NULL, 1); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y;revision 2018-10-30; }", &in)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + ly_in_free(in, 0); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0); + CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", NULL, 1); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to b {prefix b;}}"); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module b {namespace urn:b;prefix b;include y;}", &in)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_revert(UTEST_LYCTX, &unres); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0); + CHECK_LOG_CTX("Name collision between submodules of name \"y\".", NULL, 1); + + /* selecting correct revision of the submodules */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}"); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", &in)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date); + + /* reloading module in case only the compiled module resists in the context */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module w {namespace urn:w;prefix w;revision 2018-10-24;}", &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod1)); + ly_in_free(in, 0); + assert_non_null(mod1->compiled); + assert_non_null(mod1->parsed); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}"); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod2)); + ly_in_free(in, 0); + assert_non_null(mod2); + assert_non_null(mod1->parsed); + assert_string_equal("w", mod1->name); +} + +static void +test_imports(void **state) +{ + struct lys_module *mod1, *mod2, *mod3, *import; + char *str; + uint16_t ctx_options; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + ctx_options = LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_NO_YANGLIBRARY; + + /* Import callback provides newer revision of module 'a', + * however the older revision is implemented soon and therefore it is preferred. */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-17;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-16;}", + LYS_IN_YANG, &mod1)); + assert_true(LYS_MOD_LATEST_REV & mod1->latest_revision); + assert_int_equal(1, mod1->implemented); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}", + LYS_IN_YANG, &mod2)); + assert_ptr_equal(mod1, mod2->parsed->imports[0].module); + assert_true((LYS_MOD_LATEST_REV | LYS_MOD_IMPORTED_REV) & mod1->latest_revision); + assert_string_equal("2019-09-16", mod1->revision); + assert_int_equal(1, mod1->implemented); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16")); + ly_ctx_destroy(UTEST_LYCTX); + + /* Import callback provides older revision of module 'a' and it is + * imported by another module, so it is preferred even if newer + * revision is implemented later. */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-16;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}", + LYS_IN_YANG, &mod2)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-17;}", + LYS_IN_YANG, &mod1)); + ly_log_level(LY_LLVRB); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;import a {prefix a;}}", + LYS_IN_YANG, &mod3)); + CHECK_LOG_LASTMSG("Implemented module \"a@2019-09-17\" is not used for import, revision \"2019-09-16\" is imported instead."); + ly_log_level(LY_LLWRN); + assert_true(LYS_MOD_LATEST_SEARCHDIRS & mod1->latest_revision); + assert_int_equal(1, mod1->implemented); + import = mod2->parsed->imports[0].module; + assert_true(LYS_MOD_IMPORTED_REV & import->latest_revision); + assert_string_equal("2019-09-16", import->revision); + assert_int_equal(0, import->implemented); + import = mod3->parsed->imports[0].module; + assert_string_equal("2019-09-16", import->revision); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16")); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-17")); + assert_string_equal("2019-09-17", ly_ctx_get_module_implemented(UTEST_LYCTX, "a")->revision); + ly_ctx_destroy(UTEST_LYCTX); + + /* check of circular dependency */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + str = "module a {namespace urn:a; prefix a;" + "import b {prefix b;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b { yang-version 1.1; namespace urn:b; prefix b;" + "import a {prefix a;}" + "}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + ly_err_clean(UTEST_LYCTX, NULL); +} + +static void +test_get_models(void **state) +{ + struct lys_module *mod, *mod2; + const char *str0 = "module a {namespace urn:a;prefix a;}"; + const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}"; + const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}"; + struct ly_in *in0, *in1, *in2; + struct lys_glob_unres unres = {0}; + + unsigned int index = 0; + const char *names[] = { + "ietf-yang-metadata", "yang", "ietf-inet-types", "ietf-yang-types", "ietf-yang-schema-mount", + "ietf-yang-structure-ext", "ietf-datastores", "ietf-yang-library", "a", "a", "a" + }; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str0, &in0)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str2, &in2)); + + /* invalid arguments */ + assert_ptr_equal(NULL, ly_ctx_get_module(NULL, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_module())."); + assert_ptr_equal(NULL, ly_ctx_get_module(UTEST_LYCTX, NULL, NULL)); + CHECK_LOG_CTX("Invalid argument name (ly_ctx_get_module()).", NULL, 0); + assert_ptr_equal(NULL, ly_ctx_get_module_ns(NULL, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_module_ns())."); + assert_ptr_equal(NULL, ly_ctx_get_module_ns(UTEST_LYCTX, NULL, NULL)); + CHECK_LOG_CTX("Invalid argument ns (ly_ctx_get_module_ns()).", NULL, 0); + assert_null(ly_ctx_get_module(UTEST_LYCTX, "nonsence", NULL)); + + /* internal modules */ + assert_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-types")); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang"); + assert_non_null(mod); + assert_non_null(mod->parsed); + assert_string_equal("yang", mod->name); + mod2 = ly_ctx_get_module_implemented_ns(UTEST_LYCTX, mod->ns); + assert_ptr_equal(mod, mod2); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-metadata", "2016-08-05")); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-types", "2013-07-15")); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-inet-types", "2013-07-15")); + assert_non_null(ly_ctx_get_module_ns(UTEST_LYCTX, "urn:ietf:params:xml:ns:yang:ietf-datastores", "2018-02-14")); + + /* select module by revision */ + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &mod)); + /* invalid attempts - implementing module of the same name and inserting the same module */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + assert_int_equal(LY_EDENIED, lys_implement(mod2, NULL, &unres)); + CHECK_LOG_CTX("Module \"a@2018-10-24\" is already implemented in revision \"2018-10-23\".", NULL, 0); + lys_unres_glob_erase(&unres); + ly_in_reset(in1); + /* it is already there, fine */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + /* insert the second module only as imported, not implemented */ + lys_unres_glob_erase(&unres); + ly_in_reset(in2); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + lys_unres_glob_erase(&unres); + assert_non_null(mod2); + assert_ptr_not_equal(mod, mod2); + mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a"); + assert_ptr_equal(mod, mod2); + mod2 = ly_ctx_get_module_latest_ns(UTEST_LYCTX, mod->ns); + assert_ptr_equal(mod, mod2); + /* work with module with no revision */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + lys_unres_glob_erase(&unres); + assert_ptr_equal(mod, ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_ptr_not_equal(mod, ly_ctx_get_module_latest(UTEST_LYCTX, "a")); + + str1 = "submodule b {belongs-to a {prefix a;}}"; + ly_in_free(in1, 0); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); + lys_unres_glob_erase(&unres); + + while ((mod = (struct lys_module *)ly_ctx_get_module_iter(UTEST_LYCTX, &index))) { + assert_string_equal(names[index - 1], mod->name); + } + assert_int_equal(11, index); + + /* cleanup */ + ly_in_free(in0, 0); + ly_in_free(in1, 0); + ly_in_free(in2, 0); +} + +static void +test_ylmem(void **state) +{ +#define DATA_YANG_LIBRARY_START "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\ + " <module-set>\n"\ + " <name>complete</name>\n"\ + " <module>\n"\ + " <name>yang</name>\n"\ + " <revision>2022-06-16</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-library</name>\n"\ + " <revision>2019-01-04</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\ + " </module>\n" + +#define DATA_YANG_BASE_IMPORTS " <import-only-module>\n"\ + " <name>ietf-yang-metadata</name>\n"\ + " <revision>2016-08-05</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-inet-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-yang-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-datastores</name>\n"\ + " <revision>2018-02-14</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\ + " </import-only-module>\n" + +#define DATA_YANG_SCHEMA_MODULE_STATE " </module-set>\n"\ + " <schema>\n"\ + " <name>complete</name>\n"\ + " <module-set>complete</module-set>\n"\ + " </schema>\n"\ + " <content-id>9</content-id>\n"\ + "</yang-library>\n"\ + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\ + " <module-set-id>12</module-set-id>\n"\ + " <module>\n"\ + " <name>ietf-yang-metadata</name>\n"\ + " <revision>2016-08-05</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>yang</name>\n"\ + " <revision>2022-06-16</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\ + " <conformance-type>implement</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-inet-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-library</name>\n"\ + " <revision>2019-01-04</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\ + " <conformance-type>implement</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-datastores</name>\n"\ + " <revision>2018-02-14</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n" + + const char *yanglibrary_only = + DATA_YANG_LIBRARY_START + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + const char *with_netconf = + DATA_YANG_LIBRARY_START + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + " <import-only-module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " </import-only-module>\n" + DATA_YANG_SCHEMA_MODULE_STATE + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>import</conformance-type>\n" + " </module>\n" + "</modules-state>"; + + char *with_netconf_features = malloc(8096); + + strcpy(with_netconf_features, + DATA_YANG_LIBRARY_START + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>confirmed-commit</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>url</feature>\n" + " <feature>xpath</feature>\n" + " </module>\n" + " <import-only-module>\n" + " <name>ietf-yang-metadata</name>\n" + " <revision>2016-08-05</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-inet-types</name>\n" + " <revision>2013-07-15</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-yang-types</name>\n" + " <revision>2013-07-15</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-datastores</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " </import-only-module>\n"); + strcpy(with_netconf_features + strlen(with_netconf_features), + DATA_YANG_SCHEMA_MODULE_STATE + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>confirmed-commit</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>url</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>import</conformance-type>\n" + " </module>\n" + "</modules-state>"); + + const char *garbage_revision = + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module-set>\n" + " <name>complete</name>\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2022-06-16</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " </module>\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2019-01-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + const char *no_yanglibrary = + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module-set>\n" + " <name>complete</name>\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2022-06-16</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + (void) state; + /* seperate context to avoid double free during teadown */ + struct ly_ctx *ctx_test = NULL; + + /* test invalid parameters */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, NULL, LYD_XML, 0, &ctx_test)); + assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, TESTS_SRC, LYD_XML, 0, NULL)); + assert_int_equal(LY_ESYS, ly_ctx_new_ylpath(NULL, TESTS_SRC "garbage", LYD_XML, 0, &ctx_test)); + + /* basic test with ietf-yang-library-only */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", yanglibrary_only, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04")); + assert_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test loading module, should also import other module */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + assert_int_equal(1, ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")->implemented); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")); + assert_int_equal(0, ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")->implemented); + assert_int_equal(LY_ENOT, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test loading module with feature if they are present */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")); + assert_int_equal(LY_SUCCESS, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test with not matching revision */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", garbage_revision, LYD_XML, 0, &ctx_test)); + + /* test data containing ietf-yang-library which conflicts with the option */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test)); + + /* test creating without ietf-yang-library */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", no_yanglibrary, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test)); + assert_int_equal(NULL, ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04")); + ly_ctx_destroy(ctx_test); + free(with_netconf_features); +} + +static LY_ERR +check_node_priv_parsed_is_set(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue)) +{ + const struct lysp_node *pnode; + const char ***iter; + + pnode = (const struct lysp_node *)node->priv; + CHECK_POINTER(pnode, 1); + iter = (const char ***)data; + CHECK_POINTER(**iter, 1); + CHECK_STRING(pnode->name, **iter); + (*iter)++; + + return LY_SUCCESS; +} + +static LY_ERR +check_node_priv_parsed_not_set(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue)) +{ + CHECK_POINTER(node->priv, 0); + return LY_SUCCESS; +} + +static void +check_ext_instance_priv_parsed_is_set(struct lysc_ext_instance *ext) +{ + LY_ARRAY_COUNT_TYPE u, v; + struct lysc_ext_substmt *substmts; + struct lysc_node *cnode; + const char **iter; + const char *check[] = { + "tmp_cont", "lf", NULL + }; + + LY_ARRAY_FOR(ext, u) { + substmts = ext[u].substmts; + LY_ARRAY_FOR(substmts, v) { + if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) { + cnode = *(struct lysc_node **)substmts[v].storage; + iter = check; + assert_int_equal(LY_SUCCESS, lysc_tree_dfs_full(cnode, check_node_priv_parsed_is_set, &iter)); + } + } + } +} + +static void +check_ext_instance_priv_parsed_not_set(struct lysc_ext_instance *ext) +{ + LY_ARRAY_COUNT_TYPE u, v; + struct lysc_ext_substmt *substmts; + struct lysc_node *cnode; + + LY_ARRAY_FOR(ext, u) { + substmts = ext[u].substmts; + LY_ARRAY_FOR(substmts, v) { + if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) { + cnode = *(struct lysc_node **)substmts[v].storage; + if (cnode) { + CHECK_POINTER((struct lysp_node *)cnode->priv, 0); + } + } + } + } +} + +/** + * @brief Testing of LY_CTX_SET_PRIV_PARSED. + */ +static void +test_set_priv_parsed(void **state) +{ + struct lys_module *mod; + const char *schema_a; + const char **iter; + const char *check[] = { + "cont", "contnotif", "contx", "grpleaf", "augleaf", "l1", + "l1a", "l1b", "l1c", "foo1", "ll", "any", "l2", + "l2c", "l2cx", "ch", "cas", "casx", "oper", + "input", "inparam", "output", "outparam", "n1", NULL + }; + + /* each node must have a unique name. */ + schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + "\n" + " import ietf-restconf {\n" + " prefix rc;\n" + " revision-date 2017-01-26;\n" + " }\n" + "\n" + " rc:yang-data \"tmp\" {\n" + " container tmp_cont {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container cont {\n" + " notification contnotif;\n" + " leaf-list contx {\n" + " type string;\n" + " }\n" + " uses grp;\n" + " }\n" + " list l1 {\n" + " key \"l1a l1b\";\n" + " leaf l1a {\n" + " type string;\n" + " }\n" + " leaf l1b {\n" + " type string;\n" + " }\n" + " leaf l1c {\n" + " type string;\n" + " }\n" + " }\n" + " feature f1;\n" + " feature f2;\n" + " leaf foo1 {\n" + " type uint16;\n" + " if-feature f1;\n" + " }\n" + " leaf foo2 {\n" + " type uint16;\n" + " }\n" + " leaf foo3 {\n" + " type uint16;\n" + " if-feature f2;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " anydata any {\n" + " config false;\n" + " }\n" + " list l2 {\n" + " config false;\n" + " container l2c {\n" + " leaf l2cx {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " choice ch {\n" + " case cas {\n" + " leaf casx {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rpc oper {\n" + " input {\n" + " leaf inparam {\n" + " type string;\n" + " }\n" + " }\n" + " output {\n" + " leaf outparam {\n" + " type int8;\n" + " }\n" + " }\n" + " }\n" + " notification n1;\n" + " grouping grp {\n" + " leaf grpleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + " augment /cont {\n" + " leaf augleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + " deviation /a:foo2 {\n" + " deviate not-supported;\n" + " }\n" + "}\n"; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + const char *feats[] = {"f1", NULL}; + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_SET_PRIV_PARSED, &UTEST_LYCTX)); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL); + + print_message("[ ] create context\n"); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter)); + check_ext_instance_priv_parsed_is_set(mod->compiled->exts); + + print_message("[ ] unset option\n"); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_not_set, &iter)); + check_ext_instance_priv_parsed_not_set(mod->compiled->exts); + + print_message("[ ] set option\n"); + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter)); + check_ext_instance_priv_parsed_is_set(mod->compiled->exts); +} + +static void +test_explicit_compile(void **state) +{ + uint32_t i; + struct lys_module *mod; + const char *schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + " feature f1;\n" + " feature f2;\n" + " leaf foo1 {\n" + " type uint16;\n" + " if-feature f1;\n" + " }\n" + " leaf foo2 {\n" + " type uint16;\n" + " }\n" + " container cont {\n" + " leaf foo3 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + const char *schema_b = "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;yang-version 1.1;\n" + " import a {\n" + " prefix a;\n" + " }\n" + " augment /a:cont {\n" + " leaf augleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + "}\n"; + const char *schema_c = "module c {\n" + " namespace urn:tests:c;\n" + " prefix c;yang-version 1.1;\n" + " import a {\n" + " prefix a;\n" + " }\n" + " deviation /a:foo2 {\n" + " deviate not-supported;\n" + " }\n" + "}\n"; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + const char *feats[] = {"f1", NULL}; + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_EXPLICIT_COMPILE, &UTEST_LYCTX)); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_c, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_set_implemented((struct lys_module *)mod, feats)); + + /* none of the modules should be compiled */ + i = 0; + while ((mod = ly_ctx_get_module_iter(UTEST_LYCTX, &i))) { + assert_null(mod->compiled); + } + + assert_int_equal(LY_SUCCESS, ly_ctx_compile(UTEST_LYCTX)); + + /* check internal modules */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-datastores"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-library"); + assert_non_null(mod); + + /* check test modules */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c"); + assert_non_null(mod); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_searchdirs), + UTEST(test_options), + UTEST(test_models), + UTEST(test_imports), + UTEST(test_get_models), + UTEST(test_ylmem), + UTEST(test_set_priv_parsed), + UTEST(test_explicit_compile), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_hash_table.c b/tests/utests/basic/test_hash_table.c new file mode 100644 index 0000000..2ea34fa --- /dev/null +++ b/tests/utests/basic/test_hash_table.c @@ -0,0 +1,262 @@ +/** + * @file test_hash_table.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from hash_table.c + * + * Copyright (c) 2018 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 <stdlib.h> + +#include "hash_table.h" +#include "ly_common.h" + +static void +test_invalid_arguments(void **state) +{ + assert_int_equal(LY_EINVAL, lydict_insert(NULL, NULL, 0, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (lydict_insert())."); + + assert_int_equal(LY_EINVAL, lydict_insert_zc(NULL, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument ctx (lydict_insert_zc())."); + assert_int_equal(LY_EINVAL, lydict_insert_zc(UTEST_LYCTX, NULL, NULL)); + CHECK_LOG_CTX("Invalid argument str_p (lydict_insert_zc()).", NULL, 0); +} + +static void +test_dict_hit(void **state) +{ + const char *str1, *str2, *str3; + + /* insert 2 strings, one of them repeatedly */ + assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str1)); + assert_non_null(str1); + /* via zerocopy we have to get the same pointer as provided */ + assert_non_null(str2 = strdup("test2")); + assert_int_equal(LY_SUCCESS, lydict_insert_zc(UTEST_LYCTX, (char *)str2, &str3)); + assert_ptr_equal(str2, str3); + /* here we get the same pointer as in case the string was inserted first time */ + assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str2)); + assert_non_null(str2); + assert_ptr_equal(str1, str2); + + /* remove strings, but the repeatedly inserted only once */ + lydict_remove(UTEST_LYCTX, "test1"); + lydict_remove(UTEST_LYCTX, "test2"); + + /* destroy dictionary - should raise warning about data presence */ + ly_ctx_destroy(UTEST_LYCTX); + UTEST_LYCTX = NULL; + CHECK_LOG_LASTMSG("String \"test1\" not freed from the dictionary, refcount 1."); + +#ifndef NDEBUG + /* cleanup */ + free((char *)str1); +#endif +} + +static uint8_t +ht_equal_clb(void *val1, void *val2, uint8_t mod, void *cb_data) +{ + int *v1, *v2; + + (void)mod; + (void)cb_data; + + v1 = (int *)val1; + v2 = (int *)val2; + + return *v1 == *v2; +} + +static void +test_ht_basic(void **UNUSED(state)) +{ + uint32_t i; + struct ly_ht *ht; + + assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0)); + + i = 2; + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL)); + assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL)); + assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL)); + assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i)); + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL)); + assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i)); + CHECK_LOG_LASTMSG("Invalid argument hash (lyht_remove_with_resize_cb())."); + + lyht_free(ht, NULL); +} + +static void +test_ht_resize(void **UNUSED(state)) +{ + uint32_t i; + struct ly_ht *ht; + + assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1)); + assert_int_equal(8, ht->size); + + /* insert records into indexes 2-7 */ + for (i = 2; i < 8; ++i) { + assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL)); + } + /* check that table resized */ + assert_int_equal(16, ht->size); + + /* check expected content of the table */ + for (i = 0; i < 16; ++i) { + if ((i >= 2) && (i < 8)) { + /* inserted data on indexes 2-7 */ + assert_int_not_equal(UINT32_MAX, ht->hlists[i].first); + assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL)); + } else { + /* nothing otherwise */ + assert_int_equal(UINT32_MAX, ht->hlists[i].first); + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL)); + } + } + + /* removing not present data should fail */ + for (i = 0; i < 2; ++i) { + assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i)); + CHECK_LOG_LASTMSG("Invalid argument hash (lyht_remove_with_resize_cb())."); + } + /* removing present data, resize should happened + * when we are below 25% of the table filled, so with 3 records left */ + for ( ; i < 5; ++i) { + assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i)); + } + assert_int_equal(8, ht->size); + + /* remove the rest */ + for ( ; i < 8; ++i) { + assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i)); + } + + for (i = 0; i < 8; ++i) { + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL)); + } + + /* cleanup */ + lyht_free(ht, NULL); +} + +static void +test_ht_collisions(void **UNUSED(state)) +{ +#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val)) + + uint32_t i; + struct ly_ht_rec *rec; + struct ly_ht *ht; + uint32_t rec_idx; + int count; + + assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1)); + + for (i = 2; i < 6; ++i) { + assert_int_equal(lyht_insert(ht, &i, 2, NULL), 0); + } + + /* check all records */ + for (i = 0; i < 8; ++i) { + if (i == 2) { + assert_int_not_equal(UINT32_MAX, ht->hlists[i].first); + } else { + assert_int_equal(UINT32_MAX, ht->hlists[i].first); + } + } + for (i = 0; i < 8; ++i) { + if ((i >= 2) && (i < 6)) { + assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL)); + } else { + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL)); + } + } + rec_idx = ht->hlists[2].first; + count = 0; + while (rec_idx != UINT32_MAX) { + rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx); + rec_idx = rec->next; + assert_int_equal(rec->hash, 2); + count++; + } + assert_int_equal(count, 4); + + i = 4; + assert_int_equal(lyht_remove(ht, &i, 2), 0); + + rec = lyht_get_rec(ht->recs, ht->rec_size, i); + + i = 2; + assert_int_equal(lyht_remove(ht, &i, 2), 0); + + /* check all records */ + for (i = 0; i < 8; ++i) { + if (i == 2) { + assert_int_not_equal(UINT32_MAX, ht->hlists[i].first); + } else { + assert_int_equal(UINT32_MAX, ht->hlists[i].first); + } + } + for (i = 0; i < 8; ++i) { + if ((i == 3) || (i == 5)) { + assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL)); + } else { + assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL)); + } + } + rec_idx = ht->hlists[2].first; + count = 0; + while (rec_idx != UINT32_MAX) { + rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx); + rec_idx = rec->next; + assert_int_equal(rec->hash, 2); + count++; + } + assert_int_equal(count, 2); + + for (i = 0; i < 8; ++i) { + if ((i == 3) || (i == 5)) { + assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS); + } else { + assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND); + } + } + + i = 3; + assert_int_equal(lyht_remove(ht, &i, 2), 0); + i = 5; + assert_int_equal(lyht_remove(ht, &i, 2), 0); + + /* check all records */ + for (i = 0; i < 8; ++i) { + assert_int_equal(UINT32_MAX, ht->hlists[i].first); + } + + lyht_free(ht, NULL); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_invalid_arguments), + UTEST(test_dict_hit), + UTEST(test_ht_basic), + UTEST(test_ht_resize), + UTEST(test_ht_collisions), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_inout.c b/tests/utests/basic/test_inout.c new file mode 100644 index 0000000..af8cc81 --- /dev/null +++ b/tests/utests/basic/test_inout.c @@ -0,0 +1,407 @@ +/** + * @file test_inout.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for input and output handlers functions + * + * Copyright (c) 2020 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "in.h" +#include "log.h" +#include "ly_common.h" +#include "out.h" + +#define TEST_INPUT_FILE TESTS_BIN "/libyang_test_input" +#define TEST_OUTPUT_FILE TESTS_BIN "/libyang_test_output" +#define TEST_OUTPUT_FILE2 TESTS_BIN "/libyang_test_output2" + +static int +setup_files(void **state) +{ + int fd; + + UTEST_SETUP; + + /* create input */ + fd = open(TEST_INPUT_FILE, O_CREAT | O_WRONLY, 00600); + if (fd == -1) { + return 1; + } + + /* write something */ + if (write(fd, "data", 4) != 4) { + return 1; + } + close(fd); + + /* create output */ + fd = open(TEST_OUTPUT_FILE, O_CREAT | O_RDONLY, 00600); + if (fd == -1) { + return 1; + } + close(fd); + + /* create output2 */ + fd = open(TEST_OUTPUT_FILE2, O_CREAT | O_RDONLY, 00600); + if (fd == -1) { + return 1; + } + close(fd); + + return 0; +} + +static int +teardown_files(void **state) +{ + unlink(TEST_INPUT_FILE); + unlink(TEST_OUTPUT_FILE); + unlink(TEST_OUTPUT_FILE2); + + UTEST_TEARDOWN; + return 0; +} + +static void +test_input_mem(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + char *str1 = "a", *str2 = "b"; + + assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument str (ly_in_new_memory())."); + assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL)); + CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_memory())."); + assert_null(ly_in_memory(NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument in (ly_in_memory())."); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in)); + assert_int_equal(LY_IN_MEMORY, ly_in_type(in)); + assert_ptr_equal(str1, ly_in_memory(in, str2)); + assert_ptr_equal(str2, ly_in_memory(in, NULL)); + assert_ptr_equal(str2, ly_in_memory(in, NULL)); + ly_in_free(in, 0); +} + +static void +test_input_fd(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + int fd1, fd2; + struct stat statbuf; + + assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL)); + CHECK_LOG_LASTMSG("Invalid argument fd >= 0 (ly_in_new_fd())."); + assert_int_equal(-1, ly_in_fd(NULL, -1)); + CHECK_LOG_LASTMSG("Invalid argument in (ly_in_fd())."); + + assert_int_not_equal(-1, fd1 = open(TEST_INPUT_FILE, O_RDONLY)); + assert_int_not_equal(-1, fd2 = open(TEST_INPUT_FILE, O_RDONLY)); + + assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL)); + CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_fd())."); + + assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in)); + assert_int_equal(LY_IN_FD, ly_in_type(in)); + assert_ptr_equal(fd1, ly_in_fd(in, fd2)); + assert_ptr_equal(fd2, ly_in_fd(in, -1)); + assert_ptr_equal(fd2, ly_in_fd(in, -1)); + ly_in_free(in, 1); + /* fd1 is still open */ + assert_int_equal(0, fstat(fd1, &statbuf)); + close(fd1); +#ifndef _WIN32 + /* But fd2 was closed by ly_in_free(). This results in an "invalid handler" on Windows. */ + errno = 0; + assert_int_equal(-1, fstat(fd2, &statbuf)); + assert_int_equal(errno, EBADF); +#endif +} + +static void +test_input_file(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + FILE *f1 = NULL, *f2 = NULL; + + assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL)); + assert_null(ly_in_file(NULL, NULL)); + + assert_non_null(f1 = fopen(TEST_INPUT_FILE, "rb")); + assert_non_null(f2 = fopen(TEST_INPUT_FILE, "rb")); + + assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL)); + + assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in)); + assert_int_equal(LY_IN_FILE, ly_in_type(in)); + assert_ptr_equal(f1, ly_in_file(in, f2)); + assert_ptr_equal(f2, ly_in_file(in, NULL)); + assert_ptr_equal(f2, ly_in_file(in, NULL)); + ly_in_free(in, 1); + /* f1 is still open */ + assert_int_not_equal(-1, fileno(f1)); + fclose(f1); + /* but f2 was closed by ly_in_free() */ +} + +static void +test_input_filepath(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + const char *path1 = TEST_INPUT_FILE, *path2 = TEST_INPUT_FILE; + + assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL)); + assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL)); + assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0)); + + assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in)); + assert_int_equal(LY_IN_FILEPATH, ly_in_type(in)); + assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0)); + assert_string_equal(path2, ly_in_filepath(in, NULL, 0)); + ly_in_free(in, 0); +} + +static void +test_output_mem(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + char *buf1 = NULL, *buf2 = NULL; + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out)); + assert_int_equal(LY_OUT_MEMORY, ly_out_type(out)); + ly_write(out, "test", 4); + assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0)); + assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0)); + assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1))); + ly_out_free(out, NULL, 0); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + assert_string_equal("test print", buf1); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + assert_string_equal("rewrite", buf1); + ly_out_free(out, NULL, 1); +} + +static void +test_output_fd(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + int fd1, fd2; + char buf[31] = {0}; + + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + assert_int_equal(LY_OUT_FD, ly_out_type(out)); + assert_ptr_equal(fd1, ly_out_fd(out, fd2)); + assert_ptr_equal(fd2, ly_out_fd(out, -1)); + assert_ptr_equal(fd2, ly_out_fd(out, fd1)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, close(fd2)); + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + /* truncate file to start with no data */ + assert_int_equal(0, ftruncate(fd1, 0)); + + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_int_equal(10, read(fd2, buf, 30)); + assert_string_equal("test print", buf); + assert_int_equal(0, lseek(fd2, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_int_equal(8, read(fd2, buf, 30)); + assert_string_equal("rewrite", buf); + + close(fd2); + ly_out_free(out, NULL, 1); +} + +static void +test_output_file(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + FILE *f1, *f2; + char buf[31] = {0}; + + assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb")); + assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "wb")); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + assert_int_equal(LY_OUT_FILE, ly_out_type(out)); + assert_ptr_equal(f1, ly_out_file(out, f2)); + assert_ptr_equal(f2, ly_out_file(out, NULL)); + assert_ptr_equal(f2, ly_out_file(out, f1)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, fclose(f2)); + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb")); + assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "rb")); + + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f2)); + assert_string_equal("test print", buf); + assert_int_equal(0, fseek(f2, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f2)); + assert_string_equal("rewrite", buf); + + fclose(f2); + ly_out_free(out, NULL, 1); +} + +static void +test_output_filepath(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + FILE *f1; + char buf[31] = {0}; + const char *fp1 = TEST_OUTPUT_FILE; + const char *fp2 = TEST_OUTPUT_FILE2; + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out)); + assert_ptr_equal(NULL, ly_out_filepath(out, fp2)); + assert_string_equal(fp2, ly_out_filepath(out, NULL)); + assert_ptr_equal(NULL, ly_out_filepath(out, fp1)); + ly_out_free(out, NULL, 0); + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_non_null(f1 = fopen(fp1, "rb")); + + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f1)); + assert_string_equal("test print", buf); + assert_int_equal(0, fseek(f1, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f1)); + assert_string_equal("rewrite", buf); + + fclose(f1); + ly_out_free(out, NULL, 1); +} + +static ssize_t +write_clb(void *user_data, const void *buf, size_t count) +{ + return write((uintptr_t)user_data, buf, count); +} + +void +close_clb(void *arg) +{ + close((uintptr_t)arg); +} + +static void +test_output_clb(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + int fd1, fd2; + char buf[31] = {0}; + + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR)); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out)); + assert_ptr_equal(fd1, ly_out_clb_arg(out, (void *)(intptr_t)fd2)); + assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL)); + assert_ptr_equal(fd2, ly_out_clb_arg(out, (void *)(intptr_t)fd1)); + assert_ptr_equal(write_clb, ly_out_clb(out, write_clb)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, close(fd2)); + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + ly_out_free(out, close_clb, 0); + + /* writing data */ + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR)); + /* truncate file to start with no data */ + assert_int_equal(0, ftruncate(fd1, 0)); + + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + assert_int_equal(10, read(fd2, buf, 30)); + assert_string_equal("test print", buf); + + close(fd2); + ly_out_free(out, close_clb, 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_input_mem), + UTEST(test_input_fd, setup_files, teardown_files), + UTEST(test_input_file, setup_files, teardown_files), + UTEST(test_input_filepath, setup_files, teardown_files), + UTEST(test_output_mem), + UTEST(test_output_fd, setup_files, teardown_files), + UTEST(test_output_file, setup_files, teardown_files), + UTEST(test_output_filepath, setup_files, teardown_files), + UTEST(test_output_clb, setup_files, teardown_files), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c new file mode 100644 index 0000000..a760b38 --- /dev/null +++ b/tests/utests/basic/test_json.c @@ -0,0 +1,773 @@ +/** + * @file test_json.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for a generic JSON parser + * + * Copyright (c) 2020 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 "context.h" +#include "in_internal.h" +#include "json.h" +static void +test_general(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Empty JSON file.", NULL, 1); + + str = " \n\t \n"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Empty JSON file.", NULL, 3); + + /* constant values */ + str = "true"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + str = "false"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + str = "null"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + ly_in_free(in, 0); +} + +static void +test_number(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in; + const char *str; + + /* simple value */ + str = "11"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("11", jsonctx->value); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* fraction number */ + str = "37.7668"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("37.7668", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* negative number */ + str = "-122.3959"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-122.3959", jsonctx->value); + assert_int_equal(9, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* integer, positive exponent */ + str = "550E3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("550000", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-550E3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-550000", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* integer, negative exponent */ + str = "1E-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.1", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "15E-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("1.5", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-15E-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-1.5", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "16E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.16", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-16E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-0.16", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "17E-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.017", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-17E-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-0.017", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("210", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-4"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("2.1", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-7"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.0021", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* decimal number, positive exponent */ + str = "5.087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("50.87", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-5.087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-50.87", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.087E5"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("508700", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "59.1e+1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("591", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.05087", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.5087", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E6"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("5087", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.05087E6"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("50870", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E8"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("508700", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* decimal number, negative exponent */ + str = "35.94e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("3.594", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-35.94e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("-3.594", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "35.94e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.3594", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "35.94e-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.03594", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.3594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.03594", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.03594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.003594", jsonctx->value); + assert_int_equal(8, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.003594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.0003594", jsonctx->value); + assert_int_equal(9, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.3594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.003594", jsonctx->value); + assert_int_equal(8, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.03594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.0003594", jsonctx->value); + assert_int_equal(9, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.003594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.00003594", jsonctx->value); + assert_int_equal(10, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* zero */ + str = "0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_true(jsonctx->value[0] == '0'); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_true(jsonctx->value[0] == '-'); + assert_true(jsonctx->value[1] == '0'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "94E0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_true(jsonctx->value[0] == '9'); + assert_true(jsonctx->value[1] == '4'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_true(jsonctx->value[0] == '0'); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-0E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_true(jsonctx->value[0] == '-'); + assert_true(jsonctx->value[1] == '0'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.320e+2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("532", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.320e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx)); + assert_string_equal("0.532", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* various invalid inputs */ + str = "-x"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", NULL, 1); + + str = " -"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + str = "--1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Invalid character in JSON Number value (\"-\").", NULL, 1); + + str = "+1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Invalid character sequence \"+1\", expected a JSON value.", NULL, 1); + + str = " 1.x "; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", NULL, 1); + + str = "1."; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + str = " 1eo "; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Invalid character in JSON Number value (\"o\").", NULL, 1); + + str = "1e"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + str = "1E1000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", NULL, 1); + + str = "1e9999999999999999999"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", NULL, 1); + + str = "1.1e66000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e66000).", NULL, 1); + + str = "1.1e-66000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e-66000).", NULL, 1); + + ly_in_free(in, 0); +} + +/* now string is tested in file ./tests/utests/types/string.c */ +static void +test_string(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in = NULL; + const char *str; + + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + + /* unterminated string */ + str = "\"unterminated string"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + CHECK_LOG_CTX("Missing quotation-mark at the end of a JSON string.", NULL, 1); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + ly_in_free(in, 0); +} + +static void +test_object(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = " { } "; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* simple value */ + str = "{\"name\" : \"Radek\"}"; + assert_non_null(ly_in_memory(in, str)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_ptr_equal(&str[2], jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx)); + assert_string_equal("Radek\"}", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* two values */ + str = "{\"smart\" : true,\"handsom\":false}"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_string_equal("smart\" : true,\"handsom\":false}", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NEXT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_string_equal("handsom\":false}", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* inherited objects */ + str = "{\"person\" : {\"name\":\"Radek\"}}"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_string_equal("person\" : {\"name\":\"Radek\"}}", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_string_equal("name\":\"Radek\"}}", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx)); + assert_string_equal("Radek\"}}", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* unquoted string */ + str = "{ unquoted : \"data\"}"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL)); + CHECK_LOG_CTX("Invalid character sequence \"unquoted : \"data\"}\", expected a JSON object name.", NULL, 1); + lyjson_ctx_free(jsonctx); + + ly_in_free(in, 0); +} + +static void +test_array(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = " [ ] "; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* simple value */ + str = "[ null]"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx)); + assert_null(jsonctx->value); + assert_int_equal(0, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* two values */ + str = "[{\"a\":null},\"x\"]"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx)); + assert_null(jsonctx->value); + assert_int_equal(0, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx)); + assert_string_equal("a\":null},\"x\"]", jsonctx->value); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_ARRAY_NEXT, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx)); + assert_string_equal("x\"]", jsonctx->value); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx)); + + assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL)); + assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx)); + lyjson_ctx_free(jsonctx); + + /* new line is allowed only as escaped character in JSON */ + str = "[ , null]"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx)); + assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL)); + CHECK_LOG_CTX("Invalid character sequence \", null]\", expected a JSON value.", NULL, 1); + lyjson_ctx_free(jsonctx); + + ly_in_free(in, 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_general), + UTEST(test_number), + UTEST(test_string), + UTEST(test_object), + UTEST(test_array), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_plugins.c b/tests/utests/basic/test_plugins.c new file mode 100644 index 0000000..df7523a --- /dev/null +++ b/tests/utests/basic/test_plugins.c @@ -0,0 +1,153 @@ +/** + * @file test_plugins.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from set.c + * + * Copyright (c) 2018 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 <stdlib.h> +#include <string.h> + +#include "ly_config.h" +#include "plugins.h" +#include "plugins_internal.h" + +const char *simple = "module libyang-plugins-simple {" + " namespace urn:libyang:tests:plugins:simple;" + " prefix s;" + " typedef note { type string; }" + " extension hint { argument value; }" + " leaf test {" + " type s:note {length 255;}" + " s:hint \"some hint here\";" + " }" + "}"; + +static void +test_add_invalid(void **state) +{ + (void)state; + assert_int_equal(LY_ESYS, lyplg_add(TESTS_BIN "/plugins/plugin_does_not_exist" LYPLG_SUFFIX)); +} + +static void +test_add_simple(void **state) +{ + struct lys_module *mod; + struct lysc_node_leaf *leaf; + struct lyplg_ext_record *record_e; + struct lyplg_type *plugin_t; + + assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX)); + + UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod); + + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_int_equal(LYS_LEAF, leaf->nodetype); + + assert_non_null(plugin_t = lyplg_type_plugin_find(NULL, "libyang-plugins-simple", NULL, "note")); + assert_string_equal("ly2 simple test v1", plugin_t->id); + assert_ptr_equal(leaf->type->plugin, plugin_t); + + assert_int_equal(1, LY_ARRAY_COUNT(leaf->exts)); + assert_non_null(record_e = lyplg_ext_record_find(NULL, "libyang-plugins-simple", NULL, "hint")); + assert_string_equal("ly2 simple test v1", record_e->plugin.id); + assert_ptr_equal(leaf->exts[0].def->plugin, &record_e->plugin); + + /* the second loading of the same plugin - still success */ + assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX)); +} + +static void +test_not_implemented(void **state) +{ + struct lys_module *mod; + struct lyd_node *tree; + const char *schema = "module libyang-plugins-unknown {" + " namespace urn:libyang:tests:plugins:unknown;" + " prefix u;" + " extension myext;" + " typedef mytype { type string;}" + " leaf test {" + " u:myext;" + " type mytype;" + " }" + "}"; + const char *data = "<test xmlns=\"urn:libyang:tests:plugins:unknown\">xxx</test>"; + char *printed = NULL; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + free(printed); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LOG_CTX(NULL, NULL, 0); + + lyd_free_all(tree); +} + +static LY_ERR +parse_clb(struct lysp_ctx *UNUSED(pctx), struct lysp_ext_instance *ext) +{ + struct lysp_node_leaf *leaf; + + leaf = (struct lysp_node_leaf *)ext->parent; + leaf->flags |= LYS_STATUS_OBSLT; + return LY_SUCCESS; +} + +struct lyplg_ext_record memory_recs[] = { + { + .module = "libyang-plugins-simple", + .revision = NULL, + .name = "hint", + + .plugin.id = "memory-plugin-v1", + .plugin.parse = parse_clb, + .plugin.compile = NULL, + .plugin.printer_info = NULL, + .plugin.node = NULL, + .plugin.snode = NULL, + .plugin.validate = NULL, + .plugin.pfree = NULL, + .plugin.cfree = NULL + }, + {0} /* terminating zeroed item */ +}; + +static void +test_simple_from_memory(void **state) +{ + struct lys_module *mod; + struct lysc_node_leaf *leaf; + + lyplg_add_extension_plugin(UTEST_LYCTX, LYPLG_EXT_API_VERSION, memory_recs); + UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod); + + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_true(leaf->flags & LYS_STATUS_OBSLT); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_add_invalid), + UTEST(test_add_simple), + UTEST(test_not_implemented), + UTEST(test_simple_from_memory), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_set.c b/tests/utests/basic/test_set.c new file mode 100644 index 0000000..04ea6bc --- /dev/null +++ b/tests/utests/basic/test_set.c @@ -0,0 +1,276 @@ +/** + * @file test_set.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from set.c + * + * Copyright (c) 2018 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 _POSIX_C_SOURCE 200809L /* strdup */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <stdlib.h> +#include <string.h> + +#include "set.h" + +static void +test_basics(void **UNUSED(state)) +{ + struct ly_set *set; + char *str; + unsigned int u; + void *ptr; + uint32_t index; + + /* creation - everything is empty */ + assert_int_equal(LY_SUCCESS, ly_set_new(&set)); + assert_non_null(set); + assert_int_equal(0, set->count); + assert_int_equal(0, set->size); + assert_null(set->objs); + + /* add a testing object */ + str = strdup("test string"); + assert_non_null(str); + + assert_int_equal(LY_SUCCESS, ly_set_add(set, str, 0, NULL)); + assert_int_not_equal(0, set->size); + assert_int_equal(1, set->count); + assert_non_null(set->objs); + assert_non_null(set->objs[0]); + + /* check the presence of the testing data */ + assert_int_equal(1, ly_set_contains(set, str, &index)); + assert_int_equal(0, index); + assert_int_equal(0, ly_set_contains(set, str - 1, NULL)); + + /* remove data, but keep the set */ + u = set->size; + ptr = set->objs; + ly_set_clean(set, free); + assert_int_equal(0, set->count); + assert_int_equal(u, set->size); + assert_ptr_equal(ptr, set->objs); + + /* remove buffer, but keep the set object */ + ly_set_erase(set, NULL); + assert_int_equal(0, set->count); + assert_int_equal(0, set->size); + assert_ptr_equal(NULL, set->objs); + + /* final cleanup */ + ly_set_free(set, NULL); +} + +static void +test_inval(void **UNUSED(state)) +{ + struct ly_set set = {0}; + + assert_int_equal(LY_EINVAL, ly_set_dup(NULL, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument set (ly_set_dup())."); + + assert_int_equal(LY_EINVAL, ly_set_add(NULL, NULL, 0, NULL)); + CHECK_LOG_LASTMSG("Invalid argument set (ly_set_add())."); + + assert_int_equal(LY_EINVAL, ly_set_merge(NULL, NULL, 0, NULL)); + CHECK_LOG_LASTMSG("Invalid argument trg (ly_set_merge())."); + assert_int_equal(LY_SUCCESS, ly_set_merge(&set, NULL, 0, NULL)); + + assert_int_equal(LY_EINVAL, ly_set_rm_index(NULL, 0, NULL)); + CHECK_LOG_LASTMSG("Invalid argument set (ly_set_rm_index())."); + assert_int_equal(LY_EINVAL, ly_set_rm_index(&set, 1, NULL)); + CHECK_LOG_LASTMSG("Invalid argument index (ly_set_rm_index())."); + + assert_int_equal(LY_EINVAL, ly_set_rm(NULL, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument set (ly_set_rm())."); + assert_int_equal(LY_EINVAL, ly_set_rm(&set, NULL, NULL)); + CHECK_LOG_LASTMSG("Invalid argument object (ly_set_rm())."); + assert_int_equal(LY_EINVAL, ly_set_rm(&set, &set, NULL)); + CHECK_LOG_LASTMSG("Invalid argument object (ly_set_rm())."); +} + +static void +test_duplication(void **UNUSED(state)) +{ + struct ly_set *orig, *new; + char *str; + uint32_t index; + + assert_int_equal(LY_SUCCESS, ly_set_new(&orig)); + assert_non_null(orig); + + /* add a testing object */ + str = strdup("test string"); + assert_non_null(str); + assert_int_equal(LY_SUCCESS, ly_set_add(orig, str, 0, &index)); + assert_int_equal(0, index); + + /* duplicate the set - without duplicator, so the new set will point to the same string */ + assert_int_equal(LY_SUCCESS, ly_set_dup(orig, NULL, &new)); + assert_non_null(new); + assert_ptr_not_equal(orig, new); + assert_int_equal(orig->count, new->count); + assert_ptr_equal(orig->objs[0], new->objs[0]); + + ly_set_free(new, NULL); + + /* duplicate the set - with duplicator, so the new set will point to a different buffer with the same content */ + assert_int_equal(LY_SUCCESS, ly_set_dup(orig, (void *(*)(const void *))strdup, &new)); + assert_non_null(new); + assert_ptr_not_equal(orig, new); + assert_int_equal(orig->count, new->count); + assert_ptr_not_equal(orig->objs[0], new->objs[0]); + assert_string_equal(orig->objs[0], new->objs[0]); + + /* cleanup */ + ly_set_free(new, free); + ly_set_free(orig, free); +} + +static void +test_add(void **UNUSED(state)) +{ + uint32_t u, index; + char *str = "test string"; + struct ly_set set; + + memset(&set, 0, sizeof set); + + /* add a testing object */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index)); + assert_int_equal(0, index); + + /* test avoiding data duplicities */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index)); + assert_int_equal(0, index); + assert_int_equal(1, set.count); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index)); + assert_int_equal(1, index); + assert_int_equal(2, set.count); + + /* test array resizing */ + u = set.size; + for (uint32_t expected_index = 2; expected_index <= u; ++expected_index) { + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index)); + assert_int_equal(expected_index, index); + } + assert_true(u != set.size); + + /* cleanup */ + ly_set_erase(&set, NULL); +} + +static void +test_merge(void **UNUSED(state)) +{ + char *str1, *str2; + struct ly_set one, two; + + memset(&one, 0, sizeof one); + memset(&two, 0, sizeof two); + + str1 = strdup("string1"); + str2 = strdup("string2"); + + /* fill first set + * - str1 is the same as in two, so it must not be freed! */ + assert_int_equal(LY_SUCCESS, ly_set_add(&one, str1, 0, NULL)); + + /* fill second set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&two, str1, 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&two, str2, 0, NULL)); + + /* merge with checking duplicities - only one item is added into one; + * also without duplicating data, so it must not be freed at the end */ + assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 0, NULL)); + assert_int_equal(2, one.count); + assert_ptr_equal(one.objs[1], two.objs[1]); + + /* clean and re-fill one (now duplicating str1, to allow testing duplicator) */ + ly_set_clean(&one, NULL); + assert_int_equal(LY_SUCCESS, ly_set_add(&one, strdup(str1), 0, NULL)); + + /* merge without checking duplicities - two items are added into one; + * here also with duplicator */ + assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 1, (void *(*)(const void *))strdup)); + assert_int_equal(3, one.count); + assert_ptr_not_equal(one.objs[1], two.objs[0]); + assert_string_equal(one.objs[1], two.objs[0]); + + /* cleanup */ + ly_set_erase(&one, free); + ly_set_erase(&two, free); +} + +static void +test_rm(void **UNUSED(state)) +{ + char *str1, *str2, *str3; + struct ly_set set; + + memset(&set, 0, sizeof set); + + /* fill the set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string1", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, strdup("string2"), 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string3", 0, NULL)); + + /* remove by index ... */ + /* ... in the middle ... */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, free)); + assert_int_equal(2, set.count); + assert_string_not_equal("string2", set.objs[0]); + assert_string_not_equal("string2", set.objs[1]); + /* ... last .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, NULL)); + assert_int_equal(1, set.count); + assert_string_not_equal("string3", set.objs[0]); + /* ... first .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 0, NULL)); + assert_int_equal(0, set.count); + + /* fill the set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str1 = "string1", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str2 = "string2", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str3 = strdup("string3"), 0, NULL)); + + /* remove by pointer ... */ + /* ... in the middle ... */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str2, NULL)); + assert_int_equal(2, set.count); + assert_string_not_equal("string2", set.objs[0]); + assert_string_not_equal("string2", set.objs[1]); + /* ... last (with destructor) .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str3, free)); + assert_int_equal(1, set.count); + assert_string_not_equal("string3", set.objs[0]); + /* ... first .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str1, NULL)); + assert_int_equal(0, set.count); + + /* cleanup */ + ly_set_erase(&set, NULL); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_basics), + UTEST(test_duplication), + UTEST(test_add), + UTEST(test_merge), + UTEST(test_rm), + UTEST(test_inval), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_xml.c b/tests/utests/basic/test_xml.c new file mode 100644 index 0000000..25aed03 --- /dev/null +++ b/tests/utests/basic/test_xml.c @@ -0,0 +1,689 @@ +/** + * @file test_xml.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from xml.c + * + * Copyright (c) 2018 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" + +#define _POSIX_C_SOURCE 200809L /* strdup */ + +#include <string.h> + +#include "context.h" +#include "in_internal.h" +#include "xml.h" + +LY_ERR lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri); + +static void +test_element(void **state) +{ + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* end element */ + str = "</element>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Stray closing element tag (\"element\").", NULL, 1); + ly_in_free(in, 0); + + /* no element */ + str = "no data present"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"no data present\", expected element tag start ('<').", NULL, 1); + ly_in_free(in, 0); + + /* not supported DOCTYPE */ + str = "<!DOCTYPE greeting SYSTEM \"hello.dtd\"><greeting/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Document Type Declaration not supported.", NULL, 1); + ly_in_free(in, 0); + + /* invalid XML */ + str = "<!NONSENSE/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Unknown XML section \"<!NONSENSE/>\".", NULL, 1); + ly_in_free(in, 0); + + /* namespace ambiguity */ + str = "<element xmlns=\"urn1\" xmlns=\"urn2\"/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Duplicate default XML namespaces \"urn1\" and \"urn2\".", NULL, 1); + ly_in_free(in, 0); + + /* prefix duplicate */ + str = "<element xmlns:a=\"urn1\" xmlns:a=\"urn2\"/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Duplicate XML NS prefix \"a\" used for namespaces \"urn1\" and \"urn2\".", NULL, 1); + ly_in_free(in, 0); + + /* unqualified element */ + str = " < element/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_null(xmlctx->prefix); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_int_equal(1, xmlctx->elements.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("", xmlctx->value, xmlctx->value_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* element with attribute */ + str = " < element attr=\'x\'/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + assert_int_equal(1, xmlctx->elements.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_int_equal(1, xmlctx->elements.count); + assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_int_equal(0, xmlctx->elements.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* headers and comments */ + str = "<?xml version=\"1.0\"?> <!-- comment --> <?TEST xxx?> <element/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + assert_int_equal(1, xmlctx->elements.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* separate opening and closing tags, neamespaced parsed internally */ + str = "<element xmlns=\"urn\"></element>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + assert_int_equal(1, xmlctx->elements.count); + assert_int_equal(1, xmlctx->ns.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_int_equal(0, xmlctx->elements.count); + assert_int_equal(0, xmlctx->ns.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* qualified element */ + str = " < yin:element/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* non-matching closing tag */ + str = "<yin:element xmlns=\"urn\"></element>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len)); + assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len)); + assert_int_equal(1, xmlctx->elements.count); + assert_int_equal(1, xmlctx->ns.count); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Opening (\"yin:element\") and closing (\"element\") elements tag mismatch.", NULL, 1); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* invalid closing tag */ + str = "<yin:element xmlns=\"urn\"></yin:element/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"/>\", expected element tag termination ('>').", NULL, 1); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* UTF8 characters */ + str = "<𠜎€𠜎Øn:𠜎€𠜎Øn/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->name, xmlctx->name_len)); + assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->prefix, xmlctx->prefix_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* invalid UTF-8 characters */ + str = "<¢:element>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Identifier \"¢:element>\" starts with an invalid character.", NULL, 1); + ly_in_free(in, 0); + + str = "<yin:câelement>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"âelement>\", expected element tag end ('>' or '/>') or an attribute.", NULL, 1); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* mixed content */ + str = "<a>text <b>x</b></a>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("text ", xmlctx->value, xmlctx->value_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("b", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len)); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* tag mismatch */ + str = "<a>text</b>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("text", xmlctx->value, xmlctx->value_len)); + + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Opening (\"a\") and closing (\"b\") elements tag mismatch.", NULL, 1); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_attribute(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + struct lyxml_ns *ns; + + /* not an attribute */ + str = "<e unknown/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"/>\", expected '='.", NULL, 1); + ly_in_free(in, 0); + + str = "<e xxx=/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"/>\", expected either single or double quotation mark.", NULL, 1); + ly_in_free(in, 0); + + str = "<e xxx\n = yyy/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"yyy/>\", expected either single or double quotation mark.", NULL, 2); + ly_in_free(in, 0); + + /* valid attribute */ + str = "<e attr=\"val\""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len)); + assert_null(xmlctx->prefix); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("val", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 0); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* valid namespace with prefix */ + str = "<e xmlns:nc\n = \'urn\'/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_int_equal(1, xmlctx->ns.count); + ns = (struct lyxml_ns *)xmlctx->ns.objs[0]; + assert_string_equal(ns->prefix, "nc"); + assert_string_equal(ns->uri, "urn"); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_text(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + + /* empty attribute value */ + str = "<e a=\"\""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty value but in single quotes */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty element content - only formating before defining child */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(">\n <y>", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEMENT; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("\n ", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty element content is invalid - missing content terminating character < */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEM_CONTENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("xxx", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEM_CONTENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"xxx\", expected element tag start ('<').", NULL, 1); + ly_in_free(in, 0); + + lyxml_ctx_free(xmlctx); + ly_log_location_revert(0, 0, 0, 4); + + /* valid strings */ + str = "<a>€𠜎Øn \n<&"'> ROK</a>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("€𠜎Øn \n<&\"\'> ROK", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 1); + free((char *)xmlctx->value); + ly_in_free(in, 0); + + /* test using n-bytes UTF8 hexadecimal code points */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'$¢€𐍈\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("$¢€ðˆ", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 1); + ly_in_free(in, 0); + + /* CDATA value */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("> <![CDATA[ special non-escaped chars <>&\"' ]]> </a>", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTR_CONTENT; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp(" special non-escaped chars <>&\"' ", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 1); + free((char *)xmlctx->value); + ly_in_free(in, 0); + + /* invalid characters in string */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'R\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"'\", expected ;.", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"R\"", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"\"\", expected ;.", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&nonsense;\"", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Entity reference \"&nonsense;\" not supported, only predefined references allowed.", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(">&#o122;", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEMENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"&#o122;\".", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x00000006).", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000fdd0).", NULL, 1); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + ly_log_location(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000ffff).", NULL, 1); + ly_in_free(in, 0); + + lyxml_ctx_free(xmlctx); + ly_log_location_revert(0, 0, 0, 9); +} + +static void +test_ns(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const struct lyxml_ns *ns; + + /* opening element1 */ + str = "<element1/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + + /* processing namespace definitions */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default"))); + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc1"))); + /* simulate adding open element2 into context */ + xmlctx->elements.count++; + /* processing namespace definitions */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc2"))); + assert_int_equal(3, xmlctx->ns.count); + assert_int_not_equal(0, xmlctx->ns.size); + + ns = lyxml_ns_get(&xmlctx->ns, NULL, 0); + assert_non_null(ns); + assert_null(ns->prefix); + assert_string_equal("urn:default", ns->uri); + + ns = lyxml_ns_get(&xmlctx->ns, "nc", 2); + assert_non_null(ns); + assert_string_equal("nc", ns->prefix); + assert_string_equal("urn:nc2", ns->uri); + + /* simulate closing element2 */ + xmlctx->elements.count--; + lyxml_ns_rm(xmlctx); + assert_int_equal(2, xmlctx->ns.count); + + ns = lyxml_ns_get(&xmlctx->ns, "nc", 2); + assert_non_null(ns); + assert_string_equal("nc", ns->prefix); + assert_string_equal("urn:nc1", ns->uri); + + /* close element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(0, xmlctx->ns.count); + + assert_null(lyxml_ns_get(&xmlctx->ns, "nc", 2)); + assert_null(lyxml_ns_get(&xmlctx->ns, NULL, 0)); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_ns2(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + + /* opening element1 */ + str = "<element1/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + + /* default namespace defined in parent element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default"))); + assert_int_equal(1, xmlctx->ns.count); + /* going into child element1 */ + /* simulate adding open element1 into context */ + xmlctx->elements.count++; + /* no namespace defined, going out (first, simulate closing of so far open element) */ + xmlctx->elements.count--; + lyxml_ns_rm(xmlctx); + assert_int_equal(1, xmlctx->ns.count); + + /* nothing else, going out of the parent element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(0, xmlctx->ns.count); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_simple_xml(void **state) +{ + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const char *test_input = "<elem1 attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>"; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(test_input, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_string_equal(xmlctx->in->current, "=\"value\"> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "<elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_string_equal(xmlctx->in->current, "=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, " /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "/> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_string_equal(xmlctx->in->current, " </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_string_equal(xmlctx->in->current, ""); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + assert_string_equal(xmlctx->in->current, ""); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_element), + UTEST(test_attribute), + UTEST(test_text), + UTEST(test_ns), + UTEST(test_ns2), + UTEST(test_simple_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c new file mode 100644 index 0000000..7a2785b --- /dev/null +++ b/tests/utests/basic/test_xpath.c @@ -0,0 +1,1263 @@ +/** + * @file test_xpath.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for XPath evaluation + * + * Copyright (c) 2020 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 <string.h> + +#include "context.h" +#include "parser_data.h" +#include "set.h" +#include "tests_config.h" +#include "tree_data.h" +#include "tree_schema.h" + +const char *schema_a = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " identity id_a;\n" + " identity id_b {\n" + " base id_a;\n" + " }\n" + " identity id_c {\n" + " base id_b;\n" + " }\n" + "\n" + " list l1 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " }\n" + " leaf foo {\n" + " type string;\n" + " }\n" + " leaf foo2 {\n" + " type uint8;\n" + " }\n" + " leaf foo3 {\n" + " type identityref {\n" + " base id_a;\n" + " }\n" + " }\n" + " leaf foo4 {\n" + " type decimal64 {\n" + " fraction-digits 5;\n" + " }\n" + " }\n" + " container c {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " list ll {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " list ll {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " rpc r {\n" + " input {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " output {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}"; + +static int +setup(void **state) +{ + UTEST_SETUP; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL); + lys_parse_path(UTEST_LYCTX, TESTS_DIR_MODULES_YANG "/ietf-interfaces@2014-05-08.yang", LYS_IN_YANG, NULL); + + return 0; +} + +static void +test_predicate(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a1</a>" + " <b>b1</b>" + " <c>c1</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a2</a>" + " <b>b2</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a3</a>" + " <b>b3</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a4</a>" + " <b>b4</b>" + " <c>c4</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a5</a>" + " <b>b5</b>" + " <c>c5</c>" + "</l1>" + "<c xmlns=\"urn:tests:a\">" + " <x>key2</x>" + " <ll>" + " <a>key1</a>" + " <ll>" + " <a>key11</a>" + " <b>val11</b>" + " </ll>" + " <ll>" + " <a>key12</a>" + " <b>val12</b>" + " </ll>" + " <ll>" + " <a>key13</a>" + " <b>val13</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key2</a>" + " <ll>" + " <a>key21</a>" + " <b>val21</b>" + " </ll>" + " <ll>" + " <a>key22</a>" + " <b>val22</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key3</a>" + " <ll>" + " <a>key31</a>" + " <b>val31</b>" + " </ll>" + " <ll>" + " <a>key32</a>" + " <b>val32</b>" + " </ll>" + " </ll>" + "</c>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* predicate after number */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/foo2[4[3 = 3]]", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + /* reverse axis */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/child::ll[2]/preceding::ll[3]", &set)); + assert_int_equal(1, set->count); + assert_string_equal("key11", lyd_get_value(lyd_child(set->dnodes[0]))); + ly_set_free(set, NULL); + + /* special predicate evaluated using hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a=concat('a', '1')][b=substring('ab1',2)]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=../x]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* cannot use hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=substring(ll/a,1,4)]", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* nested predicate */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/a:ll[a:a=string(/a:l1[a:a='foo']/a:a)]/a:a", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_union(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* Predicate for operand. */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1[c[../a = 'a1'] | c]/a", &set)); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_invalid(void **state) +{ + const char *data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:foo2[.=]", &set)); + assert_null(set); + CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", NULL, 0); + + assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:", &set)); + assert_null(set); + CHECK_LOG_CTX("Invalid character 'a'[2] of expression '/a:'.", NULL, 0); + + lyd_free_all(tree); +} + +static void +test_hash(void **state) +{ + const char *data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>\n" + "<foo xmlns=\"urn:tests:a\">foo value</foo>\n" + "<c xmlns=\"urn:tests:a\">\n" + " <x>val</x>\n" + " <ll>\n" + " <a>val_a</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " <b>val</b>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " <b>val</b>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>val_c</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " </ll>\n" + " </ll>\n" + " <ll2>one</ll2>\n" + " <ll2>two</ll2>\n" + " <ll2>three</ll2>\n" + " <ll2>four</ll2>\n" + "</c>"; + struct lyd_node *tree, *node; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "l1"); + node = lyd_child(node); + assert_string_equal(node->schema->name, "a"); + assert_string_equal(lyd_get_value(node), "a3"); + + ly_set_free(set, NULL); + + /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "ll"); + node = lyd_child(node); + assert_string_equal(node->schema->name, "a"); + assert_string_equal(lyd_get_value(node), "val_b"); + node = node->next; + assert_string_equal(node->schema->name, "b"); + assert_null(node->next); + + ly_set_free(set, NULL); + + /* hashes are not used */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set)); + assert_int_equal(4, set->count); + + ly_set_free(set, NULL); + + /* hashes used even for leaf-lists */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "ll2"); + assert_string_equal(lyd_get_value(node), "three"); + + ly_set_free(set, NULL); + + /* not found using hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set)); + assert_int_equal(0, set->count); + + ly_set_free(set, NULL); + + /* white-spaces are also ok */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set)); + assert_int_equal(1, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_rpc(void **state) +{ + const char *data = + "<r xmlns=\"urn:tests:a\">\n" + " <l>val</l>\n" + "</r>"; + struct ly_in *in; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &tree, NULL)); + ly_in_free(in, 0); + assert_non_null(tree); + + /* name collision input/output, hashes are not used */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:r/l", &set)); + assert_int_equal(1, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_toplevel(void **state) +{ + const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " list l2 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; + const char *data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>\n" + "<foo xmlns=\"urn:tests:a\">foo value</foo>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>1</a>\n" + " <b>1</b>\n" + "</l2>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>2</a>\n" + " <b>1</b>\n" + "</l2>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>3</a>\n" + " <b>1</b>\n" + "</l2>"; + struct lyd_node *tree; + struct ly_set *set; + + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* all top-level nodes from one module (default container as well) */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set)); + assert_int_equal(5, set->count); + + ly_set_free(set, NULL); + + /* all top-level nodes from all modules */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set)); + assert_int_equal(8, set->count); + + ly_set_free(set, NULL); + + /* all nodes from one module */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set)); + assert_int_equal(13, set->count); + + ly_set_free(set, NULL); + + /* all nodes from all modules */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set)); + assert_int_equal(22, set->count); + + ly_set_free(set, NULL); + + /* all nodes from all modules #2 */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set)); + assert_int_equal(22, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_atomize(void **state) +{ + struct ly_set *set; + const struct lys_module *mod; + + mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a"); + assert_non_null(mod); + + /* some random paths just making sure the API function works */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//.", 0, &set)); + assert_in_range(set->count, 17, UINT32_MAX); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set)); + assert_int_equal(6, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/ietf-interfaces:interfaces/*", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set)); + assert_int_equal(14, set->count); + ly_set_free(set, NULL); + + /* + * axes + */ + + /* ancestor */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/a/ancestor::node()", 0, &set)); + assert_int_equal(6, set->count); + ly_set_free(set, NULL); + + /* ancestor-or-self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/ancestor-or-self::ll", 0, &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + /* attribute */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/l1/attribute::key", 0, &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* child */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::l1/child::a", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* descendant */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/descendant::c/descendant::b", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* descendant-or-self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* following */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following::a", 0, &set)); + assert_int_equal(4, set->count); + ly_set_free(set, NULL); + + /* following-sibling */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following-sibling::ll", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* parent */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set)); + assert_int_equal(11, set->count); + ly_set_free(set, NULL); + + /* preceding */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/preceding::a", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* preceding-sibling */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/ll/preceding-sibling::node()", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/self::c/ll/ll/b/self::b", 0, &set)); + assert_int_equal(4, set->count); + ly_set_free(set, NULL); +} + +static void +test_canonize(void **state) +{ + const char *data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>" + "<foo3 xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\">a:id_b</foo3>" + "<foo4 xmlns=\"urn:tests:a\">250.5</foo4>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* integer */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* identityref */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[.='id_b']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* decimal64 */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo4[.='0250.500']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_derived_from(void **state) +{ + const char *data = + "<foo3 xmlns=\"urn:tests:a\">id_c</foo3>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_b')]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_a')]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_augment(void **state) +{ + const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " import a {\n" + " prefix a;\n" + " }\n" + "\n" + " augment /a:c {\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; + const char *data = + "<c xmlns=\"urn:tests:a\">\n" + " <x>value</x>\n" + " <ll>\n" + " <a>key</a>\n" + " </ll>\n" + " <a xmlns=\"urn:tests:b\">25</a>\n" + " <ll2>c1</ll2>\n" + "</c>"; + struct lyd_node *tree; + struct ly_set *set; + + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* get all children ignoring their module */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/*", &set)); + assert_int_equal(4, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_variables(void **state) +{ + struct lyd_node *tree, *node; + struct ly_set *set; + const char *data; + struct lyxp_var *vars = NULL; + +#define LOCAL_SETUP(DATA, TREE) \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, DATA, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &TREE)); \ + assert_non_null(TREE); + +#define SET_NODE(NODE, SET, INDEX) \ + assert_non_null(SET); \ + assert_true(INDEX < SET->count); \ + NODE = SET->objs[INDEX]; + +#define LOCAL_TEARDOWN(SET, TREE, VARS) \ + ly_set_free(SET, NULL); \ + lyd_free_all(TREE); \ + lyxp_vars_free(VARS); \ + vars = NULL; + + /* Eval variable to number. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Eval variable to string. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"mstr\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Eval variable to set of nodes. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in union expr. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c[../a = 'a3']")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[c[../a = 'a1'] | $var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + SET_NODE(node, set, 1); + assert_string_equal(lyd_get_value(node), "a3"); + assert_int_equal(set->count, 2); + LOCAL_TEARDOWN(set, tree, vars); + + /* Predicate after variable. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var[../a = 'a1']]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in variable. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "$var2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"mstr\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Compare two variables. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"str\"")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"str\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[$var1 = $var2]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Arithmetic operation with variable. */ + data = + "<foo2 xmlns=\"urn:tests:a\">4</foo2>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo2[.= ($var1 * 2)]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "4"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable as function parameter. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "./c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[count($var) = 1]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in path expr. */ + /* NOTE: The variable can only be at the beginning of the expression path. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "/l1")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var/a]", vars, &set)); + assert_int_equal(set->count, 2); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable as function. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "position()")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var = 2]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Dynamic change of value. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "1")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable not defined. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\"")); + assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set)); + CHECK_LOG_CTX("Variable \"var55\" not defined.", NULL, 0); + LOCAL_TEARDOWN(set, tree, vars); + + /* Syntax error in value. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"")); + assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set)); + CHECK_LOG_CTX("Unterminated string delimited with \" (\").", "/a:foo", 0); + LOCAL_TEARDOWN(set, tree, vars); + + /* Prefix is not supported. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"")); + assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set)); + CHECK_LOG_CTX("Variable with prefix is not supported.", NULL, 0); + LOCAL_TEARDOWN(set, tree, vars); + +#undef LOCAL_SETUP +#undef LOCAL_TEARDOWN +} + +static void +test_axes(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n" + " <a>a1</a>\n" + " <b yang:operation=\"replace\">b1</b>\n" + " <c yang:operation=\"none\">c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n" + " <a yang:operation=\"none\" yang:key=\"[no-key='no-value']\">a3</a>\n" + " <b>b3</b>\n" + " <c yang:value=\"no-val\">c3</c>\n" + "</l1>" + "<c xmlns=\"urn:tests:a\">" + " <x>val</x>" + " <ll>" + " <a>key1</a>" + " <ll>" + " <a>key11</a>" + " <b>val11</b>" + " </ll>" + " <ll>" + " <a>key12</a>" + " <b>val12</b>" + " </ll>" + " <ll>" + " <a>key13</a>" + " <b>val13</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key2</a>" + " <ll>" + " <a>key21</a>" + " <b>val21</b>" + " </ll>" + " <ll>" + " <a>key22</a>" + " <b>val22</b>" + " </ll>" + " </ll>" + "</c>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* ancestor */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/a/ancestor::node()", &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* ancestor-or-self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/ancestor-or-self::ll", &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* attribute */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/@operation", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/attribute::key", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + /* child */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::l1/child::a", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* descendant */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant::c/descendant::b", &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + /* descendant-or-self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//c", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant-or-self::node()/c", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* following */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following::a", &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* following-sibling */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following-sibling::ll", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* parent */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::*/c/parent::l1", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::c//..", &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* preceding */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/preceding::a", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* preceding-sibling */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/ll/preceding-sibling::node()", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/self::c/ll/ll/b/self::b", &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_trim(void **state) +{ + const char *data; + char *str1; + struct lyd_node *tree; + + data = + "<l1 xmlns=\"urn:tests:a\">" + " <a>a1</a>" + " <b>b1</b>" + " <c>c1</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a2</a>" + " <b>b2</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a3</a>" + " <b>b3</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a4</a>" + " <b>b4</b>" + " <c>c4</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a5</a>" + " <b>b5</b>" + " <c>c5</c>" + "</l1>" + "<foo2 xmlns=\"urn:tests:a\">50</foo2>" + "<c xmlns=\"urn:tests:a\">" + " <x>key2</x>" + " <ll>" + " <a>key1</a>" + " <ll>" + " <a>key11</a>" + " <b>val11</b>" + " </ll>" + " <ll>" + " <a>key12</a>" + " <b>val12</b>" + " </ll>" + " <ll>" + " <a>key13</a>" + " <b>val13</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key2</a>" + " <ll>" + " <a>key21</a>" + " <b>val21</b>" + " </ll>" + " <ll>" + " <a>key22</a>" + " <b>val22</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key3</a>" + " <ll>" + " <a>key31</a>" + " <b>val31</b>" + " </ll>" + " <ll>" + " <a>key32</a>" + " <b>val32</b>" + " </ll>" + " </ll>" + "</c>"; + + /* trim #1 */ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[a='key11']", NULL)); + lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str1, + "<c xmlns=\"urn:tests:a\">\n" + " <ll>\n" + " <a>key1</a>\n" + " <ll>\n" + " <a>key11</a>\n" + " <b>val11</b>\n" + " </ll>\n" + " </ll>\n" + "</c>\n"); + + free(str1); + lyd_free_all(tree); + + /* trim #2 */ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[contains(.,'2')]", NULL)); + lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str1, + "<c xmlns=\"urn:tests:a\">\n" + " <ll>\n" + " <a>key1</a>\n" + " <ll>\n" + " <a>key12</a>\n" + " <b>val12</b>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>key2</a>\n" + " <ll>\n" + " <a>key21</a>\n" + " <b>val21</b>\n" + " </ll>\n" + " <ll>\n" + " <a>key22</a>\n" + " <b>val22</b>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>key3</a>\n" + " <ll>\n" + " <a>key32</a>\n" + " <b>val32</b>\n" + " </ll>\n" + " </ll>\n" + "</c>\n"); + + free(str1); + lyd_free_all(tree); + + /* trim #3 */ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/l1[4]//.", NULL)); + lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str1, + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a4</a>\n" + " <b>b4</b>\n" + " <c>c4</c>\n" + "</l1>\n"); + + free(str1); + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_predicate, setup), + UTEST(test_union, setup), + UTEST(test_invalid, setup), + UTEST(test_hash, setup), + UTEST(test_rpc, setup), + UTEST(test_toplevel, setup), + UTEST(test_atomize, setup), + UTEST(test_canonize, setup), + UTEST(test_derived_from, setup), + UTEST(test_augment, setup), + UTEST(test_variables, setup), + UTEST(test_axes, setup), + UTEST(test_trim, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_yanglib.c b/tests/utests/basic/test_yanglib.c new file mode 100644 index 0000000..0349157 --- /dev/null +++ b/tests/utests/basic/test_yanglib.c @@ -0,0 +1,144 @@ +/** + * @file test_yanglib.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for ietf-yang-library data + * + * Copyright (c) 2020 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 <string.h> + +#include "context.h" +#include "in.h" +#include "log.h" +#include "set.h" +#include "tests_config.h" +#include "tree_data.h" +#include "tree_schema.h" + +const char *schema_a = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " include a_sub;\n" + "\n" + " list l2 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; +const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " import a {\n" + " prefix a;\n" + " }\n" + "\n" + " deviation /a:l2 {\n" + " deviate add {\n" + " max-elements 40;\n" + " }\n" + " }\n" + "\n" + " leaf foo {\n" + " type string;\n" + " }\n" + "}"; + +static LY_ERR +test_imp_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, + LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + const char *schema_a_sub = + "submodule a_sub {\n" + " belongs-to a {\n" + " prefix a;\n" + " }\n" + " yang-version 1.1;\n" + "\n" + " feature feat1;\n" + "\n" + " list l3 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}\n"; + + assert_string_equal(mod_name, "a"); + assert_null(mod_rev); + if (!submod_name) { + return LY_ENOTFOUND; + } + assert_string_equal(submod_name, "a_sub"); + assert_null(sub_rev); + assert_null(user_data); + + *format = LYS_IN_YANG; + *module_data = schema_a_sub; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_yanglib(void **state) +{ + const char *feats[] = {"feat1", NULL}; + struct lyd_node *tree; + struct ly_set *set; + LY_ERR ret; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, NULL); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL); + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "<<%u>>", ly_ctx_get_change_count(UTEST_LYCTX))); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", -10)); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "")); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", ly_ctx_get_change_count(UTEST_LYCTX))); + + /* make sure there is "a" with a submodule and deviation */ + ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set/module[name='a'][submodule/name='a_sub']" + "[feature='feat1'][deviation='b']", &set); + assert_int_equal(ret, LY_SUCCESS); + + assert_int_equal(set->count, 1); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_yanglib), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c new file mode 100644 index 0000000..d85027b --- /dev/null +++ b/tests/utests/data/test_diff.c @@ -0,0 +1,1463 @@ +/** + * @file test_diff.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief tests for lyd_diff() + * + * Copyright (c) 2020 - 2023 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 "libyang.h" + +#define CHECK_PARSE_LYD(INPUT, OUTPUT) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, OUTPUT) + +#define CHECK_LYD_STRING(INPUT, TEXT) \ + CHECK_LYD_STRING_PARAM(INPUT, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS) + +#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_DIFF) \ + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_DIFF));\ + assert_non_null(OUT_DIFF) + +#define TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \ + { \ + struct lyd_node *data1;\ + struct lyd_node *data2;\ + struct lyd_node *data3;\ + /*create*/\ + CHECK_PARSE_LYD(XML1, data1);\ + CHECK_PARSE_LYD(XML2, data2);\ + CHECK_PARSE_LYD(XML3, data3);\ + /* diff1 */ \ + struct lyd_node *diff1;\ + CHECK_PARSE_LYD_DIFF(data1, data2, diff1); \ + CHECK_LYD_STRING(diff1, DIFF1); \ + assert_int_equal(lyd_diff_apply_all(&data1, diff1), LY_SUCCESS); \ + CHECK_LYD(data1, data2); \ + /* diff2 */ \ + struct lyd_node *diff2;\ + CHECK_PARSE_LYD_DIFF(data2, data3, diff2); \ + CHECK_LYD_STRING(diff2, DIFF2); \ + assert_int_equal(lyd_diff_apply_all(&data2, diff2), LY_SUCCESS);\ + CHECK_LYD(data2, data3);\ + /* merge */ \ + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS);\ + CHECK_LYD_STRING(diff1, MERGE); \ + /* cleanup */ \ + lyd_free_all(data1);\ + lyd_free_all(data2);\ + lyd_free_all(data3);\ + lyd_free_all(diff1);\ + lyd_free_all(diff2);\ + } + +const char *schema1 = + "module defaults {\n" + " yang-version 1.1;\n" + " namespace \"urn:libyang:tests:defaults\";\n" + " prefix df;\n" + "" + " feature unhide;\n" + "" + " typedef defint32 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf hiddenleaf {\n" + " if-feature \"unhide\";\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " container df {\n" + " leaf foo {\n" + " type defint32;\n" + " }\n" + "" + " leaf hiddenleaf {\n" + " if-feature \"unhide\";\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " container bar {\n" + " presence \"\";\n" + " leaf hi {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf ho {\n" + " type int32;\n" + " mandatory true;\n" + " }\n" + " }\n" + "" + " leaf-list llist {\n" + " type defint32;\n" + " ordered-by user;\n" + " }\n" + "" + " list ul {\n" + " key \"l1\";\n" + " ordered-by user;\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + "" + " leaf l2 {\n" + " type int32;\n" + " }\n" + "" + " container cont {\n" + " leaf l3 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "" + " leaf-list dllist {\n" + " type uint8;\n" + " default \"1\";\n" + " default \"2\";\n" + " default \"3\";\n" + " }\n" + "" + " list list {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + "" + " leaf value {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " list list2 {\n" + " key \"name2\";\n" + " leaf name2 {\n" + " type string;\n" + " }\n" + " leaf value2 {\n" + " type int32;\n" + " }\n" + " }\n" + " }\n"; +const char *schema2 = + " choice select {\n" + " default \"a\";\n" + " case a {\n" + " choice a {\n" + " leaf a1 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf a2 {\n" + " type int32;\n" + " default \"24\";\n" + " }\n" + " }\n" + " }\n" + "" + " leaf b {\n" + " type string;\n" + " }\n" + "" + " container c {\n" + " presence \"\";\n" + " leaf x {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " }\n" + " }\n" + "" + " choice select2 {\n" + " default \"s2b\";\n" + " leaf s2a {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " case s2b {\n" + " choice s2b {\n" + " default \"b1\";\n" + " case b1 {\n" + " leaf b1_1 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf b1_2 {\n" + " type string;\n" + " }\n" + "" + " leaf b1_status {\n" + " type int32;\n" + " default \"42\";\n" + " config false;\n" + " }\n" + " }\n" + "" + " leaf b2 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " }\n" + " }\n" + " }\n" + " list kl {\n" + " config \"false\";\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + "" + " leaf l2 {\n" + " type int32;\n" + " }\n" + " }\n" + "" + " leaf-list kll {\n" + " config \"false\";\n" + " type string;\n" + " }\n" + " }\n" + "" + " container hidden {\n" + " leaf foo {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf baz {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf papa {\n" + " type int32;\n" + " default \"42\";\n" + " config false;\n" + " }\n" + " }\n" + "" + " rpc rpc1 {\n" + " input {\n" + " leaf inleaf1 {\n" + " type string;\n" + " }\n" + "" + " leaf inleaf2 {\n" + " type string;\n" + " default \"def1\";\n" + " }\n" + " }\n" + "" + " output {\n" + " leaf outleaf1 {\n" + " type string;\n" + " default \"def2\";\n" + " }\n" + "" + " leaf outleaf2 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "" + " notification notif {\n" + " leaf ntfleaf1 {\n" + " type string;\n" + " default \"def3\";\n" + " }\n" + "" + " leaf ntfleaf2 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + +static int +setup(void **state) +{ + char *schema; + + UTEST_SETUP; + + /* create one schema, longer than 4095 chars */ + schema = malloc(strlen(schema1) + strlen(schema2) + 1); + strcpy(schema, schema1); + strcat(schema, schema2); + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + free(schema); + + return 0; +} + +static void +test_invalid(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, lyd_child(model_1), 0, &diff), LY_EINVAL); + CHECK_LOG_CTX("Invalid arguments - cannot create diff for unrelated data (lyd_diff()).", NULL, 0); + + assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, NULL), LY_EINVAL); + + lyd_free_all(model_1); + lyd_free_all(diff); +} + +static void +test_same(void **state) +{ + (void) state; + const char *xml = + "<nacm xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-acm\">\n" + " <enable-nacm>true</enable-nacm>\n" + " <read-default>permit</read-default>\n" + " <write-default>deny</write-default>\n" + " <exec-default>permit</exec-default>\n" + " <enable-external-groups>true</enable-external-groups>\n" + "</nacm><df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo><b1_1>42</b1_1>\n" + "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo><baz>42</baz></hidden>\n"; + + struct lyd_node *model_1; + struct lyd_node *model_2; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-acm", "2018-02-14", NULL)); + + CHECK_PARSE_LYD(xml, model_1); + CHECK_PARSE_LYD(xml, model_2); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, model_2, 0, &diff), LY_SUCCESS); + assert_null(diff); + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); +} + +static void +test_empty1(void **state) +{ + (void) state; + const char *xml_in = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + + struct lyd_node *model_1 = NULL; + struct lyd_node *model_2; + + CHECK_PARSE_LYD(xml_in, model_2); + + struct lyd_node *diff; + + CHECK_PARSE_LYD_DIFF(model_1, model_2, diff); + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"); + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); +} + +static void +test_empty2(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff; + + CHECK_PARSE_LYD_DIFF(model_1, NULL, diff); + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"); + + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + assert_ptr_equal(model_1, NULL); + + lyd_free_all(diff); + lyd_free_all(model_1); +} + +static void +test_empty_nested(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, &diff), LY_SUCCESS); + assert_null(diff); + + struct lyd_node *diff1; + + CHECK_PARSE_LYD_DIFF(NULL, lyd_child(model_1), diff1); + CHECK_LYD_STRING(diff1, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"create\">42</foo>\n" + "</df>\n"); + + struct lyd_node *diff2; + + CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, diff2); + CHECK_LYD_STRING(diff2, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"delete\">42</foo>\n" + "</df>\n"); + + lyd_free_all(model_1); + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +static void +test_delete_merge(void **state) +{ + (void) state; + struct lyd_node *diff1, *diff2; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list>\n" + " <name>a</name>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>a</name2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"delete\">\n" + " <name>a</name>\n" + " </list>\n" + "</df>\n"; + const char *xml_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"delete\">\n" + " <name>a</name>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>a</name2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"; + + CHECK_PARSE_LYD(xml1, diff1); + CHECK_PARSE_LYD(xml2, diff2); + + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS); + CHECK_LYD_STRING(diff1, xml_merge); + + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +static void +test_leaf(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>41</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>40</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>40</foo>\n" + "</hidden>\n"; + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">41</foo>\n" + " <b1_1 yang:operation=\"create\">42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + + const char *out_diff_2 = "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">40</foo>\n" + " <b1_1 yang:operation=\"delete\">42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>40</foo>\n" + "</hidden>\n"; + + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">40</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-value=\"42\" yang:orig-default=\"false\">40</foo>\n" + " <baz yang:operation=\"delete\">42</baz>\n" + "</hidden>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_list(void **state) +{ + (void) state; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>a</name>\n" + " <value>1</value>\n" + " </list>\n" + " <list>\n" + " <name>b</name>\n" + " <value>2</value>\n" + " </list>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>b</name>\n" + " <value>-2</value>\n" + " </list>\n" + " <list>\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>b</name>\n" + " <value>-2</value>\n" + " </list>\n" + " <list>\n" + " <name>a</name>\n" + " <value>2</value>\n" + " </list>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"delete\">\n" + " <name>a</name>\n" + " <value>1</value>\n" + " </list>\n" + " <list yang:operation=\"none\">\n" + " <name>b</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n" + " </list>\n" + " <list yang:operation=\"create\">\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"delete\">\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\n" + " <list yang:operation=\"create\">\n" + " <name>a</name>\n" + " <value>2</value>\n" + " </list>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"none\">\n" + " <name>a</name>\n" + " <value yang:operation=\"replace\" yang:orig-value=\"1\" yang:orig-default=\"false\">2</value>\n" + " </list>\n" + " <list yang:operation=\"none\">\n" + " <name>b</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n" + " </list>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_nested_list(void **state) +{ + struct lyd_node *data1, *data2, *diff; + const char *xml1, *xml2; + + (void) state; + + xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">" + " <list>" + " <name>n0</name>" + " <value>26</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " <list2>" + " <name2>n23</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n1</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n2</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n3</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n4</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + "</df>"; + xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">" + " <list>" + " <name>n0</name>" + " <value>30</value>" + " <list2>" + " <name2>n23</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + "</df>"; + + CHECK_PARSE_LYD(xml1, data1); + CHECK_PARSE_LYD(xml2, data2); + CHECK_PARSE_LYD_DIFF(data1, data2, diff); + + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list>\n" + " <name>n0</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"26\">30</value>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n1</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n2</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n3</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n4</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"); + + lyd_free_all(data1); + lyd_free_all(data2); + lyd_free_all(diff); +} + +static void +test_userord_llist(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + " <llist>4</llist>\n" + " <llist>5</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + " <llist>2</llist>\n" + " <llist>5</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>5</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + " <llist>2</llist>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"delete\" yang:orig-value=\"\">1</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\">5</llist>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\n" + " <llist yang:orig-value=\"\" yang:operation=\"delete\">1</llist>\n" + " <llist yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\" yang:operation=\"replace\">5</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_llist2(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + " <llist>4</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>2</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>4</llist>\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>3</llist>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"2\">4</llist>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">4</llist>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">4</llist>\n" + " <llist yang:orig-value=\"1\" yang:operation=\"delete\">2</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_mix(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>3</llist>\n" + " <llist>1</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">1</llist>\n" + " <llist yang:operation=\"create\" yang:value=\"1\">4</llist>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\n" + " <llist yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\" yang:operation=\"replace\">1</llist>\n" + " <llist yang:value=\"1\" yang:operation=\"create\">4</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_list(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>11</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>33</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\">11</l2>\n" + " </ul>\n" + " <ul yang:operation=\"delete\" yang:orig-key=\"[l1='a']\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"delete\" yang:orig-key=\"\">\n" + " <l1>a</l1>\n" + " <l2>11</l2>\n" + " </ul>\n" + " <ul yang:operation=\"none\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n" + " </ul>\n" + " <ul yang:operation=\"create\" yang:key=\"[l1='c']\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"delete\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + " <ul yang:operation=\"none\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n" + " </ul>\n" + " <ul yang:orig-key=\"[l1='a']\" yang:operation=\"replace\" yang:key=\"[l1='c']\">\n" + " <l1>b</l1>\n" + " </ul>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_list2(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"create\" yang:key=\"\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"create\" yang:key=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul yang:operation=\"create\" yang:key=\"[l1='a']\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"create\" yang:key=\"\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + " <ul yang:key=\"\" yang:operation=\"create\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul yang:key=\"[l1='a']\" yang:operation=\"create\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_list3(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <cont>\n" + " <l3>val1</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " <cont>\n" + " <l3>val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>44</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " <cont>\n" + " <l3>val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>44</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"create\">3</l2>\n" + " <cont yang:operation=\"none\">\n" + " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n" + " <l1>d</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='c']\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"create\">3</l2>\n" + " <cont yang:operation=\"none\">\n" + " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n" + " <l1>d</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n" + " </ul>\n" + " <ul yang:key=\"\" yang:orig-key=\"[l1='c']\" yang:operation=\"replace\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_keyless_list(void **state) +{ + (void) state; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl>\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + " <kl yang:operation=\"replace\" yang:position=\"\" yang:orig-position=\"1\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"2\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"\">\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"1\">\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"2\">\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"3\">\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + " <kl yang:orig-position=\"1\" yang:operation=\"delete\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:orig-position=\"\" yang:operation=\"delete\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:position=\"\" yang:operation=\"create\">\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl yang:position=\"1\" yang:operation=\"create\">\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl yang:position=\"2\" yang:operation=\"create\">\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl yang:position=\"3\" yang:operation=\"create\">\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_state_llist(void **state) +{ + (void) state; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>a</kll>\n" + " <kll>b</kll>\n" + " <kll>c</kll>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>b</kll>\n" + " <kll>c</kll>\n" + " <kll>a</kll>\n" + " <kll>a</kll>\n" + " <kll>a</kll>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>a</kll>\n" + " <kll>d</kll>\n" + " <kll>a</kll>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"\" yang:orig-position=\"1\">b</kll>\n" + " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"1\" yang:orig-position=\"2\">c</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"3\">a</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kll yang:operation=\"delete\" yang:orig-position=\"\">b</kll>\n" + " <kll yang:operation=\"delete\" yang:orig-position=\"\">c</kll>\n" + " <kll yang:operation=\"delete\" yang:orig-position=\"2\">a</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"1\">d</kll>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <kll yang:orig-default=\"false\" yang:orig-position=\"1\" yang:operation=\"delete\">b</kll>\n" + " <kll yang:orig-default=\"false\" yang:orig-position=\"2\" yang:operation=\"delete\">c</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\n" + " <kll yang:position=\"1\" yang:operation=\"create\">d</kll>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_wd(void **state) +{ + (void) state; + const struct lys_module *mod; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>41</foo>\n" + " <dllist>4</dllist>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <dllist>4</dllist>\n" + " <dllist>1</dllist>\n" + "</df>\n"; + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "defaults"); + assert_non_null(mod); + + struct lyd_node *model_1 = NULL; + + assert_int_equal(lyd_validate_module(&model_1, mod, 0, NULL), LY_SUCCESS); + assert_ptr_not_equal(model_1, NULL); + + struct lyd_node *model_2; + struct lyd_node *model_3; + + CHECK_PARSE_LYD_PARAM(xml2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + CHECK_PARSE_LYD_PARAM(xml3, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_3); + + /* diff1 */ + struct lyd_node *diff1 = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, model_2, LYD_DIFF_DEFAULTS, &diff1), LY_SUCCESS); + assert_non_null(diff1); + + const char *diff1_out_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"true\" yang:orig-value=\"42\">41</foo>\n" + " <dllist yang:operation=\"delete\">1</dllist>\n" + " <dllist yang:operation=\"delete\">2</dllist>\n" + " <dllist yang:operation=\"delete\">3</dllist>\n" + " <dllist yang:operation=\"create\">4</dllist>\n" + "</df>\n"; + + CHECK_LYD_STRING_PARAM(diff1, diff1_out_1, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL); + assert_int_equal(lyd_diff_apply_all(&model_1, diff1), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + /* diff2 */ + struct lyd_node *diff2; + + assert_int_equal(lyd_diff_siblings(model_2, model_3, LYD_DIFF_DEFAULTS, &diff2), LY_SUCCESS); + assert_non_null(diff2); + CHECK_LYD_STRING(diff2, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">42</foo>\n" + " <dllist yang:operation=\"create\">1</dllist>\n" + "</df>\n"); + + assert_int_equal(lyd_diff_apply_all(&model_2, diff2), LY_SUCCESS); + CHECK_LYD(model_2, model_3); + + /* merge */ + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS); + + const char *diff1_out_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:orig-default=\"true\" yang:operation=\"none\">42</foo>\n" + " <dllist yang:operation=\"none\" yang:orig-default=\"true\">1</dllist>\n" + " <dllist yang:operation=\"delete\">2</dllist>\n" + " <dllist yang:operation=\"delete\">3</dllist>\n" + " <dllist yang:operation=\"create\">4</dllist>\n" + "</df>\n"; + + CHECK_LYD_STRING_PARAM(diff1, diff1_out_2, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(model_3); + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_invalid, setup), + UTEST(test_same, setup), + UTEST(test_empty1, setup), + UTEST(test_empty2, setup), + UTEST(test_empty_nested, setup), + UTEST(test_delete_merge, setup), + UTEST(test_leaf, setup), + UTEST(test_list, setup), + UTEST(test_nested_list, setup), + UTEST(test_userord_llist, setup), + UTEST(test_userord_llist2, setup), + UTEST(test_userord_mix, setup), + UTEST(test_userord_list, setup), + UTEST(test_userord_list2, setup), + UTEST(test_userord_list3, setup), + UTEST(test_keyless_list, setup), + UTEST(test_state_llist, setup), + UTEST(test_wd, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_lyb.c b/tests/utests/data/test_lyb.c new file mode 100644 index 0000000..26f3e73 --- /dev/null +++ b/tests/utests/data/test_lyb.c @@ -0,0 +1,2841 @@ +/** + * @file test_lyb.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief Cmocka tests for LYB binary data format. + * + * Copyright (c) 2020 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 "hash_table.h" +#include "libyang.h" + +#define CHECK_PARSE_LYD(INPUT, OUT_NODE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, OUT_NODE) + +#define CHECK_LYD_STRING(MODEL, TEXT) \ + CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK) + +static void +check_print_parse(void **state, const char *data_xml) +{ + struct lyd_node *tree_1; + struct lyd_node *tree_2; + char *lyb_out; + + CHECK_PARSE_LYD(data_xml, tree_1); + assert_int_equal(lyd_print_mem(&lyb_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, lyb_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, + 0, &tree_2)); + assert_non_null(tree_2); + CHECK_LYD(tree_1, tree_2); + + free(lyb_out); + lyd_free_all(tree_1); + lyd_free_all(tree_2); +} + +static int +setup(void **state) +{ + UTEST_SETUP; + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +static void +tests_leaflist(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-leaflist\"; prefix m;" + " container cont {" + " presence \"\";" + " leaf-list ll {" + " type uint8;" + " }" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + " <ll>1</ll>\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + " <ll>1</ll>\n" + " <ll>2</ll>\n" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +tests_list(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-list\"; prefix m;" + " container cont {" + " presence \"\";" + " list lst {" + " key \"lf\";" + " leaf lf {" + " type uint8;" + " }" + " }" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-list\">\n" + " <lst>" + " <lf>1</lf>" + " </lst>" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-list\">\n" + " <lst>" + " <lf>1</lf>" + " <lf>2</lf>" + " </lst>" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +tests_any(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-any\"; prefix m;" + " container cont {" + " presence \"\";" + " anyxml anxml;\n" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + " <anxml><node>value</node></anxml>\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + " <anxml><node1>value</node1></anxml>\n" + " <anxml><node2>value</node2></anxml>\n" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +test_ietf_interfaces(void **state) +{ + const char *data_xml = + "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>eth0</name>\n" + " <description>Ethernet 0</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>true</enabled>\n" + " <mtu>1500</mtu>\n" + " <address>\n" + " <ip>192.168.2.100</ip>\n" + " <prefix-length>24</prefix-length>\n" + " </address>\n" + " </ipv4>\n" + " </interface>\n" + " <interface>\n" + " <name>eth1</name>\n" + " <description>Ethernet 1</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>true</enabled>\n" + " <mtu>1500</mtu>\n" + " <address>\n" + " <ip>10.10.1.5</ip>\n" + " <prefix-length>16</prefix-length>\n" + " </address>\n" + " </ipv4>\n" + " </interface>\n" + " <interface>\n" + " <name>gigaeth0</name>\n" + " <description>GigabitEthernet 0</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>false</enabled>\n" + " </interface>\n" + "</interfaces>\n"; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-ip", NULL, NULL)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL)); + + check_print_parse(state, data_xml); +} + +static void +test_origin(void **state) +{ + const char *origin_yang = + "module test-origin {" + " namespace \"urn:test-origin\";" + " prefix to;" + " import ietf-origin {" + " prefix or;" + " }" + "" + " container cont {" + " leaf leaf1 {" + " type string;" + " }" + " leaf leaf2 {" + " type string;" + " }" + " leaf leaf3 {" + " type uint8;" + " }" + " }" + "}"; + const char *data_xml = + "<cont xmlns=\"urn:test-origin\">\n" + " <leaf1 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:default\">value1</leaf1>\n" + " <leaf2>value2</leaf2>\n" + " <leaf3 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:system\">125</leaf3>\n" + "</cont>\n"; + + UTEST_ADD_MODULE(origin_yang, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_set_implemented(ly_ctx_get_module_latest(UTEST_LYCTX, "ietf-origin"), NULL)); + + check_print_parse(state, data_xml); +} + +static void +test_statements(void **state) +{ + const char *links_yang = + "module links {\n" + " yang-version 1.1;\n" + " namespace \"urn:module2\";\n" + " prefix mod2;\n" + "\n" + " identity just-another-identity;\n" + "\n" + " leaf one-leaf {\n" + " type string;\n" + " }\n" + "\n" + " list list-for-augment {\n" + " key keyleaf;\n" + "\n" + " leaf keyleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf just-leaf {\n" + " type int32;\n" + " }\n" + " }\n" + "\n" + " leaf rleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf-list llist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 100;\n" + " ordered-by user;\n" + " }\n" + "\n" + " grouping rgroup {\n" + " leaf rg1 {\n" + " type string;\n" + " }\n" + "\n" + " leaf rg2 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *statements_yang = + "module statements {\n" + " namespace \"urn:module\";\n" + " prefix mod;\n" + " yang-version 1.1;\n" + "\n" + " import links {\n" + " prefix mod2;\n" + " }\n" + "\n" + " identity random-identity {\n" + " base \"mod2:just-another-identity\";\n" + " base \"another-identity\";\n" + " }\n" + "\n" + " identity another-identity {\n" + " base \"mod2:just-another-identity\";\n" + " }\n" + "\n" + " typedef percent {\n" + " type uint8 {\n" + " range \"0 .. 100\";\n" + " }\n" + " units percent;\n" + " }\n" + "\n" + " container ice-cream-shop {\n" + " container employees {\n" + " list employee {\n" + " config true;\n" + " key id;\n" + " unique name;\n" + " min-elements 0;\n" + " max-elements 100;\n" + "\n" + " leaf id {\n" + " type uint64;\n" + " mandatory true;\n" + " }\n" + "\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " leaf age {\n" + " type uint32;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " container random {\n" + " choice switch {\n" + " case a {\n" + " leaf aleaf {\n" + " type string;\n" + " default aaa;\n" + " }\n" + " }\n" + "\n" + " case c {\n" + " leaf cleaf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "\n" + " anyxml xml-data;\n" + " anydata any-data;\n" + " leaf-list leaflist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 20;\n" + " ordered-by system;\n" + " }\n" + "\n" + " grouping group {\n" + " leaf g1 {\n" + " mandatory false;\n" + " type percent;\n" + " }\n" + "\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " uses group;\n" + " uses mod2:rgroup;\n" + "\n" + " leaf lref {\n" + " type leafref {\n" + " path \"/mod2:one-leaf\";\n" + " }\n" + " }\n" + "\n" + " leaf iref {\n" + " type identityref {\n" + " base \"mod2:just-another-identity\";\n" + " }\n" + " }\n" + " }\n" + "\n" + " notification notif;\n" + "\n" + " augment \"/random\" {\n" + " leaf aug-leaf {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *data_xml = + "<one-leaf xmlns=\"urn:module2\">reference leaf</one-leaf>\n" + "<ice-cream-shop xmlns=\"urn:module\">\n" + " <employees>\n" + " <employee>\n" + " <id>0</id>\n" + " <name>John Doe</name>\n" + " <age>28</age>\n" + " </employee>\n" + " <employee>\n" + " <id>1</id>\n" + " <name>Dohn Joe</name>\n" + " <age>20</age>\n" + " </employee>\n" + " </employees>\n" + "</ice-cream-shop>\n" + "<random xmlns=\"urn:module\">\n" + " <aleaf>string</aleaf>\n" + " <xml-data><anyxml>data</anyxml></xml-data>\n" + " <any-data><notif/></any-data>\n" + " <leaflist>l0</leaflist>\n" + " <leaflist>l1</leaflist>\n" + " <leaflist>l2</leaflist>\n" + " <g1>40</g1>\n" + " <g2>string</g2>\n" + " <aug-leaf>string</aug-leaf>\n" + " <rg1>string</rg1>\n" + " <rg2>string</rg2>\n" + " <lref>reference leaf</lref>\n" + " <iref>random-identity</iref>\n" + "</random>\n"; + + UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, NULL); + + check_print_parse(state, data_xml); +} + +static void +test_opaq(void **state) +{ + const char *nc_feats[] = {"writable-running", NULL}; + const char *data_xml = + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <top xmlns=\"urn:ed\">\n" + " <first>TestFirst</first>\n" + " </top>\n" + " </config>\n" + "</edit-config>\n"; + struct ly_in *in; + struct lyd_node *tree_1; + struct lyd_node *tree_2; + char *xml_out; /* tree_2 */ + LY_ERR rc; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", NULL, nc_feats)); + + ly_in_new_memory(data_xml, &in); + rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree_1, NULL); + ly_in_free(in, 0); + assert_int_equal(rc, LY_SUCCESS); + + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); + + ly_in_new_memory(xml_out, &in); + rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_LYB, LYD_TYPE_RPC_YANG, &tree_2, NULL); + ly_in_free(in, 0); + assert_int_equal(rc, LY_SUCCESS); + + /* compare models */ + CHECK_LYD(tree_1, tree_2); + + /* clean */ + free(xml_out); + lyd_free_all(tree_1); + lyd_free_all(tree_2); +} + +static void +test_collisions(void **state) +{ + char *counters_yang, *data_xml; + + counters_yang = malloc(32768); + strcpy(counters_yang, + "module counters {\n" + " namespace \"urn:counters\";\n" + " prefix c;\n" + "\n" + " container stats {\n"); + strcat(counters_yang, + " leaf counter1 {\n" + " type uint64;\n" + " }\n" + " leaf counter2 {\n" + " type uint64;\n" + " }\n" + " leaf counter3 {\n" + " type uint64;\n" + " }\n" + " leaf counter4 {\n" + " type uint64;\n" + " }\n" + " leaf counter5 {\n" + " type uint64;\n" + " }\n" + " leaf counter6 {\n" + " type uint64;\n" + " }\n" + " leaf counter7 {\n" + " type uint64;\n" + " }\n" + " leaf counter8 {\n" + " type uint64;\n" + " }\n" + " leaf counter9 {\n" + " type uint64;\n" + " }\n" + " leaf counter10 {\n" + " type uint64;\n" + " }\n" + " leaf counter11 {\n" + " type uint64;\n" + " }\n" + " leaf counter12 {\n" + " type uint64;\n" + " }\n" + " leaf counter13 {\n" + " type uint64;\n" + " }\n" + " leaf counter14 {\n" + " type uint64;\n" + " }\n" + " leaf counter15 {\n" + " type uint64;\n" + " }\n" + " leaf counter16 {\n" + " type uint64;\n" + " }\n" + " leaf counter17 {\n" + " type uint64;\n" + " }\n" + " leaf counter18 {\n" + " type uint64;\n" + " }\n" + " leaf counter19 {\n" + " type uint64;\n" + " }\n" + " leaf counter20 {\n" + " type uint64;\n" + " }\n" + " leaf counter21 {\n" + " type uint64;\n" + " }\n" + " leaf counter22 {\n" + " type uint64;\n" + " }\n" + " leaf counter23 {\n" + " type uint64;\n" + " }\n" + " leaf counter24 {\n" + " type uint64;\n" + " }\n" + " leaf counter25 {\n" + " type uint64;\n" + " }\n" + " leaf counter26 {\n" + " type uint64;\n" + " }\n" + " leaf counter27 {\n" + " type uint64;\n" + " }\n" + " leaf counter28 {\n" + " type uint64;\n" + " }\n" + " leaf counter29 {\n" + " type uint64;\n" + " }\n" + " leaf counter30 {\n" + " type uint64;\n" + " }\n" + " leaf counter31 {\n" + " type uint64;\n" + " }\n" + " leaf counter32 {\n" + " type uint64;\n" + " }\n" + " leaf counter33 {\n" + " type uint64;\n" + " }\n" + " leaf counter34 {\n" + " type uint64;\n" + " }\n" + " leaf counter35 {\n" + " type uint64;\n" + " }\n" + " leaf counter36 {\n" + " type uint64;\n" + " }\n" + " leaf counter37 {\n" + " type uint64;\n" + " }\n" + " leaf counter38 {\n" + " type uint64;\n" + " }\n" + " leaf counter39 {\n" + " type uint64;\n" + " }\n" + " leaf counter40 {\n" + " type uint64;\n" + " }\n" + " leaf counter41 {\n" + " type uint64;\n" + " }\n" + " leaf counter42 {\n" + " type uint64;\n" + " }\n" + " leaf counter43 {\n" + " type uint64;\n" + " }\n" + " leaf counter44 {\n" + " type uint64;\n" + " }\n" + " leaf counter45 {\n" + " type uint64;\n" + " }\n" + " leaf counter46 {\n" + " type uint64;\n" + " }\n" + " leaf counter47 {\n" + " type uint64;\n" + " }\n" + " leaf counter48 {\n" + " type uint64;\n" + " }\n" + " leaf counter49 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter50 {\n" + " type uint64;\n" + " }\n" + " leaf counter51 {\n" + " type uint64;\n" + " }\n" + " leaf counter52 {\n" + " type uint64;\n" + " }\n" + " leaf counter53 {\n" + " type uint64;\n" + " }\n" + " leaf counter54 {\n" + " type uint64;\n" + " }\n" + " leaf counter55 {\n" + " type uint64;\n" + " }\n" + " leaf counter56 {\n" + " type uint64;\n" + " }\n" + " leaf counter57 {\n" + " type uint64;\n" + " }\n" + " leaf counter58 {\n" + " type uint64;\n" + " }\n" + " leaf counter59 {\n" + " type uint64;\n" + " }\n" + " leaf counter60 {\n" + " type uint64;\n" + " }\n" + " leaf counter61 {\n" + " type uint64;\n" + " }\n" + " leaf counter62 {\n" + " type uint64;\n" + " }\n" + " leaf counter63 {\n" + " type uint64;\n" + " }\n" + " leaf counter64 {\n" + " type uint64;\n" + " }\n" + " leaf counter65 {\n" + " type uint64;\n" + " }\n" + " leaf counter66 {\n" + " type uint64;\n" + " }\n" + " leaf counter67 {\n" + " type uint64;\n" + " }\n" + " leaf counter68 {\n" + " type uint64;\n" + " }\n" + " leaf counter69 {\n" + " type uint64;\n" + " }\n" + " leaf counter70 {\n" + " type uint64;\n" + " }\n" + " leaf counter71 {\n" + " type uint64;\n" + " }\n" + " leaf counter72 {\n" + " type uint64;\n" + " }\n" + " leaf counter73 {\n" + " type uint64;\n" + " }\n" + " leaf counter74 {\n" + " type uint64;\n" + " }\n" + " leaf counter75 {\n" + " type uint64;\n" + " }\n" + " leaf counter76 {\n" + " type uint64;\n" + " }\n" + " leaf counter77 {\n" + " type uint64;\n" + " }\n" + " leaf counter78 {\n" + " type uint64;\n" + " }\n" + " leaf counter79 {\n" + " type uint64;\n" + " }\n" + " leaf counter80 {\n" + " type uint64;\n" + " }\n" + " leaf counter81 {\n" + " type uint64;\n" + " }\n" + " leaf counter82 {\n" + " type uint64;\n" + " }\n" + " leaf counter83 {\n" + " type uint64;\n" + " }\n" + " leaf counter84 {\n" + " type uint64;\n" + " }\n" + " leaf counter85 {\n" + " type uint64;\n" + " }\n" + " leaf counter86 {\n" + " type uint64;\n" + " }\n" + " leaf counter87 {\n" + " type uint64;\n" + " }\n" + " leaf counter88 {\n" + " type uint64;\n" + " }\n" + " leaf counter89 {\n" + " type uint64;\n" + " }\n" + " leaf counter90 {\n" + " type uint64;\n" + " }\n" + " leaf counter91 {\n" + " type uint64;\n" + " }\n" + " leaf counter92 {\n" + " type uint64;\n" + " }\n" + " leaf counter93 {\n" + " type uint64;\n" + " }\n" + " leaf counter94 {\n" + " type uint64;\n" + " }\n" + " leaf counter95 {\n" + " type uint64;\n" + " }\n" + " leaf counter96 {\n" + " type uint64;\n" + " }\n" + " leaf counter97 {\n" + " type uint64;\n" + " }\n" + " leaf counter98 {\n" + " type uint64;\n" + " }\n" + " leaf counter99 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter100 {\n" + " type uint64;\n" + " }\n" + " leaf counter101 {\n" + " type uint64;\n" + " }\n" + " leaf counter102 {\n" + " type uint64;\n" + " }\n" + " leaf counter103 {\n" + " type uint64;\n" + " }\n" + " leaf counter104 {\n" + " type uint64;\n" + " }\n" + " leaf counter105 {\n" + " type uint64;\n" + " }\n" + " leaf counter106 {\n" + " type uint64;\n" + " }\n" + " leaf counter107 {\n" + " type uint64;\n" + " }\n" + " leaf counter108 {\n" + " type uint64;\n" + " }\n" + " leaf counter109 {\n" + " type uint64;\n" + " }\n" + " leaf counter110 {\n" + " type uint64;\n" + " }\n" + " leaf counter111 {\n" + " type uint64;\n" + " }\n" + " leaf counter112 {\n" + " type uint64;\n" + " }\n" + " leaf counter113 {\n" + " type uint64;\n" + " }\n" + " leaf counter114 {\n" + " type uint64;\n" + " }\n" + " leaf counter115 {\n" + " type uint64;\n" + " }\n" + " leaf counter116 {\n" + " type uint64;\n" + " }\n" + " leaf counter117 {\n" + " type uint64;\n" + " }\n" + " leaf counter118 {\n" + " type uint64;\n" + " }\n" + " leaf counter119 {\n" + " type uint64;\n" + " }\n" + " leaf counter120 {\n" + " type uint64;\n" + " }\n" + " leaf counter121 {\n" + " type uint64;\n" + " }\n" + " leaf counter122 {\n" + " type uint64;\n" + " }\n" + " leaf counter123 {\n" + " type uint64;\n" + " }\n" + " leaf counter124 {\n" + " type uint64;\n" + " }\n" + " leaf counter125 {\n" + " type uint64;\n" + " }\n" + " leaf counter126 {\n" + " type uint64;\n" + " }\n" + " leaf counter127 {\n" + " type uint64;\n" + " }\n" + " leaf counter128 {\n" + " type uint64;\n" + " }\n" + " leaf counter129 {\n" + " type uint64;\n" + " }\n" + " leaf counter130 {\n" + " type uint64;\n" + " }\n" + " leaf counter131 {\n" + " type uint64;\n" + " }\n" + " leaf counter132 {\n" + " type uint64;\n" + " }\n" + " leaf counter133 {\n" + " type uint64;\n" + " }\n" + " leaf counter134 {\n" + " type uint64;\n" + " }\n" + " leaf counter135 {\n" + " type uint64;\n" + " }\n" + " leaf counter136 {\n" + " type uint64;\n" + " }\n" + " leaf counter137 {\n" + " type uint64;\n" + " }\n" + " leaf counter138 {\n" + " type uint64;\n" + " }\n" + " leaf counter139 {\n" + " type uint64;\n" + " }\n" + " leaf counter140 {\n" + " type uint64;\n" + " }\n" + " leaf counter141 {\n" + " type uint64;\n" + " }\n" + " leaf counter142 {\n" + " type uint64;\n" + " }\n" + " leaf counter143 {\n" + " type uint64;\n" + " }\n" + " leaf counter144 {\n" + " type uint64;\n" + " }\n" + " leaf counter145 {\n" + " type uint64;\n" + " }\n" + " leaf counter146 {\n" + " type uint64;\n" + " }\n" + " leaf counter147 {\n" + " type uint64;\n" + " }\n" + " leaf counter148 {\n" + " type uint64;\n" + " }\n" + " leaf counter149 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter150 {\n" + " type uint64;\n" + " }\n" + " leaf counter151 {\n" + " type uint64;\n" + " }\n" + " leaf counter152 {\n" + " type uint64;\n" + " }\n" + " leaf counter153 {\n" + " type uint64;\n" + " }\n" + " leaf counter154 {\n" + " type uint64;\n" + " }\n" + " leaf counter155 {\n" + " type uint64;\n" + " }\n" + " leaf counter156 {\n" + " type uint64;\n" + " }\n" + " leaf counter157 {\n" + " type uint64;\n" + " }\n" + " leaf counter158 {\n" + " type uint64;\n" + " }\n" + " leaf counter159 {\n" + " type uint64;\n" + " }\n" + " leaf counter160 {\n" + " type uint64;\n" + " }\n" + " leaf counter161 {\n" + " type uint64;\n" + " }\n" + " leaf counter162 {\n" + " type uint64;\n" + " }\n" + " leaf counter163 {\n" + " type uint64;\n" + " }\n" + " leaf counter164 {\n" + " type uint64;\n" + " }\n" + " leaf counter165 {\n" + " type uint64;\n" + " }\n" + " leaf counter166 {\n" + " type uint64;\n" + " }\n" + " leaf counter167 {\n" + " type uint64;\n" + " }\n" + " leaf counter168 {\n" + " type uint64;\n" + " }\n" + " leaf counter169 {\n" + " type uint64;\n" + " }\n" + " leaf counter170 {\n" + " type uint64;\n" + " }\n" + " leaf counter171 {\n" + " type uint64;\n" + " }\n" + " leaf counter172 {\n" + " type uint64;\n" + " }\n" + " leaf counter173 {\n" + " type uint64;\n" + " }\n" + " leaf counter174 {\n" + " type uint64;\n" + " }\n" + " leaf counter175 {\n" + " type uint64;\n" + " }\n" + " leaf counter176 {\n" + " type uint64;\n" + " }\n" + " leaf counter177 {\n" + " type uint64;\n" + " }\n" + " leaf counter178 {\n" + " type uint64;\n" + " }\n" + " leaf counter179 {\n" + " type uint64;\n" + " }\n" + " leaf counter180 {\n" + " type uint64;\n" + " }\n" + " leaf counter181 {\n" + " type uint64;\n" + " }\n" + " leaf counter182 {\n" + " type uint64;\n" + " }\n" + " leaf counter183 {\n" + " type uint64;\n" + " }\n" + " leaf counter184 {\n" + " type uint64;\n" + " }\n" + " leaf counter185 {\n" + " type uint64;\n" + " }\n" + " leaf counter186 {\n" + " type uint64;\n" + " }\n" + " leaf counter187 {\n" + " type uint64;\n" + " }\n" + " leaf counter188 {\n" + " type uint64;\n" + " }\n" + " leaf counter189 {\n" + " type uint64;\n" + " }\n" + " leaf counter190 {\n" + " type uint64;\n" + " }\n" + " leaf counter191 {\n" + " type uint64;\n" + " }\n" + " leaf counter192 {\n" + " type uint64;\n" + " }\n" + " leaf counter193 {\n" + " type uint64;\n" + " }\n" + " leaf counter194 {\n" + " type uint64;\n" + " }\n" + " leaf counter195 {\n" + " type uint64;\n" + " }\n" + " leaf counter196 {\n" + " type uint64;\n" + " }\n" + " leaf counter197 {\n" + " type uint64;\n" + " }\n" + " leaf counter198 {\n" + " type uint64;\n" + " }\n" + " leaf counter199 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter200 {\n" + " type uint64;\n" + " }\n" + " leaf counter201 {\n" + " type uint64;\n" + " }\n" + " leaf counter202 {\n" + " type uint64;\n" + " }\n" + " leaf counter203 {\n" + " type uint64;\n" + " }\n" + " leaf counter204 {\n" + " type uint64;\n" + " }\n" + " leaf counter205 {\n" + " type uint64;\n" + " }\n" + " leaf counter206 {\n" + " type uint64;\n" + " }\n" + " leaf counter207 {\n" + " type uint64;\n" + " }\n" + " leaf counter208 {\n" + " type uint64;\n" + " }\n" + " leaf counter209 {\n" + " type uint64;\n" + " }\n" + " leaf counter210 {\n" + " type uint64;\n" + " }\n" + " leaf counter211 {\n" + " type uint64;\n" + " }\n" + " leaf counter212 {\n" + " type uint64;\n" + " }\n" + " leaf counter213 {\n" + " type uint64;\n" + " }\n" + " leaf counter214 {\n" + " type uint64;\n" + " }\n" + " leaf counter215 {\n" + " type uint64;\n" + " }\n" + " leaf counter216 {\n" + " type uint64;\n" + " }\n" + " leaf counter217 {\n" + " type uint64;\n" + " }\n" + " leaf counter218 {\n" + " type uint64;\n" + " }\n" + " leaf counter219 {\n" + " type uint64;\n" + " }\n" + " leaf counter220 {\n" + " type uint64;\n" + " }\n" + " leaf counter221 {\n" + " type uint64;\n" + " }\n" + " leaf counter222 {\n" + " type uint64;\n" + " }\n" + " leaf counter223 {\n" + " type uint64;\n" + " }\n" + " leaf counter224 {\n" + " type uint64;\n" + " }\n" + " leaf counter225 {\n" + " type uint64;\n" + " }\n" + " leaf counter226 {\n" + " type uint64;\n" + " }\n" + " leaf counter227 {\n" + " type uint64;\n" + " }\n" + " leaf counter228 {\n" + " type uint64;\n" + " }\n" + " leaf counter229 {\n" + " type uint64;\n" + " }\n" + " leaf counter230 {\n" + " type uint64;\n" + " }\n" + " leaf counter231 {\n" + " type uint64;\n" + " }\n" + " leaf counter232 {\n" + " type uint64;\n" + " }\n" + " leaf counter233 {\n" + " type uint64;\n" + " }\n" + " leaf counter234 {\n" + " type uint64;\n" + " }\n" + " leaf counter235 {\n" + " type uint64;\n" + " }\n" + " leaf counter236 {\n" + " type uint64;\n" + " }\n" + " leaf counter237 {\n" + " type uint64;\n" + " }\n" + " leaf counter238 {\n" + " type uint64;\n" + " }\n" + " leaf counter239 {\n" + " type uint64;\n" + " }\n" + " leaf counter240 {\n" + " type uint64;\n" + " }\n" + " leaf counter241 {\n" + " type uint64;\n" + " }\n" + " leaf counter242 {\n" + " type uint64;\n" + " }\n" + " leaf counter243 {\n" + " type uint64;\n" + " }\n" + " leaf counter244 {\n" + " type uint64;\n" + " }\n" + " leaf counter245 {\n" + " type uint64;\n" + " }\n" + " leaf counter246 {\n" + " type uint64;\n" + " }\n" + " leaf counter247 {\n" + " type uint64;\n" + " }\n" + " leaf counter248 {\n" + " type uint64;\n" + " }\n" + " leaf counter249 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter250 {\n" + " type uint64;\n" + " }\n" + " leaf counter251 {\n" + " type uint64;\n" + " }\n" + " leaf counter252 {\n" + " type uint64;\n" + " }\n" + " leaf counter253 {\n" + " type uint64;\n" + " }\n" + " leaf counter254 {\n" + " type uint64;\n" + " }\n" + " leaf counter255 {\n" + " type uint64;\n" + " }\n" + " leaf counter256 {\n" + " type uint64;\n" + " }\n" + " leaf counter257 {\n" + " type uint64;\n" + " }\n" + " leaf counter258 {\n" + " type uint64;\n" + " }\n" + " leaf counter259 {\n" + " type uint64;\n" + " }\n" + " leaf counter260 {\n" + " type uint64;\n" + " }\n" + " leaf counter261 {\n" + " type uint64;\n" + " }\n" + " leaf counter262 {\n" + " type uint64;\n" + " }\n" + " leaf counter263 {\n" + " type uint64;\n" + " }\n" + " leaf counter264 {\n" + " type uint64;\n" + " }\n" + " leaf counter265 {\n" + " type uint64;\n" + " }\n" + " leaf counter266 {\n" + " type uint64;\n" + " }\n" + " leaf counter267 {\n" + " type uint64;\n" + " }\n" + " leaf counter268 {\n" + " type uint64;\n" + " }\n" + " leaf counter269 {\n" + " type uint64;\n" + " }\n" + " leaf counter270 {\n" + " type uint64;\n" + " }\n" + " leaf counter271 {\n" + " type uint64;\n" + " }\n" + " leaf counter272 {\n" + " type uint64;\n" + " }\n" + " leaf counter273 {\n" + " type uint64;\n" + " }\n" + " leaf counter274 {\n" + " type uint64;\n" + " }\n" + " leaf counter275 {\n" + " type uint64;\n" + " }\n" + " leaf counter276 {\n" + " type uint64;\n" + " }\n" + " leaf counter277 {\n" + " type uint64;\n" + " }\n" + " leaf counter278 {\n" + " type uint64;\n" + " }\n" + " leaf counter279 {\n" + " type uint64;\n" + " }\n" + " leaf counter280 {\n" + " type uint64;\n" + " }\n" + " leaf counter281 {\n" + " type uint64;\n" + " }\n" + " leaf counter282 {\n" + " type uint64;\n" + " }\n" + " leaf counter283 {\n" + " type uint64;\n" + " }\n" + " leaf counter284 {\n" + " type uint64;\n" + " }\n" + " leaf counter285 {\n" + " type uint64;\n" + " }\n" + " leaf counter286 {\n" + " type uint64;\n" + " }\n" + " leaf counter287 {\n" + " type uint64;\n" + " }\n" + " leaf counter288 {\n" + " type uint64;\n" + " }\n" + " leaf counter289 {\n" + " type uint64;\n" + " }\n" + " leaf counter290 {\n" + " type uint64;\n" + " }\n" + " leaf counter291 {\n" + " type uint64;\n" + " }\n" + " leaf counter292 {\n" + " type uint64;\n" + " }\n" + " leaf counter293 {\n" + " type uint64;\n" + " }\n" + " leaf counter294 {\n" + " type uint64;\n" + " }\n" + " leaf counter295 {\n" + " type uint64;\n" + " }\n" + " leaf counter296 {\n" + " type uint64;\n" + " }\n" + " leaf counter297 {\n" + " type uint64;\n" + " }\n" + " leaf counter298 {\n" + " type uint64;\n" + " }\n" + " leaf counter299 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter300 {\n" + " type uint64;\n" + " }\n" + " leaf counter301 {\n" + " type uint64;\n" + " }\n" + " leaf counter302 {\n" + " type uint64;\n" + " }\n" + " leaf counter303 {\n" + " type uint64;\n" + " }\n" + " leaf counter304 {\n" + " type uint64;\n" + " }\n" + " leaf counter305 {\n" + " type uint64;\n" + " }\n" + " leaf counter306 {\n" + " type uint64;\n" + " }\n" + " leaf counter307 {\n" + " type uint64;\n" + " }\n" + " leaf counter308 {\n" + " type uint64;\n" + " }\n" + " leaf counter309 {\n" + " type uint64;\n" + " }\n" + " leaf counter310 {\n" + " type uint64;\n" + " }\n" + " leaf counter311 {\n" + " type uint64;\n" + " }\n" + " leaf counter312 {\n" + " type uint64;\n" + " }\n" + " leaf counter313 {\n" + " type uint64;\n" + " }\n" + " leaf counter314 {\n" + " type uint64;\n" + " }\n" + " leaf counter315 {\n" + " type uint64;\n" + " }\n" + " leaf counter316 {\n" + " type uint64;\n" + " }\n" + " leaf counter317 {\n" + " type uint64;\n" + " }\n" + " leaf counter318 {\n" + " type uint64;\n" + " }\n" + " leaf counter319 {\n" + " type uint64;\n" + " }\n" + " leaf counter320 {\n" + " type uint64;\n" + " }\n" + " leaf counter321 {\n" + " type uint64;\n" + " }\n" + " leaf counter322 {\n" + " type uint64;\n" + " }\n" + " leaf counter323 {\n" + " type uint64;\n" + " }\n" + " leaf counter324 {\n" + " type uint64;\n" + " }\n" + " leaf counter325 {\n" + " type uint64;\n" + " }\n" + " leaf counter326 {\n" + " type uint64;\n" + " }\n" + " leaf counter327 {\n" + " type uint64;\n" + " }\n" + " leaf counter328 {\n" + " type uint64;\n" + " }\n" + " leaf counter329 {\n" + " type uint64;\n" + " }\n" + " leaf counter330 {\n" + " type uint64;\n" + " }\n" + " leaf counter331 {\n" + " type uint64;\n" + " }\n" + " leaf counter332 {\n" + " type uint64;\n" + " }\n" + " leaf counter333 {\n" + " type uint64;\n" + " }\n" + " leaf counter334 {\n" + " type uint64;\n" + " }\n" + " leaf counter335 {\n" + " type uint64;\n" + " }\n" + " leaf counter336 {\n" + " type uint64;\n" + " }\n" + " leaf counter337 {\n" + " type uint64;\n" + " }\n" + " leaf counter338 {\n" + " type uint64;\n" + " }\n" + " leaf counter339 {\n" + " type uint64;\n" + " }\n" + " leaf counter340 {\n" + " type uint64;\n" + " }\n" + " leaf counter341 {\n" + " type uint64;\n" + " }\n" + " leaf counter342 {\n" + " type uint64;\n" + " }\n" + " leaf counter343 {\n" + " type uint64;\n" + " }\n" + " leaf counter344 {\n" + " type uint64;\n" + " }\n" + " leaf counter345 {\n" + " type uint64;\n" + " }\n" + " leaf counter346 {\n" + " type uint64;\n" + " }\n" + " leaf counter347 {\n" + " type uint64;\n" + " }\n" + " leaf counter348 {\n" + " type uint64;\n" + " }\n" + " leaf counter349 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter350 {\n" + " type uint64;\n" + " }\n" + " leaf counter351 {\n" + " type uint64;\n" + " }\n" + " leaf counter352 {\n" + " type uint64;\n" + " }\n" + " leaf counter353 {\n" + " type uint64;\n" + " }\n" + " leaf counter354 {\n" + " type uint64;\n" + " }\n" + " leaf counter355 {\n" + " type uint64;\n" + " }\n" + " leaf counter356 {\n" + " type uint64;\n" + " }\n" + " leaf counter357 {\n" + " type uint64;\n" + " }\n" + " leaf counter358 {\n" + " type uint64;\n" + " }\n" + " leaf counter359 {\n" + " type uint64;\n" + " }\n" + " leaf counter360 {\n" + " type uint64;\n" + " }\n" + " leaf counter361 {\n" + " type uint64;\n" + " }\n" + " leaf counter362 {\n" + " type uint64;\n" + " }\n" + " leaf counter363 {\n" + " type uint64;\n" + " }\n" + " leaf counter364 {\n" + " type uint64;\n" + " }\n" + " leaf counter365 {\n" + " type uint64;\n" + " }\n" + " leaf counter366 {\n" + " type uint64;\n" + " }\n" + " leaf counter367 {\n" + " type uint64;\n" + " }\n" + " leaf counter368 {\n" + " type uint64;\n" + " }\n" + " leaf counter369 {\n" + " type uint64;\n" + " }\n" + " leaf counter370 {\n" + " type uint64;\n" + " }\n" + " leaf counter371 {\n" + " type uint64;\n" + " }\n" + " leaf counter372 {\n" + " type uint64;\n" + " }\n" + " leaf counter373 {\n" + " type uint64;\n" + " }\n" + " leaf counter374 {\n" + " type uint64;\n" + " }\n" + " leaf counter375 {\n" + " type uint64;\n" + " }\n" + " leaf counter376 {\n" + " type uint64;\n" + " }\n" + " leaf counter377 {\n" + " type uint64;\n" + " }\n" + " leaf counter378 {\n" + " type uint64;\n" + " }\n" + " leaf counter379 {\n" + " type uint64;\n" + " }\n" + " leaf counter380 {\n" + " type uint64;\n" + " }\n" + " leaf counter381 {\n" + " type uint64;\n" + " }\n" + " leaf counter382 {\n" + " type uint64;\n" + " }\n" + " leaf counter383 {\n" + " type uint64;\n" + " }\n" + " leaf counter384 {\n" + " type uint64;\n" + " }\n" + " leaf counter385 {\n" + " type uint64;\n" + " }\n" + " leaf counter386 {\n" + " type uint64;\n" + " }\n" + " leaf counter387 {\n" + " type uint64;\n" + " }\n" + " leaf counter388 {\n" + " type uint64;\n" + " }\n" + " leaf counter389 {\n" + " type uint64;\n" + " }\n" + " leaf counter390 {\n" + " type uint64;\n" + " }\n" + " leaf counter391 {\n" + " type uint64;\n" + " }\n" + " leaf counter392 {\n" + " type uint64;\n" + " }\n" + " leaf counter393 {\n" + " type uint64;\n" + " }\n" + " leaf counter394 {\n" + " type uint64;\n" + " }\n" + " leaf counter395 {\n" + " type uint64;\n" + " }\n" + " leaf counter396 {\n" + " type uint64;\n" + " }\n" + " leaf counter397 {\n" + " type uint64;\n" + " }\n" + " leaf counter398 {\n" + " type uint64;\n" + " }\n" + " leaf counter399 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter400 {\n" + " type uint64;\n" + " }\n" + " leaf counter401 {\n" + " type uint64;\n" + " }\n" + " leaf counter402 {\n" + " type uint64;\n" + " }\n" + " leaf counter403 {\n" + " type uint64;\n" + " }\n" + " leaf counter404 {\n" + " type uint64;\n" + " }\n" + " leaf counter405 {\n" + " type uint64;\n" + " }\n" + " leaf counter406 {\n" + " type uint64;\n" + " }\n" + " leaf counter407 {\n" + " type uint64;\n" + " }\n" + " leaf counter408 {\n" + " type uint64;\n" + " }\n" + " leaf counter409 {\n" + " type uint64;\n" + " }\n" + " leaf counter410 {\n" + " type uint64;\n" + " }\n" + " leaf counter411 {\n" + " type uint64;\n" + " }\n" + " leaf counter412 {\n" + " type uint64;\n" + " }\n" + " leaf counter413 {\n" + " type uint64;\n" + " }\n" + " leaf counter414 {\n" + " type uint64;\n" + " }\n" + " leaf counter415 {\n" + " type uint64;\n" + " }\n" + " leaf counter416 {\n" + " type uint64;\n" + " }\n" + " leaf counter417 {\n" + " type uint64;\n" + " }\n" + " leaf counter418 {\n" + " type uint64;\n" + " }\n" + " leaf counter419 {\n" + " type uint64;\n" + " }\n" + " leaf counter420 {\n" + " type uint64;\n" + " }\n" + " leaf counter421 {\n" + " type uint64;\n" + " }\n" + " leaf counter422 {\n" + " type uint64;\n" + " }\n" + " leaf counter423 {\n" + " type uint64;\n" + " }\n" + " leaf counter424 {\n" + " type uint64;\n" + " }\n" + " leaf counter425 {\n" + " type uint64;\n" + " }\n" + " leaf counter426 {\n" + " type uint64;\n" + " }\n" + " leaf counter427 {\n" + " type uint64;\n" + " }\n" + " leaf counter428 {\n" + " type uint64;\n" + " }\n" + " leaf counter429 {\n" + " type uint64;\n" + " }\n" + " leaf counter430 {\n" + " type uint64;\n" + " }\n" + " leaf counter431 {\n" + " type uint64;\n" + " }\n" + " leaf counter432 {\n" + " type uint64;\n" + " }\n" + " leaf counter433 {\n" + " type uint64;\n" + " }\n" + " leaf counter434 {\n" + " type uint64;\n" + " }\n" + " leaf counter435 {\n" + " type uint64;\n" + " }\n" + " leaf counter436 {\n" + " type uint64;\n" + " }\n" + " leaf counter437 {\n" + " type uint64;\n" + " }\n" + " leaf counter438 {\n" + " type uint64;\n" + " }\n" + " leaf counter439 {\n" + " type uint64;\n" + " }\n" + " leaf counter440 {\n" + " type uint64;\n" + " }\n" + " leaf counter441 {\n" + " type uint64;\n" + " }\n" + " leaf counter442 {\n" + " type uint64;\n" + " }\n" + " leaf counter443 {\n" + " type uint64;\n" + " }\n" + " leaf counter444 {\n" + " type uint64;\n" + " }\n" + " leaf counter445 {\n" + " type uint64;\n" + " }\n" + " leaf counter446 {\n" + " type uint64;\n" + " }\n" + " leaf counter447 {\n" + " type uint64;\n" + " }\n" + " leaf counter448 {\n" + " type uint64;\n" + " }\n" + " leaf counter449 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter450 {\n" + " type uint64;\n" + " }\n" + " leaf counter451 {\n" + " type uint64;\n" + " }\n" + " leaf counter452 {\n" + " type uint64;\n" + " }\n" + " leaf counter453 {\n" + " type uint64;\n" + " }\n" + " leaf counter454 {\n" + " type uint64;\n" + " }\n" + " leaf counter455 {\n" + " type uint64;\n" + " }\n" + " leaf counter456 {\n" + " type uint64;\n" + " }\n" + " leaf counter457 {\n" + " type uint64;\n" + " }\n" + " leaf counter458 {\n" + " type uint64;\n" + " }\n" + " leaf counter459 {\n" + " type uint64;\n" + " }\n" + " leaf counter460 {\n" + " type uint64;\n" + " }\n" + " leaf counter461 {\n" + " type uint64;\n" + " }\n" + " leaf counter462 {\n" + " type uint64;\n" + " }\n" + " leaf counter463 {\n" + " type uint64;\n" + " }\n" + " leaf counter464 {\n" + " type uint64;\n" + " }\n" + " leaf counter465 {\n" + " type uint64;\n" + " }\n" + " leaf counter466 {\n" + " type uint64;\n" + " }\n" + " leaf counter467 {\n" + " type uint64;\n" + " }\n" + " leaf counter468 {\n" + " type uint64;\n" + " }\n" + " leaf counter469 {\n" + " type uint64;\n" + " }\n" + " leaf counter470 {\n" + " type uint64;\n" + " }\n" + " leaf counter471 {\n" + " type uint64;\n" + " }\n" + " leaf counter472 {\n" + " type uint64;\n" + " }\n" + " leaf counter473 {\n" + " type uint64;\n" + " }\n" + " leaf counter474 {\n" + " type uint64;\n" + " }\n" + " leaf counter475 {\n" + " type uint64;\n" + " }\n" + " leaf counter476 {\n" + " type uint64;\n" + " }\n" + " leaf counter477 {\n" + " type uint64;\n" + " }\n" + " leaf counter478 {\n" + " type uint64;\n" + " }\n" + " leaf counter479 {\n" + " type uint64;\n" + " }\n" + " leaf counter480 {\n" + " type uint64;\n" + " }\n" + " leaf counter481 {\n" + " type uint64;\n" + " }\n" + " leaf counter482 {\n" + " type uint64;\n" + " }\n" + " leaf counter483 {\n" + " type uint64;\n" + " }\n" + " leaf counter484 {\n" + " type uint64;\n" + " }\n" + " leaf counter485 {\n" + " type uint64;\n" + " }\n" + " leaf counter486 {\n" + " type uint64;\n" + " }\n" + " leaf counter487 {\n" + " type uint64;\n" + " }\n" + " leaf counter488 {\n" + " type uint64;\n" + " }\n" + " leaf counter489 {\n" + " type uint64;\n" + " }\n" + " leaf counter490 {\n" + " type uint64;\n" + " }\n" + " leaf counter491 {\n" + " type uint64;\n" + " }\n" + " leaf counter492 {\n" + " type uint64;\n" + " }\n" + " leaf counter493 {\n" + " type uint64;\n" + " }\n" + " leaf counter494 {\n" + " type uint64;\n" + " }\n" + " leaf counter495 {\n" + " type uint64;\n" + " }\n" + " leaf counter496 {\n" + " type uint64;\n" + " }\n" + " leaf counter497 {\n" + " type uint64;\n" + " }\n" + " leaf counter498 {\n" + " type uint64;\n" + " }\n" + " leaf counter499 {\n" + " type uint64;\n" + " }\n" + " }\n" + "}\n"); + + data_xml = malloc(16384); + strcpy(data_xml, + "<stats xmlns=\"urn:counters\">\n"); + strcat(data_xml, + " <counter1>1</counter1>\n" + " <counter2>2</counter2>\n" + " <counter3>3</counter3>\n" + " <counter4>4</counter4>\n" + " <counter5>5</counter5>\n" + " <counter6>6</counter6>\n" + " <counter7>7</counter7>\n" + " <counter8>8</counter8>\n" + " <counter9>9</counter9>\n" + " <counter10>10</counter10>\n" + " <counter11>11</counter11>\n" + " <counter12>12</counter12>\n" + " <counter13>13</counter13>\n" + " <counter14>14</counter14>\n" + " <counter15>15</counter15>\n" + " <counter16>16</counter16>\n" + " <counter17>17</counter17>\n" + " <counter18>18</counter18>\n" + " <counter19>19</counter19>\n" + " <counter20>20</counter20>\n" + " <counter21>21</counter21>\n" + " <counter22>22</counter22>\n" + " <counter23>23</counter23>\n" + " <counter24>24</counter24>\n" + " <counter25>25</counter25>\n" + " <counter26>26</counter26>\n" + " <counter27>27</counter27>\n" + " <counter28>28</counter28>\n" + " <counter29>29</counter29>\n" + " <counter30>30</counter30>\n" + " <counter31>31</counter31>\n" + " <counter32>32</counter32>\n" + " <counter33>33</counter33>\n" + " <counter34>34</counter34>\n" + " <counter35>35</counter35>\n" + " <counter36>36</counter36>\n" + " <counter37>37</counter37>\n" + " <counter38>38</counter38>\n" + " <counter39>39</counter39>\n" + " <counter40>40</counter40>\n" + " <counter41>41</counter41>\n" + " <counter42>42</counter42>\n" + " <counter43>43</counter43>\n" + " <counter44>44</counter44>\n" + " <counter45>45</counter45>\n" + " <counter46>46</counter46>\n" + " <counter47>47</counter47>\n" + " <counter48>48</counter48>\n" + " <counter49>49</counter49>\n" + " <counter50>50</counter50>\n" + " <counter51>51</counter51>\n" + " <counter52>52</counter52>\n" + " <counter53>53</counter53>\n" + " <counter54>54</counter54>\n" + " <counter55>55</counter55>\n" + " <counter56>56</counter56>\n" + " <counter57>57</counter57>\n" + " <counter58>58</counter58>\n" + " <counter59>59</counter59>\n" + " <counter60>60</counter60>\n" + " <counter61>61</counter61>\n" + " <counter62>62</counter62>\n" + " <counter63>63</counter63>\n" + " <counter64>64</counter64>\n" + " <counter65>65</counter65>\n" + " <counter66>66</counter66>\n" + " <counter67>67</counter67>\n" + " <counter68>68</counter68>\n" + " <counter69>69</counter69>\n" + " <counter70>70</counter70>\n" + " <counter71>71</counter71>\n" + " <counter72>72</counter72>\n" + " <counter73>73</counter73>\n" + " <counter74>74</counter74>\n" + " <counter75>75</counter75>\n" + " <counter76>76</counter76>\n" + " <counter77>77</counter77>\n" + " <counter78>78</counter78>\n" + " <counter79>79</counter79>\n" + " <counter80>80</counter80>\n" + " <counter81>81</counter81>\n" + " <counter82>82</counter82>\n" + " <counter83>83</counter83>\n" + " <counter84>84</counter84>\n" + " <counter85>85</counter85>\n" + " <counter86>86</counter86>\n" + " <counter87>87</counter87>\n" + " <counter88>88</counter88>\n" + " <counter89>89</counter89>\n" + " <counter90>90</counter90>\n" + " <counter91>91</counter91>\n" + " <counter92>92</counter92>\n" + " <counter93>93</counter93>\n" + " <counter94>94</counter94>\n" + " <counter95>95</counter95>\n" + " <counter96>96</counter96>\n" + " <counter97>97</counter97>\n" + " <counter98>98</counter98>\n" + " <counter99>99</counter99>\n"); + strcat(data_xml, + " <counter100>100</counter100>\n" + " <counter101>101</counter101>\n" + " <counter102>102</counter102>\n" + " <counter103>103</counter103>\n" + " <counter104>104</counter104>\n" + " <counter105>105</counter105>\n" + " <counter106>106</counter106>\n" + " <counter107>107</counter107>\n" + " <counter108>108</counter108>\n" + " <counter109>109</counter109>\n" + " <counter110>110</counter110>\n" + " <counter111>111</counter111>\n" + " <counter112>112</counter112>\n" + " <counter113>113</counter113>\n" + " <counter114>114</counter114>\n" + " <counter115>115</counter115>\n" + " <counter116>116</counter116>\n" + " <counter117>117</counter117>\n" + " <counter118>118</counter118>\n" + " <counter119>119</counter119>\n" + " <counter120>120</counter120>\n" + " <counter121>121</counter121>\n" + " <counter122>122</counter122>\n" + " <counter123>123</counter123>\n" + " <counter124>124</counter124>\n" + " <counter125>125</counter125>\n" + " <counter126>126</counter126>\n" + " <counter127>127</counter127>\n" + " <counter128>128</counter128>\n" + " <counter129>129</counter129>\n" + " <counter130>130</counter130>\n" + " <counter131>131</counter131>\n" + " <counter132>132</counter132>\n" + " <counter133>133</counter133>\n" + " <counter134>134</counter134>\n" + " <counter135>135</counter135>\n" + " <counter136>136</counter136>\n" + " <counter137>137</counter137>\n" + " <counter138>138</counter138>\n" + " <counter139>139</counter139>\n" + " <counter140>140</counter140>\n" + " <counter141>141</counter141>\n" + " <counter142>142</counter142>\n" + " <counter143>143</counter143>\n" + " <counter144>144</counter144>\n" + " <counter145>145</counter145>\n" + " <counter146>146</counter146>\n" + " <counter147>147</counter147>\n" + " <counter148>148</counter148>\n" + " <counter149>149</counter149>\n" + " <counter150>150</counter150>\n" + " <counter151>151</counter151>\n" + " <counter152>152</counter152>\n" + " <counter153>153</counter153>\n" + " <counter154>154</counter154>\n" + " <counter155>155</counter155>\n" + " <counter156>156</counter156>\n" + " <counter157>157</counter157>\n" + " <counter158>158</counter158>\n" + " <counter159>159</counter159>\n" + " <counter160>160</counter160>\n" + " <counter161>161</counter161>\n" + " <counter162>162</counter162>\n" + " <counter163>163</counter163>\n" + " <counter164>164</counter164>\n" + " <counter165>165</counter165>\n" + " <counter166>166</counter166>\n" + " <counter167>167</counter167>\n" + " <counter168>168</counter168>\n" + " <counter169>169</counter169>\n" + " <counter170>170</counter170>\n" + " <counter171>171</counter171>\n" + " <counter172>172</counter172>\n" + " <counter173>173</counter173>\n" + " <counter174>174</counter174>\n" + " <counter175>175</counter175>\n" + " <counter176>176</counter176>\n" + " <counter177>177</counter177>\n" + " <counter178>178</counter178>\n" + " <counter179>179</counter179>\n" + " <counter180>180</counter180>\n" + " <counter181>181</counter181>\n" + " <counter182>182</counter182>\n" + " <counter183>183</counter183>\n" + " <counter184>184</counter184>\n" + " <counter185>185</counter185>\n" + " <counter186>186</counter186>\n" + " <counter187>187</counter187>\n" + " <counter188>188</counter188>\n" + " <counter189>189</counter189>\n" + " <counter190>190</counter190>\n" + " <counter191>191</counter191>\n" + " <counter192>192</counter192>\n" + " <counter193>193</counter193>\n" + " <counter194>194</counter194>\n" + " <counter195>195</counter195>\n" + " <counter196>196</counter196>\n" + " <counter197>197</counter197>\n" + " <counter198>198</counter198>\n" + " <counter199>199</counter199>\n"); + strcat(data_xml, + " <counter200>200</counter200>\n" + " <counter201>201</counter201>\n" + " <counter202>202</counter202>\n" + " <counter203>203</counter203>\n" + " <counter204>204</counter204>\n" + " <counter205>205</counter205>\n" + " <counter206>206</counter206>\n" + " <counter207>207</counter207>\n" + " <counter208>208</counter208>\n" + " <counter209>209</counter209>\n" + " <counter210>210</counter210>\n" + " <counter211>211</counter211>\n" + " <counter212>212</counter212>\n" + " <counter213>213</counter213>\n" + " <counter214>214</counter214>\n" + " <counter215>215</counter215>\n" + " <counter216>216</counter216>\n" + " <counter217>217</counter217>\n" + " <counter218>218</counter218>\n" + " <counter219>219</counter219>\n" + " <counter220>220</counter220>\n" + " <counter221>221</counter221>\n" + " <counter222>222</counter222>\n" + " <counter223>223</counter223>\n" + " <counter224>224</counter224>\n" + " <counter225>225</counter225>\n" + " <counter226>226</counter226>\n" + " <counter227>227</counter227>\n" + " <counter228>228</counter228>\n" + " <counter229>229</counter229>\n" + " <counter230>230</counter230>\n" + " <counter231>231</counter231>\n" + " <counter232>232</counter232>\n" + " <counter233>233</counter233>\n" + " <counter234>234</counter234>\n" + " <counter235>235</counter235>\n" + " <counter236>236</counter236>\n" + " <counter237>237</counter237>\n" + " <counter238>238</counter238>\n" + " <counter239>239</counter239>\n" + " <counter240>240</counter240>\n" + " <counter241>241</counter241>\n" + " <counter242>242</counter242>\n" + " <counter243>243</counter243>\n" + " <counter244>244</counter244>\n" + " <counter245>245</counter245>\n" + " <counter246>246</counter246>\n" + " <counter247>247</counter247>\n" + " <counter248>248</counter248>\n" + " <counter249>249</counter249>\n" + " <counter250>250</counter250>\n" + " <counter251>251</counter251>\n" + " <counter252>252</counter252>\n" + " <counter253>253</counter253>\n" + " <counter254>254</counter254>\n" + " <counter255>255</counter255>\n" + " <counter256>256</counter256>\n" + " <counter257>257</counter257>\n" + " <counter258>258</counter258>\n" + " <counter259>259</counter259>\n" + " <counter260>260</counter260>\n" + " <counter261>261</counter261>\n" + " <counter262>262</counter262>\n" + " <counter263>263</counter263>\n" + " <counter264>264</counter264>\n" + " <counter265>265</counter265>\n" + " <counter266>266</counter266>\n" + " <counter267>267</counter267>\n" + " <counter268>268</counter268>\n" + " <counter269>269</counter269>\n" + " <counter270>270</counter270>\n" + " <counter271>271</counter271>\n" + " <counter272>272</counter272>\n" + " <counter273>273</counter273>\n" + " <counter274>274</counter274>\n" + " <counter275>275</counter275>\n" + " <counter276>276</counter276>\n" + " <counter277>277</counter277>\n" + " <counter278>278</counter278>\n" + " <counter279>279</counter279>\n" + " <counter280>280</counter280>\n" + " <counter281>281</counter281>\n" + " <counter282>282</counter282>\n" + " <counter283>283</counter283>\n" + " <counter284>284</counter284>\n" + " <counter285>285</counter285>\n" + " <counter286>286</counter286>\n" + " <counter287>287</counter287>\n" + " <counter288>288</counter288>\n" + " <counter289>289</counter289>\n" + " <counter290>290</counter290>\n" + " <counter291>291</counter291>\n" + " <counter292>292</counter292>\n" + " <counter293>293</counter293>\n" + " <counter294>294</counter294>\n" + " <counter295>295</counter295>\n" + " <counter296>296</counter296>\n" + " <counter297>297</counter297>\n" + " <counter298>298</counter298>\n" + " <counter299>299</counter299>\n"); + strcat(data_xml, + " <counter300>300</counter300>\n" + " <counter301>301</counter301>\n" + " <counter302>302</counter302>\n" + " <counter303>303</counter303>\n" + " <counter304>304</counter304>\n" + " <counter305>305</counter305>\n" + " <counter306>306</counter306>\n" + " <counter307>307</counter307>\n" + " <counter308>308</counter308>\n" + " <counter309>309</counter309>\n" + " <counter310>310</counter310>\n" + " <counter311>311</counter311>\n" + " <counter312>312</counter312>\n" + " <counter313>313</counter313>\n" + " <counter314>314</counter314>\n" + " <counter315>315</counter315>\n" + " <counter316>316</counter316>\n" + " <counter317>317</counter317>\n" + " <counter318>318</counter318>\n" + " <counter319>319</counter319>\n" + " <counter320>320</counter320>\n" + " <counter321>321</counter321>\n" + " <counter322>322</counter322>\n" + " <counter323>323</counter323>\n" + " <counter324>324</counter324>\n" + " <counter325>325</counter325>\n" + " <counter326>326</counter326>\n" + " <counter327>327</counter327>\n" + " <counter328>328</counter328>\n" + " <counter329>329</counter329>\n" + " <counter330>330</counter330>\n" + " <counter331>331</counter331>\n" + " <counter332>332</counter332>\n" + " <counter333>333</counter333>\n" + " <counter334>334</counter334>\n" + " <counter335>335</counter335>\n" + " <counter336>336</counter336>\n" + " <counter337>337</counter337>\n" + " <counter338>338</counter338>\n" + " <counter339>339</counter339>\n" + " <counter340>340</counter340>\n" + " <counter341>341</counter341>\n" + " <counter342>342</counter342>\n" + " <counter343>343</counter343>\n" + " <counter344>344</counter344>\n" + " <counter345>345</counter345>\n" + " <counter346>346</counter346>\n" + " <counter347>347</counter347>\n" + " <counter348>348</counter348>\n" + " <counter349>349</counter349>\n" + " <counter350>350</counter350>\n" + " <counter351>351</counter351>\n" + " <counter352>352</counter352>\n" + " <counter353>353</counter353>\n" + " <counter354>354</counter354>\n" + " <counter355>355</counter355>\n" + " <counter356>356</counter356>\n" + " <counter357>357</counter357>\n" + " <counter358>358</counter358>\n" + " <counter359>359</counter359>\n" + " <counter360>360</counter360>\n" + " <counter361>361</counter361>\n" + " <counter362>362</counter362>\n" + " <counter363>363</counter363>\n" + " <counter364>364</counter364>\n" + " <counter365>365</counter365>\n" + " <counter366>366</counter366>\n" + " <counter367>367</counter367>\n" + " <counter368>368</counter368>\n" + " <counter369>369</counter369>\n" + " <counter370>370</counter370>\n" + " <counter371>371</counter371>\n" + " <counter372>372</counter372>\n" + " <counter373>373</counter373>\n" + " <counter374>374</counter374>\n" + " <counter375>375</counter375>\n" + " <counter376>376</counter376>\n" + " <counter377>377</counter377>\n" + " <counter378>378</counter378>\n" + " <counter379>379</counter379>\n" + " <counter380>380</counter380>\n" + " <counter381>381</counter381>\n" + " <counter382>382</counter382>\n" + " <counter383>383</counter383>\n" + " <counter384>384</counter384>\n" + " <counter385>385</counter385>\n" + " <counter386>386</counter386>\n" + " <counter387>387</counter387>\n" + " <counter388>388</counter388>\n" + " <counter389>389</counter389>\n" + " <counter390>390</counter390>\n" + " <counter391>391</counter391>\n" + " <counter392>392</counter392>\n" + " <counter393>393</counter393>\n" + " <counter394>394</counter394>\n" + " <counter395>395</counter395>\n" + " <counter396>396</counter396>\n" + " <counter397>397</counter397>\n" + " <counter398>398</counter398>\n" + " <counter399>399</counter399>\n"); + strcat(data_xml, + " <counter400>400</counter400>\n" + " <counter401>401</counter401>\n" + " <counter402>402</counter402>\n" + " <counter403>403</counter403>\n" + " <counter404>404</counter404>\n" + " <counter405>405</counter405>\n" + " <counter406>406</counter406>\n" + " <counter407>407</counter407>\n" + " <counter408>408</counter408>\n" + " <counter409>409</counter409>\n" + " <counter410>410</counter410>\n" + " <counter411>411</counter411>\n" + " <counter412>412</counter412>\n" + " <counter413>413</counter413>\n" + " <counter414>414</counter414>\n" + " <counter415>415</counter415>\n" + " <counter416>416</counter416>\n" + " <counter417>417</counter417>\n" + " <counter418>418</counter418>\n" + " <counter419>419</counter419>\n" + " <counter420>420</counter420>\n" + " <counter421>421</counter421>\n" + " <counter422>422</counter422>\n" + " <counter423>423</counter423>\n" + " <counter424>424</counter424>\n" + " <counter425>425</counter425>\n" + " <counter426>426</counter426>\n" + " <counter427>427</counter427>\n" + " <counter428>428</counter428>\n" + " <counter429>429</counter429>\n" + " <counter430>430</counter430>\n" + " <counter431>431</counter431>\n" + " <counter432>432</counter432>\n" + " <counter433>433</counter433>\n" + " <counter434>434</counter434>\n" + " <counter435>435</counter435>\n" + " <counter436>436</counter436>\n" + " <counter437>437</counter437>\n" + " <counter438>438</counter438>\n" + " <counter439>439</counter439>\n" + " <counter440>440</counter440>\n" + " <counter441>441</counter441>\n" + " <counter442>442</counter442>\n" + " <counter443>443</counter443>\n" + " <counter444>444</counter444>\n" + " <counter445>445</counter445>\n" + " <counter446>446</counter446>\n" + " <counter447>447</counter447>\n" + " <counter448>448</counter448>\n" + " <counter449>449</counter449>\n" + " <counter450>450</counter450>\n" + " <counter451>451</counter451>\n" + " <counter452>452</counter452>\n" + " <counter453>453</counter453>\n" + " <counter454>454</counter454>\n" + " <counter455>455</counter455>\n" + " <counter456>456</counter456>\n" + " <counter457>457</counter457>\n" + " <counter458>458</counter458>\n" + " <counter459>459</counter459>\n" + " <counter460>460</counter460>\n" + " <counter461>461</counter461>\n" + " <counter462>462</counter462>\n" + " <counter463>463</counter463>\n" + " <counter464>464</counter464>\n" + " <counter465>465</counter465>\n" + " <counter466>466</counter466>\n" + " <counter467>467</counter467>\n" + " <counter468>468</counter468>\n" + " <counter469>469</counter469>\n" + " <counter470>470</counter470>\n" + " <counter471>471</counter471>\n" + " <counter472>472</counter472>\n" + " <counter473>473</counter473>\n" + " <counter474>474</counter474>\n" + " <counter475>475</counter475>\n" + " <counter476>476</counter476>\n" + " <counter477>477</counter477>\n" + " <counter478>478</counter478>\n" + " <counter479>479</counter479>\n" + " <counter480>480</counter480>\n" + " <counter481>481</counter481>\n" + " <counter482>482</counter482>\n" + " <counter483>483</counter483>\n" + " <counter484>484</counter484>\n" + " <counter485>485</counter485>\n" + " <counter486>486</counter486>\n" + " <counter487>487</counter487>\n" + " <counter488>488</counter488>\n" + " <counter489>489</counter489>\n" + " <counter490>490</counter490>\n" + " <counter491>491</counter491>\n" + " <counter492>492</counter492>\n" + " <counter493>493</counter493>\n" + " <counter494>494</counter494>\n" + " <counter495>495</counter495>\n" + " <counter496>496</counter496>\n" + " <counter497>497</counter497>\n" + " <counter498>498</counter498>\n" + " <counter499>499</counter499>\n" + "</stats>\n"); + + UTEST_ADD_MODULE(counters_yang, LYS_IN_YANG, NULL, NULL); + + check_print_parse(state, data_xml); + + free(counters_yang); + free(data_xml); +} + +#if 0 + +static void +test_types(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "types", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/types.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_annotations(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/annotations.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_similar_annot_names(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/similar-annot-names.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_many_child_annot(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/many-childs-annot.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_union(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "union", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_union2(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "statements", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union2.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_collisions(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/collisions.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_anydata(void **state) +{ + struct state *st = (*state); + const struct lys_module *mod; + int ret; + const char *test_anydata = + "module test-anydata {" + " namespace \"urn:test-anydata\";" + " prefix ya;" + "" + " container cont {" + " anydata ntf;" + " }" + "}"; + + assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-notifications", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL); + assert_ptr_not_equal(st->dt1, NULL); + + / *get notification in LYB format to set as anydata content * / + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + lyd_free_withsiblings(st->dt1); + st->dt1 = NULL; + + / *now comes the real test, test anydata * / + mod = lys_parse_mem(st->ctx, test_anydata, LYS_YANG); + assert_non_null(mod); + + st->dt1 = lyd_new(NULL, mod, "cont"); + assert_non_null(st->dt1); + + assert_non_null(lyd_new_anydata(st->dt1, NULL, "ntf", st->mem, LYD_ANYDATA_LYBD)); + st->mem = NULL; + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + ret = lyd_validate(&st->dt1, LYD_OPT_CONFIG, NULL); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); + + /* and also test the embedded notification itself */ + free(st->mem); + ret = lyd_lyb_data_length(((struct lyd_node_anydata *)st->dt1->child)->value.mem); + st->mem = malloc(ret); + memcpy(st->mem, ((struct lyd_node_anydata *)st->dt1->child)->value.mem, ret); + + lyd_free_withsiblings(st->dt2); + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_NOTIF | LYD_OPT_STRICT | LYD_OPT_NOEXTDEPS, NULL); + assert_ptr_not_equal(st->dt2, NULL); + + /* parse the JSON again for this comparison */ + lyd_free_withsiblings(st->dt1); + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL); + assert_ptr_not_equal(st->dt1, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_submodule_feature(void **state) +{ + struct state *st = (*state); + const struct lys_module *mod; + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + mod = ly_ctx_load_module(st->ctx, "feature-submodule-main", NULL); + assert_non_null(mod); + assert_int_equal(lys_features_enable(mod, "test-submodule-feature"), 0); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/test-submodule-feature.json", LYD_JSON, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_coliding_augments(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "augment-target", NULL)); + assert_non_null(ly_ctx_load_module(st->ctx, "augment0", NULL)); + assert_non_null(ly_ctx_load_module(st->ctx, "augment1", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/augment.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_leafrefs(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "leafrefs2", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/leafrefs2.json", LYD_JSON, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +#endif + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(tests_leaflist), + UTEST(tests_list), + UTEST(tests_any), + UTEST(test_ietf_interfaces, setup), + UTEST(test_origin, setup), + UTEST(test_statements, setup), + UTEST(test_opaq, setup), + UTEST(test_collisions, setup), +#if 0 + cmocka_unit_test_setup_teardown(test_types, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_annotations, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_similar_annot_names, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_many_child_annot, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_union, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_union2, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_collisions, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_anydata, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_submodule_feature, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_coliding_augments, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_leafrefs, setup_f, teardown_f), +#endif + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_merge.c b/tests/utests/data/test_merge.c new file mode 100644 index 0000000..3e7b772 --- /dev/null +++ b/tests/utests/data/test_merge.c @@ -0,0 +1,756 @@ +/** + * @file test_merge.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief tests for complex data merges. + * + * Copyright (c) 2020 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 "libyang.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +#define CONTEXT_CREATE \ + CONTEXT_CREATE_PATH(NULL) + +#define LYD_TREE_CHECK_CHAR(MODEL, TEXT, PARAMS) \ + CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | PARAMS) + +static void +test_batch(void **state) +{ + const char *start = + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n"; + const char *data[] = { + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n" + }; + const char *output_template = + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n"; + + struct lyd_node *target; + + CHECK_PARSE_LYD_PARAM(start, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, target); + + for (int32_t i = 0; i < 11; ++i) { + struct lyd_node *source; + + CHECK_PARSE_LYD_PARAM(data[i], LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, source); + assert_int_equal(LY_SUCCESS, lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT)); + } + + LYD_TREE_CHECK_CHAR(target, output_template, 0); + + lyd_free_all(target); +} + +static void +test_leaf(void **state) +{ + const char *sch = "module x {" + " namespace urn:x;" + " prefix x;" + " container A {" + " leaf f1 {type string;}" + " container B {" + " leaf f2 {type string;}" + " }" + " }" + " }"; + const char *trg = "<A xmlns=\"urn:x\"> <f1>block</f1> </A>"; + const char *src = "<A xmlns=\"urn:x\"> <f1>aa</f1> <B> <f2>bb</f2> </B> </A>"; + const char *result = "<A xmlns=\"urn:x\"><f1>aa</f1><B><f2>bb</f2></B></A>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_container(void **state) +{ + const char *sch = + "module A {\n" + " namespace \"aa:A\";\n" + " prefix A;\n" + " container A {\n" + " leaf f1 {type string;}\n" + " container B {\n" + " leaf f2 {type string;}\n" + " }\n" + " container C {\n" + " leaf f3 {type string;}\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = "<A xmlns=\"aa:A\"> <B> <f2>aaa</f2> </B> </A>"; + const char *src = "<A xmlns=\"aa:A\"> <C> <f3>bbb</f3> </C> </A>"; + const char *result = "<A xmlns=\"aa:A\"><B><f2>aaa</f2></B><C><f3>bbb</f3></C></A>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK); + + /* destroy */ + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_list(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " list b-list1 {\n" + " key p1;\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " leaf p3 {\n" + " type boolean;\n" + " default false;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <p3>true</p3>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " <p3>true</p3>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_list2(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " list b-list1 {\n" + " key p1;\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " container inner2 {\n" + " leaf p3 {\n" + " type boolean;\n" + " default false;\n" + " }\n" + " leaf p4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dup_inst_list(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " config false;\n" + " list b-list1 {\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " container inner2 {\n" + " leaf p4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>2</p1>\n" + " <p2>a</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>2</p1>\n" + " <p2>a</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dup_inst_llist(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " config false;\n" + " leaf-list b-llist1 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_case(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + " container cont {\n" + " choice ch {\n" + " container inner {\n" + " leaf p1 {\n" + " type string;\n" + " }\n" + " }\n" + " case c2 {\n" + " leaf p1 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<cont xmlns=\"http://test/merge\">\n" + " <inner>\n" + " <p1>1</p1>\n" + " </inner>\n" + "</cont>\n"; + const char *src = + "<cont xmlns=\"http://test/merge\">\n" + " <p1>1</p1>\n" + "</cont>\n"; + const char *result = + "<cont xmlns=\"http://test/merge\">\n" + " <p1>1</p1>\n" + "</cont>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dflt(void **state) +{ + const char *sch = + "module merge-dflt {\n" + " namespace \"urn:merge-dflt\";\n" + " prefix md;\n" + " container top {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " default \"c_dflt\";\n" + " }\n" + " }\n" + "}\n"; + struct lyd_node *target = NULL; + struct lyd_node *source = NULL; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS); + assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT | LYD_MERGE_DEFAULTS), LY_SUCCESS); + source = NULL; + + /* c should be replaced and now be default */ + assert_string_equal(lyd_child(target)->prev->schema->name, "c"); + assert_true(lyd_child(target)->prev->flags & LYD_DEFAULT); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_dflt2(void **state) +{ + const char *sch = + "module merge-dflt {\n" + " namespace \"urn:merge-dflt\";\n" + " prefix md;\n" + " container top {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " default \"c_dflt\";\n" + " }\n" + " }\n" + "}\n"; + struct lyd_node *target; + struct lyd_node *source; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS); + assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + + /* c should not be replaced, so c remains not default */ + assert_false(lyd_child(target)->flags & LYD_DEFAULT); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_leafrefs(void **state) +{ + const char *sch = "module x {" + " namespace urn:x;" + " prefix x;" + " list l {" + " key n;" + " leaf n { type string; }" + " leaf t { type string; }" + " leaf r { type leafref { path '/l/n'; } }}}"; + const char *trg = "<l xmlns=\"urn:x\"><n>a</n></l>" + "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>"; + const char *src = "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>" + "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>"; + const char *res = "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>" + "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>" + "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + + LYD_TREE_CHECK_CHAR(target, res, LYD_PRINT_SHRINK); + + lyd_free_all(source); + lyd_free_all(target); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_batch), + UTEST(test_leaf), + UTEST(test_container), + UTEST(test_list), + UTEST(test_list2), + UTEST(test_dup_inst_list), + UTEST(test_dup_inst_llist), + UTEST(test_case), + UTEST(test_dflt), + UTEST(test_dflt2), + UTEST(test_leafrefs), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c new file mode 100644 index 0000000..7194893 --- /dev/null +++ b/tests/utests/data/test_new.c @@ -0,0 +1,536 @@ +/** + * @file test_new.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions for creating data + * + * Copyright (c) 2020 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 "libyang.h" + +/* common module for the tests */ +const char *schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + " list l1 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " }\n" + " list l11 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint32;\n" + " }\n" + " leaf b {\n" + " type uint32;\n" + " }\n" + " }\n" + " leaf foo {\n" + " type uint16;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " container c {\n" + " leaf-list x {\n" + " type string;\n" + " }\n" + " }\n" + " anydata any {\n" + " config false;\n" + " }\n" + " anyxml anyx;\n" + " leaf-list ll2 {\n" + " config false;\n" + " type string;\n" + " }\n" + " list l2 {\n" + " config false;\n" + " container c {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container c2 {\n" + " config false;\n" + " list l3 {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " leaf y {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rpc oper {\n" + " input {\n" + " leaf param {\n" + " type string;\n" + " }\n" + " }\n" + " output {\n" + " leaf param {\n" + " type int8;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + +static void +test_top_level(void **state) +{ + struct lys_module *mod; + struct lyd_node *node, *rpc; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + + /* list */ + assert_int_equal(lyd_new_list(NULL, mod, "l1", 0, &node, "val_a", "val_b"), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[]", 0, &node), LY_EVALID); + CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", "/a:l1", 0); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[key1='a'][key2='b']", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Not found node \"key1\" in path.", "/a:l1", 0); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID); + CHECK_LOG_CTX("Key expected instead of leaf \"c\" in path.", "/a:l1", 0); + + assert_int_equal(lyd_new_list2(NULL, mod, "c", "[a='a'][b='b']", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("List node \"c\" not found.", NULL, 0); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a=''][b='']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a:a='a'][a:b='b']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a= 'a']\n[b =\t'b']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + const char *key_vals[] = {"a", "b"}; + + assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, NULL, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + uint32_t val_lens[] = {1, 1}; + + assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, val_lens, LYD_NEW_VAL_BIN, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, val_lens, LYD_NEW_VAL_CANON, &node), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, val_lens, LYD_NEW_VAL_CANON | LYD_NEW_VAL_STORE_ONLY, &node), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)) (lyd_new_list3()).", NULL, 0); + + assert_int_equal(lyd_new_list(NULL, mod, "l1", LYD_NEW_VAL_BIN, &node, "val_a", 5, "val_b", 5), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_list(NULL, mod, "l1", LYD_NEW_VAL_BIN | LYD_NEW_VAL_STORE_ONLY, &node, "val_a", 5, "val_b", 5), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)) (lyd_new_list()).", NULL, 0); + + assert_int_equal(lyd_new_list(NULL, mod, "l1", LYD_NEW_VAL_CANON, &node, "val_a", "val_b"), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_list(NULL, mod, "l1", LYD_NEW_VAL_CANON | LYD_NEW_VAL_STORE_ONLY, &node, "val_a", "val_b"), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)) (lyd_new_list()).", NULL, 0); + + /* leaf */ + assert_int_equal(lyd_new_term(NULL, mod, "foo", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID); + CHECK_LOG_CTX("Invalid type uint16 value \"[a='a'][b='b'][c='c']\".", "/a:foo", 0); + + assert_int_equal(lyd_new_term(NULL, mod, "c", "value", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Term node \"c\" not found.", NULL, 0); + + assert_int_equal(lyd_new_term(NULL, mod, "foo", "256", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_term(NULL, mod, "foo", "25", LYD_NEW_VAL_BIN, &node), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(options & 0x04) (lyd_new_term()).", NULL, 0); + assert_int_equal(lyd_new_term_bin(NULL, mod, "foo", "25", 2, LYD_NEW_VAL_BIN, &node), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_term_bin(NULL, mod, "foo", "25", 2, LYD_NEW_VAL_STORE_ONLY, &node), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)) (_lyd_new_term()).", NULL, 0); + + assert_int_equal(lyd_new_term(NULL, mod, "foo", "25", LYD_NEW_VAL_CANON, &node), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_term(NULL, mod, "foo", "25", LYD_NEW_VAL_CANON | LYD_NEW_VAL_STORE_ONLY, &node), LY_EINVAL); + CHECK_LOG_CTX("Invalid argument !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)) (_lyd_new_term()).", NULL, 0); + + /* leaf-list */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "ahoy", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* container */ + assert_int_equal(lyd_new_inner(NULL, mod, "c", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_inner(NULL, mod, "l1", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l1\" not found.", NULL, 0); + + assert_int_equal(lyd_new_inner(NULL, mod, "l2", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l2\" not found.", NULL, 0); + + /* anydata */ + assert_int_equal(lyd_new_any(NULL, mod, "any", "{\"node\":\"val\"}", LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_any(NULL, mod, "any", "<node>val</node>", LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* key-less list */ + assert_int_equal(lyd_new_list2(NULL, mod, "l2", "[a='a'][b='b']", 0, &node), LY_EVALID); + CHECK_LOG_CTX("List predicate defined for keyless list \"l2\" in path.", "/a:l2", 0); + + assert_int_equal(lyd_new_list2(NULL, mod, "l2", "", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l2", NULL, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list(NULL, mod, "l2", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* RPC */ + assert_int_equal(lyd_new_inner(NULL, mod, "oper", 0, &rpc), LY_SUCCESS); + assert_int_equal(lyd_new_term(rpc, mod, "param", "22", 0, &node), LY_SUCCESS); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_node_leaf *)node->schema)->type->basetype); + assert_int_equal(lyd_new_term(rpc, mod, "param", "22", LYD_NEW_VAL_OUTPUT, &node), LY_SUCCESS); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_node_leaf *)node->schema)->type->basetype); + lyd_free_tree(rpc); +} + +static void +test_opaq(void **state) +{ + struct lyd_node *root, *node; + struct lyd_node_opaq *opq; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_opaq(NULL, UTEST_LYCTX, "node1", NULL, NULL, "my-module", &root), LY_SUCCESS); + assert_null(root->schema); + opq = (struct lyd_node_opaq *)root; + assert_string_equal(opq->name.name, "node1"); + assert_string_equal(opq->name.module_name, "my-module"); + assert_string_equal(opq->value, ""); + + assert_int_equal(lyd_new_opaq(root, NULL, "node2", "value", NULL, "my-module2", &node), LY_SUCCESS); + assert_null(node->schema); + opq = (struct lyd_node_opaq *)node; + assert_string_equal(opq->name.name, "node2"); + assert_string_equal(opq->name.module_name, "my-module2"); + assert_string_equal(opq->value, "value"); + assert_ptr_equal(opq->parent, root); + + lyd_free_tree(root); +} + +static void +test_path(void **state) +{ + LY_ERR ret; + struct lyd_node *root, *node, *parent; + struct lys_module *mod; + char *str; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + + /* create 2 nodes */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c/x[.='val']", "vvv", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(root->schema->name, "c"); + assert_non_null(node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val", lyd_get_value(node)); + + /* append another */ + ret = lyd_new_path2(root, NULL, "/a:c/x", "val2", 0, 0, 0, &parent, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_ptr_equal(parent, node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val2", lyd_get_value(node)); + + /* and a last one */ + ret = lyd_new_path2(root, NULL, "x", "val3", 0, 0, 0, &parent, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_ptr_equal(parent, node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val3", lyd_get_value(node)); + + lyd_free_tree(root); + + /* try LYD_NEWOPT_OPAQ */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, 0, NULL, NULL); + assert_int_equal(ret, LY_EINVAL); + CHECK_LOG_CTX("Predicate missing for list \"l1\" in path \"/a:l1\".", "/a:l1", 0); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + lyd_free_tree(root); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, 0, NULL, NULL); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Invalid type uint16 empty value.", "/a:foo", 0); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + lyd_free_tree(root); + + ret = lyd_new_path(NULL, UTEST_LYCTX, "/a:l11", NULL, LYD_NEW_PATH_OPAQ, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + ret = lyd_new_path(root, NULL, "a", NULL, LYD_NEW_PATH_OPAQ, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(lyd_child(root)); + assert_null(lyd_child(root)->schema); + + ret = lyd_new_path(root, NULL, "b", NULL, LYD_NEW_PATH_OPAQ, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(lyd_child(root)->next); + assert_null(lyd_child(root)->next->schema); + + lyd_free_tree(root); + + /* key-less list */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c2/l3/x", "val1", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val1", lyd_get_value(node)); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[1]", NULL, 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EEXIST); + CHECK_LOG_CTX("Path \"/a:c2/l3[1]\" already exists.", "/a:c2/l3[1]", 0); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[2]/x", "val2", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3/x", "val3", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "empty", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "val4", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[5]/x", "val5", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[6]/x", "val6", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<c2 xmlns=\"urn:tests:a\">\n" + " <l3>\n" + " <x>val1</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val2</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val3</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val4</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val5</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val6</x>\n" + " </l3>\n" + "</c2>\n"); + free(str); + lyd_free_siblings(root); + + /* state leaf-list */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:ll2", "val_first", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(node->schema->name, "ll2"); + assert_string_equal("val_first", lyd_get_value(node)); + + ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EEXIST); + CHECK_LOG_CTX("Path \"/a:ll2[1]\" already exists.", "/a:ll2[1]", 0); + + ret = lyd_new_path2(root, NULL, "/a:ll2[2]", "val2", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + + ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "val", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, UTEST_LYCTX, "/a:ll2", "val3", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:ll2[3][.='val3']", NULL, 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Unparsed characters \"[.='val3']\" left at the end of path.", NULL, 0); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<ll2 xmlns=\"urn:tests:a\">val</ll2>\n" + "<ll2 xmlns=\"urn:tests:a\">val2</ll2>\n" + "<ll2 xmlns=\"urn:tests:a\">val3</ll2>\n"); + free(str); + lyd_free_siblings(root); + + /* anydata */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", "<elem>val</elem>", 0, LYD_ANYDATA_XML, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<any xmlns=\"urn:tests:a\">\n" + " <elem>val</elem>\n" + "</any>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:any\": {\n" + " \"elem\": \"val\"\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); + + /* anyxml */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "<a/><b/><c/>", 0, LYD_ANYDATA_XML, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<anyx xmlns=\"urn:tests:a\">\n" + " <a/>\n" + " <b/>\n" + " <c/>\n" + "</anyx>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": {\n" + " \"a\": [null],\n" + " \"b\": [null],\n" + " \"c\": [null]\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "{\"a\":[null],\"b\":[null],\"c\":[null]}", 0, LYD_ANYDATA_JSON, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<anyx xmlns=\"urn:tests:a\">\n" + " <a/>\n" + " <b/>\n" + " <c/>\n" + "</anyx>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": {\n" + " \"a\": [null],\n" + " \"b\": [null],\n" + " \"c\": [null]\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); +} + +static void +test_path_ext(void **state) +{ + LY_ERR ret; + struct lyd_node *root, *node; + struct lys_module *mod; + const char *mod_str = "module ext {yang-version 1.1; namespace urn:tests:extensions:ext; prefix e;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template {container c {leaf x {type string;} leaf y {type string;} leaf z {type string;}}}}"; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + + UTEST_ADD_MODULE(mod_str, LYS_IN_YANG, NULL, &mod); + + /* create x */ + ret = lyd_new_ext_path(NULL, &mod->compiled->exts[0], "/ext:c/x", "xxx", 0, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(root->schema->name, "c"); + assert_non_null(node = lyd_child(root)); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("xxx", lyd_get_value(node)); + + /* append y */ + ret = lyd_new_ext_path(root, &mod->compiled->exts[0], "/ext:c/y", "yyy", 0, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_string_equal(node->schema->name, "y"); + assert_string_equal("yyy", lyd_get_value(node)); + + /* append z */ + ret = lyd_new_path(root, NULL, "ext:z", "zzz", 0, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_string_equal(node->schema->name, "z"); + assert_string_equal("zzz", lyd_get_value(node)); + + lyd_free_tree(root); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_top_level), + UTEST(test_opaq), + UTEST(test_path), + UTEST(test_path_ext), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c new file mode 100644 index 0000000..1eff781 --- /dev/null +++ b/tests/utests/data/test_parser_json.c @@ -0,0 +1,945 @@ +/** + * @file test_parser_json.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for JSON parser + * + * Copyright (c) 2019 - 2023 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 "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1; import ietf-yang-metadata {prefix md;}" + "md:annotation hint { type int8;}" + "list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}" + " leaf d {type string;}" + " container cont {leaf e {type boolean;}}" + "}" + "leaf foo { type string;}" + "container c {" + " leaf x {type string;}" + " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }" + " notification n1 { leaf nl {type string;} }" + "}" + "container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}" + "anydata any {config false;}" + "anyxml axml;" + "leaf-list ll1 { type uint8; }" + "leaf foo2 { type string; default \"default-val\"; }" + "leaf foo3 { type uint32; }" + "leaf foo4 { type uint64; }" + "rpc r1 {input {leaf l1 {type string;} leaf l2 {type string;}}}" + "notification n2;" + "}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH, ERR_LINE) \ + assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\ + CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH, ERR_LINE);\ + assert_null(MODEL) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_JSON, PRINT_OPTION) + +static void +test_leaf(void **state) +{ + struct lyd_node *tree; + struct lyd_node_term *leaf; + const char *data; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + data = "{\"a:foo\":\"foo value\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree->next->next; + + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* make foo2 explicit */ + data = "{\"a:foo2\":\"default-val\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_false(leaf->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* parse foo2 but make it implicit */ + data = "{\"a:foo2\":\"default-val\",\"@a:foo2\":{\"ietf-netconf-with-defaults:default\":true}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + + /* print default values */ + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG, data); + lyd_free_all(tree); + + /* skip leaf */ + data = "{\"a:cp\":{\"x\":\"val\",\"y\":\"valy\",\"z\":5}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:cp\":{\"y\":\"valy\",\"z\":5}}"); + lyd_free_all(tree); + + /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */ + data = "{\"@a:foo\":{\"a:hint\":1,\"a:hint\":2,\"x:xxx\":{\"value\":\"/x:no/x:yes\"}},\"a:foo\":\"xxx\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + CHECK_LYD_META(tree->meta, 1, "hint", 1, 1, INT8, "1", 1); + CHECK_LYD_META(tree->meta->next, 1, "hint", 0, 1, INT8, "2", 2); + assert_null(tree->meta->next->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:foo\":\"xxx\",\"@a:foo\":{\"a:hint\":1,\"a:hint\":2}}"); + lyd_free_all(tree); + + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Unknown (or not implemented) YANG module \"x\" of metadata \"x:xxx\".", "/@a:foo", 1); + + /* missing referenced metadata node */ + PARSER_CHECK_ERROR("{\"@a:foo\" : { \"a:hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance to be coupled with @a:foo metadata.", "/@a:foo", 1); + + /* missing namespace for meatadata*/ + PARSER_CHECK_ERROR("{\"a:foo\" : \"value\", \"@a:foo\" : { \"hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Metadata in JSON must be namespace-qualified, missing prefix for \"hint\".", "/a:foo", 1); + + /* invalid JSON type */ + data = "{\"a:l1\" : [{ \"a\" : \"val-a\", \"b\" : \"val-b\", \"c\" : 1, \"cont\" : { \"e\" : \"0\" } }]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-boolean-encoded boolean value \"0\".", "/a:l1[a='val-a'][b='val-b'][c='1']/cont/e", 1); + + /* reverse solidus in JSON object member name */ + data = "{\"@a:foo\":{\"a:hi\\nt\":1},\"a:foo\":\"xxx\"}"; + assert_int_equal(LY_EINVAL, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LOG_CTX("Annotation definition for attribute \"a:hi\nt\" not found.", "/@a:foo/@a:hi\nt", 1); + + data = "{\"a:foo\": null}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1); + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); +} + +static void +test_leaflist(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_term *ll; + + data = "{\"a:ll1\":[10,11]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "10", 10); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "11", 11); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* accept empty */ + data = "{\"a:ll1\":[]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); + + /* simple metadata */ + data = "{\"a:ll1\":[10,11],\"@a:ll1\":[null,{\"a:hint\":2}]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "10", 10); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "11", 11); + CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "2", 2); + assert_null(ll->meta->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */ + data = "{\"@a:ll1\" : [{\"a:hint\" : 1, \"x:xxx\" : { \"value\" : \"/x:no/x:yes\" }, " + "\"a:hint\" : 10},null,{\"a:hint\" : 3}], \"a:ll1\" : [1,2,3]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "1", 1); + CHECK_LYD_META(ll->meta->next, 1, "hint", 1, 1, INT8, "1", 1); + CHECK_LYD_META(ll->meta->next->next, 1, "hint", 0, 1, INT8, "10", 10); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "2", 2); + assert_null(ll->meta); + + assert_non_null(tree->next->next); + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next->next; + CHECK_LYD_VALUE(ll->value, UINT8, "3", 3); + CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "3", 3); + assert_null(ll->meta->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:ll1\":[1,2,3],\"@a:ll1\":[{\"a:hint\":1,\"a:hint\":10},null,{\"a:hint\":3}]}"); + lyd_free_all(tree); + + /* missing referenced metadata node */ + PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance to be coupled with @a:ll1 metadata.", "/@a:ll1", 1); + + PARSER_CHECK_ERROR("{\"a:ll1\":[1],\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance #2 of a:ll1 to be coupled with metadata.", "/a:ll1", 1); + + PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2},{\"a:hint\":3}],\"a:ll1\" : [1, 2]}", 0, LYD_VALIDATE_PRESENT, + tree, LY_EVALID, "Missing JSON data instance #3 to be coupled with @a:ll1 metadata.", "/@a:ll1", 1); +} + +static void +test_anydata(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:any\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:any\":{\"node\":20}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:any\": null}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Expecting JSON name/object but anydata \"any\" is represented in input data as name/null.", NULL, 1); + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); +} + +static void +test_anyxml(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "{\"a:axml\":\"some-value in string\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":\"\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":55}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":false}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":null}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":[null,true,false]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":[null,true,{\"name\":[25,40, false]}]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* same as anydata tests */ + data = "{\"a:axml\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_list(void **state) +{ + const char *data; + struct lyd_node *tree, *iter; + struct lyd_node_inner *list; + struct lyd_node_term *leaf; + + /* check hashes */ + data = "{\"a:l1\":[{\"a\":\"one\",\"b\":\"one\",\"c\":1}]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", + 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + LY_LIST_FOR(list->child, iter) { + assert_int_not_equal(0, iter->hash); + } + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* accept empty */ + data = "{\"a:l1\":[]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); + + /* missing keys */ + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"a\".", "/a:l1[b='b'][c='1']", 1); + + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"a\" : \"a\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"b\".", "/a:l1[a='a']", 1); + + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"b\" : \"b\", \"a\" : \"a\"}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"c\".", "/a:l1[a='a'][b='b']", 1); + + /* key duplicate */ + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\", \"a\" : \"a\", \"c\" : 1}]}", 0, LYD_VALIDATE_PRESENT, + tree, LY_EVALID, "Duplicate instance of \"c\".", "/a:l1[a='a'][b='b'][c='1'][c='1']/c", 1); + + /* keys order, in contrast to XML, JSON accepts keys in any order even in strict mode */ + CHECK_PARSE_LYD("{ \"a:l1\": [ {\"d\" : \"d\", \"a\" : \"a\", \"c\" : 1, \"b\" : \"b\"}]}", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", + 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0); + CHECK_LOG_CTX(NULL, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1,\"d\":\"d\"}]}"); + lyd_free_all(tree); + + CHECK_PARSE_LYD("{\"a:l1\":[{\"c\":1,\"b\":\"b\",\"a\":\"a\"}]}", LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", + 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", + 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", + 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", + 1, LYS_LEAF, 1, 0, NULL, 0); + CHECK_LOG_CTX(NULL, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1}]}"); + lyd_free_all(tree); + + /* skip unknown nested nodes */ + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3,\"counters\":{\"count1\":\"c1\",\"count2\":\"c2\"}}]}"; + CHECK_PARSE_LYD(data, LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3}]}"); + lyd_free_all(tree); + + data = "{\"a:cp\":{\"@\":{\"a:hint\":1}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + CHECK_LYD_META(tree->meta, 1, "hint", 0, 1, INT8, "1", 1); + assert_null(tree->meta->next); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_container(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_inner *cont; + + CHECK_PARSE_LYD("{\"a:c\":{}}", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_true(cont->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}"); + lyd_free_all(tree); + + data = "{\"a:cp\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_false(cont->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* skip container */ + CHECK_PARSE_LYD("{\"a:unknown\":{\"a\":\"val\",\"b\":5}}", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}"); + lyd_free_all(tree); + + data = "{\"a:c\": null}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Expecting JSON name/object but container \"c\" is represented in input data as name/null.", NULL, 1); + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); +} + +static void +test_opaq(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid value, no flags */ + data = "{\"a:foo3\":[null]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-number-encoded uint32 value \"\".", "/a:foo3", 1); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* special chars */ + data = "{\"a:foo3\":\"ab\\\"\\\\\\r\\t\"}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong encoding */ + data = "{\"a:foo3\":\"25\"}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, "25"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:foo4\":25}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo4", 0, 0, NULL, 0, "25"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* missing key, no flags */ + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"d\":\"val_d\"}]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"c\".", "/a:l1[a='val_a'][b='val_b']", 1); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* invalid key, no flags */ + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-number-encoded int16 value \"val_c\".", "/a:l1[a='val_a'][b='val_b']/c", 1); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":{\"val\":\"val_c\"}}]}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\"}]}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* invalid metadata */ + data = "{\"@a:foo\":\"str\",\"@a:foo3\":1,\"a:foo3\":2}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Unknown module of node \"@a:foo\".", "/", 0); + CHECK_LOG_CTX("Missing JSON data instance to be coupled with @a:foo metadata.", "/@a:foo", 1); + + /* empty name */ + PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "JSON object member name cannot be a zero-length string.", "/@a:foo", 1); + + /* opaque data tree format print */ + data = + "{\n" + " \"ietf-netconf-nmda:get-data\": {\n" + " \"data\": {\n" + " \"ietf-keystore:keystore\": {\n" + " \"asymmetric-keys\": {\n" + " \"asymmetric-key\": [\n" + " {\n" + " \"name\": \"genkey\",\n" + " \"algorithm\": \"rsa2048\"\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " \"ietf-netconf-server:netconf-server\": {\n" + " \"listen\": {\n" + " \"idle-timeout\": 3600,\n" + " \"endpoint\": [\n" + " {\n" + " \"name\": \"default-ssh\",\n" + " \"ssh\": {\n" + " \"tcp-server-parameters\": {\n" + " \"local-address\": \"0.0.0.0\",\n" + " \"local-port\": 830\n" + " },\n" + " \"ssh-server-parameters\": {\n" + " \"server-identity\": {\n" + " \"host-key\": [\n" + " {\n" + " \"name\": \"default-key\",\n" + " \"public-key\": {\n" + " \"keystore-reference\": \"genkey\"\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"client-authentication\": {\n" + " \"supported-authentication-methods\": {\n" + " \"publickey\": [null],\n" + " \"passsword\": [null],\n" + " \"other\": [\n" + " \"interactive\",\n" + " \"gssapi\"\n" + " ]\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_rpc(void **state) +{ + const char *data; + char *str; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "Edit data in an NMDA datastore.\n" + "\n" + "If an error condition occurs such that an error severity\n" + "<rpc-error> element is generated, the server will stop\n" + "processing the <edit-data> operation and restore the\n" + "specified configuration to its complete state at\n" + "the start of this <edit-data> operation."; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + data = "{\"ietf-netconf-nmda:edit-data\":{" + "\"datastore\":\"ietf-datastores:running\"," + "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}}," + "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}" + "}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-data", LYS_RPC, + 0, 0, 0, 0, 0, NULL, 0); + + node = tree; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-data", LYS_RPC, + 0, 0, 0, 0, 0, NULL, 0); + node = lyd_child(node)->next; + CHECK_LYSC_NODE(node->schema, "Inline config content.", 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", + 0, LYS_ANYDATA, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + node = lyd_child(node); + /* z has no value */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_JSON, "z", 0, 0, NULL, 0, ""); + node = node->parent->next; + /* l1 key c has invalid value so it is at the end */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* append to parent */ + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:r1", NULL, 0, &op)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("{\"l1\": \"some str\", \"l2\": \"some other str\"}", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str, op, LYD_JSON, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); + lyd_free_tree(op); + assert_string_equal(str, "{\"a:r1\":{\"l1\":\"some str\",\"l2\":\"some other str\"}}"); + free(str); +} + +static void +test_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "{\"a:c\":{\"act\":{\"al\":\"value\"}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:n2\":{}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0); + + assert_non_null(tree); + assert_ptr_equal(ntf, tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + + data = "{\"a:c\":{\"act\":{\"al\":25}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_REPLY_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + node = lyd_child(op); + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + /* TODO print only rpc-reply node and then output subtree */ + CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:al\":25}"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:c\":{\"act\":{\"al\":25}}}"); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_restconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree)); + + data = "{\"ietf-netconf-nmda:input\":{" + "\"datastore\":\"ietf-datastores:running\"," + "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}}," + "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}" + "}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_JSON, LYD_TYPE_RPC_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* the same just connected to the edit-data RPC */ + data = "{\"ietf-netconf-nmda:edit-data\":{" + "\"datastore\":\"ietf-datastores:running\"," + "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}}," + "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}" + "}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_restconf_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"a:c\":{\"n1\":{\"nl\":\"value\"}}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + ly_in_free(in, 0); + + /* envelopes separately */ + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\"}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + /* notification with the parent node */ + data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}"; + CHECK_LYD_STRING(lyd_parent(ntf), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + lyd_free_all(ntf); + + /* wrong order */ + data = "{\"ietf-restconf:notification\":{\"a:n2\":{},\"eventTime\":\"2013-12-21T00:01:00Z\"}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + ly_in_free(in, 0); + + lyd_free_all(tree); + lyd_free_all(ntf); + + /* unknown notification */ + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"invalid:n2\":{}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + UTEST_LOG_CTX_CLEAN; + ly_in_free(in, 0); + lyd_free_all(tree); +} + +static void +test_restconf_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree)); + + data = "{\"a:output\":{\"al\":25}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_JSON, LYD_TYPE_REPLY_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* connected to the RPC with the parent */ + data = "{\"a:c\":{\"act\":{\"al\":25}}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_metadata(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid metadata value */ + data = "{\"a:c\":{\"x\":\"xval\",\"@x\":{\"a:hint\":\"value\"}}}"; + assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + CHECK_LOG_CTX("Invalid non-number-encoded int8 value \"value\".", "/a:c/x/@a:hint", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_leaf, setup), + UTEST(test_leaflist, setup), + UTEST(test_anydata, setup), + UTEST(test_anyxml, setup), + UTEST(test_list, setup), + UTEST(test_container, setup), + UTEST(test_opaq, setup), + UTEST(test_rpc, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_reply, setup), + UTEST(test_restconf_rpc, setup), + UTEST(test_restconf_notification, setup), + UTEST(test_restconf_reply, setup), + UTEST(test_metadata, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c new file mode 100644 index 0000000..d5336c0 --- /dev/null +++ b/tests/utests/data/test_parser_xml.c @@ -0,0 +1,1034 @@ +/** + * @file test_parser_xml.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions from parser_xml.c + * + * Copyright (c) 2019 - 2022 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 "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + " import ietf-yang-metadata {prefix md;}" + " list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}" + " leaf d {type string;}" + " container cont {leaf e {type boolean;}}" + " }" + " leaf foo { type string;}\n" + " container c {\n" + " leaf x {type string;}\n" + " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }\n" + " notification n1 { leaf nl {type string;}}}\n" + " container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}\n" + " anydata any {config false;}\n" + " anyxml anyx;\n" + " leaf foo2 { type string; default \"default-val\"; }\n" + " leaf foo3 { type uint32; }\n" + " notification n2;" + " md:annotation attr {type enumeration {enum val;}}" + "}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH, ERR_LINE) \ + assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\ + CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH, ERR_LINE);\ + assert_null(MODEL) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION) + +static void +test_leaf(void **state) +{ + const char *data = "<foo xmlns=\"urn:tests:a\">foo value</foo>"; + struct lyd_node *tree; + struct lyd_node_term *leaf; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree->next->next; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* make foo2 explicit */ + data = "<foo2 xmlns=\"urn:tests:a\">default-val</foo2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_false(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* parse foo2 but make it implicit, skip metadata xxx from missing schema */ + data = "<foo2 xmlns=\"urn:tests:a\" xmlns:wd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" " + "wd:default=\"true\" xmlns:x=\"urn:x\" x:xxx=\"false\">default-val</foo2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* invalid value */ + data = "<l1 xmlns=\"urn:tests:a\"><a>val-a</a><b>val-b</b><c>1</c><cont><e>0</e></cont></l1>"; + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid boolean value \"0\".", "/a:l1[a='val-a'][b='val-b'][c='1']/cont/e", 1); +} + +static void +test_anydata(void **state) +{ + const char *data; + char *str; + struct lyd_node *tree; + + data = "<any xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <x:element2 x:attr2=\"test\" xmlns:a=\"urn:tests:a\" xmlns:x=\"urn:x\">a:data</x:element2>\n" + " </element1>\n" + " <element1a/>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + + const char *data_expected = + "<any xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 xmlns=\"urn:x\" xmlns:x=\"urn:x\" x:attr2=\"test\" xmlns:a=\"urn:tests:a\">a:data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</any>\n"; + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + + assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str)); + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL)); + free(str); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + lyd_free_all(tree); +} + +static void +test_anyxml(void **state) +{ + const char *data; + char *str; + struct lyd_node *tree; + + data = "<anyx xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 x:attr2=\"test\" xmlns:x=\"urn:x\">data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</anyx>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + + const char *data_expected = + "<anyx xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 xmlns:x=\"urn:x\" x:attr2=\"test\">data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</anyx>\n"; + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + + assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str)); + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL)); + free(str); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + lyd_free_all(tree); +} + +static void +test_list(void **state) +{ + const char *data; + struct lyd_node *tree, *iter; + struct lyd_node_inner *list; + struct lyd_node_term *leaf; + + /* check hashes */ + data = "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>1</c></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", + 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + LY_LIST_FOR(list->child, iter) { + assert_int_not_equal(0, iter->hash); + } + lyd_free_all(tree); + + /* missing keys */ + PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"a\".", "/a:l1[b='b'][c='1']", 1); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL, 0); + + PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"b\".", "/a:l1[a='a']", 1); + + PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><b>b</b><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"c\".", "/a:l1[a='a'][b='b']", 1); + CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL, 0); + + /* key duplicate */ + PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a><c>1</c></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Duplicate instance of \"c\".", "/a:l1[a='a'][b='b'][c='1'][c='1']/c", 1); + CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL, 0); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL, 0); + + /* keys order */ + CHECK_PARSE_LYD("<l1 xmlns=\"urn:tests:a\"><d>d</d><a>a</a><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", + 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL, 0); + lyd_free_all(tree); + + data = "<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", 1, LYS_LIST, 0, 0, NULL, 0); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_non_null(leaf = (struct lyd_node_term *)leaf->next); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0); + CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL, 0); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL, 0); + lyd_free_all(tree); + + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid position of the key \"b\" in a list.", "/a:l1[c='1']/b", 1); +} + +static void +test_container(void **state) +{ + struct lyd_node *tree; + struct lyd_node_inner *cont; + + CHECK_PARSE_LYD("<c xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_true(cont->flags & LYD_DEFAULT); + lyd_free_all(tree); + + CHECK_PARSE_LYD("<cp xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_false(cont->flags & LYD_DEFAULT); + lyd_free_all(tree); +} + +static void +test_opaq(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid value, no flags */ + data = "<foo3 xmlns=\"urn:tests:a\"/>"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid type uint32 empty value.", "/a:foo3", 1); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 1, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<foo3 xmlns=\"urn:tests:a\"/>\n"); + lyd_free_all(tree); + + /* list, opaq flag */ + data = "<l1 xmlns=\"urn:tests:a\"/>"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n"); + lyd_free_all(tree); + + /* missing key, no flags */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <d>val_d</d>\n" + "</l1>\n"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"c\".", "/a:l1[a='val_a'][b='val_b']", 5); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* invalid key, no flags */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + "</l1>\n"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid type int16 value \"val_c\".", "/a:l1[a='val_a'][b='val_b']/c", 4); + + /* opaq flag */ + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* opaq flag and fail */ + assert_int_equal(LY_EVALID, lyd_parse_data_mem(UTEST_LYCTX, + "<a xmlns=\"ns\">\n" + " <b>x</b>\n" + " <c xmld:id=\"D\">1</c>\n" + "</a>\n", + LYD_XML, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "/a", 3); +} + +static void +test_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "The <edit-config> operation loads all or part of a specified\n" + "configuration to the specified target configuration."; + const char *ref = "RFC 6241, Section 7.2"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + + data = "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z nc:operation=\"delete\"/>\n" + " </cp>\n" + " </config>\n" + "</edit-config>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + + assert_non_null(tree); + + node = tree; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + node = lyd_child(node)->next; + dsc = "Inline Config content."; + CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + + node = lyd_child(node); + /* z has no value */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, ""); + node = node->parent->next; + /* l1 key c has invalid value so it is at the end */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n" + " </cp>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " </config>\n" + "</edit-config>\n"); + + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n"); + + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* top-level notif without envelope */ + data = "<n2 xmlns=\"urn:tests:a\"/>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0); + + assert_non_null(tree); + assert_ptr_equal(ntf, tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>25</al>\n" + " </act>\n" + "</c>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + node = lyd_child(op); + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + /* TODO print only rpc-reply node and then output subtree */ + CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_WITHSIBLINGS, "<al xmlns=\"urn:tests:a\">25</al>\n"); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_netconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "The <edit-config> operation loads all or part of a specified\n" + "configuration to the specified target configuration."; + const char *ref = "RFC 6241, Section 7.2"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + + data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "<edit-config>\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z nc:operation=\"delete\"/>\n" + " </cp>\n" + " </config>\n" + "</edit-config>\n" + "</rpc>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + node = tree; + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 1, 0, LY_VALUE_XML, "rpc", 0, 0, 0, 0, ""); + + assert_non_null(tree); + + node = op; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + node = lyd_child(node)->next; + dsc = "Inline Config content."; + CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + + node = lyd_child(node); + /* z has no value */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, ""); + node = node->parent->next; + /* l1 key c has invalid value so it is at the end */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n"); + CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS, + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n" + " </cp>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " </config>\n" + "</edit-config>\n"); + + lyd_free_all(tree); + lyd_free_all(op); + + /* invalid anyxml nested metadata value */ + data = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"1\" pid=\"4114692032\">\n" + " <copy-config>\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <source>\n" + " <config>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>5</c>\n" + " <cont nc:operation=\"merge\">\n" + " <e nc:operation=\"merge2\">false</e>\n" + " </cont>\n" + " </l1>\n" + " </config>\n" + " </source>\n" + " </copy-config>\n" + "</rpc>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Invalid enumeration value \"merge2\".", + "/ietf-netconf:copy-config/source/config/a:l1[a='val_a'][b='val_b'][c='5']/cont/e/@ietf-netconf:operation", 13); + lyd_free_all(tree); + assert_null(op); +} + +static void +test_netconf_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">" + "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n" + "</action>\n" + "</rpc>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "action", 0, 0, 0, 0, ""); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\">\n" + " <action xmlns=\"urn:ietf:params:xml:ns:yang:1\"/>\n" + "</rpc>\n"); + CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS, + "<act xmlns=\"urn:tests:a\">\n" + " <al>value</al>\n" + "</act>\n"); + + lyd_free_all(tree); + lyd_free_all(op); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_netconf_reply_or_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *action, *tree, *op, *op2; + + /* parse the action */ + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &action, &op)); + ly_in_free(in, 0); + + /* parse notification first */ + data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + "<eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\n" + "</notification>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 1, LY_VALUE_XML, "notification", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "eventTime", 0, 0, 0, 0, + "2010-12-06T08:00:01Z"); + + assert_non_null(op2); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)op2->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + " <eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "</notification>\n"); + CHECK_LYD_STRING(op2, LYD_PRINT_WITHSIBLINGS, + "<n1 xmlns=\"urn:tests:a\">\n" + " <nl>value</nl>\n" + "</n1>\n"); + + lyd_free_all(tree); + lyd_free_all(op2); + + /* notification with a different order */ + data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\n" + "<eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "</notification>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2)); + ly_in_free(in, 0); + + lyd_free_all(tree); + lyd_free_all(op2); + + /* parse a data reply */ + data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <al xmlns=\"urn:tests:a\">25</al>\n" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 0, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\"/>\n"); + + lyd_free_all(tree); + /* it was connected to the action, do not free */ + + /* parse an ok reply */ + data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n" + " <ok/>\n" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "ok", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + + /* parse an error reply */ + data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n" + " <rpc-error>\n" + " <error-type>rpc</error-type>\n" + " <error-tag>missing-attribute</error-tag>\n" + " <error-severity>error</error-severity>\n" + " <error-info>\n" + " <bad-attribute>message-id</bad-attribute>\n" + " <bad-element>rpc</bad-element>\n" + " </error-info>\n" + " </rpc-error>\n" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 1, LY_VALUE_XML, "rpc-error", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + + lyd_free_all(action); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_restconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree)); + + data = "<input xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">" + "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>" + "<config>" + "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>" + "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">" + "<a>val_a</a><b>val_b</b><c>val_c</c>" + "</l1>" + "</config></input>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_XML, LYD_TYPE_RPC_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* the same just connected to the edit-data RPC */ + data = "<edit-data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">" + "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>" + "<config>" + "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>" + "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">" + "<a>val_a</a><b>val_b</b><c>val_c</c>" + "</l1>" + "</config></edit-data>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_restconf_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree)); + + data = "<output xmlns=\"urn:tests:a\"><al>25</al></output>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_XML, LYD_TYPE_REPLY_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* connected to the RPC with the parent */ + data = "<c xmlns=\"urn:tests:a\"><act><al>25</al></act></c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_filter_attributes(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree; + const struct lyd_node *node; + const char *dsc; + const char *ref = "RFC 6241, Section 7.7"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "notifications", "2008-07-14", NULL))); + + data = "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <filter type=\"xpath\" select=\"/*\"/>\n" + "</get>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + assert_non_null(tree); + + node = tree; + dsc = "Retrieve running configuration and device state information."; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "get", LYS_RPC, + 1, 0, 0, 0, 0, ref, 0); + node = lyd_child(node); + dsc = "This parameter specifies the portion of the system\nconfiguration and state data to retrieve."; + CHECK_LYSC_NODE(node->schema, dsc, 1, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "filter", 0, LYS_ANYXML, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<create-subscription xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + " <filter type=\"subtree\">\n" + " <inner-node xmlns=\"my:urn\"/>\n" + " </filter>\n" + "</create-subscription>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + assert_non_null(tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_data_skip(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_term *leaf; + + /* add invalid data to a module that is not implemented */ + data = "<foo xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\"><u/></foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + + /* add invalid data to a module that is implemented */ + data = "<fooX xmlns=\"urn:tests:a\"><u/><list><value/></list></fooX>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + + /* first invalid, next valid */ + data = "<fooX xmlns=\"urn:tests:a\"><u/></fooX> <foo xmlns=\"urn:tests:a\">foo value</foo>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + lyd_free_all(tree); +} + +static void +test_metadata(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid metadata value */ + data = "<c xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\"><x a:attr=\"value\">xval</x></c>"; + assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + CHECK_LOG_CTX("Invalid enumeration value \"value\".", "/a:c/x/@a:attr", 1); +} + +static void +test_subtree(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree; + + /* prepare data with the parent */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>1</c>\n" + "</l1>\n"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + + /* parse a subtree of it */ + data = "<cont xmlns=\"urn:tests:a\">\n" + " <e>true</e>\n" + "</cont>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL)); + ly_in_free(in, 0); + + /* parse another container, fails */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Duplicate instance of \"cont\".", "/a:l1[a='val_a'][b='val_b'][c='1']/cont", 0); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_leaf, setup), + UTEST(test_anydata, setup), + UTEST(test_anyxml, setup), + UTEST(test_list, setup), + UTEST(test_container, setup), + UTEST(test_opaq, setup), + UTEST(test_rpc, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_reply, setup), + UTEST(test_netconf_rpc, setup), + UTEST(test_netconf_action, setup), + UTEST(test_netconf_reply_or_notification, setup), + UTEST(test_restconf_rpc, setup), + UTEST(test_restconf_reply, setup), + UTEST(test_filter_attributes, setup), + UTEST(test_data_skip, setup), + UTEST(test_metadata, setup), + UTEST(test_subtree, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_printer_json.c b/tests/utests/data/test_printer_json.c new file mode 100644 index 0000000..65539f0 --- /dev/null +++ b/tests/utests/data/test_printer_json.c @@ -0,0 +1,83 @@ +/** + * @file test_printer_json.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from printer_yang.c + * + * Copyright (c) 2019-2020 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" + +static int +setup(void **state) +{ + const char *schema1 = "module schema1 {namespace urn:tests:schema1;prefix schema1;yang-version 1.1;" + "revision 2014-05-08;" + "anydata data;" + "}"; + const char *schema2 = "module schema2 {namespace urn:tests:schema2;prefix s2;yang-version 1.1;" + " container a {" + " container b {" + " leaf c {" + " type string;" + " default \"dflt\";" + " }" + " }" + " }" + "}"; + + UTEST_SETUP; + UTEST_ADD_MODULE(schema1, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + return 0; +} + +static void +test_container_presence(void **state) +{ + struct lyd_node *tree; + char *buffer = NULL; + const char *data = "{\"schema1:data\":{\"cont1\":{}}}"; + + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_int_equal(LY_SUCCESS, lyd_print_mem(&buffer, tree, LYD_JSON, LYD_PRINT_SHRINK)); + CHECK_STRING(buffer, data); + free(buffer); + lyd_free_all(tree); +} + +static void +test_empty_container_wd_trim(void **state) +{ + struct lyd_node *tree; + char *buffer = NULL; + const char *data = "{\"schema2:a\":{\"b\":{\"c\":\"dflt\"}}}"; + + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_int_equal(LY_SUCCESS, lyd_print_mem(&buffer, tree, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WD_TRIM)); + CHECK_STRING(buffer, "{}"); + free(buffer); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&buffer, tree, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WD_TRIM | LYD_PRINT_KEEPEMPTYCONT)); + CHECK_STRING(buffer, "{\"schema2:a\":{\"b\":{}}}"); + free(buffer); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_container_presence, setup), + UTEST(test_empty_container_wd_trim, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c new file mode 100644 index 0000000..6213a37 --- /dev/null +++ b/tests/utests/data/test_printer_xml.c @@ -0,0 +1,344 @@ +/* + * @file test_printer_xml.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from printer_yang.c + * + * Copyright (c) 2019-2020 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 <string.h> + +#include "context.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema_defs = "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;" + "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;} identity fast-ethernet {base ethernet;}}"; + const char *schema_types = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}" + "feature f; identity gigabit-ethernet { base defs:ethernet;}" + "container cont {leaf leaftarget {type empty;}" + " list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}" + " action test {input {leaf a {type string;}} output {leaf b {type string;}}}}" + " leaf-list leaflisttarget {type uint8; max-elements 5;}}" + "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}" + "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}" + "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}" + "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}" + "leaf-list leaflisttarget {type string;}" + "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}" + "leaf binary-norestr {type binary;}" + "leaf int8 {type int8 {range 10..20;}}" + "leaf uint8 {type uint8 {range 150..200;}}" + "leaf int16 {type int16 {range -20..-10;}}" + "leaf uint16 {type uint16 {range 150..200;}}" + "leaf int32 {type int32;}" + "leaf uint32 {type uint32;}" + "leaf int64 {type int64;}" + "leaf uint64 {type uint64;}" + "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}" + "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}" + "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}" + "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}" + "leaf str {type string {length 8..10; pattern '[a-z ]*';}}" + "leaf str-norestr {type string;}" + "leaf bool {type boolean;}" + "leaf empty {type empty;}" + "leaf ident {type identityref {base defs:interface-type;}}" + "leaf inst {type instance-identifier {require-instance true;}}" + "leaf inst-noreq {type instance-identifier {require-instance false;}}" + "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}" + "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}" + "leaf un1 {type union {" + " type leafref {path /int8; require-instance true;}" + " type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }" + " type string {length 1..20;}}}" + "anydata any;" + "rpc sum {input {leaf x {type uint8;} leaf y {type uint8;}} output {leaf result {type uint16;}}}}"; + const char *schema_defaults = + "module defaults {\n" + " namespace \"urn:defaults\";\n" + " prefix d;\n" + " leaf a {\n" + " type union {\n" + " type instance-identifier;\n" + " type string;\n" + " }\n" + " default \"/d:b\";\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + "}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema_defs, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_types, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_defaults, LYS_IN_YANG, NULL, NULL); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION) + +static void +test_anydata(void **state) +{ + struct lyd_node *tree; + const char *data; + + data = "<any xmlns=\"urn:tests:types\"><somexml xmlns:x=\"url:x\" xmlns=\"example.com\"><x:x/></somexml></any>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + /* canonized */ + data = "<any xmlns=\"urn:tests:types\"><somexml xmlns=\"example.com\"><x xmlns=\"url:x\"/></somexml></any>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\"/>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont>\n" + " <defs:elem1 xmlns:defs=\"urn:tests:defs\">\n" + " <elem2 xmlns:defaults=\"urn:defaults\" defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n" + " </elem2>\n" + " </defs:elem1>\n" + " </cont>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + /* cont should be normally parsed */ + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "any", 0, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_NODE_ANY((struct lyd_node_any *)tree, 0, 0, 0, LYD_ANYDATA_DATATREE); + struct lyd_node *tree_tmp = ((struct lyd_node_any *)tree)->value.tree; + + CHECK_LYSC_NODE(tree_tmp->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "cont", 1, LYS_CONTAINER, 0, 0, NULL, 0); + /* but its children not */ + assert_null(((struct lyd_node_inner *)tree_tmp)->child->schema); + /* canonized */ + data = + "<any xmlns=\"urn:tests:types\">\n" + " <cont>\n" + " <elem1 xmlns=\"urn:tests:defs\">\n" + " <elem2 xmlns=\"urn:tests:types\" xmlns:defs=\"urn:tests:defs\" xmlns:defaults=\"urn:defaults\" " + "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n" + " </elem2>\n" + " </elem1>\n" + " </cont>\n" + "</any>\n"; + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <ahoj attr=\"<test\">\n" + " ahoj jak se vede < how are you" + " </ahoj>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <leaflisttarget> ahoj </leaflisttarget>\n" + " <leaflisttarget> nazdar </leaflisttarget>\n" + " <leaflisttarget> ÄŒau </leaflisttarget>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont2/>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont>\n" + " < how are you" + " </cont>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_defaults(void **state) +{ + struct lyd_node *tree; + const char *data; + const char *data_trim; + const char *data_all; + const char *data_all_tag; + const char *data_impl_tag; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + /* standard default value */ + data = "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* string value equal to the default but default is an unresolved instance-identifier, so they are not considered equal */ + data = "<a xmlns=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* instance-identifier value equal to the default, should be considered equal */ + data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_trim = "<b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_all = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_all_tag = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<b xmlns=\"urn:defaults\">val</b>" + "<c xmlns=\"urn:defaults\">aa</c>"; + data_impl_tag = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_trim); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all_tag); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_impl_tag); + lyd_free_all(tree); +} + +#if 0 + +static void +test_rpc(void **state) +{ + struct state_s *s = (struct state_s *)(*state); + struct lyd_node *tree1; + struct lyd_node *tree2; + const struct lyd_node **trees; + const char *request; + const char *reply, *result; + char *printed; + ssize_t len; + struct ly_out *out; + + s->func = test_rpc; + assert_non_null(out = ly_out_new_memory(&printed, 0)); + + request = "<sum xmlns=\"urn:tests:types\"><x>10</x><y>20</y></sum>"; + reply = "<result xmlns=\"urn:tests:types\">30</result>"; + result = "<sum xmlns=\"urn:tests:types\"><result>30</result></sum>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* no arguments */ + request = "<sum xmlns=\"urn:tests:types\"/>"; + reply = ""; + result = "<sum xmlns=\"urn:tests:types\"/>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* action + * "container cont {leaf leaftarget {type empty;}" + "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}" + "action test {input {leaf a {type string;}} output {leaf b {type string;}}}}" + "leaf-list leaflisttarget {type uint8; max-elements 5;}}" + */ + request = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><a>test</a></test></listtarget></cont>"; + reply = "<b xmlns=\"urn:tests:types\">test-reply</b>"; + result = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><b>test-reply</b></test></listtarget></cont>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + ly_out_free(out, NULL, 1); + s->func = NULL; +} + +#endif + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_anydata, setup), + UTEST(test_defaults, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c new file mode 100644 index 0000000..fabd170 --- /dev/null +++ b/tests/utests/data/test_tree_data.c @@ -0,0 +1,822 @@ +/** + * @file test_tree_data.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from tree_data.c + * + * Copyright (c) 2018-2023 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 "libyang.h" +#include "ly_common.h" +#include "path.h" +#include "xpath.h" + +static int +setup(void **state) +{ + const char *schema1 = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;" + "revision 2014-05-08;" + "leaf bar {type string;}" + "list l1 { key \"a b\"; leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "leaf foo { type string;}" + "leaf-list ll { type string;}" + "container c {leaf-list x {type string;}}" + "anydata any {config false;}" + "list l2 {config false;" + " container c{leaf x {type string;} leaf-list d {type string;}}" + "}}"; + + const char *schema2 = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}}" + "anydata any {config false;}" + "}"; + + const char *schema3 = "module c {yang-version 1.1; namespace \"http://example.com/main\";prefix m;" + "import \"ietf-inet-types\" {prefix inet;}" + "typedef optional-ip-address {type union {" + " type inet:ip-address;" + " type string;" + "}}" + "container cont {" + " list nexthop {min-elements 1; key \"gateway\";" + " leaf gateway {type optional-ip-address;}" + " }" + " leaf-list pref {type inet:ipv6-prefix;}" + "}}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema1, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema3, LYS_IN_YANG, NULL, NULL); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define CHECK_PARSE_LYD_PARAM_CTX(CTX, INPUT, PARSE_OPTION, OUT_NODE) \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(CTX, INPUT, LYD_XML, PARSE_OPTION, LYD_VALIDATE_PRESENT, &OUT_NODE)); \ + assert_non_null(OUT_NODE); + +#define RECREATE_CTX_WITH_MODULE(CTX, MODULE) \ + ly_ctx_destroy(CTX); \ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &CTX)); \ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(MODULE, &_UC->in)); \ + assert_int_equal(LY_SUCCESS, lys_parse(CTX, _UC->in, LYS_IN_YANG, NULL, NULL)); \ + ly_in_free(_UC->in, 0); + +static void +test_compare(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *data1; + const char *data2; + + assert_int_equal(LY_SUCCESS, lyd_compare_single(NULL, NULL, 0)); + + data1 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + data2 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>y</c></l1>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + assert_int_equal(LY_ENOT, lyd_compare_single(((struct lyd_node_inner *)tree1)->child, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next->next, tree2->next, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<ll xmlns=\"urn:tests:a\">a</ll><ll xmlns=\"urn:tests:a\">b</ll>"; + data2 = "<ll xmlns=\"urn:tests:a\">b</ll>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(NULL, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, NULL, 0)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>y</x></c>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>x</x><x>y</x></c>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<any xmlns=\"urn:tests:a\"><x>x</x></any>"; + data2 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, 0)); + lyd_free_all(tree1); + data1 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>c</x><x>a</x><x>b</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>a</x><x>b</x><x>c</x></c>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); +} + +static void +test_compare_diff_ctx(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *data1, *data2; + struct ly_ctx *ctx2 = NULL; + const char *module; + + /* create second context with the same schema */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different name */ + module = "module c {namespace urn:tests:c;prefix c;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:c\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different revision */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2015-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has no revision */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different parent nodetype */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "container l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(lyd_child(lyd_child(tree1)), lyd_child(lyd_child(tree2)), 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the same opaq data nodes */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:x</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the different opaq data node value */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:y</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the wrong prefix in opaq data node value */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>cc:x</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* clean up */ + ly_ctx_destroy(ctx2); + _UC->in = NULL; +} + +static void +test_dup(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *result; + const char *data; + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, LYD_DUP_RECURSIVE, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + result = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2)); + lyd_free_all(tree1); + CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + result = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_siblings(tree1, NULL, LYD_DUP_RECURSIVE, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree2); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, LYD_DUP_RECURSIVE, &tree2)); + lyd_free_all(tree1); + CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree2); + + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2)); + lyd_free_all(tree1); + result = "<l2 xmlns=\"urn:tests:a\"/>"; + CHECK_PARSE_LYD_PARAM(result, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<any xmlns=\"urn:tests:a\"><c><a>a</a></c></any>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), NULL, LYD_DUP_WITH_PARENTS, &tree2)); + int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM; + + CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "x", 1, LYS_LEAF, 1, 0, NULL, 0); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, (struct lyd_node *)tree2->parent->parent, + LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev, NULL, + LYD_DUP_WITH_PARENTS, &tree2)); + flag = LYS_CONFIG_W | LYS_SET_ENUM; + CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "c", 0, LYS_LEAF, 1, 0, NULL, 0); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, (struct lyd_node *)tree2->parent, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), (struct lyd_node_inner *)tree2, + LYD_DUP_WITH_PARENTS, NULL)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* invalid */ + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_EINVAL, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev, + (struct lyd_node_inner *)tree1->next, LYD_DUP_WITH_PARENTS, NULL)); + CHECK_LOG_CTX("None of the duplicated node \"c\" schema parents match the provided parent \"c\".", NULL, 0); + lyd_free_all(tree1); +} + +static void +test_target(void **state) +{ + const struct lyd_node_term *term; + struct lyd_node *tree; + struct lyxp_expr *exp; + struct ly_path *path; + const char *path_str = "/a:l2[2]/c/d[3]"; + const char *data = + "<l2 xmlns=\"urn:tests:a\"><c>" + " <d>a</d>" + " </c></l2>" + "<l2 xmlns=\"urn:tests:a\"><c>" + " <d>a</d>" + " <d>b</d>" + " <d>b</d>" + " <d>c</d>" + "</c></l2>" + "<l2 xmlns=\"urn:tests:a\"><c>" + "</c></l2>"; + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path_str, strlen(path_str), 0, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp)); + assert_int_equal(LY_SUCCESS, ly_path_compile(UTEST_LYCTX, NULL, NULL, NULL, exp, LY_PATH_OPER_INPUT, + LY_PATH_TARGET_SINGLE, 1, LY_VALUE_JSON, NULL, &path)); + assert_int_equal(LY_SUCCESS, lyd_find_target(path, tree, (struct lyd_node **)&term)); + + const int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM | LYS_ORDBY_USER; + + CHECK_LYSC_NODE(term->schema, NULL, 0, flag, 1, "d", 0, LYS_LEAFLIST, 1, 0, NULL, 0); + assert_string_equal(lyd_get_value(&term->node), "b"); + assert_string_equal(lyd_get_value(term->prev), "b"); + + lyd_free_all(tree); + ly_path_free(UTEST_LYCTX, path); + lyxp_expr_free(UTEST_LYCTX, exp); +} + +static void +test_list_pos(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "<bar xmlns=\"urn:tests:a\">test</bar>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>" + "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>" + "<foo xmlns=\"urn:tests:a\">test</foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_int_equal(0, lyd_list_pos(tree)); + assert_int_equal(1, lyd_list_pos(tree->next)); + assert_int_equal(2, lyd_list_pos(tree->next->next)); + assert_int_equal(0, lyd_list_pos(tree->next->next->next)); + lyd_free_all(tree); + + data = "<ll xmlns=\"urn:tests:a\">one</ll>" + "<ll xmlns=\"urn:tests:a\">two</ll>" + "<ll xmlns=\"urn:tests:a\">three</ll>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_int_equal(1, lyd_list_pos(tree)); + assert_int_equal(2, lyd_list_pos(tree->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next)); + lyd_free_all(tree); + + data = "<ll xmlns=\"urn:tests:a\">one</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>" + "<ll xmlns=\"urn:tests:a\">two</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>" + "<ll xmlns=\"urn:tests:a\">three</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>three</a><b>three</b></l1>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_string_equal("l1", tree->schema->name); + assert_int_equal(1, lyd_list_pos(tree)); + assert_int_equal(2, lyd_list_pos(tree->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next)); + assert_string_equal("ll", tree->next->next->next->schema->name); + assert_int_equal(1, lyd_list_pos(tree->next->next->next)); + assert_int_equal(2, lyd_list_pos(tree->next->next->next->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next->next->next->next)); + lyd_free_all(tree); +} + +static void +test_first_sibling(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_inner *parent; + + data = "<bar xmlns=\"urn:tests:a\">test</bar>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>one</c></l1>" + "<foo xmlns=\"urn:tests:a\">test</foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_ptr_equal(tree, lyd_first_sibling(tree->next)); + assert_ptr_equal(tree, lyd_first_sibling(tree)); + assert_ptr_equal(tree, lyd_first_sibling(tree->prev)); + parent = (struct lyd_node_inner *)tree->next; + assert_int_equal(LYS_LIST, parent->schema->nodetype); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->next)); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child)); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->prev)); + lyd_free_all(tree); +} + +static void +test_find_path(void **state) +{ + struct lyd_node *root; + const struct lys_module *mod; + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c"); + assert_non_null(mod); + + assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "cont", 0, &root)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='10.0.0.1']", NULL, LYD_NEW_PATH_UPDATE, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='2100::1']", NULL, LYD_NEW_PATH_UPDATE, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/pref[.='fc00::/64']", NULL, 0, NULL)); + + assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL)); + + assert_int_equal(LY_EVALID, lyd_find_path(root, "/cont", 0, NULL)); + CHECK_LOG_CTX("Prefix missing for \"cont\" in path.", "/c:cont", 0); + assert_int_equal(LY_SUCCESS, lyd_find_path(root, "nexthop[gateway='2100::1']", 0, NULL)); + + lyd_free_all(root); +} + +static void +test_data_hash(void **state) +{ + struct lyd_node *tree; + const char *schema, *data; + + schema = + "module test-data-hash {" + " yang-version 1.1;" + " namespace \"urn:tests:tdh\";" + " prefix t;" + " container c {" + " leaf-list ll {" + " type string;" + " }" + " }" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* The number of <ll/> must be greater or equal to LYD_HT_MIN_ITEMS + * for the correct test run. It should guarantee the creation of a hash table. + */ + assert_true(LYD_HT_MIN_ITEMS <= 4); + data = + "<c xmlns='urn:tests:tdh'>" + " <ll/>" + " <ll/>" + " <ll/>" + " <ll/>" + "</c>"; + + /* The run must not crash due to the assert that checks the hash. */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "/test-data-hash:c/ll[.='']", 1); + lyd_free_all(tree); +} + +static void +test_lyxp_vars(void **UNUSED(state)) +{ + struct lyxp_var *vars; + + /* Test free. */ + vars = NULL; + lyxp_vars_free(vars); + + /* Bad arguments for lyxp_vars_add(). */ + assert_int_equal(LY_EINVAL, lyxp_vars_set(NULL, "var1", "val1")); + assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, NULL, "val1")); + assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, "var1", NULL)); + lyxp_vars_free(vars); + vars = NULL; + + /* Add one item. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_ARRAY_COUNT(vars), 1); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "val1"); + lyxp_vars_free(vars); + vars = NULL; + + /* Add three items. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var3", "val3")); + assert_int_equal(LY_ARRAY_COUNT(vars), 3); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "val1"); + assert_string_equal(vars[1].name, "var2"); + assert_string_equal(vars[1].value, "val2"); + assert_string_equal(vars[2].name, "var3"); + assert_string_equal(vars[2].value, "val3"); + lyxp_vars_free(vars); + vars = NULL; + + /* Change value of a variable. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "new_value")); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "new_value"); + assert_string_equal(vars[1].name, "var2"); + assert_string_equal(vars[1].value, "val2"); + lyxp_vars_free(vars); + vars = NULL; +} + +static void +test_data_leafref_nodes(void **state) +{ + struct lyd_node *tree, *iter; + struct lyd_node_term *target_node = NULL, *leafref_node; + const struct lyd_leafref_links_rec *rec; + const char *schema, *data, *value; + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING); + + schema = + "module test-data-hash {" + " yang-version 1.1;" + " namespace \"urn:tests:tdh\";" + " prefix t;" + " leaf-list ll {" + " type string;" + " }" + " container c1 {" + " leaf ref1 {" + " type leafref {" + " path \"../../ll\";" + " }" + " }" + " }" + " leaf ref2 {" + " type leafref {" + " path \"../ll\";" + " }" + " }" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = + "{" + " \"test-data-hash:ll\": [\"qwe\", \"asd\"]," + " \"test-data-hash:c1\": { \"ref1\": \"qwe\"}," + " \"test-data-hash:ref2\": \"asd\"" + "}"; + + /* The run must not crash due to the assert that checks the hash. */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + LY_LIST_FOR(tree, iter) { + if (strcmp(iter->schema->name, "ll") == 0) { + value = lyd_get_value(iter); + if (strcmp(value, "asd") == 0) { + target_node = (struct lyd_node_term *)iter; + } + } + if (strcmp(iter->schema->name, "ref2") == 0) { + leafref_node = (struct lyd_node_term *)iter; + } + } + + /* verify state after leafref plugin validation */ + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); + /* value modification of target */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "ASD")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* change back to original value */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "asd")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* linking the whole tree again */ + assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); + /* value modification of leafref */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "qwe")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "asd")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* linking the whole tree again */ + assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->target_nodes)); + assert_ptr_equal(rec->target_nodes[0], target_node); + /* freeing whole tree */ + lyd_free_all(tree); +} + +static void +test_data_leafref_nodes2(void **state) +{ + struct lyd_node *tree, *iter; + const char *schema, *data; + struct lyd_node_term *leafref_node = NULL; + const struct lyd_node_term *target_node1, *target_node2; + const struct lyd_leafref_links_rec *rec; + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING); + + schema = + "module test-data-hash {" + " yang-version 1.1;" + " namespace \"urn:tests:tdh\";" + " prefix t;" + " list l1 {" + " key \"l1 l2\";" + " leaf l1 {" + " type string;" + " }" + " leaf l2 {" + " type string;" + " }" + " }" + " leaf ref1 {" + " type leafref {" + " path \"../l1/l1\";" + " }" + " }" + " leaf-list ll1 {" + " type string;" + " config false;" + " }" + " leaf ref2 {" + " type leafref {" + " path \"../ll1\";" + " }" + " config false;" + " }" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = + "{" + " \"test-data-hash:l1\": [" + " {\"l1\": \"A\", \"l2\": \"B\"}," + " {\"l1\": \"A\", \"l2\": \"C\"}" + " ]," + " \"test-data-hash:ref1\": \"A\"," + " \"test-data-hash:ll1\": [\"asd\", \"qwe\", \"asd\"]," + " \"test-data-hash:ref2\": \"asd\"" + "}"; + + /* The run must not crash due to the assert that checks the hash. */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + LY_LIST_FOR(tree, iter) { + if (strcmp(iter->schema->name, "ref1") == 0) { + leafref_node = (struct lyd_node_term *)iter; + } + } + + /* verify state after leafref plugin validation */ + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(2, LY_ARRAY_COUNT(rec->target_nodes)); + target_node1 = rec->target_nodes[0]; + target_node2 = rec->target_nodes[1]; + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node1, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + /* value modification of leafref to remove all links*/ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "qwe")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node1, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node2, &rec)); + /* linking the whole tree again */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "A")); + assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(2, LY_ARRAY_COUNT(rec->target_nodes)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node1, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + + /* verify duplicated value in leaf-list */ + LY_LIST_FOR(tree, iter) { + if (strcmp(iter->schema->name, "ref2") == 0) { + leafref_node = (struct lyd_node_term *)iter; + } + } + + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(2, LY_ARRAY_COUNT(rec->target_nodes)); + target_node1 = rec->target_nodes[0]; + target_node2 = rec->target_nodes[1]; + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node1, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node2, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_ptr_equal(rec->leafref_nodes[0], leafref_node); + /* freeing whole tree */ + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_compare, setup), + UTEST(test_compare_diff_ctx, setup), + UTEST(test_dup, setup), + UTEST(test_target, setup), + UTEST(test_list_pos, setup), + UTEST(test_first_sibling, setup), + UTEST(test_find_path, setup), + UTEST(test_data_hash, setup), + UTEST(test_lyxp_vars), + UTEST(test_data_leafref_nodes), + UTEST(test_data_leafref_nodes2), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_tree_data_sorted.c b/tests/utests/data/test_tree_data_sorted.c new file mode 100644 index 0000000..0e812e7 --- /dev/null +++ b/tests/utests/data/test_tree_data_sorted.c @@ -0,0 +1,1638 @@ +/** + * @file test_tree_data_sorted.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief Unit tests for functions from tree_data_sorted.h + * + * Copyright (c) 2018-2023 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 "libyang.h" +#include "ly_common.h" +#include "tree_data_internal.h" +#include "tree_data_sorted.h" + +#define META_NAME "lyds_tree" + +static void * +get_rbt(struct lyd_meta *meta) +{ + struct lyd_value_lyds_tree *lt; + + if (!meta) { + return NULL; + } + + LYD_VALUE_GET(&meta->value, lt); + return lt ? lt->rbt : NULL; +} + +static void +test_insert_top_level_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "list lst {key \"k\"; leaf k {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_list(NULL, mod, "lst", 0, &first, "2"), LY_SUCCESS); + assert_int_equal(lyd_new_list(NULL, mod, "lst", 0, &node, "1"), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_list(NULL, mod, "lst", 0, &node, "3"), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + assert_true(first->next && first->prev && first->prev->meta); + assert_string_equal(first->prev->meta->name, META_NAME); + assert_string_equal(lyd_get_value(lyd_child(first->prev)), "1"); + assert_string_equal(lyd_get_value(lyd_child(first)), "2"); + assert_string_equal(lyd_get_value(lyd_child(first->next)), "3"); + lyd_free_all(first); +} + +static void +test_insert_top_level_leaflist(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &first), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + assert_true(first->next && first->prev && first->prev->meta); + assert_string_equal(first->prev->meta->name, META_NAME); + assert_string_equal(lyd_get_value(first->prev), "1"); + assert_string_equal(lyd_get_value(first), "2"); + assert_string_equal(lyd_get_value(first->next), "3"); + lyd_free_all(first); +} + +static void +test_insert_cont_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { list lst {key \"k\"; leaf k {type uint32;}}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "2"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "3"), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->meta && node->next && node->next->next); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(lyd_child(node)), "1"); + assert_string_equal(lyd_get_value(lyd_child(node->next)), "2"); + assert_string_equal(lyd_get_value(lyd_child(node->next->next)), "3"); + lyd_free_all(cont); +} + +static void +test_insert_cont_leaflist(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->next->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "2"); + assert_string_equal(lyd_get_value(node->next->next), "3"); + lyd_free_all(cont); +} + +static void +test_try_user_order_func(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *sibl, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &sibl), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_before(sibl, node), LY_EINVAL); + CHECK_LOG_CTX("Can be used only for user-ordered nodes.", NULL, 0); + assert_int_equal(lyd_insert_after(sibl, node), LY_EINVAL); + CHECK_LOG_CTX("Can be used only for user-ordered nodes.", NULL, 0); + lyd_free_all(node); + lyd_free_all(sibl); +} + +static void +test_ordered_by_user(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32; ordered-by user;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "1"); + lyd_free_all(cont); +} + +static void +test_remove(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node, *deleted; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* Remove first */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->next->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + deleted = node; + lyd_unlink_tree(deleted); + lyd_free_tree(deleted); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "3"); + lyd_free_all(cont); + + /* Remove middle */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->next->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + deleted = node->next; + lyd_unlink_tree(deleted); + lyd_free_tree(deleted); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "3"); + lyd_free_all(cont); + + /* Remove last */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->next->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + deleted = node->next->next; + lyd_unlink_tree(deleted); + lyd_free_tree(deleted); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "2"); + lyd_free_all(cont); + + /* Remove all */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_non_null(node); + deleted = node; + lyd_unlink_tree(deleted); + lyd_free_tree(deleted); + node = lyd_child(cont); + assert_null(node); + lyd_free_all(cont); +} + +static void +test_remove_then_insert(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node, *deleted; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* remove first */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + deleted = node; + lyd_unlink_tree(deleted); + lyd_free_tree(deleted); + node = lyd_child(cont); + assert_non_null(node->meta); + assert_string_equal(node->meta->name, META_NAME); + + /* insert last */ + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "3"); + + /* insert first */ + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "2"); + assert_string_equal(lyd_get_value(node->next->next), "3"); + lyd_free_all(cont); +} + +static void +test_unlink_all(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *first, *second; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* unlink second then first */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &first), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &second), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, second, NULL), LY_SUCCESS); + lyd_unlink_tree(second); + assert_non_null(first->meta); + lyd_unlink_tree(first); + assert_true(first->meta && !second->meta); + + /* unlink first then second */ + assert_int_equal(lyd_insert_sibling(first, second, NULL), LY_SUCCESS); + lyd_unlink_tree(first); + assert_true(!first->meta && second->meta); + + lyd_free_all(first); + lyd_free_all(second); +} + +static void +test_insert_before_anchor(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn {" + " leaf-list llm {" + " type string;" + " }" + " leaf-list lln {" + " type string;" + " }" + "}}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "lln", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta); + assert_string_equal(lyd_get_value(node), "1"); + + assert_int_equal(lyd_new_term(cont, mod, "llm", "2", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta && node->next); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "1"); + + lyd_free_all(cont); +} + +static void +test_insert_after_anchor(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn {" + " leaf-list llm {" + " type string;" + " }" + " leaf-list lln {" + " type string;" + " }" + "}}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "llm", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta); + assert_string_equal(lyd_get_value(node), "1"); + + assert_int_equal(lyd_new_term(cont, mod, "lln", "2", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta && node->next); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "2"); + + lyd_free_all(cont); +} + +static void +test_insert_same_values_leaflist(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *n1, *n2; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + n1 = lyd_child(cont); + assert_true(n1 && !n1->meta); + assert_string_equal(lyd_get_value(n1), "1"); + + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + n2 = lyd_child(cont); + assert_true(n2 && n2->meta && n2->next); + assert_string_equal(n2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(n2), "1"); + assert_string_equal(lyd_get_value(n2->next), "1"); + + assert_ptr_equal(n1, n2); + lyd_free_all(cont); +} + +static void +test_insert_same_values_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *n1, *n2; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { list lst {key \"k\"; leaf k {type uint32;}}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1"), LY_SUCCESS); + n1 = lyd_child(cont); + assert_true(n1 && !n1->meta); + assert_string_equal(lyd_get_value(lyd_child(n1)), "1"); + + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1"), LY_SUCCESS); + n2 = lyd_child(cont); + assert_true(n2 && n2->meta); + assert_string_equal(n2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(lyd_child(n2)), "1"); + assert_string_equal(lyd_get_value(lyd_child(n2->next)), "1"); + + assert_ptr_equal(n1, n2); + lyd_free_all(cont); +} + +static void +test_remove_same_values_leaflist(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *n1, *n2, *n3, *n4, *n5, *child; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, &n1), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, &n2), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, &n3), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, &n4), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, &n5), LY_SUCCESS); + + /* remove second node */ + lyd_free_tree(n2); + child = lyd_child(cont); + assert_true((child == n1) && (child->next == n3) && (child->next->next == n4) && (child->next->next->next == n5)); + + /* remove first node */ + lyd_free_tree(n1); + child = lyd_child(cont); + assert_true((child == n3) && (child->next == n4) && (child->next->next == n5)); + + /* remove fifth node */ + lyd_free_tree(n5); + child = lyd_child(cont); + assert_true((child == n3) && (child->next == n4)); + + /* remove fourth node */ + lyd_free_tree(n4); + child = lyd_child(cont); + assert_true(child == n3); + + lyd_free_all(cont); +} + +static void +test_insert_keyless_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *lst, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { list lst {config false; leaf lf {type uint32;}}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* lyds tree is not used for keyless list */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, &lst), LY_SUCCESS); + assert_int_equal(lyd_new_term(lst, mod, "lf", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, &lst), LY_SUCCESS); + assert_int_equal(lyd_new_term(lst, mod, "lf", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta && node->next); + assert_string_equal(lyd_get_value(lyd_child(node)), "2"); + assert_string_equal(lyd_get_value(lyd_child(node->next)), "1"); + lyd_free_all(cont); +} + +static void +test_leaflist_default(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {default \"1\"; type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && !node->meta); + assert_string_equal(lyd_get_value(node), "2"); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->next && node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + lyd_free_all(cont); +} + +static void +test_unlink_then_insert(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *first, *second, *third; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* unlink first and second */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + first = lyd_child(cont); + lyd_unlink_tree(first); + second = lyd_child(cont); + lyd_unlink_tree(second); + third = lyd_child(cont); + assert_true(third && third->meta && !first->meta && !second->meta); + assert_string_equal(third->meta->name, META_NAME); + + /* insert first */ + lyd_insert_child(cont, first); + assert_true(first && first->meta && !third->meta); + assert_string_equal(first->meta->name, META_NAME); + + /* insert second */ + lyd_insert_child(cont, second); + assert_true(first && first->meta && !second->meta && !third->meta); + assert_string_equal(first->meta->name, META_NAME); + + /* check the order */ + assert_ptr_equal(lyd_child(cont), first); + first = lyd_child(cont); + assert_string_equal(lyd_get_value(first), "1"); + assert_string_equal(lyd_get_value(first->next), "2"); + assert_string_equal(lyd_get_value(first->next->next), "3"); + + /* unlink all nodes */ + lyd_unlink_tree(lyd_child(cont)); + lyd_unlink_tree(lyd_child(cont)); + lyd_unlink_tree(lyd_child(cont)); + assert_null(lyd_child(cont)); + assert_true(!first->meta && !second->meta && third->meta && get_rbt(third->meta)); + assert_string_equal(third->meta->name, META_NAME); + + /* insert third */ + lyd_insert_child(cont, third); + assert_non_null(third->meta); + assert_string_equal(third->meta->name, META_NAME); + + lyd_free_all(cont); + lyd_free_all(first); + lyd_free_all(second); +} + +static void +test_change_term(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + first = lyd_child(cont); + + /* change node which has no meta */ + node = lyd_child(cont)->next; + assert_int_equal(lyd_change_term(node, "5"), LY_SUCCESS); + assert_string_equal(lyd_get_value(node), "5"); + assert_true(first && first->meta && first->next && first->next->next); + assert_string_equal(lyd_get_value(first), "1"); + assert_string_equal(lyd_get_value(first->next), "3"); + assert_string_equal(lyd_get_value(first->next->next), "5"); + + /* change node which has meta */ + assert_int_equal(lyd_change_term(first, "6"), LY_SUCCESS); + assert_string_equal(lyd_get_value(first), "6"); + first = lyd_child(cont); + assert_true(first && first->meta && first->next && first->next->next); + assert_string_equal(lyd_get_value(first), "3"); + assert_string_equal(lyd_get_value(first->next), "5"); + assert_string_equal(lyd_get_value(first->next->next), "6"); + + lyd_free_all(cont); +} + +static void +test_change_key(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { list lst {key \"k\"; leaf k {type uint32;}}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "2"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "3"), LY_SUCCESS); + first = lyd_child(cont); + + /* change node which has no meta */ + node = lyd_child(cont)->next; + assert_int_equal(lyd_change_term(lyd_child(node), "5"), LY_SUCCESS); + assert_string_equal(lyd_get_value(lyd_child(node)), "5"); + assert_true(first && first->meta && first->next && first->next->next); + assert_string_equal(lyd_get_value(lyd_child(first)), "1"); + assert_string_equal(lyd_get_value(lyd_child(first->next)), "3"); + assert_string_equal(lyd_get_value(lyd_child(first->next->next)), "5"); + + /* change node which has meta */ + assert_int_equal(lyd_change_term(lyd_child(first), "6"), LY_SUCCESS); + assert_string_equal(lyd_get_value(lyd_child(first)), "6"); + first = lyd_child(cont); + assert_true(first && first->meta && first->next && first->next->next); + assert_string_equal(lyd_get_value(lyd_child(first)), "3"); + assert_string_equal(lyd_get_value(lyd_child(first->next)), "5"); + assert_string_equal(lyd_get_value(lyd_child(first->next->next)), "6"); + + lyd_free_all(cont); +} + +static void +test_lyd_dup_meta(void **state) +{ + const char *schema; + struct lys_module *mod, *mod2; + struct lyd_node *node, *sibl, *par, *par2; + struct lyd_meta *meta, *meta2; + struct ly_ctx *ctx2; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* metadata duplication in the same context */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &sibl), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(sibl, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &par), LY_SUCCESS); + assert_int_equal(lyd_dup_meta_single(node->meta, par, &meta), LY_SUCCESS); + assert_non_null(node->meta); + assert_string_equal(node->meta->name, META_NAME); + assert_ptr_equal(meta->annotation, node->meta->annotation); + assert_true(get_rbt(node->meta) && !get_rbt(meta)); + lyd_free_meta_single(meta); + lyd_free_all(par); + + /* metadata duplication where parameters are from different contexts */ + assert_int_equal(ly_ctx_new(NULL, 0, &ctx2), LY_SUCCESS); + assert_int_equal(ly_in_new_memory(schema, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lys_parse(ctx2, UTEST_IN, LYS_IN_YANG, NULL, &mod2), LY_SUCCESS); + ly_in_free(UTEST_IN, 0), UTEST_IN = NULL; + assert_int_equal(lyd_new_term(NULL, mod2, "ll", "1", 0, &par2), LY_SUCCESS); + assert_int_equal(lyd_dup_meta_single_to_ctx(ctx2, node->meta, par2, &meta2), LY_SUCCESS); + assert_ptr_not_equal(node->meta->annotation, meta2->annotation); + assert_null(get_rbt(meta2)); + lyd_free_all(par2); + ly_ctx_destroy(ctx2); + + lyd_free_all(node); +} + +static void +test_insert_into_duplicate(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node, *dup; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + /* create duplicate */ + assert_int_equal(lyd_dup_single(cont, NULL, LYD_DUP_RECURSIVE, &dup), LY_SUCCESS); + node = lyd_child(dup); + assert_true(node && node->next && !get_rbt(node->meta)); + assert_string_equal(node->meta->name, META_NAME); + /* insert into duplicate */ + assert_int_equal(lyd_new_term(dup, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_non_null(node->next->next); + assert_string_equal(lyd_get_value(node), "1"); + assert_string_equal(lyd_get_value(node->next), "2"); + assert_string_equal(lyd_get_value(node->next->next), "3"); + assert_non_null(get_rbt(node->meta)); + lyd_free_all(cont); + lyd_free_all(dup); +} + +static void +test_option_dup_no_meta(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *dup, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type uint32;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_dup_siblings(cont, NULL, LYD_DUP_NO_META | LYD_DUP_RECURSIVE, &dup), LY_SUCCESS); + node = lyd_child(dup); + assert_true(node && !node->meta); + assert_int_equal(lyd_new_term(dup, mod, "ll", "1", 0, NULL), LY_SUCCESS); + node = lyd_child(dup); + assert_non_null(node->meta && get_rbt(node->meta)); + assert_string_equal(node->meta->name, META_NAME); + assert_string_equal(lyd_get_value(node), "1"); + + lyd_free_all(cont); + lyd_free_all(dup); +} + +static void +test_free_meta_single(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *first, *second, *third; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* user free metadata with lyds tree but it will created again */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "7", 0, &first), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "8", 0, &second), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, second, NULL), LY_SUCCESS); + assert_non_null(first->meta); + assert_string_equal(first->meta->name, META_NAME); + lyd_free_meta_single(first->meta); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "6", 0, &third), LY_SUCCESS); + lyd_insert_sibling(first, third, NULL); + assert_string_equal(lyd_get_value(first->prev), "6"); + assert_string_equal(lyd_get_value(first), "7"); + assert_string_equal(lyd_get_value(first->next), "8"); + assert_non_null(first->prev->meta); + assert_string_equal(first->prev->meta->name, META_NAME); + lyd_free_all(first->prev); +} + +static void +test_no_metadata_remains(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *f1, *node, *f2, *dup; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* setup */ + /* create one node with metadata which contains the lyds tree */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &f1), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f1, node, NULL), LY_SUCCESS); + lyd_unlink_tree(node); + lyd_free_all(node); + assert_non_null(get_rbt(f1->meta)); + assert_string_equal(f1->meta->name, META_NAME); + /* do it again with another data tree */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &f2), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "4", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f2, node, NULL), LY_SUCCESS); + lyd_unlink_tree(node); + lyd_free_all(node); + assert_non_null(get_rbt(f2->meta)); + assert_string_equal(f2->meta->name, META_NAME); + /* also create a duplicate */ + lyd_dup_single(f2, NULL, 0, &dup); + assert_true(dup->meta && !get_rbt(dup->meta)); + assert_string_equal(dup->meta->name, META_NAME); + + /* test: insert node which also has metadata */ + assert_int_equal(lyd_insert_sibling(f1, f2, NULL), LY_SUCCESS); + /* inserted node no longer has metadata */ + assert_true(f1->next && !f1->next->meta); + lyd_unlink_tree(f2); + lyd_free_all(f2); + + /* test: insert duplicate node which also has metadata but no lyds_tree */ + assert_int_equal(lyd_insert_sibling(f1, dup, NULL), LY_SUCCESS); + /* inserted node no longer has metadata */ + assert_true(f1->next && !f1->next->meta); + lyd_unlink_tree(dup); + lyd_free_all(dup); + + /* teardown */ + lyd_free_all(f1); +} + +static void +test_insert_multiple_keys(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *node, *key; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { list lst {key \"k1 k2 k3\";" + "leaf k1 {type uint32;} leaf k2 {type uint32;} leaf k3 {type uint32;}}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "2", "0", "0"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1", "5", "0"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1", "5", "1"), LY_SUCCESS); + assert_int_equal(lyd_new_list(cont, mod, "lst", 0, NULL, "1", "6", "0"), LY_SUCCESS); + node = lyd_child(cont); + assert_true(node && node->meta && node->next && node->next->next && node->next->next->next); + assert_string_equal(node->meta->name, META_NAME); + key = lyd_child(node); + assert_string_equal(lyd_get_value(key), "1"); + assert_string_equal(lyd_get_value(key->next), "5"); + assert_string_equal(lyd_get_value(key->next->next), "0"); + key = lyd_child(node->next); + assert_string_equal(lyd_get_value(key), "1"); + assert_string_equal(lyd_get_value(key->next), "5"); + assert_string_equal(lyd_get_value(key->next->next), "1"); + key = lyd_child(node->next->next); + assert_string_equal(lyd_get_value(key), "1"); + assert_string_equal(lyd_get_value(key->next), "6"); + assert_string_equal(lyd_get_value(key->next->next), "0"); + key = lyd_child(node->next->next->next); + assert_string_equal(lyd_get_value(key), "2"); + assert_string_equal(lyd_get_value(key->next), "0"); + assert_string_equal(lyd_get_value(key->next->next), "0"); + lyd_free_all(cont); +} + +static void +test_merge_siblings(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *f1, *f2, *s1, *s2; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* both source and target trees have metadata */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &f1), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &s1), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f1, s1, NULL), LY_SUCCESS); + assert_non_null(f1->meta); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "21", 0, &f2), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "22", 0, &s2), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f2, s2, NULL), LY_SUCCESS); + assert_non_null(f2->meta); + assert_int_equal(lyd_merge_siblings(&f2, f1, 0), LY_SUCCESS); + assert_true(f2->meta && f2->next && f2->next->next && f2->next->next->next); + assert_string_equal(f2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(f2), "1"); + assert_string_equal(lyd_get_value(f2->next), "2"); + assert_string_equal(lyd_get_value(f2->next->next), "21"); + assert_string_equal(lyd_get_value(f2->next->next->next), "22"); + lyd_free_all(f1); + lyd_free_all(f2); + + /* only target tree have metadata */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &f1), LY_SUCCESS); + assert_null(f1->meta); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "21", 0, &f2), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "22", 0, &s2), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f2, s2, NULL), LY_SUCCESS); + assert_non_null(f2->meta); + assert_int_equal(lyd_merge_siblings(&f2, f1, 0), LY_SUCCESS); + assert_true(f2->meta && f2->next); + assert_string_equal(f2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(f2), "1"); + assert_string_equal(lyd_get_value(f2->next), "21"); + assert_string_equal(lyd_get_value(f2->next->next), "22"); + lyd_free_all(f1); + lyd_free_all(f2); + + /* only source tree have metadata */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &f1), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &s1), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(f1, s1, NULL), LY_SUCCESS); + assert_non_null(f1->meta); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "21", 0, &f2), LY_SUCCESS); + assert_null(f2->meta); + assert_int_equal(lyd_merge_siblings(&f2, f1, 0), LY_SUCCESS); + assert_true(f2->meta && f2->next && f2->next->next); + assert_string_equal(f2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(f2), "1"); + assert_string_equal(lyd_get_value(f2->next), "2"); + assert_string_equal(lyd_get_value(f2->next->next), "21"); + lyd_free_all(f1); + lyd_free_all(f2); + + /* none have metadata */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &f1), LY_SUCCESS); + assert_null(f1->meta); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "21", 0, &f2), LY_SUCCESS); + assert_null(f2->meta); + assert_int_equal(lyd_merge_siblings(&f2, f1, 0), LY_SUCCESS); + assert_true(f2->meta && f2->next); + assert_string_equal(f2->meta->name, META_NAME); + assert_string_equal(lyd_get_value(f2), "1"); + assert_string_equal(lyd_get_value(f2->next), "21"); + lyd_free_all(f1); + lyd_free_all(f2); +} + +static void +test_merge_siblings_destruct(void **state) +{ + const char *schema, *data; + struct lys_module *mod; + struct lyd_node *dst, *src, *first; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* only source have metadata */ + data = "{\"a:ll\":[1,2,3,4]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && !dst->meta); + data = "{\"a:ll\":[21,22]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && src->meta); + assert_int_equal(lyd_merge_siblings(&dst, src, LYD_MERGE_DESTRUCT), LY_SUCCESS); + assert_true(dst->meta && get_rbt(dst->meta)); + assert_string_equal(dst->meta->name, META_NAME); + assert_string_equal(lyd_get_value(dst), "1"); + assert_string_equal(lyd_get_value(dst->next), "2"); + assert_string_equal(lyd_get_value(dst->next->next), "3"); + assert_string_equal(lyd_get_value(dst->next->next->next), "4"); + assert_string_equal(lyd_get_value(dst->next->next->next->next), "21"); + assert_string_equal(lyd_get_value(dst->next->next->next->next->next), "22"); + lyd_free_all(dst); + + /* both source and target trees have metadata */ + data = "{\"a:ll\":[1,2,3]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && dst->meta); + data = "{\"a:ll\":[21,22,23]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && src->meta); + assert_int_equal(lyd_merge_siblings(&dst, src, LYD_MERGE_DESTRUCT), LY_SUCCESS); + assert_true(dst->meta && get_rbt(dst->meta)); + assert_string_equal(dst->meta->name, META_NAME); + assert_string_equal(lyd_get_value(dst), "1"); + assert_string_equal(lyd_get_value(dst->next), "2"); + assert_string_equal(lyd_get_value(dst->next->next), "3"); + assert_string_equal(lyd_get_value(dst->next->next->next), "21"); + assert_string_equal(lyd_get_value(dst->next->next->next->next), "22"); + assert_string_equal(lyd_get_value(dst->next->next->next->next->next), "23"); + lyd_free_all(dst); + + schema = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;revision 2014-05-08;" + "container cont {" + " leaf-list ll {type uint32;}}" + "}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* destination is empty, source have metadata */ + data = "{\"b:cont\": {} }"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && !dst->meta); + data = "{\"b:cont\": {\"ll\":[1,2,3]} }"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && lyd_child(src)->meta); + assert_int_equal(lyd_merge_siblings(&dst, src, LYD_MERGE_DESTRUCT), LY_SUCCESS); + first = lyd_child(dst); + assert_true(first->meta && get_rbt(first->meta)); + assert_string_equal(first->meta->name, META_NAME); + assert_string_equal(lyd_get_value(first), "1"); + assert_string_equal(lyd_get_value(first->next), "2"); + assert_string_equal(lyd_get_value(first->next->next), "3"); + lyd_free_all(dst); +} + +static void +test_parse_data(void **state) +{ + const char *schema, *data; + char *lyb_out; + struct lys_module *mod; + struct lyd_node *tree, *tree2; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* json */ + data = "{\"a:ll\":[2,1]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_true(tree && tree->meta && tree->next); + assert_string_equal(tree->meta->name, META_NAME); + CHECK_LYD_VALUE(((struct lyd_node_term *)tree)->value, UINT32, "1", 1); + lyd_free_all(tree); + + /* xml */ + data = "<ll xmlns=\"urn:tests:a\">2</ll>" + "<ll xmlns=\"urn:tests:a\">1</ll>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_true(tree && tree->meta && tree->next); + assert_string_equal(tree->meta->name, META_NAME); + CHECK_LYD_VALUE(((struct lyd_node_term *)tree)->value, UINT32, "1", 1); + /* data tree is used in the next check */ + + /* lyb */ + assert_int_equal(lyd_print_mem(&lyb_out, tree, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); + assert_int_equal(lyd_parse_data_mem(UTEST_LYCTX, lyb_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, + 0, &tree2), LY_SUCCESS); + assert_true(tree2 && tree2->meta && tree2->next); + assert_string_equal(tree2->meta->name, META_NAME); + CHECK_LYD_VALUE(((struct lyd_node_term *)tree2)->value, UINT32, "1", 1); + free(lyb_out); + lyd_free_all(tree2); + lyd_free_all(tree); +} + +static void +test_parse_ordered_data(void **state) +{ + const char *schema, *data; + struct lys_module *mod; + struct lyd_node *tree, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + data = "{\"a:ll\":[1,2,3]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_true(tree && !tree->meta && tree->next && tree->next->next); + CHECK_LYD_VALUE(((struct lyd_node_term *)tree)->value, UINT32, "1", 1); + + /* unlink node with value 2 */ + node = tree->next; + lyd_unlink_tree(node); + assert_true(tree && !tree->meta && tree->next); + lyd_free_all(node); + + /* unlink node with value 1 */ + node = tree; + tree = tree->next; + assert_null(tree->meta); + lyd_unlink_tree(node); + assert_true(tree && !tree->meta && !tree->next); + + /* insert node with value 1 to node with value 3 */ + lyd_insert_sibling(tree, node, NULL); + tree = tree->prev; + assert_true(tree && tree->meta && tree->next && !tree->next->meta && get_rbt(tree->meta)); + assert_string_equal(tree->meta->name, META_NAME); + assert_string_equal(lyd_get_value(tree), "1"); + assert_string_equal(lyd_get_value(tree->next), "3"); + + lyd_free_all(tree); +} + +static void +test_print_data(void **state) +{ + const char *schema, *exp; + char *out; + struct lys_module *mod; + struct lyd_node *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* lyds metadata must not be printed */ + + /* json */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &first), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_print_mem(&out, node, LYD_JSON, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK), 0); + exp = "{\"a:ll\":[1,2]}"; + assert_string_equal(out, exp); + free(out); + + /* xml */ + assert_int_equal(lyd_print_mem(&out, node, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK), 0); + exp = "<ll xmlns=\"urn:tests:a\">1</ll><ll xmlns=\"urn:tests:a\">2</ll>"; + assert_string_equal(out, exp); + free(out); + + lyd_free_all(first); +} + +static void +test_manipulation_of_many_nodes(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *cont, *iter, *prev, *node; + uint32_t i; + char *data[] = { + "q", "q", "n", "h", "c", "b", "h", "n", "p", "t", + "p", "j", "c", "h", "n", "k", "n", "q", "p", "p", + "s", "l", "p", "x", "h", "e", "i", "f", "u", "z", + "l", "n", "o", "k", "n", "t", "w", "o", "d", "b", + "k", "w", "w", "q", "e", "b", "x", "a", "g", "w", + "b", "e", "p", "r", "s", "w", "u", "w", "d", "r", + }; + uint32_t data_len = sizeof(data) / sizeof(data[0]); + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn { leaf-list ll {type string;}}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + + /* insert nodes */ + for (i = 0; i < data_len; i++) { + assert_int_equal(lyd_new_term(cont, mod, "ll", data[i], 0, NULL), LY_SUCCESS); + } + + /* sort check */ + prev = lyd_child(cont); + LY_LIST_FOR(prev->next, iter) { + assert_true(lyd_get_value(prev)[0] <= lyd_get_value(iter)[0]); + prev = iter; + } + + /* remove every even node */ + LY_LIST_FOR(lyd_child(cont), iter) { + node = iter->next; + lyd_unlink_tree(node); + lyd_free_tree(node); + } + data_len /= 2; + + /* sort check */ + prev = lyd_child(cont); + i = 1; + LY_LIST_FOR(prev->next, iter) { + assert_true(lyd_get_value(prev)[0] <= lyd_get_value(iter)[0]); + prev = iter; + i++; + } + assert_int_equal(i, data_len); + + lyd_free_all(cont); +} + +static void +test_lyds_free_metadata(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *first, *node; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_term(NULL, mod, "ll", "1", 0, &first), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(first, node, NULL), LY_SUCCESS); + lyds_free_metadata(first); + assert_null(first->meta); + assert_null(node->meta); + + lyd_free_all(first); +} + +static void +test_move_whole_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *src, *dst, *node, *first; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list tll {type string;}" + "container cn {" + " leaf head {type string;}" + " leaf-list ll {type string;}" + " leaf tail {type string;}" + "}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* move into the void: top-level, nothing will happen */ + assert_int_equal(lyd_new_term(NULL, mod, "tll", "1", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "tll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(NULL, src, &first), LY_SUCCESS); + assert_true(src && src->meta && get_rbt(src->meta) && src == first); + assert_string_equal(src->meta->name, META_NAME); + lyd_free_all(src); + + /* move to childless parent: lyd_insert_child */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + first = lyd_child(src); + assert_int_equal(lyd_unlink_siblings(first), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_insert_child(dst, first), LY_SUCCESS); + first = lyd_child(dst); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + assert_true(!lyd_child(src)); + lyd_free_all(src); + lyd_free_all(dst); + + /* move before the anchor */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + first = lyd_child(src); + assert_int_equal(lyd_unlink_siblings(lyd_child(src)), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_new_term(dst, mod, "tail", "a", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(lyd_child(dst), first, NULL), LY_SUCCESS); + first = lyd_child(dst); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + assert_true(!lyd_child(src)); + lyd_free_all(src); + lyd_free_all(dst); + + /* move to the last position */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + first = lyd_child(src); + assert_int_equal(lyd_unlink_siblings(lyd_child(src)), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_new_term(dst, mod, "head", "a", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(lyd_child(dst), first, NULL), LY_SUCCESS); + first = lyd_child(dst); + assert_true(first && first->next); + node = first->next; + assert_true(node && node->meta && get_rbt(node->meta) && node->next); + assert_string_equal(node->meta->name, META_NAME); + assert_true(!lyd_child(src)); + lyd_free_all(src); + lyd_free_all(dst); +} + +static void +test_move_part_list(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyd_node *src, *dst, *node, *first; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list tll {type string;}" + "container cn {" + " leaf head {type string;}" + " leaf-list ll {type string;}" + " leaf tail {type string;}" + "}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* move into the void: top-level, first node is unlinked */ + assert_int_equal(lyd_new_term(NULL, mod, "tll", "1", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "tll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "tll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(NULL, src->next, &first), LY_SUCCESS); + assert_true(src && src->meta && get_rbt(src->meta)); + assert_string_equal(src->meta->name, META_NAME); + assert_true(src && src->next); + assert_string_equal(lyd_get_value(first), "2"); + lyd_free_all(src); + lyd_free_all(first); + + /* move into the void: inner node, node is unlinked */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(NULL, lyd_child(src), &dst), LY_SUCCESS); + first = lyd_child(src); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + assert_true(dst && !dst->meta && !dst->next); + lyd_free_all(src); + lyd_free_all(dst); + + /* move to childless parent: lyd_insert_child */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_insert_child(dst, lyd_child(src)), LY_SUCCESS); + first = lyd_child(src); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + first = lyd_child(dst); + assert_true(first && !first->meta); + lyd_free_all(src); + lyd_free_all(dst); + + /* move before the anchor */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_new_term(dst, mod, "tail", "a", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(lyd_child(dst), lyd_child(src), NULL), LY_SUCCESS); + first = lyd_child(src); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + first = lyd_child(dst); + assert_true(first && !first->meta && first->next); + assert_string_equal(lyd_get_value(first), "1"); + lyd_free_all(src); + lyd_free_all(dst); + + /* move to the last position */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS); + assert_int_equal(lyd_new_term(dst, mod, "head", "a", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(lyd_child(dst), lyd_child(src), NULL), LY_SUCCESS); + first = lyd_child(src); + assert_true(first && first->meta && get_rbt(first->meta) && first->next); + assert_string_equal(first->meta->name, META_NAME); + first = lyd_child(dst); + assert_true(first && first->next); + node = first->next; + assert_true(node && !node->meta && !node->next); + assert_string_equal(lyd_get_value(node), "1"); + lyd_free_all(src); + lyd_free_all(dst); +} + +static void +test_merge_whole_list(void **state) +{ + const char *schema, *data; + char *check[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; + struct lys_module *mod; + struct lyd_node *src, *dst, *first, *iter; + int i; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* src with LYD_PARSE_ORDERED and dst with LYD_PARSE_ORDERED */ + data = "{\"a:ll\":[1,2,5,6,9,10]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && !src->meta); + data = "{\"a:ll\":[3,4,7,8]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && !dst->meta); + assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS); + assert_true(first->meta && get_rbt(first->meta)); + assert_string_equal(first->meta->name, META_NAME); + i = 0; + LY_LIST_FOR(first, iter) { + assert_string_equal(lyd_get_value(iter), check[i]); + ++i; + } + lyd_free_all(dst); + + /* src with LYD_PARSE_ORDERED dst with lyds_tree */ + data = "{\"a:ll\":[1,2,5,6,9,10]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && !src->meta); + data = "{\"a:ll\":[3,4,7,8]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && dst->meta); + assert_string_equal(dst->meta->name, META_NAME); + assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS); + assert_true(first->meta && get_rbt(first->meta) && !first->next->next->meta); + assert_string_equal(first->meta->name, META_NAME); + i = 0; + LY_LIST_FOR(first, iter) { + assert_string_equal(lyd_get_value(iter), check[i]); + ++i; + } + lyd_free_all(dst); + + /* src with lyds_tree and dst with LYD_PARSE_ORDERED */ + data = "{\"a:ll\":[1,2,5,6,9,10]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && src->meta); + assert_string_equal(src->meta->name, META_NAME); + data = "{\"a:ll\":[3,4,7,8]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && !dst->meta); + assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS); + assert_true(first->meta && get_rbt(first->meta)); + assert_string_equal(first->meta->name, META_NAME); + i = 0; + LY_LIST_FOR(first, iter) { + assert_string_equal(lyd_get_value(iter), check[i]); + ++i; + } + lyd_free_all(dst); + + /* src with lyds_tree and dst with lyds_tree */ + data = "{\"a:ll\":[1,2,5,6,9,10]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src); + assert_true(src && src->meta); + assert_string_equal(src->meta->name, META_NAME); + data = "{\"a:ll\":[3,4,7,8]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); + assert_true(dst && dst->meta); + assert_string_equal(dst->meta->name, META_NAME); + assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS); + assert_true(first->meta && get_rbt(first->meta)); + assert_string_equal(first->meta->name, META_NAME); + i = 0; + LY_LIST_FOR(first, iter) { + assert_string_equal(lyd_get_value(iter), check[i]); + ++i; + } + lyd_free_all(dst); +} + +static void +test_unlink_siblings(void **state) +{ + const char *schema, *data; + struct lys_module *mod; + struct lyd_node *cont, *node, *first; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "container cn {" + " leaf-list ll {type uint32;}" + " leaf-list tail {type string;}" + "}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "tail", "a", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "tail", "b", 0, NULL), LY_SUCCESS); + + node = lyd_child(cont)->next; + assert_int_equal(lyd_unlink_siblings(node), LY_SUCCESS); + first = lyd_child(cont); + assert_true(first && first->meta && !first->next); + assert_string_equal(first->meta->name, META_NAME); + assert_string_equal(lyd_get_value(first), "1"); + assert_true(node && !node->meta && node->next && node->next->next && node->next->next->next); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "3"); + + lyd_free_all(node); + lyd_free_all(cont); + + /* call lyds_split but no metadata is set */ + data = "{\"a:cn\": {\"ll\":[1,2,3,4]} }"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, cont); + first = lyd_child(cont); + assert_true(!first->meta); + node = lyd_child(cont)->next; + lyd_unlink_siblings(node); + assert_true(node && !node->meta && node->next); + assert_string_equal(lyd_get_value(node), "2"); + assert_string_equal(lyd_get_value(node->next), "3"); + assert_string_equal(lyd_get_value(node->next->next), "4"); + lyd_free_all(node); + lyd_free_all(cont); + + /* call lyds_split, metadata is set, two items */ + assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS); + first = lyd_child(cont); + assert_true(first->meta); + node = lyd_child(cont)->next; + lyd_unlink_siblings(node); + assert_true(node && !node->meta); + assert_string_equal(lyd_get_value(node), "2"); + lyd_free_all(node); + lyd_free_all(cont); +} + +static void +test_order_violation(void **state) +{ + const char *schema, *data; + struct lys_module *mod; + struct lyd_node *tree, *node, *dst, *first; + + schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;" + "leaf-list ll {type uint32;}}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* inserting a new node causes the nodes to be sorted */ + data = "{\"a:ll\":[1,8,2]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_true(tree && !tree->meta && tree->next && tree->next->next); + assert_string_equal(lyd_get_value(tree), "1"); + assert_string_equal(lyd_get_value(tree->next), "8"); + assert_string_equal(lyd_get_value(tree->next->next), "2"); +#ifndef NDEBUG + CHECK_LOG_CTX("Data in \"ll\" are not sorted, inserted node should not be added to the end.", NULL, 0); +#endif + assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &node), LY_SUCCESS); + lyd_insert_sibling(tree, node, NULL); + assert_string_equal(lyd_get_value(tree), "1"); + assert_string_equal(lyd_get_value(tree->next), "2"); + assert_string_equal(lyd_get_value(tree->next->next), "3"); + assert_string_equal(lyd_get_value(tree->next->next->next), "8"); + lyd_free_all(tree); + + /* move unsorted nodes causes the nodes to be sorted */ + data = "{\"a:ll\":[1,8,2]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); +#ifndef NDEBUG + CHECK_LOG_CTX("Data in \"ll\" are not sorted, inserted node should not be added to the end.", NULL, 0); +#endif + data = "{\"a:ll\":[7,5]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst); +#ifndef NDEBUG + CHECK_LOG_CTX("Data in \"ll\" are not sorted, inserted node should not be added to the end.", NULL, 0); +#endif + lyd_insert_sibling(dst, tree, &first); + assert_string_equal(lyd_get_value(first), "1"); + assert_string_equal(lyd_get_value(first->next), "2"); + assert_string_equal(lyd_get_value(first->next->next), "5"); + assert_string_equal(lyd_get_value(first->next->next->next), "7"); + assert_string_equal(lyd_get_value(first->next->next->next->next), "8"); + lyd_free_all(first); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_insert_top_level_list), + UTEST(test_insert_top_level_leaflist), + UTEST(test_insert_cont_list), + UTEST(test_insert_cont_leaflist), + UTEST(test_try_user_order_func), + UTEST(test_ordered_by_user), + UTEST(test_remove), + UTEST(test_remove_then_insert), + UTEST(test_unlink_all), + UTEST(test_insert_before_anchor), + UTEST(test_insert_after_anchor), + UTEST(test_insert_same_values_leaflist), + UTEST(test_insert_same_values_list), + UTEST(test_remove_same_values_leaflist), + UTEST(test_insert_keyless_list), + UTEST(test_leaflist_default), + UTEST(test_unlink_then_insert), + UTEST(test_change_term), + UTEST(test_change_key), + UTEST(test_lyd_dup_meta), + UTEST(test_insert_into_duplicate), + UTEST(test_option_dup_no_meta), + UTEST(test_no_metadata_remains), + UTEST(test_free_meta_single), + UTEST(test_insert_multiple_keys), + UTEST(test_merge_siblings), + UTEST(test_merge_siblings_destruct), + UTEST(test_parse_data), + UTEST(test_parse_ordered_data), + UTEST(test_print_data), + UTEST(test_manipulation_of_many_nodes), + UTEST(test_lyds_free_metadata), + UTEST(test_move_whole_list), + UTEST(test_move_part_list), + UTEST(test_merge_whole_list), + UTEST(test_unlink_siblings), + UTEST(test_order_violation), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c new file mode 100644 index 0000000..17fd078 --- /dev/null +++ b/tests/utests/data/test_validation.c @@ -0,0 +1,1541 @@ +/** + * @file test_validation.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from validation.c + * + * Copyright (c) 2020 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 <stdio.h> +#include <string.h> + +#include "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +static void +test_when(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf a {\n" + " when \"../../c = 'val_c'\";\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " when \"/cont/b = 'val_b'\";\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<c xmlns=\"urn:tests:a\">hey</c>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"/cont/b = 'val_b'\" not satisfied.", "/a:c", 0); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><b>val_b</b></cont><c xmlns=\"urn:tests:a\">hey</c>", tree); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->flags); + lyd_free_all(tree); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val</a><b>val_b</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree); + CHECK_LYSC_NODE(lyd_child(tree)->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->flags); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->flags); + lyd_free_all(tree); +} + +static void +test_mandatory_when(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " when \"../a = 'val_a'\";\n" + " mandatory true;\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " leaf d {\n" + " when \"../c = 'val_c'\";\n" + " mandatory true;\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:a\">hey</d>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"../c = 'val_c'\" not satisfied.", "/a:d", 0); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:a\"><b>hey</b></cont>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"../a = 'val_a'\" not satisfied.", "/a:cont/b", 0); + + LYD_TREE_CREATE("<c xmlns=\"urn:tests:a\">val_c</c><d xmlns=\"urn:tests:a\">hey</d>", tree); + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "d", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->next->flags); + lyd_free_all(tree); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_a</a><b>hey</b></cont>", tree); + CHECK_LYSC_NODE(lyd_child(tree)->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "b", 0, LYS_LEAF, tree->schema, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->next->flags); + lyd_free_all(tree); +} + +static void +test_type_incomplete_when(void **state) +{ + struct lys_module *mod; + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " when \"../c = 'val_c'\";\n" + " leaf a {\n" + " type leafref {\n" + " path \"/a:c\";\n" + " }\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_c</a><b>val</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree); + + /* make the when false */ + assert_int_equal(LY_SUCCESS, lyd_change_term(tree->next, "wrong-val")); + + /* autodelete when with a leafref */ + assert_int_equal(LY_SUCCESS, lyd_validate_module(&tree, mod, 0, NULL)); + assert_string_equal(LYD_NAME(tree), "c"); + assert_null(tree->next); + + lyd_free_all(tree); +} + +static void +test_mandatory(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " mandatory true;\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " leaf c {\n" + " mandatory true;\n" + " type string;\n" + " }\n" + " leaf d {\n" + " type empty;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Mandatory choice \"choic\" data do not exist.", "/b:choic", 0, "missing-choice"); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:b\">string</l><d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "/b:c", 0); + + CHECK_PARSE_LYD_PARAM("<a xmlns=\"urn:tests:b\">string</a>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "/b:c", 0); + + LYD_TREE_CREATE("<a xmlns=\"urn:tests:b\">string</a><c xmlns=\"urn:tests:b\">string2</c>", tree); + lyd_free_siblings(tree); +} + +static void +test_minmax(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module c {\n" + " namespace urn:tests:c;\n" + " prefix c;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf-list l {\n" + " min-elements 3;\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " list lt {\n" + " max-elements 4;\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type empty;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">mate</l>" + "<d xmlns=\"urn:tests:c\"/>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements"); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements"); + + LYD_TREE_CREATE("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>" + "<l xmlns=\"urn:tests:c\">val3</l>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>" + "<l xmlns=\"urn:tests:c\">val3</l>" + "<lt xmlns=\"urn:tests:c\"><k>val1</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val2</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val3</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val4</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val5</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val6</k></lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too many \"lt\" instances.", "/c:lt[k='val5']", 0, "too-many-elements"); +} + +const char *schema_d = + "module d {\n" + " namespace urn:tests:d;\n" + " prefix d;\n" + " yang-version 1.1;\n" + "\n" + " list lt {\n" + " key \"k\";\n" + " unique \"l1\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + " }\n" + " list lt2 {\n" + " key \"k\";\n" + " unique \"cont/l2 l4\";\n" + " unique \"l5 l6\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " container cont {\n" + " leaf l2 {\n" + " type string;\n" + " }\n" + " }\n" + " leaf l4 {\n" + " type string;\n" + " }\n" + " leaf l5 {\n" + " type string;\n" + " }\n" + " leaf l6 {\n" + " type string;\n" + " }\n" + " list lt3 {\n" + " key \"kk\";\n" + " unique \"l3\";\n" + " leaf kk {\n" + " type string;\n" + " }\n" + " leaf l3 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}"; + +static void +test_unique(void **state) +{ + struct lyd_node *tree; + + UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + "</lt>", tree); + lyd_free_all(tree); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>not-same</l1>\n" + "</lt>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>same</l1>\n" + "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val1']\" and \"/d:lt[k='val2']\".", + "/d:lt[k='val2']", 0, "data-not-unique"); + + /* now try with more instances */ + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <l1>3</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <l1>4</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <l1>5</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + " <l1>6</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + " <l1>7</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + " <l1>8</l1>\n" + "</lt>", tree); + lyd_free_all(tree); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <l1>3</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <l1>5</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + " <l1>6</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + "</lt>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <l1>4</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + " <l1>8</l1>\n" + "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val7']\" and \"/d:lt[k='val2']\".", + "/d:lt[k='val2']", 0, "data-not-unique"); +} + +static void +test_unique_nested(void **state) +{ + struct lyd_node *tree; + + UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL); + + /* nested list uniquest are compared only with instances in the same parent list instance */ + LYD_TREE_CREATE("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val2</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>3</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <l4>4</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>5</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val2</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val3</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l3\" not satisfied in \"/d:lt2[k='val2']/lt3[kk='val3']\" and " + "\"/d:lt2[k='val2']/lt3[kk='val1']\".", "/d:lt2[k='val2']/lt3[kk='val1']", 0, "data-not-unique"); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>3</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>5</l4>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"cont/l2 l4\" not satisfied in \"/d:lt2[k='val4']\" and \"/d:lt2[k='val2']\".", + "/d:lt2[k='val2']", 0, "data-not-unique"); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>1</l5>\n" + " <l6>1</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>1</l5>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>3</l5>\n" + " <l6>3</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l6>1</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>3</l5>\n" + " <l6>3</l6>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l5 l6\" not satisfied in \"/d:lt2[k='val5']\" and \"/d:lt2[k='val3']\".", + "/d:lt2[k='val3']", 0, "data-not-unique"); +} + +static void +test_dup(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module e {\n" + " namespace urn:tests:e;\n" + " prefix e;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf-list l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " list lt {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " container cont {\n" + " list lt {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " leaf-list ll2 {\n" + " type enumeration {\n" + " enum one;\n" + " enum two;\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:e\">25</d><d xmlns=\"urn:tests:e\">50</d>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"d\".", "/e:d", 0); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:e\"><k>A</k></lt>" + "<lt xmlns=\"urn:tests:e\"><k>B</k></lt>" + "<lt xmlns=\"urn:tests:e\"><k>A</k></lt>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"lt\".", "/e:lt[k='A']", 0); + + CHECK_PARSE_LYD_PARAM("<ll xmlns=\"urn:tests:e\">A</ll>" + "<ll xmlns=\"urn:tests:e\">B</ll>" + "<ll xmlns=\"urn:tests:e\">B</ll>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "/e:ll[.='B']", 0); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"></cont><cont xmlns=\"urn:tests:e\"/>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"cont\".", "/e:cont", 0); + + /* same tests again but using hashes */ + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><d>25</d><d>50</d><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"d\".", "/e:cont/d", 1); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>" + "<lt><k>a</k></lt>" + "<lt><k>b</k></lt>" + "<lt><k>c</k></lt>" + "<lt><k>d</k></lt>" + "<lt><k>c</k></lt></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"lt\".", "/e:cont/lt[k='c']", 1); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>" + "<ll>a</ll><ll>b</ll><ll>c</ll><ll>d</ll><ll>d</ll></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "/e:cont/ll[.='d']", 1); + + /* cases */ + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l>" + "<l xmlns=\"urn:tests:e\">b</l>" + "<l xmlns=\"urn:tests:e\">c</l>" + "<l xmlns=\"urn:tests:e\">b</l>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"l\".", "/e:l[.='b']", 0); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l><l xmlns=\"urn:tests:e\">b</l>" + "<l xmlns=\"urn:tests:e\">c</l>" + "<a xmlns=\"urn:tests:e\">aa</a>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Data for both cases \"a\" and \"b\" exist.", "/e:choic", 0); +} + +static void +test_defaults(void **state) +{ + struct lyd_node *tree, *node, *diff; + struct lys_module *mod; + const char *schema = + "module f {\n" + " namespace urn:tests:f;\n" + " prefix f;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " default \"c\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " case c {\n" + " leaf-list ll1 {\n" + " type string;\n" + " default \"def1\";\n" + " default \"def2\";\n" + " default \"def3\";\n" + " }\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " default 15;\n" + " }\n" + " leaf dd {\n" + " type uint32;\n" + " when '../d = 666';\n" + " default 15;\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " default \"dflt1\";\n" + " default \"dflt2\";\n" + " }\n" + " container cont {\n" + " choice choic {\n" + " default \"c\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " case c {\n" + " leaf-list ll1 {\n" + " type string;\n" + " default \"def1\";\n" + " default \"def2\";\n" + " default \"def3\";\n" + " }\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " default 15;\n" + " }\n" + " leaf dd {\n" + " type uint32;\n" + " when '../d = 666';\n" + " default 15;\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " default \"dflt1\";\n" + " default \"dflt2\";\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL));\ + + /* get defaults */ + tree = NULL; + assert_int_equal(lyd_validate_module(&tree, mod, 0, &diff), LY_SUCCESS); + assert_non_null(tree); + assert_non_null(diff); + + /* check all defaults exist */ + CHECK_LYD_STRING_PARAM(tree, + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <ll1 yang:operation=\"create\">def1</ll1>\n" + " <ll1 yang:operation=\"create\">def2</ll1>\n" + " <ll1 yang:operation=\"create\">def3</ll1>\n" + " <d yang:operation=\"create\">15</d>\n" + " <ll2 yang:operation=\"create\">dflt1</ll2>\n" + " <ll2 yang:operation=\"create\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create another explicit case and validate */ + assert_int_equal(lyd_new_term(NULL, mod, "l", "value", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def3</ll1>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create explicit leaf-list and leaf and validate */ + assert_int_equal(lyd_new_term(NULL, mod, "d", "15", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll2", "dflt2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt2</ll2>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create first explicit container, which should become implicit */ + assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + /* check diff */ + assert_null(diff); + + /* create second explicit container, which should become implicit, so the first tree node should be removed */ + assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + /* check diff */ + assert_null(diff); + + /* similar changes for nested defaults */ + assert_int_equal(lyd_new_term(tree->prev, NULL, "ll1", "def3", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(tree->prev, NULL, "d", "5", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(tree->prev, NULL, "ll2", "non-dflt", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1>def3</ll1>\n" + " <d>5</d>\n" + " <ll2>non-dflt</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ll1 yang:operation=\"delete\">def1</ll1>\n" + " <ll1 yang:operation=\"delete\">def2</ll1>\n" + " <ll1 yang:operation=\"delete\">def3</ll1>\n" + " <d yang:operation=\"delete\">15</d>\n" + " <ll2 yang:operation=\"delete\">dflt1</ll2>\n" + " <ll2 yang:operation=\"delete\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + lyd_free_all(tree); + + /* check data tree - when enabled node */ + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:f\">666</d><cont xmlns=\"urn:tests:f\"><d>666</d></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_STRING_PARAM(tree, + "<ll1 xmlns=\"urn:tests:f\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\">666</d>\n" + "<dd xmlns=\"urn:tests:f\">15</dd>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1>def1</ll1>\n" + " <ll1>def2</ll1>\n" + " <ll1>def3</ll1>\n" + " <d>666</d>\n" + " <dd>15</dd>\n" + " <ll2>dflt1</ll2>\n" + " <ll2>dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); +} + +static void +test_state(void **state) +{ + const char *data; + struct lyd_node *tree; + const char *schema = + "module h {\n" + " namespace urn:tests:h;\n" + " prefix h;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " container cont2 {\n" + " config false;\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "<cont xmlns=\"urn:tests:h\">\n" + " <cont2>\n" + " <l>val</l>\n" + " </cont2>\n" + "</cont>\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_NO_STATE, 0, LY_EVALID, tree); + CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.", "/h:cont/cont2", 3); + + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE, NULL)); + CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.", "/h:cont/cont2", 0); + lyd_free_all(tree); +} + +static void +test_must(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module i {\n" + " namespace urn:tests:i;\n" + " prefix i;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " leaf l2 {\n" + " must \"../l = 'right'\";\n" + " type string;\n" + " }\n" + " leaf l3 {\n" + " must \"../l = 'left'\" {\n" + " error-app-tag \"not-left\";\n" + " error-message \"l leaf is not left\";\n" + " }\n" + " type string;\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n" + " <l>wrong</l>\n" + " <l2>val</l2>\n" + "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/i:cont/l2", 0, "must-violation"); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:i\">\n" + " <l>right</l>\n" + " <l2>val</l2>\n" + "</cont>\n", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n" + " <l>wrong</l>\n" + " <l3>val</l3>\n" + "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "/i:cont/l3", 0, "not-left"); +} + +static void +test_multi_error(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module ii {\n" + " namespace urn:tests:ii;\n" + " prefix ii;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " leaf l2 {\n" + " must \"../l = 'right'\";\n" + " type string;\n" + " }\n" + " leaf l3 {\n" + " must \"../l = 'left'\" {\n" + " error-app-tag \"not-left\";\n" + " error-message \"l leaf is not left\";\n" + " }\n" + " type string;\n" + " }\n" + " leaf-list ll {\n" + " type uint32;\n" + " min-elements 2;\n" + " }\n" + " }\n" + "}"; + const char *data; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* xml */ + data = + "<cont xmlns=\"urn:tests:ii\">\n" + " <l>wrong</l>\n" + " <l>wrong2</l>\n" + " <l2>val</l2>\n" + " <l3>val</l3>\n" + " <ll>ahoy</ll>\n" + "</cont>\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "/ii:cont/l3", 0, "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/ii:cont/l2", 0, "must-violation"); + CHECK_LOG_CTX_APPTAG("Invalid type uint32 value \"ahoy\".", "/ii:cont/ll", 6, NULL); + + /* json */ + data = "{\n" + " \"ii:cont\": {\n" + " \"l\": \"wrong\",\n" + " \"l\": \"wrong2\",\n" + " \"l2\": \"val\",\n" + " \"l3\": \"val\",\n" + " \"ll\": [\"ahoy\"]\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "/ii:cont/l3", 0, "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/ii:cont/l2", 0, "must-violation"); + CHECK_LOG_CTX_APPTAG("Invalid non-number-encoded uint32 value \"ahoy\".", "/ii:cont/ll", 7, NULL); + + /* validation */ + data = "{\n" + " \"ii:cont\": {\n" + " \"l\": \"wrong\",\n" + " \"l\": \"wrong2\",\n" + " \"l2\": \"val\",\n" + " \"l3\": \"val\",\n" + " \"ll\": [25]\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, NULL)); + lyd_free_tree(tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "/ii:cont/l3", 0, "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/ii:cont/l2", 0, "must-violation"); + CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "/ii:cont/l", 0, NULL); + CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "/ii:cont/l", 0, NULL); +} + +const char *schema_j = + "module j {\n" + " namespace urn:tests:j;\n" + " prefix j;\n" + " yang-version 1.1;\n" + "\n" + " feature feat1;\n" + "\n" + " container cont {\n" + " must \"false()\";\n" + " list l1 {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " if-feature feat1;\n" + " input {\n" + " must \"../../lf1 = 'true'\";\n" + " leaf lf2 {\n" + " type leafref {\n" + " path /lf3;\n" + " }\n" + " }\n" + " }\n" + " output {\n" + " must \"../../lf1 = 'true2'\";\n" + " leaf lf2 {\n" + " type leafref {\n" + " path /lf4;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " leaf lf3 {\n" + " type string;\n" + " }\n" + "\n" + " leaf lf4 {\n" + " type string;\n" + " }\n" + "}"; +const char *feats_j[] = {"feat1", NULL}; + +static void +test_action(void **state) +{ + struct ly_in *in; + struct lyd_node *tree, *op_tree; + + UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + UTEST_LOG_CTX_CLEAN; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory( + "<cont xmlns=\"urn:tests:j\">\n" + " <l1>\n" + " <k>val1</k>\n" + " <act>\n" + " <lf2>target</lf2>\n" + " </act>\n" + " </l1>\n" + "</cont>\n", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &op_tree, NULL)); + assert_non_null(op_tree); + + /* missing leafref */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_RPC_YANG, NULL)); + CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf3\" with the same value.", + "/j:cont/l1[k='val1']/act/lf2", 0); + ly_in_free(in, 0); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>not true</lf1>\n" + "</cont>\n" + "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* input must false */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL)); + CHECK_LOG_CTX("Must condition \"../../lf1 = 'true'\" not satisfied.", "/j:cont/l1[k='val1']/act", 0); + + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>true</lf1>\n" + "</cont>\n" + "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* success */ + assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL)); + + lyd_free_tree(op_tree); + lyd_free_siblings(tree); +} + +static void +test_rpc(void **state) +{ + const char *schema, *data; + struct ly_in *in; + struct lyd_node *tree; + + /* Testing constraint violation in RPC. */ + schema = + "module val-str {\n" + " namespace \"urn:vstr\";\n" + " prefix v;\n" + "\n" + " rpc modify-user-password {\n" + " input {\n" + " leaf old-password {\n" + " type string {\n" + " length \"4..8\";\n" + " }\n" + " }\n" + " leaf new-password {\n" + " type string {\n" + " length \"4..8\";\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "<modify-user-password xmlns=\"urn:vstr\">\n" + " <old-password>12345</old-password>\n" + " <new-password>123</new-password>\n" + "</modify-user-password>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, NULL)); + CHECK_LOG_CTX("Unsatisfied length - string \"123\" length is not allowed.", + "/val-str:modify-user-password/new-password", 3); + ly_in_free(in, 0); +} + +static void +test_reply(void **state) +{ + struct ly_in *in; + struct lyd_node *tree, *op_tree; + + UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + UTEST_LOG_CTX_CLEAN; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory( + "<cont xmlns=\"urn:tests:j\">\n" + " <l1>\n" + " <k>val1</k>\n" + " <act>\n" + " <lf2>target</lf2>\n" + " </act>\n" + " </l1>\n" + "</cont>\n", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &op_tree, NULL)); + assert_non_null(op_tree); + ly_in_free(in, 0); + + /* missing leafref */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_REPLY_YANG, NULL)); + CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf4\" with the same value.", + "/j:cont/l1[k='val1']/act/lf2", 0); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>not true</lf1>\n" + "</cont>\n" + "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* input must false */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL)); + CHECK_LOG_CTX("Must condition \"../../lf1 = 'true2'\" not satisfied.", "/j:cont/l1[k='val1']/act", 0); + + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>true2</lf1>\n" + "</cont>\n" + "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* success */ + assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL)); + + lyd_free_tree(op_tree); + lyd_free_all(tree); +} + +static void +test_case(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module k {\n" + " namespace urn:tests:k;\n" + " prefix k;\n" + " yang-version 1.1;\n" + "\n" + " container ch {\n" + " choice a0 {\n" + " case v0 {\n" + " leaf g0 {\n" + " type string;\n" + " }\n" + " }\n" + " case v1 {\n" + " choice a1 {\n" + " case r0 {\n" + " leaf g1 {\n" + " type string;\n" + " }\n" + " }\n" + " case r1 {\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " leaf g3 {\n" + " type string;\n" + " }\n" + " }\n" + " case r2 {\n" + " leaf g4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " case v2 {\n" + " choice a2 {\n" + " case y0 {\n" + " leaf g5 {\n" + " type string;\n" + " }\n" + " }\n" + " case y1 {\n" + " leaf g6 {\n" + " type string;\n" + " }\n" + " leaf g7 {\n" + " type string;\n" + " }\n" + " }\n" + " case y2 {\n" + " leaf g8 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM( + "{\n" + " \"k:ch\": {\n" + " \"g0\": \"value_g0\",\n" + " \"g7\": \"value_g7\"\n" + " }\n" + "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.", "/k:ch", 6); + + CHECK_PARSE_LYD_PARAM( + "{\n" + " \"k:ch\": {\n" + " \"g7\": \"value_g7\",\n" + " \"g0\": \"value_g0\"\n" + " }\n" + "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.", "/k:ch", 6); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_when), + UTEST(test_mandatory), + UTEST(test_mandatory_when), + UTEST(test_type_incomplete_when), + UTEST(test_minmax), + UTEST(test_unique), + UTEST(test_unique_nested), + UTEST(test_dup), + UTEST(test_defaults), + UTEST(test_state), + UTEST(test_must), + UTEST(test_multi_error), + UTEST(test_action), + UTEST(test_rpc), + UTEST(test_reply), + UTEST(test_case), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c new file mode 100644 index 0000000..fcaa9da --- /dev/null +++ b/tests/utests/extensions/test_metadata.c @@ -0,0 +1,205 @@ +/** + * @file test_metadata.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for Metadata extension (annotation) support + * + * Copyright (c) 2019 - 2022 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 "libyang.h" +#include "plugins_exts.h" +#include "plugins_exts/metadata.h" + +static void +test_yang(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + const char *units; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:metadata:a; prefix a;" + "import ietf-yang-metadata {prefix md;}" + "feature f;" + "md:annotation x {" + " description \"test\";" + " if-feature f;" + " reference \"test\";" + " status \"current\";" + " type uint8;" + " units meters;" + "}}"; + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, feats, &mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts)); + e = &mod->compiled->exts[0]; + assert_non_null(e->compiled); + assert_non_null(e->substmts); + lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units); + assert_string_equal("meters", units); + + /* invalid */ + /* missing mandatory type substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".", + "/aa:{extension='md:annotation'}/aa", 0); + + /* not allowed substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {default x;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.", + "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of units substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; units x; units y;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"units\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of status substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; status current; status obsolete;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"status\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of status substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; type uint8;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"type\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* duplication of the same annotation */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string;} md:annotation aa {type uint8;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.", + "/aa:{extension='md:annotation'}/aa", 0); +} + +static void +test_yin(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + const char *data, *units; + + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"a\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:a\"/><prefix value=\"a\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<feature name=\"f\"/>\n" + "<md:annotation name=\"x\">\n" + " <description><text>test</text></description>\n" + " <reference><text>test</text></reference>\n" + " <if-feature name=\"f\"/>\n" + " <status value=\"current\"/>\n" + " <type name=\"uint8\"/>\n" + " <units name=\"meters\"/>\n" + "</md:annotation></module>"; + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(data, LYS_IN_YIN, feats, &mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts)); + e = &mod->compiled->exts[0]; + assert_non_null(e->compiled); + assert_non_null(e->substmts); + lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units); + assert_string_equal("meters", units); + + /* invalid */ + /* missing mandatory type substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\"/>\n" + "</module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".", + "/aa:{extension='md:annotation'}/aa", 0); + + /* not allowed substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <default value=\"x\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.", + "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of units substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <units name=\"x\"/>\n" + " <units name=\"y\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"units\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of status substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <status value=\"current\"/>\n" + " <status value=\"obsolete\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"status\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* invalid cardinality of status substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <type name=\"uint8\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"type\".", "/aa:{extension='md:annotation'}/aa", 0); + + /* duplication of the same annotation */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + "</md:annotation><md:annotation name=\"aa\">\n" + " <type name=\"uint8\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.", + "/aa:{extension='md:annotation'}/aa", 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_yang), + UTEST(test_yin), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_nacm.c b/tests/utests/extensions/test_nacm.c new file mode 100644 index 0000000..0b1cb8b --- /dev/null +++ b/tests/utests/extensions/test_nacm.c @@ -0,0 +1,124 @@ +/* + * @file test_nacm.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for NACM extensions support + * + * Copyright (c) 2019-2020 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 "libyang.h" + +static int +setup(void **state) +{ + UTEST_SETUP; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-acm", "2018-02-14", NULL)); + + return 0; +} + +static void +test_deny_all(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + struct lysc_node_leaf *leaf; + struct lysc_ext_instance *e; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "container a { nacm:default-deny-all; leaf aa {type string;}}" + "leaf b {type string;}}"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data); + assert_non_null(leaf = (struct lysc_node_leaf *)cont->child); + assert_non_null(e = &cont->exts[0]); + assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1); + assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */ + assert_ptr_equal(e->def, leaf->exts[0].def); + assert_int_equal(1, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-all */ + assert_null(cont->next->exts); + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "nacm:default-deny-all;}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.", + "/b:{extension='nacm:default-deny-all'}", 0); + + /* invalid */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "leaf l { type string; nacm:default-deny-all; nacm:default-deny-write;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.", + "/aa:l/{extension='nacm:default-deny-all'}", 0); +} + +static void +test_deny_write(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + struct lysc_node_leaf *leaf; + struct lysc_ext_instance *e; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "container a { nacm:default-deny-write; leaf aa {type string;}}" + "leaf b {type string;}}"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data); + assert_non_null(leaf = (struct lysc_node_leaf *)cont->child); + assert_non_null(e = &cont->exts[0]); + assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1); + assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */ + assert_ptr_equal(e->def, leaf->exts[0].def); + assert_int_equal(2, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-write */ + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "notification notif {nacm:default-deny-write;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is not allowed in notification statement.", + "/b:notif/{extension='nacm:default-deny-write'}", 0); + + /* invalid */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "leaf l { type string; nacm:default-deny-write; nacm:default-deny-write;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is instantiated multiple times.", + "/aa:l/{extension='nacm:default-deny-write'}", 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_deny_all, setup), + UTEST(test_deny_write, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c new file mode 100644 index 0000000..f27e168 --- /dev/null +++ b/tests/utests/extensions/test_schema_mount.c @@ -0,0 +1,1657 @@ +/** + * @file test_schema_mount.c + * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for Schema Mount extension support + * + * Copyright (c) 2021 - 2022 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 "libyang.h" + +void **glob_state; + +static int +setup(void **state) +{ + const char *schema = + "module sm {yang-version 1.1;namespace \"urn:sm\";prefix \"sm\";" + "import ietf-yang-schema-mount {prefix yangmnt;}" + "import ietf-interfaces {prefix if;}" + "container root {yangmnt:mount-point \"root\";}" + "container root2 {yangmnt:mount-point \"root\";}" + "container root3 {" + " list ls { key name; leaf name {type string;}" + " yangmnt:mount-point \"mnt-root\";" + " }" + "}" + "container root4 {config false; yangmnt:mount-point \"root\";}" + "leaf target{type string;}" + "augment /if:interfaces/if:interface {" + " leaf sm-name {type leafref {path \"/sm:target\";}}" + "}" + "}"; + + UTEST_SETUP; + glob_state = state; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL)); + + return 0; +} + +static void +test_schema(void **state) +{ + struct lys_module *mod; + const char *schema; + char *str; + + /* invalid */ + schema = + "module sm {\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance not allowed in YANG version 1 module.", + "/sm:root/{extension='yangmnt:mount-point'}/root", 0); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " yangmnt:mount-point \"root\";\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.", + "/sm:{extension='yangmnt:mount-point'}/root", 0); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " leaf l {\n" + " type empty;\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.", + "/sm:root/l/{extension='yangmnt:mount-point'}/root", 0); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " list l {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"root\";\n" + " yangmnt:mount-point \"root2\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Multiple extension \"yangmnt:mount-point\" instances.", + "/sm:l/{extension='yangmnt:mount-point'}/root", 0); + + /* valid */ + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod)); + lys_print_mem(&str, mod, LYS_OUT_YANG, 0); + assert_string_equal(str, schema); + free(str); +} + +static LY_ERR +test_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + void **state = glob_state; + struct lyd_node *data = NULL; + + (void)ext; + + if (user_data) { + CHECK_PARSE_LYD_PARAM(user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +static void +test_parse_invalid(void **state) +{ + const char *xml, *json; + struct lyd_node *data; + + /* no callback set */ + xml = + "<root xmlns=\"urn:sm\">" + " <unknown xmlns=\"unknown\">" + " <interface>" + " <name>bu</name>" + " <type xmlns:ii=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ii:ethernetCsmacd</type>" + " </interface>" + " </unknown>" + "</root>"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.", + NULL, 0); + + json = + "{" + " \"sm:root\": {" + " \"unknown:unknown\": {" + " \"interface\": [" + " {" + " \"name\": \"bu\"," + " \"type\": \"iana-if-type:ethernetCsmacd\"" + " }" + " ]" + " }" + " }" + "}"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.", + NULL, 0); + + /* unknown data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, NULL); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_string_equal(LYD_NAME(data), "root"); + assert_null(lyd_child(data)); + assert_non_null(data->next); + assert_true(data->next->flags & LYD_DEFAULT); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("No module with namespace \"unknown\" in the context.", "/sm:root", 1); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_string_equal(LYD_NAME(data), "root"); + assert_null(lyd_child(data)); + assert_non_null(data->next); + assert_true(data->next->flags & LYD_DEFAULT); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("No module named \"unknown\" in the context.", "/sm:root", 1); + + /* missing required callback data */ + xml = + "<root xmlns=\"urn:sm\">" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">" + " <interface>" + " <name>bu</name>" + " </interface>" + " </interfaces>" + "</root>"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + + json = + "{" + " \"sm:root\": {" + " \"ietf-interfaces:interfaces\": {" + " \"interface\": [" + " {" + " \"name\": \"bu\"" + " }" + " ]" + " }" + " }" + "}"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + + /* missing module in yang-library data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", "/sm:root", 1); + + /* callback data correct, invalid YANG data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Mandatory node \"type\" instance does not exist.", + "/ietf-interfaces:interfaces/interface[name='bu']", 0); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Mandatory node \"type\" instance does not exist.", + "/ietf-interfaces:interfaces/interface[name='bu']", 0); + + /* same validation fail in separate validation */ + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Mandatory node \"type\" instance does not exist.", + "/ietf-interfaces:interfaces/interface[name='bu']", 0); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Mandatory node \"type\" instance does not exist.", + "/ietf-interfaces:interfaces/interface[name='bu']", 0); + lyd_free_siblings(data); + + /* success */ + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +static void +test_parse_inline(void **state) +{ + const char *xml, *json; + char *lyb; + struct lyd_node *data; + const struct ly_ctx *ext_ctx; + + /* valid */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + ext_ctx = LYD_CTX(lyd_child(data)); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + lyd_free_siblings(data); + + /* different yang-lib data with the same content-id */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_ptr_not_equal(ext_ctx, LYD_CTX(lyd_child(data))); + ext_ctx = LYD_CTX(lyd_child(data)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, 0)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + free(lyb); + lyd_free_siblings(data); +} + +static void +test_parse_shared(void **state) +{ + const char *xml, *json; + char *lyb; + struct lyd_node *data; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + /* different yang-lib data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>2</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root2 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root2>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Shared-schema yang-library content-id \"2\" differs from \"1\" used previously.", + "/ietf-yang-library:yang-library/content-id", 0); + + /* data for 2 mount points */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n" + "<root2 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>fu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>fu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n" + " <oper-status>down</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2020-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root2>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " \"sm:root2\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"fu\",\n" + " \"type\": \"iana-if-type:fddi\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"fu\",\n" + " \"type\": \"iana-if-type:fddi\",\n" + " \"oper-status\": \"down\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2020-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, LYD_PRINT_WITHSIBLINGS)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + free(lyb); + lyd_free_siblings(data); +} + +static void +test_parse_shared_parent_ref(void **state) +{ + const char *xml, *json; + struct lyd_node *data; + + /* wrong leafref value */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>sm</name>" + " <namespace>urn:sm</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <namespace>" + " <prefix>smp</prefix>" + " <uri>urn:sm</uri>" + " </namespace>" + " <mount-point>" + " <module>sm</module>" + " <label>mnt-root</label>" + " <shared-schema>" + " <parent-reference>/smp:target[. = current()/smp:name]</parent-reference>" + " </shared-schema>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root3 xmlns=\"urn:sm\">\n" + " <ls>\n" + " <name>target-value</name>\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n" + " </interface>\n" + " </interfaces>\n" + " </ls>\n" + "</root3>\n" + "<target xmlns=\"urn:sm\">wrong-target-value</target>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.", + "/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name", 0); + + json = + "{\n" + " \"sm:root3\": {\n" + " \"ls\": [" + " {\n" + " \"name\": \"target-value\",\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"sm:sm-name\": \"target-value\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"sm:target\": \"wrong-target-value\"\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.", + "/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name", 0); + + /* success */ + xml = + "<root3 xmlns=\"urn:sm\">\n" + " <ls>\n" + " <name>target-value</name>\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n" + " </interface>\n" + " </interfaces>\n" + " </ls>\n" + "</root3>\n" + "<target xmlns=\"urn:sm\">target-value</target>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root3\": {\n" + " \"ls\": [\n" + " {\n" + " \"name\": \"target-value\",\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"sm:sm-name\": \"target-value\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"sm:target\": \"target-value\"\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +static void +test_parse_config(void **state) +{ + const char *xml; + char *lyb; + struct lyd_node *data; + const struct lyd_node *node; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <config>false</config>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " </interface>\n" + " </interfaces>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + + node = lyd_child(data); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_print_mem(&lyb, data, LYD_LYB, 0); + lyd_free_siblings(data); + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + free(lyb); + + node = lyd_child(data); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_free_siblings(data); + + /* the same effect but use a config false mount point instead of the separate metadata node */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root4 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " </interface>\n" + " </interfaces>\n" + "</root4>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + + node = lyd_child(data->next->next->next); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_free_siblings(data); +} + +static void +test_new(void **state) +{ + const char *xml; + const struct lys_module *mod; + struct lyd_node *data, *node; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>false</enabled>\n" + " </ipv4>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + + /* create the data manually with simple new functions */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "sm"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "root", 0, &data)); + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu")); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL)); + mod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-ip"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(node, mod, "ipv4", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "enabled", "false", 0, NULL)); + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces-state", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu")); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "oper-status", "not-present", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_inner(node, NULL, "statistics", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "discontinuity-time", "2022-01-01T10:00:00-00:00", 0, NULL)); + + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + /* create the data using lyd_new_path */ + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, + "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, &data)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/ietf-ip:ipv4/enabled", "false", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/oper-status", "not-present", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/statistics/discontinuity-time", + "2022-01-01T10:00:00-00:00", 0, NULL)); + + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +static void +test_lys_getnext(void **state) +{ + const struct lysc_node *parent, *node; + struct ly_ctx *sm_ctx; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + + parent = lys_find_path(UTEST_LYCTX, NULL, "/sm:root", 0); + assert_non_null(parent); + + node = lys_getnext(NULL, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_non_null(node); + assert_string_equal(node->name, "schema-mounts"); + sm_ctx = node->module->ctx; + + node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_non_null(node); + assert_string_equal(node->name, "yang-library"); + + node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_non_null(node); + assert_string_equal(node->name, "modules-state"); + + node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_non_null(node); + assert_string_equal(node->name, "interfaces"); + + node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_non_null(node); + assert_string_equal(node->name, "interfaces-state"); + + node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT); + assert_null(node); + + ly_ctx_destroy(sm_ctx); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema), + UTEST(test_parse_invalid, setup), + UTEST(test_parse_inline, setup), + UTEST(test_parse_shared, setup), + UTEST(test_parse_shared_parent_ref, setup), + UTEST(test_parse_config, setup), + UTEST(test_new, setup), + UTEST(test_lys_getnext, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_structure.c b/tests/utests/extensions/test_structure.c new file mode 100644 index 0000000..cc8ed71 --- /dev/null +++ b/tests/utests/extensions/test_structure.c @@ -0,0 +1,255 @@ +/** + * @file test_structure.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for structure extensions support + * + * Copyright (c) 2022 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 "libyang.h" + +static void +test_schema(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + char *printed = NULL; + const char *data, *info; + + /* valid data */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {" + " must \"/n2/l\";" + " status deprecated;" + " description desc;" + " reference no-ref;" + " typedef my-type {type string;}" + " grouping my-grp {leaf gl {type my-type;}}" + " container n1 {leaf l {config false; type uint32;}}" + " list n2 {leaf l {type leafref {path /n1/l;}}}" + " uses my-grp;" + "}}"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + + /* valid augment data */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:struct/a:n1\" {" + " status obsolete;" + " reference none;" + " leaf aug-leaf {type string;}" + "}}"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + + /* yang compiled print */ + info = "module a {\n" + " namespace \"urn:tests:extensions:structure:a\";\n" + " prefix a;\n" + "\n" + " ietf-yang-structure-ext:structure \"struct\" {\n" + " must \"/n2/l\";\n" + " status deprecated;\n" + " description\n" + " \"desc\";\n" + " reference\n" + " \"no-ref\";\n" + " container n1 {\n" + " status deprecated;\n" + " leaf l {\n" + " type uint32;\n" + " status deprecated;\n" + " }\n" + " leaf aug-leaf {\n" + " type string;\n" + " status obsolete;\n" + " }\n" + " }\n" + " list n2 {\n" + " min-elements 0;\n" + " max-elements 4294967295;\n" + " ordered-by user;\n" + " status deprecated;\n" + " leaf l {\n" + " type leafref {\n" + " path \"/n1/l\";\n" + " require-instance true;\n" + " type uint32;\n" + " }\n" + " status deprecated;\n" + " }\n" + " }\n" + " leaf gl {\n" + " type string;\n" + " status deprecated;\n" + " }\n" + " }\n" + "}\n"; + + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a")); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + info = "module b {\n" + " namespace \"urn:tests:extensions:structure:b\";\n" + " prefix b;\n" + "\n" + " ietf-yang-structure-ext:augment-structure \"/a:struct/a:n1\";\n" + "}\n"; + + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b")); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* no substatements */ + data = "module c {yang-version 1.1; namespace urn:tests:extensions:structure:c; prefix c;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct;}"; + info = "module c {\n" + " namespace \"urn:tests:extensions:structure:c\";\n" + " prefix c;\n" + "\n" + " ietf-yang-structure-ext:structure \"struct\";\n" + "}\n"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); +} + +static void +test_schema_invalid(void **state) +{ + const char *data; + + /* structure */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {import yang;}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid keyword \"import\" as a child of \"sx:structure struct\" extension instance.", + "/a:{extension='sx:structure'}/struct", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "container b { sx:structure struct { container x { leaf x {type string;}}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": " + "Extension sx:structure must not be used as a non top-level statement in \"container\" statement.", + "/a:b/{extension='sx:structure'}/struct", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure { container x { leaf x {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"sx:structure\" missing argument element \"name\".", NULL, 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x {type string;}}}" + "sx:structure struct { container y { leaf y {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure is instantiated multiple times.", + "/a:{extension='sx:structure'}/struct", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x {type string;}}}" + "choice struct { container y { leaf y {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure collides with a choice with the same identifier.", + "/a:{extension='sx:structure'}/struct", 0); + + /* augment-structure */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {" + " container n1 {leaf l {config false; type uint32;}}" + " list n2 {leaf l {type string;}}" + "}" + "container n1 {leaf l2 {type uint8;}}}"; + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, NULL); + + data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:n1\" {" + " leaf aug-leaf {type string;}" + "}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_ENOTFOUND); + CHECK_LOG_CTX("Augment extension target node \"/a:n1\" from module \"b\" was not found.", + "/b:{extension='sx:augment-structure'}/{augment='/a:n1'}", 0); +} + +static void +test_parse(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + struct lyd_node *tree = NULL; + const char *yang; + const char *xml = "<x xmlns=\"urn:tests:extensions:structure:a\">" + "<x>test</x>" + "<x2 xmlns=\"urn:tests:extensions:structure:b\">25</x2>" + "</x>"; + const char *json = "{\"a:x\":{\"x\":\"test\",\"b:x2\":25}}"; + + yang = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x { type string;}}}}"; + UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, &mod); + + yang = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:struct/a:x\" {" + " leaf x2 {type uint32;}" + "}}"; + UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, NULL); + + /* get extension after recompilation */ + assert_non_null(e = mod->compiled->exts); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN)); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); + + ly_in_memory(UTEST_IN, json); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema), + UTEST(test_schema_invalid), + UTEST(test_parse), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_yangdata.c b/tests/utests/extensions/test_yangdata.c new file mode 100644 index 0000000..4de8980 --- /dev/null +++ b/tests/utests/extensions/test_yangdata.c @@ -0,0 +1,267 @@ +/* + * @file test_yangdata.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for yang-data extensions support + * + * Copyright (c) 2019-2021 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 "libyang.h" + +static int +setup(void **state) +{ + UTEST_SETUP; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + + return 0; +} + +static void +test_schema(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + char *printed = NULL; + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "feature x;" + "rc:yang-data template { container x { list l { leaf x { type string;}} leaf y {if-feature x; type string; config false;}}}}"; + const char *info = "module a {\n" + " namespace \"urn:tests:extensions:yangdata:a\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template\" {\n" + " container x {\n" + " status current;\n" + " list l {\n" /* no key */ + " min-elements 0;\n" + " max-elements 4294967295;\n" + " ordered-by user;\n" + " status current;\n" + " leaf x {\n" + " type string;\n" + " status current;\n" + " }\n" + " }\n" + " leaf y {\n" /* config and if-feature are ignored */ + " type string;\n" + " status current;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + data = "module c {yang-version 1.1; namespace urn:tests:extensions:yangdata:c; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "grouping g { choice ch { container a {presence a; config false;} container b {presence b; config true;}}}" + "rc:yang-data template { uses g;}}"; + info = "module c {\n" + " namespace \"urn:tests:extensions:yangdata:c\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template\" {\n" + " choice ch {\n" + " status current;\n" + " case a {\n" + " status current;\n" + " container a {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " case b {\n" + " status current;\n" + " container b {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:yangdata:b; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "container b { rc:yang-data template { container x { leaf x {type string;}}}}}"; + info = "module b {\n" + " namespace \"urn:tests:extensions:yangdata:b\";\n" + " prefix self;\n\n" + " container b {\n" + " config true;\n" + " status current;\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_null(mod->compiled->exts); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is ignored since it appears as a non top-level statement in \"container\" statement.", + "/b:b/{extension='rc:yang-data'}/template", 0); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* sama data nodes name, but not conflicting */ + data = "module d {yang-version 1.1; namespace urn:tests:extensions:yangdata:d; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "leaf d { type string;}" + "rc:yang-data template1 { container d {presence d;}}" + "rc:yang-data template2 { container d {presence d;}}}"; + info = "module d {\n" + " namespace \"urn:tests:extensions:yangdata:d\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template1\" {\n" + " container d {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " ietf-restconf:yang-data \"template2\" {\n" + " container d {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n\n" + " leaf d {\n" + " type string;\n" + " config true;\n" + " status current;\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 2); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); +} + +static void +test_schema_invalid(void **state) +{ + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { leaf x {type string;}}}"; + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid keyword \"leaf\" as a child of \"rc:yang-data template\" extension instance.", + "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { choice x { leaf x {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with leaf top level data node (inside a choice), " + "but only a single container data node is allowed.", "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { choice x { case x { container z {presence ppp;} leaf x {type string;}}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with multiple top level data nodes (inside a single choice's case), " + "but only a single container data node is allowed.", "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x {type string;}} container y { leaf y {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with multiple top level data nodes, " + "but only a single container data node is allowed.", "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated without any top level data node, " + "but exactly one container data node is expected.", "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data { container x { leaf x {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"rc:yang-data\" missing argument element \"name\".", NULL, 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x {type string;}}}" + "rc:yang-data template { container y { leaf y {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated multiple times.", "/a:{extension='rc:yang-data'}/template", 0); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "grouping t { leaf-list x {type string;}}" + "rc:yang-data template { uses t;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with leaf-list top level data node, " + "but only a single container data node is allowed.", "/a:{extension='rc:yang-data'}/template", 0); +} + +static void +test_parse(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + struct lyd_node *tree = NULL; + const char *schema = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x { type string;}}}}"; + const char *xml = "<x xmlns=\"urn:tests:extensions:yangdata:a\"><x>test</x></x>"; + const char *json = "{\"a:x\":{\"x\":\"test\"}}"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN)); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); + + ly_in_memory(UTEST_IN, json); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema, setup), + UTEST(test_schema_invalid, setup), + UTEST(test_parse, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/node/list.c b/tests/utests/node/list.c new file mode 100644 index 0000000..4a69dbb --- /dev/null +++ b/tests/utests/node/list.c @@ -0,0 +1,1625 @@ +/** + * @file list.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for list node + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_list *lysc_leaf; + struct lysc_node *lysc_node; + + schema = MODULE_CREATE_YANG("T0", "list user {" + "key uid;" + "unique name;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T1", "list user {" + "key uid;" + "container name{" + " leaf fist {type string;}" + " container second{leaf sub { type int32;}}" + "}" + "leaf uid{type int32;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 0, LYS_CONTAINER, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T2", "list grup {" + "key \"guid\";" + "leaf guid{type int32;}" + "list users{ key name; leaf name {type string;}}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "grup", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0); + + /* restriction */ + schema = MODULE_CREATE_YANG("T3", "list grup {" + "key guid;" + "min-elements 10;" + "max-elements 20;" + "leaf guid{type int32;}" + "list users{ key name; leaf name {type string;}}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_ORDBY_SYSTEM, 1, \ + "grup", 0, 0, 0, 0, 0, 1, 20, 10, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T4", "list user {" + "key \"uid name\";" + "unique name;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE | LYS_KEY, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T5", "list rule {" + "key \"id\";" + "unique \"name\";" + "unique \"ip port\";" + "leaf id{type int32;}" + "leaf name{type string;}" + "leaf ip{type string;}" + "leaf port{type int16;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "rule", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 2, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "id", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "ip", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + + /* test error */ + schema = MODULE_CREATE_YANG("TERR_0", "list user {" + "key uid;" + "min-elements 10;" + "max-elements -1;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"max-elements\".", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_0", "list user {" + "key uid;" + "min-elements 10;" + "max-elements 4294967298;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Value \"4294967298\" is out of \"max-elements\" bounds.", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_0", "list user {" + "key uid;" + "min-elements 20;" + "max-elements 10;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "/TERR_0:user", 0); + + schema = MODULE_CREATE_YANG("TERR_0", "list user {" + "key uid;" + "min-elements -1;" + "max-elements 20;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"min-elements\".", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_0", "list user {" + "key uid;" + "key name;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate keyword \"key\".", NULL, 5); + + schema = MODULE_CREATE_YANG("T6", "list user {" + "config false;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_KEYLESS | LYS_SET_CONFIG, \ + 1, "user", 0, 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T7", "list user {" + "key uid;" + "unique name;" + "ordered-by user;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T8", "list user {" + "key uid;" + "unique name;" + "ordered-by system;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("TERROR0", "list user {" + "key uid;" + "unique name;" + "ordered-by systeme;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"systeme\" of \"ordered-by\".", NULL, 5); + + schema = MODULE_CREATE_YANG("TERROR0", "list \"\" {" + "key uid;" + "unique name;" + "ordered-by system;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL, 0); + CHECK_LOG_CTX("Statement argument is required.", NULL, 5); + + schema = MODULE_CREATE_YANG("T9", "list user {" + "key uid;" + "unique name;" + "ordered-by system;" + "leaf uid{type int32;}" + "leaf name{type string;}" + "leaf group{type string; default \"abcd\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T10", "list user {" + "key uid;" + "leaf uid{type int32; default \"25\";}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY | LYS_SET_DFLT, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T11", + "typedef my_type {" + " type int8; default \"25\";" + "}" + "list user {" + " key uid;" + " leaf uid{type my_type;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_list *lysc_leaf; + struct lysc_node *lysc_node; + + schema = MODULE_CREATE_YIN("T0", "<list name=\"user\">" + " <key value=\"uid\"/>" + " <unique tag=\"name\"/>" + " <leaf name=\"uid\"><type name=\"int32\"/></leaf>" + " <leaf name=\"name\"><type name=\"string\"/></leaf>" + " <leaf name=\"group\"><type name=\"string\"/></leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T00", "<list name=\"user\">" + " <key value=\"u<id\"/>" + " <leaf name=\"uid\"><type name=\"int32\"/></leaf>" + " <leaf name=\"name\"><type name=\"string\"/></leaf>" + " <leaf name=\"group\"><type name=\"string\"/></leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("The list's key \"u<id\" not found.", "/T00:user", 0); + + schema = MODULE_CREATE_YIN("T1", "<list name=\"user\"> " + " <key value=\"uid\"/>" + " <container name=\"name\">" + " <leaf name=\"fist\"> <type name=\"string\"/> </leaf>" + " <container name=\"second\">" + " <leaf name=\"sub\"> <type name=\"int32\"/></leaf>" + " </container>" + " </container>" + " <leaf name=\"uid\"> <type name=\"int32\"/></leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "name", 0, LYS_CONTAINER, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T2", "<list name=\"grup\">" + "<key value=\"guid\"/>" + "<leaf name=\"guid\"> <type name=\"int32\"/> </leaf>" + "<list name=\"users\">" + " <key value=\"name\"/>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + "</list>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "grup", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0); + + /* restriction */ + schema = MODULE_CREATE_YIN("T3", + "<list name = \"grup\">" + " <key value=\"guid\"/>" + " <min-elements value=\"10\"/>" + " <max-elements value=\"20\"/>" + " <leaf name=\"guid\"> <type name=\"int32\"/> </leaf>" + " <list name=\"users\"> <key value=\"name\"/>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " </list>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_ORDBY_SYSTEM, 1, "grup", \ + 0, 0, 0, 0, 0, 1, 20, 10, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "guid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "users", 0, LYS_LIST, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T4", + "<list name=\"user\">" + " <key value=\"uid name\"/>" + " <unique tag=\"name\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"> <type name=\"string\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE | LYS_KEY, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T5", + "<list name=\"rule\">" + " <key value=\"id\"/>" + " <unique tag=\"name\"/>" + " <unique tag=\"ip port\"/>" + " <leaf name=\"id\"> <type name=\"int32\"/></leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"ip\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"port\"> <type name=\"int16\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "rule", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 2, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "id", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "ip", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + + /* test error */ + schema = MODULE_CREATE_YIN("TERR_0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <min-elements value=\"10\"/>" + " <max-elements value=\"-1\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"max-elements\" element.", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <min-elements value=\"10\"/>" + " <max-elements value=\"4294967298\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"> <type name=\"string\"/> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Value \"4294967298\" of \"value\" attribute in \"max-elements\" element is out of bounds.", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <min-elements value=\"20\"/>" + " <max-elements value=\"10\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"> <type name=\"string\"/> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid combination of min-elements and max-elements: min value 20 is bigger than the max value 10.", + NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <min-elements value=\"-1\"/>" + " <max-elements value=\"20\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"> <type name=\"string\"/> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Value \"-1\" of \"value\" attribute in \"min-elements\" element is out of bounds.", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <key value=\"name\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"> <type name=\"string\"/> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL, 0); + CHECK_LOG_CTX("Redefinition of \"key\" sub-element in \"list\" element.", NULL, 8); + + schema = MODULE_CREATE_YIN("T6", + "<list name=\"user\">" + " <config value=\"false\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"><type name=\"string\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_KEYLESS | LYS_SET_CONFIG, \ + 1, "user", 0, 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 0, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T7", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <unique tag=\"name\"/>" + " <ordered-by value=\"user\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"><type name=\"string\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T8", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <unique tag=\"name\"/>" + " <ordered-by value=\"system\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"><type name=\"string\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("TERROR0", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <unique tag=\"name\"/>" + " <ordered-by value=\"systeme\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"><type name=\"string\"/> </leaf>" + "</list>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"systeme\" of \"value\" attribute in \"ordered-by\" element. Valid values are \"system\" and \"user\".", + NULL, 8); + + schema = MODULE_CREATE_YIN("T_DEFS1", + "<list name=\"user\">" + " <key value=\"uid\"/>" + " <unique tag=\"name\"/>" + " <ordered-by value=\"system\"/>" + " <leaf name=\"uid\"> <type name=\"int32\"/> </leaf>" + " <leaf name=\"name\"> <type name=\"string\"/> </leaf>" + " <leaf name=\"group\"><type name=\"string\"/> <default value=\"ath\"/> </leaf>" + "</list>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LIST(lysc_leaf, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "user", 0, \ + 0, 0, 0, 0, 1, 0xffffffff, 0, 0, 0, 1, 0); + lysc_node = lysc_leaf->child; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "uid", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_UNIQUE, 1, "name", 1, LYS_LEAF, 1, 0, 0, 0); + lysc_node = lysc_node->next; + CHECK_LYSC_NODE(lysc_node, 0, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "group", 0, LYS_LEAF, 1, 0, 0, 0); + +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", + "list user {" + " min-elements 10;" + " max-elements 20;" + " key \"uid name\";" + " unique name;" + " leaf uid{type int32;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}"); + schema_yin = MODULE_CREATE_YIN("PRINT0", + " <list name=\"user\">\n" + " <key value=\"uid name\"/>\n" + " <unique tag=\"name\"/>\n" + " <min-elements value=\"10\"/>\n" + " <max-elements value=\"20\"/>\n" + " <leaf name=\"uid\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"group\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </list>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " list user {\n" + " key \"uid name\";\n" + " unique \"name\";\n" + " min-elements 10;\n" + " max-elements 20;\n" + " leaf uid {\n" + " type int32;\n" + " }\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf group {\n" + " type string;\n" + " }\n" + " }\n"); + schema_yin = MODULE_CREATE_YIN("PRINT1", + " <list name=\"user\">\n" + " <key value=\"uid name\"/>\n" + " <unique tag=\"name\"/>\n" + " <min-elements value=\"10\"/>\n" + " <max-elements value=\"20\"/>\n" + " <leaf name=\"uid\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"group\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </list>\n"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); + + /* test no segmentation fold due ignoring default value */ + schema_yang = MODULE_CREATE_YANG("PRINT2", "list user {" + "key uid;" + "leaf uid{type int32; default \"25\";}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + free(printed); +} + +static void +test_xml(void **state) +{ + struct lyd_node *tree; + const char *data, *schema; + struct lyd_node_inner *list_tree; + struct lyd_node_term *list_leaf; + + schema = MODULE_CREATE_YANG("T0", "list user {" + "key uid;" + "unique \"name group\";" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* add data */ + data = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Martin Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + lyd_free_all(tree); + + /* add data */ + data = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin"); + lyd_free_all(tree); + + data = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Duplicate instance of \"user\".", "/T0:user[uid='0']", 0); + + data = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Unique data leaf(s) \"name group\" not satisfied in \"/T0:user[uid='0']\" and \"/T0:user[uid='1']\".", + "/T0:user[uid='1']", 0); + + /* double key */ + schema = MODULE_CREATE_YANG("T1", "list user {" + "key \"uid group\";" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "<user xmlns=\"urn:tests:T1\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T1\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Admin"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Tomáš Novák"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "User"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Tomáš Novák"); + lyd_free_all(tree); + + data = + "<user xmlns=\"urn:tests:T1\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T1\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Duplicate instance of \"user\".", "/T1:user[uid='0'][group='User']", 0); + + /* min elements max elements */ + schema = MODULE_CREATE_YANG("T2", + "list user {" + " key uid;" + " min-elements 3;" + " max-elements 5;" + " leaf uid{type uint32;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "<user xmlns=\"urn:tests:T2\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>2</uid>" + " <name>Tomáš Jak</name>" + " <group>Admin</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin"); + /* check third item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "2", 2); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Jak"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin"); + lyd_free_all(tree); + + data = + "<user xmlns=\"urn:tests:T2\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>2</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>3</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>4</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + lyd_free_all(tree); + + /* check wrong number of items */ + data = + "<user xmlns=\"urn:tests:T2\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0); + + data = + "<user xmlns=\"urn:tests:T2\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>1</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>2</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>3</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>4</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T2\">" + " <uid>5</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Too many \"user\" instances.", "/T2:user[uid='5']", 0); + + /* empty list */ + schema = MODULE_CREATE_YANG("T_EMPTY_LIST", + "container user_list {" + " list user {" + " key uid;" + " unique \"name group\";" + " leaf uid{type uint32;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* empty list */ + data = "<user_list xmlns=\"urn:tests:T_EMPTY_LIST\"/>"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 0, 0, 0, 1, 0, 0, 0, 1); + lyd_free_all(tree); +} + +static void +test_json(void **state) +{ + struct lyd_node *tree; + const char *data, *schema; + struct lyd_node_inner *list_tree; + struct lyd_node_term *list_leaf; + + schema = MODULE_CREATE_YANG("T0", "list user {" + "key uid;" + "unique \"name group\";" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* add data */ + data = + "{\"T0:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"Martin Novák\", \"group\":\"User\"}" + "]}"; + + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Martin Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + lyd_free_all(tree); + + /* Unique */ + data = + "{\"T0:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}" + "]}"; + + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin"); + lyd_free_all(tree); + + data = + "{\"T0:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Duplicate instance of \"user\".", "/T0:user[uid='0']", 0); + + data = + "{\"T0:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"Jan Kuba\", \"group\":\"User\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Unique data leaf(s) \"name group\" not satisfied in \"/T0:user[uid='0']\" and \"/T0:user[uid='1']\".", + "/T0:user[uid='1']", 0); + + /* double key */ + schema = MODULE_CREATE_YANG("T1", "list user {" + "key \"uid group\";" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "{\"T1:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"Admin\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Admin"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Jan Kuba"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "User"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Jan Kuba"); + lyd_free_all(tree); + + data = + "{\"T1:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}" + "]}"; + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Duplicate instance of \"user\".", "/T1:user[uid='0'][group='User']", 0); + + /* min elements max elements */ + schema = MODULE_CREATE_YANG("T2", + "list user {" + " key uid;" + " min-elements 3;" + " max-elements 5;" + " leaf uid{type uint32;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "{\"T2:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":2, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}" + "]}"; + + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "0", 0); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Jan Kuba"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check second item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "1", 1); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "AntonÃn Kuba"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "User"); + /* check third item */ + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + list_leaf = (void *) list_tree->child; + assert_string_equal(list_leaf->schema->name, "uid"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, UINT32, "2", 2); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "name"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 1, 1, 1, STRING, "Tomáš Novák"); + list_leaf = (void *) list_leaf->next; + assert_string_equal(list_leaf->schema->name, "group"); + CHECK_LYD_NODE_TERM(list_leaf, 0, 0, 0, 1, 1, STRING, "Admin"); + lyd_free_all(tree); + + data = + "{\"T2:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":2, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":3, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 1, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 1, 0, 0, 1); + list_tree = (void *) list_tree->next; + CHECK_LYD_NODE_INNER(list_tree, 1, 0, 0, 0, 0, 0, 0, 1); + lyd_free_all(tree); + + /* check wrong number of items */ + data = + "{\"T2:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0); + + data = + "{\"T2:user\": [" + " {\"uid\":0, \"name\":\"Jan Kuba\", \"group\":\"User\"}," + " {\"uid\":1, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":2, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":3, \"name\":\"AntonÃn Kuba\", \"group\":\"User\"}," + " {\"uid\":4, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}," + " {\"uid\":5, \"name\":\"Tomáš Novák\", \"group\":\"Admin\"}" + "]}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_null(tree); + CHECK_LOG_CTX("Too many \"user\" instances.", "/T2:user[uid='5']", 0); + + schema = MODULE_CREATE_YANG("T_EMPTY_LIST", + "container user_list {" + " list user {" + " key uid;" + " unique \"name group\";" + " leaf uid{type uint32;}" + " leaf name{type string;}" + " leaf group{type string;}" + "}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* empty list */ + data = + "{\"T_EMPTY_LIST:user_list\": {}" + "}"; + + /* check first item */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + list_tree = (void *)tree; + CHECK_LYD_NODE_INNER(list_tree, 0, 0, 0, 1, 0, 0, 0, 1); + lyd_free_all(tree); +} + +static void +test_diff(void **state) +{ + const char *schema; + struct lyd_node *model_1, *model_2; + struct lyd_node *diff; + const char *data_1; + const char *data_2; + const char *diff_expected; + + schema = MODULE_CREATE_YANG("T0", "list user {" + "key uid;" + "unique \"name group\";" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string;}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* delete item */ + data_1 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + data_2 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>"; + + diff_expected = + "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"delete\">" + "<uid>1</uid>" + "<name>Martin Novák</name>" + "<group>User</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* add item */ + data_1 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>"; + + data_2 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + diff_expected = + "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"create\">" + "<uid>1</uid><name>Martin Novák</name><group>User</group>" + "</user>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* diff one */ + data_1 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + data_2 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>Admin</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + diff_expected = + "<user xmlns=\"urn:tests:T0\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"none\">" + "<uid>0</uid>" + "<group yang:operation=\"replace\" yang:orig-default=\"false\"" + " yang:orig-value=\"User\">Admin</group></user>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* diff same */ + data_1 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + data_2 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + " <group>User</group>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>User</group>" + "</user>"; + + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_null(diff); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); +} + +static void +test_print(void **state) +{ + + const char *schema; + const char *expected_string; + + schema = MODULE_CREATE_YANG("T0", + "list user {" + "key uid;" + "leaf uid{type uint32;}" + "leaf name{type string;}" + "leaf group{type string; default \"User\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + struct lyd_node *model_1; + const char *data_1 = + "<user xmlns=\"urn:tests:T0\">" + " <uid>0</uid>" + " <name>Tomáš Novák</name>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + " <uid>1</uid>" + " <name>Martin Novák</name>" + " <group>Admin</group>" + "</user>"; + + LYD_TREE_CREATE(data_1, model_1); + + /* XML */ + expected_string = + "<user xmlns=\"urn:tests:T0\">" + "<uid>0</uid><name>Tomáš Novák</name>" + "</user>" + "<user xmlns=\"urn:tests:T0\">" + "<uid>1</uid><name>Martin Novák</name><group>Admin</group>" + "</user>"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + /* JSON */ + expected_string = "{\"T0:user\":[" + "{\"uid\":0,\"name\":\"Tomáš Novák\"}," + "{\"uid\":1,\"name\":\"Martin Novák\",\"group\":\"Admin\"}" + "]}"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + lyd_free_all(model_1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + + UTEST(test_xml), + UTEST(test_json), + UTEST(test_diff), + UTEST(test_print), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/restriction/test_pattern.c b/tests/utests/restriction/test_pattern.c new file mode 100644 index 0000000..56c1499 --- /dev/null +++ b/tests/utests/restriction/test_pattern.c @@ -0,0 +1,394 @@ +/** + * @file test_pattern.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for int8 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_ERROR_JSON(MOD_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_pattern *pattern; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type string {" + "pattern \"[A-Za-z]*\"{" + "description \"pattern description\";" + "error-app-tag \"pattern err-apt-tag\";" + "error-message \"pattern error message\";}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Za-z]*", "pattern description", + "pattern err-apt-tag", "pattern error message", 0, NULL); + + /* heredity */ + schema = MODULE_CREATE_YANG("T1", "typedef my_type {type string {" + "pattern \"[A-Za-z]*\"{" + "description \"pattern description\";" + "error-app-tag \"pattern err-apt-tag\";" + "error-message \"pattern error message\";}}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* heredity new pattern */ + schema = MODULE_CREATE_YANG("T2", "typedef my_type {type string {" + "pattern \"[A-Za-z]*\"{" + "description \"pattern description\";" + "error-app-tag \"pattern err-apt-tag\";" + "error-message \"pattern error message\";}}}" + "leaf port {type my_type{pattern \"[A-Z]*\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[A-Z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", NULL, NULL, NULL, 0, NULL); + + /* heredity new pattern */ + schema = MODULE_CREATE_YANG("T3", "typedef my_type {type string {" + "pattern \"[A-Za-z]*\"{" + " description \"pattern 0 description\";" + " error-app-tag \"pattern 0 err-apt-tag\";" + " error-message \"pattern 0 error message\";}}}" + "leaf port {type my_type{pattern \"[A-Z]*\"{" + " description \"pattern 1 description\";" + " error-app-tag \"pattern 1 err-apt-tag\";" + " error-message \"pattern 1 error message\";" + "}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern 0 description", "pattern 0 err-apt-tag", + "pattern 0 error message", "[A-Za-z]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, "pattern 1 description", "pattern 1 err-apt-tag", + "pattern 1 error message", "[A-Z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", "pattern 1 description", + "pattern 1 err-apt-tag", "pattern 1 error message", 0, NULL); +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_pattern *pattern; + + schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"string\">" + "<pattern value=\"[A-Za-z]*\">" + " <description><text>pattern description</text></description>" + " <error-app-tag value=\"pattern err-apt-tag\"/>" + " <error-message> <value>pattern error message</value></error-message>" + "</pattern></type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Za-z]*", "pattern description", + "pattern err-apt-tag", "pattern error message", 0, NULL); + + /* heredity */ + schema = MODULE_CREATE_YIN("T1", "<typedef name=\"my_type\"> <type name=\"string\">" + "<pattern value=\"[A-Za-z]*\">" + " <description><text>pattern description</text></description>" + " <error-app-tag value=\"pattern err-apt-tag\"/>" + " <error-message><value>pattern error message</value></error-message>" + "</pattern></type></typedef>" + "<leaf name=\"port\"><type name=\"my_type\"/></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* heredity new pattern */ + schema = MODULE_CREATE_YIN("T2", "<typedef name=\"my_type\"> <type name=\"string\">" + "<pattern value=\"[A-Za-z]*\">" + " <description><text>pattern description</text></description>" + " <error-app-tag value=\"pattern err-apt-tag\"/>" + " <error-message><value>pattern error message</value></error-message>" + "</pattern></type></typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"><pattern value=\"[A-Z]*\"/>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern description", "pattern err-apt-tag", + "pattern error message", "[A-Za-z]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[A-Z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", NULL, NULL, NULL, 0, NULL); + + /* heredity new pattern */ + schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\"> <type name=\"string\">" + "<pattern value=\"[A-Za-z]*\">" + " <description> <text>pattern 0 description</text></description>" + " <error-app-tag value=\"pattern 0 err-apt-tag\"/>" + " <error-message> <value>pattern 0 error message</value></error-message>" + "</pattern></type></typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + "<pattern value=\"[A-Z]*\">" + " <description><text>pattern 1 description</text></description>" + " <error-app-tag value=\"pattern 1 err-apt-tag\"/>" + " <error-message><value>pattern 1 error message</value></error-message>" + "</pattern></type></leaf>"); + + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, "pattern 0 description", "pattern 0 err-apt-tag", + "pattern 0 error message", "[A-Za-z]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, "pattern 1 description", "pattern 1 err-apt-tag", + "pattern 1 error message", "[A-Z]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[A-Z]*", "pattern 1 description", + "pattern 1 err-apt-tag", "pattern 1 error message", 0, NULL); +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", "leaf port {type string {" + "pattern \"[A-Z]*\"{" + "description \"desc < \";" + "error-app-tag \"err-apt-tag <\";" + "error-message \"error message <\";}}}"); + + schema_yin = MODULE_CREATE_YIN("PRINT0", + " <leaf name=\"port\">\n" + " <type name=\"string\">\n" + " <pattern value=\"[A-Z]*\">\n" + " <error-message>\n" + " <value>error message <</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-apt-tag <\"/>\n" + " <description>\n" + " <text>desc < </text>\n" + " </description>\n" + " </pattern>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " leaf port {\n" + " type string {\n" + " pattern \"[A-Z]*\" {\n" + " error-message\n" + " \"error message <\";\n" + " error-app-tag \"err-apt-tag <\";\n" + " description\n" + " \"desc < \";\n" + " }\n" + " }\n" + " }\n"); + + schema_yin = MODULE_CREATE_YIN("PRINT1", + " <leaf name=\"port\">\n" + " <type name=\"string\">\n" + " <pattern value=\"[A-Z]*\">\n" + " <error-message>\n" + " <value>error message <</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-apt-tag <\"/>\n" + " <description>\n" + " <text>desc < </text>\n" + " </description>\n" + " </pattern>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); +} + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("TPATTERN_0", "typedef my_type {type string {" + "pattern \"[A-Za-z]*\"{" + " description \"pattern 0 description\";" + " error-app-tag \"pattern 0 err-apt-tag\";" + " error-message \"pattern 0 error message\";}}}" + "leaf port {type my_type{pattern \"[A-Z]*\"{" + " description \"pattern 1 description\";" + " error-app-tag \"pattern 1 err-apt-tag\";" + " error-message \"pattern 1 error message\";" + "}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* test success */ + TEST_SUCCESS_XML("TPATTERN_0", "AHOJ", STRING, "AHOJ"); + /* test print error */ + TEST_ERROR_XML("TPATTERN_0", "T128"); + CHECK_LOG_CTX("pattern 0 error message", "/TPATTERN_0:port", 1); + TEST_ERROR_XML("TPATTERN_0", "ahoj"); + CHECK_LOG_CTX("pattern 1 error message", "/TPATTERN_0:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/restriction/test_range.c b/tests/utests/restriction/test_range.c new file mode 100644 index 0000000..13cccfa --- /dev/null +++ b/tests/utests/restriction/test_range.c @@ -0,0 +1,424 @@ +/** + * @file test_range.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for int8 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_ERROR_JSON(MOD_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_range *range; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type int8 {" + "range \"0 .. 50 | 127\"{" + "description \"description test\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "description test", "err-apt-tag", "error message", 0, 2, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", "description test", "err-apt-tag", "error message", 0, NULL); + + /* heredity */ + schema = MODULE_CREATE_YANG("T1", "typedef my_type {type uint16 {" + "range \"0 .. 100\"{" + "description \"percentage\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "percentage", "err-apt-tag", "error message", 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* heredity new range */ + schema = MODULE_CREATE_YANG("T2", "typedef my_type {type uint16 {" + "range \"0 .. 100\"{" + "description \"percentage\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}" + "leaf port {type my_type{range \"0 .. 20\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0); + + /* change */ + schema = MODULE_CREATE_YANG("T3", "typedef my_type {type uint16 {" + "range \"0 .. 100\"{" + "description \"percentage\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}" + "leaf port {type my_type{" + " range \"0 .. 50\"{" + " description \"description 0-50\";" + " error-app-tag \"err-apt-tag 0-50\";" + " error-message \"error message 0-50\";}}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_UINT16, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "description 0-50", "err-apt-tag 0-50", "error message 0-50", 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50", "description 0-50", "err-apt-tag 0-50", "error message 0-50", 0, NULL); + +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_range *range; + + schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\">" + "<type name=\"int64\">" + "<range value = \"0 .. 50 | 256\">" + " <description>" + " <text>desc</text>\n" + " </description>\n" + "<error-app-tag value=\"text < tag\"/>" + " <error-message>" + " <value>yin error message <</value>\n" + " </error-message>\n" + "</range>" + "</type>" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT64, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "desc", "text < tag", "yin error message <", 0, 2, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int64", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 256", "desc", "text < tag", "yin error message <", 0, NULL); + + /* heredity */ + schema = MODULE_CREATE_YIN("T1", "<typedef name=\"my_type\">" + "<type name=\"int16\">" + "<range value = \"0 .. 50\">" + " <description>" + " <text>percentage</text>\n" + " </description>\n" + "<error-app-tag value=\"text < tag\"/>" + " <error-message>" + " <value>yin error message <</value>\n" + " </error-message>\n" + "</range>" + "</type>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>"); + + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT16, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "percentage", "text < tag", "yin error message <", 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* heredity new range */ + schema = MODULE_CREATE_YIN("T2", "<typedef name=\"my_type\">" + "<type name=\"int32\">" + "<range value = \"0 .. 100\">" + " <description>" + " <text>percentage</text>\n" + " </description>\n" + " <error-app-tag value=\"text < tag\"/>" + " <error-message>" + " <value>yin error message <</value>\n" + " </error-message>\n" + " </range>" + " </type>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <range value = \"0 .. 50\"/>" + "</type></leaf>"); + + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT32, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0); + + /* change */ + schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\">" + "<type name=\"int32\">" + "<range value = \"0 .. 100\">" + " <description>" + " <text>percentage</text>\n" + " </description>\n" + " <error-app-tag value=\"text < tag\"/>" + " <error-message>" + " <value>yin error message <</value>\n" + " </error-message>\n" + " </range>" + " </type>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <range value = \"0 .. 50\">" + " <description>" + " <text>percentage 0-50</text>\n" + " </description>\n" + " <error-app-tag value=\"text tag 0-50\"/>" + " <error-message>" + " <value>yin error message 0-50</value>\n" + " </error-message>\n" + " </range>" + "</type></leaf>"); + + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT32, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, "percentage 0-50", "text tag 0-50", "yin error message 0-50", 0, 1, NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50", "percentage 0-50", "text tag 0-50", "yin error message 0-50", 0, NULL); + +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", "leaf port {type int32 {" + "range \"0 .. 50\"{" + "description \"desc < \";" + "error-app-tag \"err-apt-tag <\";" + "error-message \"error message <\";}}}"); + + schema_yin = MODULE_CREATE_YIN("PRINT0", + " <leaf name=\"port\">\n" + " <type name=\"int32\">\n" + " <range value=\"0 .. 50\">\n" + " <error-message>\n" + " <value>error message <</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-apt-tag <\"/>\n" + " <description>\n" + " <text>desc < </text>\n" + " </description>\n" + " </range>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " leaf port {\n" + " type int32 {\n" + " range \"0 .. 50\" {\n" + " error-message\n" + " \"error message <\";\n" + " error-app-tag \"err-apt-tag <\";\n" + " description\n" + " \"desc < \";\n" + " }\n" + " }\n" + " }\n"); + + schema_yin = MODULE_CREATE_YIN("PRINT1", + " <leaf name=\"port\">\n" + " <type name=\"int32\">\n" + " <range value=\"0 .. 50\">\n" + " <error-message>\n" + " <value>error message <</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-apt-tag <\"/>\n" + " <description>\n" + " <text>desc < </text>\n" + " </description>\n" + " </range>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); +} + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("TRANGE_0", "leaf port {type int8 {" + "range \"0 .. 50 | 126\"{" + "description \"description test\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* test success */ + TEST_SUCCESS_XML("TRANGE_0", "126", INT8, "126", 126); + /* test print error */ + TEST_ERROR_XML("TRANGE_0", "-1"); + CHECK_LOG_CTX("error message", "/TRANGE_0:port", 1); + TEST_ERROR_XML("TRANGE_0", "51"); + CHECK_LOG_CTX("error message", "/TRANGE_0:port", 1); + TEST_ERROR_XML("TRANGE_0", "127"); + CHECK_LOG_CTX("error message", "/TRANGE_0:port", 1); + + /* xml test */ + schema = MODULE_CREATE_YANG("TRANGE_1", "leaf port {type uint8 {" + "range \"30 .. 50 | 126\"{" + "description \"description test\";" + "error-app-tag \"err-apt-tag\";" + "error-message \"error message\";}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* test success */ + TEST_SUCCESS_XML("TRANGE_1", "126", UINT8, "126", 126); + /* test print error */ + TEST_ERROR_XML("TRANGE_1", "0"); + CHECK_LOG_CTX("error message", "/TRANGE_1:port", 1); + TEST_ERROR_XML("TRANGE_1", "51"); + CHECK_LOG_CTX("error message", "/TRANGE_1:port", 1); + TEST_ERROR_XML("TRANGE_1", "127"); + CHECK_LOG_CTX("error message", "/TRANGE_1:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c new file mode 100644 index 0000000..5438c61 --- /dev/null +++ b/tests/utests/schema/test_printer_tree.c @@ -0,0 +1,2522 @@ +/* + * @file test_printer_tree.c + * @author: Adam Piecek <piecek@cesnet.cz> + * @brief unit tests for functions from printer_tree.c + * + * Copyright (c) 2019-2021 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 "context.h" +#include "ly_common.h" +#include "out.h" +#include "printer_schema.h" +#include "tree_schema.h" + +#define TEST_LOCAL_SETUP \ + char *printed; \ + struct lys_module *mod; \ + const char *orig; \ + const char *expect; \ + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &UTEST_OUT)); + +#define TEST_LOCAL_PRINT(MOD, LINE_LENGTH) \ + assert_int_equal(LY_SUCCESS, lys_print_module(UTEST_OUT, MOD, LYS_OUT_TREE, LINE_LENGTH, 0)); + +#define TEST_LOCAL_TEARDOWN \ + ly_out_free(UTEST_OUT, NULL, 1); + +static void +base_sections(void **state) +{ + TEST_LOCAL_SETUP; + struct lys_module *modxx; + + orig = + "module a01xx {\n" + " yang-version 1.1;\n" + " namespace \"xx:y\";\n" + " prefix xx;\n" + " container c;\n" + " container d;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &modxx); + + /* module with import statement */ + orig = + "module a01 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import a01xx {\n" + " prefix xx;\n" + " }\n" + "\n" + " grouping g1;\n" + "\n" + " grouping g2;\n" + " container g;\n" + " augment \"/xx:c\" {\n" + " container e;\n" + " }\n" + " augment \"/xx:d\" {\n" + " container f;\n" + " }\n" + " rpc rpc1;\n" + " rpc rpc2;\n" + " notification n1;\n" + " notification n2;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* from pyang */ + expect = + "module: a01\n" + " +--rw g\n" + "\n" + " augment /xx:c:\n" + " +--rw e\n" + " augment /xx:d:\n" + " +--rw f\n" + "\n" + " rpcs:\n" + " +---x rpc1\n" + " +---x rpc2\n" + "\n" + " notifications:\n" + " +---n n1\n" + " +---n n2\n" + "\n" + " grouping g1\n" + " grouping g2\n"; + + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang */ + expect = + "module: a01\n" + " +--rw g\n" + "\n" + " augment /xx:c:\n" + " +--rw e\n" + " augment /xx:d:\n" + " +--rw f\n" + "\n" + " rpcs:\n" + " +---x rpc1\n" + " +---x rpc2\n" + "\n" + " notifications:\n" + " +---n n1\n" + " +---n n2\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang */ + expect = + "module: a01xx\n" + " +--rw c\n" + " | +--rw x:e\n" + " +--rw d\n" + " +--rw x:f\n"; + + TEST_LOCAL_PRINT(modxx, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_status(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a02 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container l {\n" + " status current;\n" + " }\n" + " container m {\n" + " status deprecated;\n" + " }\n" + " container n {\n" + " status obsolete;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a02\n" + " +--rw l\n" + " x--rw m\n" + " o--rw n\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_config_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a03 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container l {\n" + " config true;\n" + " }\n" + " container m {\n" + " config false;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a03\n" + " +--rw l\n" + " +--ro m\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_rpcs_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a04 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container cont {\n" + " action rpc1 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " output {\n" + " leaf out {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a04\n" + " +--rw cont\n" + " +---x rpc1\n" + " +---w input\n" + " | +---w in? string\n" + " +--ro output\n" + " +--ro out? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_grouping_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a05 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " grouping g {\n" + " leaf a {\n" + " type string;\n" + " config true;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " config false;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " container d {\n" + " config false;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " }\n" + " container f {\n" + " leaf g {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container d {\n" + " uses g;\n" + " }\n" + "}\n"; + + /* from yanglint1 */ + expect = + "module: a05\n" + " +--rw d\n" + " +---u g\n" + "\n" + " grouping g:\n" + " +--rw a? string\n" + " +--ro b? string\n" + " +---- c? string\n" + " +--ro d\n" + " | +--ro e? string\n" + " +---- f\n" + " +---- g? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + /* from pyang */ + expect = + "module: a05\n" + " +--rw d\n" + " +--rw a? string\n" + " +--ro b? string\n" + " +--rw c? string\n" + " +--ro d\n" + " | +--ro e? string\n" + " +--rw f\n" + " +--rw g? string\n"; + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +notif_inside_container(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a06 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container c {\n" + " notification notif;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a06\n" + " +--rw c\n" + " +---n notif\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_choice(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a07 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " choice my_choice;\n" + "}\n"; + + /* from pyang */ + expect = + "module: a07\n" + " +--rw (my_choice)?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_case(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a08 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature foo;\n" + " choice my_choice {\n" + " case my_case;\n" + " }\n" + " choice shorthand {\n" + " container cont1 {\n" + " if-feature \"foo\";\n" + " status obsolete;\n" + " }\n" + " container cont2 {\n" + " container cont3;\n" + " }\n" + " }\n" + " container top {\n" + " choice shorthand1 {\n" + " container cont1;\n" + " }\n" + " choice shorthand2 {\n" + " container cont2 {\n" + " container cont3;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a08\n" + " +--rw (my_choice)?\n" + " | +--:(my_case)\n" + " +--rw (shorthand)?\n" + " | o--:(cont1)\n" + " | | o--rw cont1 {foo}?\n" + " | +--:(cont2)\n" + " | +--rw cont2\n" + " | +--rw cont3\n" + " +--rw top\n" + " +--rw (shorthand1)?\n" + " | +--:(cont1)\n" + " | +--rw cont1\n" + " +--rw (shorthand2)?\n" + " +--:(cont2)\n" + " +--rw cont2\n" + " +--rw cont3\n"; + + const char *feats[] = {"foo", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +optional_opts(void **state) +{ + TEST_LOCAL_SETUP; + /* throws libyang warn: Use of anydata to define configuration data is not recommended... */ + orig = + "module a09 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf l1 {\n" + " type string;\n" + " mandatory true;\n" + " }\n" + " leaf l2 {\n" + " type string;\n" + " mandatory false;\n" + " }\n" + " choice c1 {\n" + " mandatory true;\n" + " }\n" + " choice c2 {\n" + " mandatory false;\n" + " }\n" + " anydata a1 {\n" + " mandatory true;\n" + " }\n" + " anydata a2 {\n" + " mandatory false;\n" + " }\n" + " anyxml x1 {\n" + " mandatory true;\n" + " }\n" + " anyxml x2 {\n" + " mandatory false;\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a09\n" + " +--rw l1 string\n" + " +--rw l2? string\n" + " +--rw (c1)\n" + " +--rw (c2)?\n" + " +--rw a1 anydata\n" + " +--rw a2? anydata\n" + " +--rw x1 anyxml\n" + " +--rw x2? anyxml\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +presence_container(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a10 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container c;\n" + " container d {\n" + " presence \"str1\";\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a10\n" + " +--rw c\n" + " +--rw d!\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_keys(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a11 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " list l1 {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " }\n" + " list l2 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a11\n" + " +--rw l1* [a]\n" + " | +--rw a string\n" + " +--rw l2* [a b]\n" + " | +--rw a string\n" + " | +--rw b string\n" + " +--rw ll* string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_type_target(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a12 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf a {\n" + " type leafref {\n" + " path \"/x:b\";\n" + " }\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a12\n" + " +--rw a? -> /x:b\n" + " +--rw b? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_type_leafref(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a13 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters {\n" + " type string;\n" + " }\n" + " leaf a {\n" + " type leafref {\n" + " path \"/x:pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-no-leafref-target --tree-line-length=72 */ + expect = + "module: a13\n" + " +--rw pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters?\n" + " | string\n" + " +--rw a?\n" + " leafref\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_iffeatures(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a14 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature foo;\n" + "\n" + " feature bar;\n" + " container c {\n" + " if-feature \"foo or bar\";\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a14\n" + " +--rw c {foo or bar}?\n"; + + const char *feats[] = {"foo", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +indent_wrapper(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a15 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " container b;\n" + " }\n" + " container c {\n" + " container d {\n" + " container e;\n" + " }\n" + " container f {\n" + " container g;\n" + " }\n" + " }\n" + " container h;\n" + " container i {\n" + " container j;\n" + " container k;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a15\n" + " +--rw a\n" + " | +--rw b\n" + " +--rw c\n" + " | +--rw d\n" + " | | +--rw e\n" + " | +--rw f\n" + " | +--rw g\n" + " +--rw h\n" + " +--rw i\n" + " +--rw j\n" + " +--rw k\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +line_length_twiddling(void **state) +{ + TEST_LOCAL_SETUP; + /* node_fits_tight */ + + orig = + "module a16 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature f;\n" + "\n" + " typedef some-long-type {\n" + " type string;\n" + " }\n" + " list my-list-name {\n" + " key \"key\";\n" + " leaf key {\n" + " type string;\n" + " }\n" + " leaf nod-leaf {\n" + " if-feature \"f\";\n" + " type some-long-type;\n" + " }\n" + " leaf nos-leaf {\n" + " if-feature \"f\";\n" + " type int32;\n" + " }\n" + " }\n" + "}\n"; + + /* pyang --tree-line-length 42 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf? some-long-type {f}?\n" + " +--rw nos-leaf? int32 {f}?\n"; + + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 42); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 42); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_iffeature */ + + /* pyang --tree-line-length 41 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf? some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf? int32 {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 41); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 41); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_type */ + + /* pyang --tree-line-length 29 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf? int32\n" + " {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 29); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 29); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_keys */ + + /* pyang --tree-line-length 23 */ + expect = + "module: a16\n" + " +--rw my-list-name*\n" + " [key]\n" + " +--rw key\n" + " | string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf?\n" + " int32 {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 23); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 23); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* every_node_name_is_too_long */ + + /* pyang --tree-line-length 14 */ + expect = + "module: a16\n" + " +--rw my-list-name*\n" + " [key]\n" + " +--rw key\n" + " | string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf?\n" + " int32\n" + " {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +break_before_leafref(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a17 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " leaf abcd {\n" + " type leafref {\n" + " path \"/x:e\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-line-length 14 */ + expect = + "module: a17\n" + " +--rw e?\n" + " | string\n" + " +--rw abcd?\n" + " -> /x:e\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +break_before_leafref_and_iffeature(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a18 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature f;\n" + " leaf some-long-id {\n" + " type string;\n" + " }\n" + " leaf a {\n" + " if-feature \"f\";\n" + " type leafref {\n" + " path \"/x:some-long-id\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-no-leafref-target --tree-line-length=20 */ + expect = + "module: a18\n" + " +--rw some-long-id?\n" + " | string\n" + " +--rw a?\n" + " leafref\n" + " {f}?\n"; + + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 20); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 20); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +basic_unified_indent_before_type(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a19 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " typedef longType {\n" + " type string;\n" + " }\n" + " container A {\n" + " leaf Bnode {\n" + " type int8;\n" + " }\n" + " leaf Cnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf Dnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf E {\n" + " type longType;\n" + " mandatory true;\n" + " }\n" + " leaf G {\n" + " type int8;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a19\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw Cnode int8\n" + " +--rw Dnode int8\n" + " +--rw E longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +twiddling_unified_indent_before_type(void **state) +{ + TEST_LOCAL_SETUP; + /* basic_functionality */ + + orig = + "module a20 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " typedef longType {\n" + " type string;\n" + " }\n" + " container A {\n" + " leaf Bnode {\n" + " type int8;\n" + " }\n" + " leaf CnodeIsBigger {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf Dnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf E {\n" + " type longType;\n" + " mandatory true;\n" + " }\n" + " leaf G {\n" + " type int8;\n" + " }\n" + " }\n" + "}\n"; + + /* pyang --tree-line-length 36 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw CnodeIsBigger int8\n" + " +--rw Dnode int8\n" + " +--rw E longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 36); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 36); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* unified_indent_before_type_long_node_name */ + + /* pyang --tree-line-length 32 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw CnodeIsBigger int8\n" + " +--rw Dnode int8\n" + " +--rw E\n" + " | longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 32); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 32); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* unified_indent_before_type_long_node_type */ + + /* pyang --tree-line-length 31 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode?\n" + " | int8\n" + " +--rw CnodeIsBigger\n" + " | int8\n" + " +--rw Dnode\n" + " | int8\n" + " +--rw E\n" + " | longType\n" + " +--rw G?\n" + " int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 31); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 31); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +inheritance_of_config_flag(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a21 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " config false;\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a21\n" + " +--ro a\n" + " +--ro b? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +inheritance_of_status_flag(void **state) +{ + TEST_LOCAL_SETUP; + /* throws libyang warn: Missing explicit "..." status that was already specified in parent, inheriting. */ + orig = + "module a22 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " status current;\n" + " container b {\n" + " status deprecated;\n" + " leaf f {\n" + " type string;\n" + " }\n" + " }\n" + " container c {\n" + " status obsolete;\n" + " leaf g {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container d {\n" + " status deprecated;\n" + " container h {\n" + " status obsolete;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a22\n" + " +--rw a\n" + " | x--rw b\n" + " | | x--rw f? string\n" + " | o--rw c\n" + " | o--rw g? string\n" + " x--rw d\n" + " o--rw h\n" + " o--rw e? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +key_leaf_is_always_mandatory_true(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a23 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " list a {\n" + " key \"k1\";\n" + " list b {\n" + " key \"k2\";\n" + " leaf k1 {\n" + " type string;\n" + " }\n" + " leaf k2 {\n" + " type string;\n" + " }\n" + " }\n" + " leaf k1 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a23\n" + " +--rw a* [k1]\n" + " +--rw b* [k2]\n" + " | +--rw k1? string\n" + " | +--rw k2 string\n" + " +--rw k1 string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang but with some swapped lines */ + expect = + "module: a23\n" + " +--rw a* [k1]\n" + " +--rw k1 string\n" + " +--rw b* [k2]\n" + " +--rw k2 string\n" + " +--rw k1? string\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +transition_between_rpc_and_notif(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a24 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container top {\n" + " leaf g {\n" + " type string;\n" + " }\n" + " action rpc1 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " action rpc2 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " output {\n" + " leaf out {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " notification n1;\n" + " notification n2;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a24\n" + " +--rw top\n" + " +--rw g? string\n" + " +---x rpc1\n" + " | +---w input\n" + " | +---w in? string\n" + " +---x rpc2\n" + " | +---w input\n" + " | | +---w in? string\n" + " | +--ro output\n" + " | +--ro out? string\n" + " +---n n1\n" + " +---n n2\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +local_augment(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module a25 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container g;\n" + " augment \"/x:g\" {\n" + " container e;\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* from pyang */ + expect = + "module: a25\n" + " +--rw g\n" + " +--rw e\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +print_compiled_node(void **state) +{ + TEST_LOCAL_SETUP; + const struct lysc_node *node; + + orig = + "module a26 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " container g {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " container h {\n" + " leaf b {\n" + " type string;\n" + " mandatory true;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " list l {\n" + " key \"ip\";\n" + " leaf ip {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* pyang -f tree --tree-path /g/h/c */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw c? string\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/c", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang -f tree --tree-path /g/h/l */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw l* [ip]\n" + " +--rw ip string\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/l", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang -f tree --tree-path /g/h */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw b string\n" + " +--rw c? string\n" + " +--rw l* [ip]\n" + " +--rw ip string\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang whose output is adjusted manually */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, LYS_PRINT_NO_SUBSTMT)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +print_compiled_node_augment(void **state) +{ + TEST_LOCAL_SETUP; + const struct lysc_node *node; + + orig = + "module b26xx {\n" + " yang-version 1.1;\n" + " namespace \"xx:y\";\n" + " prefix xx;\n" + " container c;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* module with import statement */ + orig = + "module b26 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import b26xx {\n" + " prefix xx;\n" + " }\n" + "\n" + " augment \"/xx:c\" {\n" + " container e;\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* pyang -f tree --tree-path /c/e ... but prefixes modified */ + expect = + "module: b26\n" + " +--rw xx:c\n" + " +--rw e\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + node = lys_find_path(UTEST_LYCTX, NULL, "/b26xx:c/b26:e", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static LY_ERR +local_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +print_parsed_submodule(void **state) +{ + TEST_LOCAL_SETUP; + + orig = "module a27 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " include \"a27-sub\";\n" + "}\n"; + + char *submodule = + "submodule a27-sub {\n" + " yang-version 1.1;\n" + " belongs-to a27 {\n" + " prefix x;\n" + " }\n" + "\n" + " container h {\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* edited pyang output */ + expect = + "submodule: a27-sub\n" + " +--rw h\n" + " +--rw b? string\n"; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, local_imp_clb, submodule); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_submodule(UTEST_OUT, mod->parsed->includes[0].submodule, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + TEST_LOCAL_TEARDOWN; +} + +static void +yang_data(void **state) +{ + TEST_LOCAL_SETUP; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + + orig = + "module a28 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import ietf-restconf {\n" + " prefix rc;\n" + " revision-date 2017-01-26;\n" + " }\n" + "\n" + " rc:yang-data \"tmp1\" {\n" + " container cont1 {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " list l2 {\n" + " key\n" + " \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " rc:yang-data \"tmp2\" {\n" + " container con2 {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rc:yang-data \"tmp3\" {\n" + " uses g1;\n" + " uses g2;\n" + " }\n" + " rc:yang-data \"tmp4\" {\n" + " choice x {\n" + " case a {\n" + " container z;\n" + " }\n" + " case b {\n" + " container y;\n" + " }\n" + " }\n" + " }\n" + "\n" + " grouping g1 {\n" + " description\n" + " \"Some Text\";\n" + " }\n" + "\n" + " grouping g2 {\n" + " container cont3;\n" + " }\n" + " container mont;\n" + "}\n"; + + /* from pyang (--tree-print-yang-data --tree-print-groupings -p "...") + * but with these adjustments: + * - <flags> is always '--' for yang-data nodes + * - yang-data tmp3 has two 'uses' nodes + * - grouping g2 has ':' character at the end + */ + expect = + "module: a28\n" + " +--rw mont\n" + "\n" + " grouping g1\n" + " grouping g2:\n" + " +---- cont3\n" + "\n" + " yang-data tmp1:\n" + " +---- cont1\n" + " +---- lf? string\n" + " +---- l2* [a b]\n" + " +---- a string\n" + " +---- b string\n" + " yang-data tmp2:\n" + " +---- con2\n" + " +---- lf? string\n" + " yang-data tmp3:\n" + " +---u g1\n" + " +---u g2\n" + " yang-data tmp4:\n" + " +---- (x)?\n" + " +--:(a)\n" + " | +---- z\n" + " +--:(b)\n" + " +---- y\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang (--tree-print-yang-data -p "...") + * but with these adjustments: + * <flags> is always '--' for yang-data nodes + */ + expect = + "module: a28\n" + " +--rw mont\n" + "\n" + " yang-data tmp1:\n" + " +---- cont1\n" + " +---- lf? string\n" + " +---- l2* [a b]\n" + " +---- a string\n" + " +---- b string\n" + " yang-data tmp2:\n" + " +---- con2\n" + " +---- lf? string\n" + " yang-data tmp3:\n" + " +---- cont3\n" + " yang-data tmp4:\n" + " +---- (x)?\n" + " +--:(a)\n" + " | +---- z\n" + " +--:(b)\n" + " +---- y\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static LY_ERR +getter(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + struct ly_ctx *ctx; + struct lyd_node *data = NULL; + + ctx = ext->module->ctx; + if (user_data) { + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, user_data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &data)); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +#define SM_MODNAME_EXT "sm-extension" +#define SM_MOD_EXT_NAMESPACE "urn:sm-ext" +#define SM_PREF "sm" +#define SCHEMA_REF_INLINE "<inline></inline>" +#define SCHEMA_REF_SHARED(REF) "<shared-schema>"REF"</shared-schema>" + +#define EXT_DATA(MPMOD_NAME, MODULES, SCHEMA_REF) \ + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"\n" \ + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">\n" \ + "<module-set>\n" \ + " <name>test-set</name>\n" \ + " <module>\n" \ + " <name>"SM_MODNAME_EXT"</name>\n" \ + " <namespace>"SM_MOD_EXT_NAMESPACE"</namespace>\n" \ + " </module>\n" \ + MODULES \ + "</module-set>\n" \ + "<content-id>1</content-id>\n" \ + "</yang-library>\n" \ + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" \ + "<module-set-id>1</module-set-id>\n" \ + "</modules-state>\n" \ + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">\n" \ + "<namespace>\n" \ + " <prefix>"SM_PREF"</prefix>\n" \ + " <uri>x:"MPMOD_NAME"</uri>\n" \ + "</namespace>\n" \ + "<mount-point>\n" \ + " <module>"MPMOD_NAME"</module>\n" \ + " <label>mnt-root</label>\n" \ + SCHEMA_REF \ + "</mount-point>\n" \ + "</schema-mounts>" + +#define SM_MOD_MAIN(NAME, BODY) \ + "module "NAME" {\n" \ + " yang-version 1.1;\n" \ + " namespace \"x:"NAME"\";\n" \ + " prefix \"x\";\n" \ + " import ietf-yang-schema-mount {\n" \ + " prefix yangmnt;\n" \ + " }\n" \ + BODY \ + "}" + +static void +mount_point(void **state) +{ + char *data; + + TEST_LOCAL_SETUP; + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + /* interested in sm-extension.yang and sm-mod.yang */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + /* + * 'mp' flag for list and container + */ + orig = SM_MOD_MAIN("a29", + "list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a29\n" + " +--mp lt* [name]\n" + " | +--rw name string\n" + " +--mp cont\n"; + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema by 'inline' schema-ref + */ + orig = SM_MOD_MAIN("a30", + "list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a30\n" + " +--mp lt* [name]\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw name string\n"; + data = EXT_DATA("a30", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema into empty container + */ + orig = SM_MOD_MAIN("a31", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n"); + expect = + "module: a31\n" + " +--mp cont\n" + " | +--rw tlist/* [name]\n" + " | | +--rw name uint32\n" + " | +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw lf? string\n"; + data = EXT_DATA("a31", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema into non-empty container + */ + orig = SM_MOD_MAIN("a32", + "container cont {\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + " leaf lf2 {\n" + " type string;\n" + " }\n" + "}\n"); + expect = + "module: a32\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw lf1? string\n" + " +--rw lf2? string\n"; + data = EXT_DATA("a32", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting with parent-reference + */ + orig = SM_MOD_MAIN("a33", + "list pr {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf prlf {\n" + " type string;\n" + " }\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n" + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + " list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"); + expect = + "module: a33\n" + " +--rw pr* [name]\n" + " | +--rw name string\n" + " | +--rw prlf? string\n" + " +--rw lf? string\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw pr@* [name]\n" + " | +--rw prlf? string\n" + " +--rw lf@? string\n" + " +--rw lt* [name]\n" + " +--rw name string\n"; + data = EXT_DATA("a33", "", SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n" + "<parent-reference>/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting with parent-reference into empty container + */ + orig = SM_MOD_MAIN("a34", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n"); + expect = + "module: a34\n" + " +--mp cont\n" + " | +--rw tlist/* [name]\n" + " | | +--rw name uint32\n" + " | +--rw tcont/\n" + " | | +--rw tleaf? uint32\n" + " | +--rw lf@? string\n" + " +--rw lf? string\n"; + data = EXT_DATA("a34", "", + SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting module which is only parsed + */ + orig = SM_MOD_MAIN("a35", + "import sm-mod {\n" + " prefix smm;\n" + "}\n" + "container pr {\n" + " leaf prlf {\n" + " type uint32;\n" + " }\n" + "}\n" + "list lt {\n" + " key \"name\";\n" + " yangmnt:mount-point \"mnt-root\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + "}\n"); + expect = + "module: a35\n" + " +--rw pr\n" + " | +--rw prlf? uint32\n" + " +--mp lt* [name]\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--mp ncmp/\n" + " +--rw not-compiled/\n" + " | +--rw first? string\n" + " | +--rw second? string\n" + " +--rw pr@\n" + " | +--rw prlf? uint32\n" + " +--rw name string\n"; + data = EXT_DATA("a35", + "<module>\n" + " <name>sm-mod</name>\n" + " <namespace>urn:sm-mod</namespace>\n" + "</module>\n", + SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * notifications and rpcs in mounted module + */ + orig = SM_MOD_MAIN("a36", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a36\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw cont/\n" + " | +---x cr\n" + " | | +---w input\n" + " | | | +---w in? string\n" + " | | +--ro output\n" + " | | +--ro out? string\n" + " | +---n cn\n" + " +---x r1/\n" + " +---x r2/\n" + " +---n n1/\n" + " +---n n2/\n"; + data = EXT_DATA("a36", + "<module>\n" + " <name>sm-rpcnotif</name>\n" + " <namespace>urn:rpcnotif</namespace>\n" + "</module>\n", + SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * parent-ref composes the '@' subtree + */ + orig = SM_MOD_MAIN("a37", + "container pr {\n" + " leaf ignored_node {\n" + " type string;\n" + " }\n" + " container cont {\n" + " leaf ignored_lf {\n" + " type uint32;\n" + " }\n" + " }\n" + " container ignored_subtree {\n" + " leaf ignored_lf {\n" + " type uint32;\n" + " }\n" + " }\n" + " container cont_sibl {\n" + " leaf slf {\n" + " type string;\n" + " }\n" + " }\n" + " leaf lf {\n" + " type uint32;\n" + " }\n" + "}\n" + "container cont_mount {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a37\n" + " +--rw pr\n" + " | +--rw ignored_node? string\n" + " | +--rw cont\n" + " | | +--rw ignored_lf? uint32\n" + " | +--rw ignored_subtree\n" + " | | +--rw ignored_lf? uint32\n" + " | +--rw cont_sibl\n" + " | | +--rw slf? string\n" + " | +--rw lf? uint32\n" + " +--mp cont_mount\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw pr@\n" + " +--rw cont\n" + " +--rw cont_sibl\n" + " | +--rw slf? string\n" + " +--rw lf? uint32\n"; + data = EXT_DATA("a37", "", SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont_sibl/slf</parent-reference>\n" + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont</parent-reference>\n" + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_TEARDOWN; +} + +static void +structure(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module example-module {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:example-module\";\n" + " prefix exm;\n" + "\n" + " import ietf-yang-structure-ext {\n" + " prefix sx;\n" + " }\n" + "\n" + " sx:structure address-book {\n" + " list address {\n" + " key \"last first\";\n" + " leaf last {\n" + " type string;\n" + " }\n" + " leaf first {\n" + " type string;\n" + " }\n" + " leaf street {\n" + " type string;\n" + " }\n" + " leaf city {\n" + " type string;\n" + " }\n" + " leaf state {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from RFC 8791, Appendix A.1 */ + expect = + "module: example-module\n" + "\n" + " structure address-book:\n" + " +-- address* [last first]\n" + " +-- last string\n" + " +-- first string\n" + " +-- street? string\n" + " +-- city? string\n" + " +-- state? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + orig = + "module example-module-aug {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:example-module-aug\";\n" + " prefix exma;\n" + "\n" + " import ietf-yang-structure-ext {\n" + " prefix sx;\n" + " }\n" + " import example-module {\n" + " prefix exm;\n" + " }\n" + "\n" + " sx:augment-structure \"/exm:address-book/exm:address\" {\n" + " leaf county {\n" + " type string;\n" + " }\n" + " leaf zipcode {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from RFC 8791, Appendix A.2 */ + expect = + "module: example-module-aug\n" + "\n" + " augment-structure /exm:address-book/exm:address:\n" + " +-- county? string\n" + " +-- zipcode? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +annotation(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module ann {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:ann\";\n" + " prefix an;\n" + "\n" + " import ietf-yang-metadata {\n" + " prefix md;\n" + " }\n" + "\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " md:annotation avalue {\n" + " type string;\n" + " }\n" + "}\n"; + + expect = + "module: ann\n" + " +--rw lf1? string\n"; + + /* annotation is ignored without error message */ + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + TEST_LOCAL_TEARDOWN; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(base_sections), + UTEST(node_status), + UTEST(node_config_flags), + UTEST(node_rpcs_flags), + UTEST(node_grouping_flags), + UTEST(notif_inside_container), + UTEST(node_choice), + UTEST(node_case), + UTEST(optional_opts), + UTEST(presence_container), + UTEST(node_keys), + UTEST(node_type_target), + UTEST(node_type_leafref), + UTEST(node_iffeatures), + UTEST(indent_wrapper), + UTEST(line_length_twiddling), + UTEST(break_before_leafref), + UTEST(break_before_leafref_and_iffeature), + UTEST(basic_unified_indent_before_type), + UTEST(twiddling_unified_indent_before_type), + UTEST(inheritance_of_config_flag), + UTEST(inheritance_of_status_flag), + UTEST(key_leaf_is_always_mandatory_true), + UTEST(transition_between_rpc_and_notif), + UTEST(local_augment), + UTEST(print_compiled_node), + UTEST(print_compiled_node_augment), + UTEST(print_parsed_submodule), + UTEST(yang_data), + UTEST(mount_point), + UTEST(structure), + UTEST(annotation), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c new file mode 100644 index 0000000..953aad8 --- /dev/null +++ b/tests/utests/schema/test_schema.c @@ -0,0 +1,1915 @@ +/** + * @file test_schema.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for schema related functions + * + * Copyright (c) 2018 - 2022 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 <string.h> + +#include "compat.h" +#include "context.h" +#include "log.h" +#include "parser_schema.h" +#include "plugins_exts.h" +#include "set.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + if ((*module_data)[0] == '<') { + *format = LYS_IN_YIN; + } else { + *format = LYS_IN_YANG; + } + *free_module_data = NULL; + return LY_SUCCESS; +} + +#define TEST_YANG_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "module "MOD_NAME" { namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}" + +#define TEST_YANG_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "module "MOD_NAME" {yang-version 1.1; namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}" + +#define TEST_YIN_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\">" \ + "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>" + +#define TEST_YIN_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\"><yang-version value=\"1.1\"/>" \ + "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>" + +#define TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, STR) \ + if (YIN) { \ + if (RFC7950) { \ + STR = TEST_YIN_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } else { \ + STR = TEST_YIN_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } \ + } else { /* YANG */ \ + if (RFC7950) { \ + STR = TEST_YANG_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } else { \ + STR = TEST_YANG_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } \ + } + +#define TEST_SCHEMA_OK(RFC7950, YIN, MOD_NAME, CONTENT, RESULT) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, &(RESULT))); \ + } + +#define TEST_SCHEMA_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \ + CHECK_LOG_CTX(ERRMSG, ERRPATH, 0); \ + } + +#define TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH, ERRLINE) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \ + CHECK_LOG_CTX("Parsing module \""MOD_NAME"\" failed.", NULL, 0); \ + CHECK_LOG_CTX(ERRMSG, ERRPATH, ERRLINE); \ + } + +#define TEST_STMT_DUP(RFC7950, YIN, STMT, MEMBER, VALUE1, VALUE2, LINE) \ + if (YIN) { \ + TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", "", "Duplicate keyword \""MEMBER"\".", NULL, LINE); \ + } else { \ + TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", STMT"{"MEMBER" "VALUE1";"MEMBER" "VALUE2";}", \ + "Duplicate keyword \""MEMBER"\".", NULL, LINE); \ + } + +#define TEST_STMT_SUBSTM_ERR(RFC7950, STMT, SUBSTMT, VALUE) ;\ + TEST_SCHEMA_PARSE_ERR(RFC7950, 0, "inv", STMT" test {"SUBSTMT" "VALUE";}", \ + "Invalid keyword \""SUBSTMT"\" as a child of \""STMT"\".", NULL, 1); + +struct module_clb_list { + const char *name; + const char *data; +}; + +static LY_ERR +module_clb(const char *mod_name, const char *UNUSED(mod_rev), const char *submod_name, + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + struct module_clb_list *list = (struct module_clb_list *)user_data; + + for (unsigned int i = 0; list[i].data; i++) { + + if ((submod_name && !strcmp(list[i].name, submod_name)) || + (!submod_name && mod_name && !strcmp(list[i].name, mod_name))) { + *module_data = list[i].data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; + } + } + return LY_EINVAL; +} + +static void +test_getnext(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node = NULL, *four; + const struct lysc_node_container *cont; + const struct lysc_action *rpc; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;" + "container a { container one {presence test;} leaf two {type string;} leaf-list three {type string;}" + " list four {config false;} choice x { leaf five {type string;} case y {leaf six {type string;}}}" + " anyxml seven; action eight {input {leaf eight-input {type string;}} output {leaf eight-output {type string;}}}" + " notification nine {leaf nine-data {type string;}}}" + "leaf b {type string;} leaf-list c {type string;} list d {config false;}" + "choice x { case empty-x { choice empty-xc { case nothing;}} leaf e {type string;} case y {leaf f {type string;}}} anyxml g;" + "rpc h {input {leaf h-input {type string;}} output {leaf h-output {type string;}}}" + "rpc i;" + "notification j {leaf i-data {type string;}}" + "notification k;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("a", node->name); + cont = (const struct lysc_node_container *)node; + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("b", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("d", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("e", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("f", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("g", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("h", node->name); + rpc = (const struct lysc_action *)node; + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("i", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("j", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("k", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + /* Inside container */ + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("one", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("two", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("three", node->name); + assert_non_null(node = four = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("four", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("five", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("six", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("seven", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("eight", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("nine", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + /* Inside RPC */ + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0)); + assert_string_equal("h-input", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0)); + + /* options */ + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE)); + assert_string_equal("x", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_NOCHOICE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("five", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("y", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT)); + assert_string_equal("one", node->name); + + assert_non_null(node = lys_getnext(NULL, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT)); + assert_string_equal("h-output", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; rpc c;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; notification d;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("d", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; container c {container cc;} leaf a {type string;}}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT)); + assert_string_equal("a", node->name); +} + +static void +test_date(void **UNUSED(state)) +{ + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date")); + CHECK_LOG_LASTMSG("Invalid argument date (lysp_check_date())."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "x", 1, "date")); + CHECK_LOG_LASTMSG("Invalid length 1 of a date."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "nonsencexx", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"nonsencexx\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "123x-11-11", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"123x-11-11\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-13-11", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-13-11\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-11-41", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-11-41\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02-29", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-02-29\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018.02-28", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018.02-28\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02.28", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-02.28\" of \"date\"."); + + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date")); + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date")); + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date")); +} + +static void +test_revisions(void **UNUSED(state)) +{ + struct lysp_revision *revs = NULL, *rev; + + /* revisions are stored in wrong order - the newest is the last */ + LY_ARRAY_NEW_RET(NULL, revs, rev, ); + strcpy(rev->date, "2018-01-01"); + LY_ARRAY_NEW_RET(NULL, revs, rev, ); + strcpy(rev->date, "2018-12-31"); + + assert_int_equal(2, LY_ARRAY_COUNT(revs)); + assert_string_equal("2018-01-01", revs[0].date); + assert_string_equal("2018-12-31", revs[1].date); + /* the order should be fixed, so the newest revision will be the first in the array */ + lysp_sort_revisions(revs); + assert_string_equal("2018-12-31", revs[0].date); + assert_string_equal("2018-01-01", revs[1].date); + + LY_ARRAY_FREE(revs); +} + +static void +test_collision_typedef(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision with a built-in type */ + str = "module a {namespace urn:a; prefix a; typedef binary {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"binary\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef bits {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"bits\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef boolean {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"boolean\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef decimal64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"decimal64\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef empty {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"empty\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef enumeration {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"enumeration\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int8 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int8\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int16 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int16\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int32 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int32\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int64\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef instance-identifier {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"instance-identifier\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef identityref {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"identityref\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef leafref {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"leafref\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef string {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"string\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef union {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"union\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint8 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint8\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint16 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint16\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint32 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint32\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint64\" of typedef statement - name collision with a built-in type.", NULL, 0); + + str = "module mytypes {namespace urn:types; prefix t; typedef binary_ {type string;} typedef bits_ {type string;} typedef boolean_ {type string;} " + "typedef decimal64_ {type string;} typedef empty_ {type string;} typedef enumeration_ {type string;} typedef int8_ {type string;} typedef int16_ {type string;}" + "typedef int32_ {type string;} typedef int64_ {type string;} typedef instance-identifier_ {type string;} typedef identityref_ {type string;}" + "typedef leafref_ {type string;} typedef string_ {type int8;} typedef union_ {type string;} typedef uint8_ {type string;} typedef uint16_ {type string;}" + "typedef uint32_ {type string;} typedef uint64_ {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + + /* collision in node's scope */ + str = "module a {namespace urn:a; prefix a; container c {typedef y {type int8;} typedef y {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with sibling type.", NULL, 0); + + /* collision with parent node */ + str = "module a {namespace urn:a; prefix a; container c {container d {typedef y {type int8;}} typedef y {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with another scoped type.", NULL, 0); + + /* collision with module's top-level */ + str = "module a {namespace urn:a; prefix a; typedef x {type string;} container c {typedef x {type int8;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of submodule's node with module's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {typedef x {type string;}}}"); + str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of module's node with submodule's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type int8;}}"); + str = "module a {namespace urn:a; prefix a; include b; container c {typedef x {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of submodule's node with another submodule's top-level */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {typedef g {type int;}}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of module's top-levels */ + str = "module a {namespace urn:a; prefix a; typedef test {type string;} typedef test {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"test\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} typedef g {type int;} typedef g {type int;}}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type string;}}"); + str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} typedef g {type int;}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* error in type-stmt */ + str = "module a {namespace urn:a; prefix a; container c {typedef x {type t{}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + /* no collision if the same names are in different scope */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "container c {typedef g {type int;}} container d {typedef g {type int;}}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); +} + +static void +test_collision_grouping(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision in node's scope */ + str = "module a {namespace urn:a; prefix a; container c {grouping y; grouping y;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with sibling grouping.", NULL, 0); + + /* collision with parent node */ + str = "module a {namespace urn:a; prefix a; container c {container d {grouping y;} grouping y;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with another scoped grouping.", NULL, 0); + + /* collision with module's top-level */ + str = "module a {namespace urn:a; prefix a; grouping x; container c {grouping x;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of submodule's node with module's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {grouping x;}}"); + str = "module a {namespace urn:a; prefix a; include b; grouping x;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of module's node with submodule's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}"); + str = "module a {namespace urn:a; prefix a; include b; container c {grouping x;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of submodule's node with another submodule's top-level */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {grouping g;}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of module's top-levels */ + str = "module a {namespace urn:a; prefix a; grouping test; grouping test;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"test\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} grouping g; grouping g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}"); + str = "module a {namespace urn:a; prefix a; include b; grouping x;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} grouping g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision in nested groupings, top-level */ + str = "module a {namespace urn:a; prefix a; grouping g {grouping g;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision in nested groupings, in node */ + str = "module a {namespace urn:a; prefix a; container c {grouping g {grouping g;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another scoped grouping.", NULL, 0); + + /* no collision if the same names are in different scope */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "container c {grouping g;} container d {grouping g;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL, 0); + CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL, 0); +} + +static void +test_collision_identity(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision of module's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; identity g; identity g;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} identity g; identity g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} identity g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; identity g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} identity g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} identity g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); +} + +static void +test_collision_feature(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision of module's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; feature g; feature g;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} feature g; feature g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} feature g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; feature g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} feature g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} feature g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); +} + +static void +test_accessible_tree(void **state) +{ + const char *str; + + /* config -> config */ + str = "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* config -> state leafref */ + str = "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid leafref path \"/cont/l\" - target is supposed to represent configuration data" + " (as the leafref does), but it does not.", "/b:cont2/l2", 0); + + /* config -> state must */ + str = "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"cont\" for parent \"<config-root>\" not found; in expr \"../../cont\" " + "with context node \"/b:cont2/l2\".", NULL, 0); + + /* state -> config */ + str = "module c {\n" + " namespace urn:c;\n" + " prefix c;\n" + " container cont {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " config false;\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* notif -> state */ + str = "module d {\n" + " namespace urn:d;\n" + " prefix d;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* notif -> notif */ + str = "module e {\n" + " namespace urn:e;\n" + " prefix e;\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " leaf l2 {\n" + " must ../../notif/l;\n" + " type leafref {\n" + " path /notif/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> state */ + str = "module f {\n" + " namespace urn:f;\n" + " prefix f;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> rpc input */ + str = "module g {\n" + " namespace urn:g;\n" + " prefix g;\n" + " rpc rp {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " leaf l2 {\n" + " must ../l;\n" + " type leafref {\n" + " path /rp/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> rpc output leafref */ + str = "module h {\n" + " namespace urn:h;\n" + " prefix h;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " type leafref {\n" + " path /rp/l;\n" + " }\n" + " }\n" + " }\n" + " output {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"l\" in path.", "/h:rp/input/l2", 0); + + /* rpc input -> rpc output must */ + str = "module h {\n" + " namespace urn:h;\n" + " prefix h;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must ../l;\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"l\" for parent \"/h:rp\" not found; in expr \"../l\" with context node \"/h:rp/input/l2\".", + NULL, 0); + + /* rpc input -> notif leafref */ + str = "module i {\n" + " namespace urn:i;\n" + " prefix i;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " type leafref {\n" + " path ../../notif/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"notif\" in path.", "/i:rp/input/l2", 0); + + /* rpc input -> notif must */ + str = "module i {\n" + " namespace urn:i;\n" + " prefix i;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must /notif/l;\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"notif\" for parent \"<root>\" not found; in expr \"/notif\" " + "with context node \"/i:rp/input/l2\".", NULL, 0); + + /* action output -> state */ + str = "module j {\n" + " yang-version 1.1;\n" + " namespace urn:j;\n" + " prefix j;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " output {\n" + " leaf l2 {\n" + " must /cont/l;\n" + " type leafref {\n" + " path ../../../l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " leaf l {\n" + " config false;\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* action output -> action input leafref */ + str = "module k {\n" + " yang-version 1.1;\n" + " namespace urn:k;\n" + " prefix k;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l2 {\n" + " type leafref {\n" + " path ../l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"l\" in path.", "/k:cont/ll/act/output/l2", 0); + + /* action output -> action input must */ + str = "module k {\n" + " yang-version 1.1;\n" + " namespace urn:k;\n" + " prefix k;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l2 {\n" + " must /cont/ll/act/l;\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"l\" for parent \"/k:cont/ll/act\" not found; in expr \"/cont/ll/act/l\" " + "with context node \"/k:cont/ll/act/output/l2\".", NULL, 0); +} + +static void +test_includes(void **state) +{ + struct lys_module *mod; + + { + /* YANG 1.0 - the missing include sub_a_two in main_a will be injected from sub_a_one */ + struct module_clb_list list[] = { + {"main_a", "module main_a { namespace urn:test:main_a; prefix ma; include sub_a_one;}"}, + {"sub_a_one", "submodule sub_a_one { belongs-to main_a { prefix ma; } include sub_a_two;}"}, + {"sub_a_two", "submodule sub_a_two { belongs-to main_a { prefix ma; } }"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_a", NULL, NULL); + assert_non_null(mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes)); + assert_true(mod->parsed->includes[1].injected); + } + + { + /* YANG 1.1 - the missing include sub_b_two in main_b is error */ + struct module_clb_list list[] = { + {"main_b", "module main_b { yang-version 1.1; namespace urn:test:main_b; prefix mb; include sub_b_one;}"}, + {"sub_b_one", "submodule sub_b_one { yang-version 1.1; belongs-to main_b { prefix mb; } include sub_b_two;}"}, + {"sub_b_two", "submodule sub_b_two { yang-version 1.1; belongs-to main_b { prefix mb; } }"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_b", NULL, NULL); + assert_null(mod); + CHECK_LOG_CTX("Loading \"main_b\" module failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"main_b\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing module \"main_b\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"sub_b_one\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule \"sub_b_one\" failed.", NULL, 0); + CHECK_LOG_CTX("YANG 1.1 requires all submodules to be included from main module. But submodule \"sub_b_one\" includes " + "submodule \"sub_b_two\" which is not included by main module \"main_b\".", NULL, 0); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_b_one) are not necessary.", + NULL, 0); + } + + { + /* YANG 1.1 - all includes are in main_c, includes in submodules are not necessary, so expect warning */ + struct module_clb_list list[] = { + {"main_c", "module main_c { yang-version 1.1; namespace urn:test:main_c; prefix mc; include sub_c_one; include sub_c_two;}"}, + {"sub_c_one", "submodule sub_c_one { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_two;}"}, + {"sub_c_two", "submodule sub_c_two { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_one;}"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_c", NULL, NULL); + assert_non_null(mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes)); + assert_false(mod->parsed->includes[1].injected); + /* result is ok, but log includes the warning */ + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_two) are not necessary.", + NULL, 0); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_one) are not necessary.", + NULL, 0); + } +} + +static void +test_key_order(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + + struct module_clb_list list1[] = { + { + "a", "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "list l {" + " key \"k1 k2\";" + " leaf k2 {type string;}" + " leaf k1 {type string;}" + "}" + "}" + }, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list1); + mod = ly_ctx_load_module(UTEST_LYCTX, "a", NULL, NULL); + assert_non_null(mod); + + node = lysc_node_child(mod->compiled->data); + assert_string_equal("k1", node->name); + node = node->next; + assert_string_equal("k2", node->name); + + struct module_clb_list list2[] = { + { + "b", "module b {" + "yang-version 1.1;" + "namespace urn:test:b;" + "prefix b;" + "list l {" + " key \"k1 k2 k3 k4\";" + " leaf k4 {type string;}" + " container c {" + " leaf l1 {type string;}" + " }" + " leaf k2 {type string;}" + " leaf l2 {type string;}" + " leaf k1 {type string;}" + " leaf k3 {type string;}" + "}" + "}" + }, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list2); + mod = ly_ctx_load_module(UTEST_LYCTX, "b", NULL, NULL); + assert_non_null(mod); + + node = lysc_node_child(mod->compiled->data); + assert_string_equal("k1", node->name); + node = node->next; + assert_string_equal("k2", node->name); + node = node->next; + assert_string_equal("k3", node->name); + node = node->next; + assert_string_equal("k4", node->name); +} + +static void +test_disabled_enum(void **state) +{ + const char *str; + + /* no enabled enum */ + str = "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "feature f;" + "leaf l {type enumeration {" + " enum e1 {if-feature f;}" + " enum e2 {if-feature f;}" + "}}" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Node \"l\" without any (or all disabled) valid values.", "/a:l", 0); + + /* disabled default value */ + str = "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "feature f;" + "leaf l {" + " type enumeration {" + " enum e1 {if-feature f;}" + " enum e2;" + " }" + " default e1;" + "}" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid enumeration value \"e1\".).", "/a:l", 0); +} + +static void +test_identity(void **state) +{ + struct lys_module *mod, *mod_imp; + + /* + * parsing YANG + */ + TEST_STMT_DUP(1, 0, "identity id", "description", "a", "b", 1); + TEST_STMT_DUP(1, 0, "identity id", "reference", "a", "b", 1); + TEST_STMT_DUP(1, 0, "identity id", "status", "current", "obsolete", 1); + + /* full content */ + TEST_SCHEMA_OK(1, 0, "identityone", + "identity test {base \"a\";base b; description text;reference \'another text\';status current; if-feature x;if-feature y; identityone:ext;}" + "identity a; identity b; extension ext; feature x; feature y;", mod); + assert_non_null(mod->parsed->identities); + assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->identities)); + + /* invalid substatement */ + TEST_STMT_SUBSTM_ERR(0, "identity", "organization", "XXX"); + + /* + * parsing YIN + */ + /* max subelems */ + TEST_SCHEMA_OK(1, 1, "identityone-yin", "<identity name=\"ident-name\">" + "<if-feature name=\"iff\"/>" + "<base name=\"base-name\"/>" + "<status value=\"deprecated\"/>" + "<description><text>desc</text></description>" + "<reference><text>ref</text></reference>" + "<myext:ext xmlns:myext=\"urn:libyang:test:identityone-yin\"/>" + "</identity><extension name=\"ext\"/><identity name=\"base-name\"/><feature name=\"iff\"/>", mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->identities)); + assert_string_equal(mod->parsed->identities[0].name, "ident-name"); + assert_string_equal(mod->parsed->identities[0].bases[0], "base-name"); + assert_string_equal(mod->parsed->identities[0].iffeatures[0].str, "iff"); + assert_string_equal(mod->parsed->identities[0].dsc, "desc"); + assert_string_equal(mod->parsed->identities[0].ref, "ref"); + assert_true(mod->parsed->identities[0].flags & LYS_STATUS_DEPRC); + assert_string_equal(mod->parsed->identities[0].exts[0].name, "myext:ext"); + + /* min subelems */ + TEST_SCHEMA_OK(1, 1, "identitytwo-yin", "<identity name=\"ident-name\" />", mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->identities)); + assert_string_equal(mod->parsed->identities[0].name, "ident-name"); + + /* invalid substatement */ + TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<identity name=\"ident-name\"><if-feature name=\"iff\"/></identity>", + "Invalid sub-elemnt \"if-feature\" of \"identity\" element - " + "this sub-element is allowed only in modules with version 1.1 or newer.", NULL, 1); + + /* + * compiling + */ + TEST_SCHEMA_OK(0, 0, "a", "identity a1;", mod_imp); + TEST_SCHEMA_OK(1, 0, "b", "import a {prefix a;}" + "identity b1; identity b2; identity b3 {base b1; base b:b2; base a:a1;}" + "identity b4 {base b:b1; base b3;}", mod); + assert_non_null(mod_imp->compiled); + assert_non_null(mod_imp->identities); + assert_non_null(mod->identities); + assert_non_null(mod_imp->identities[0].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod_imp->identities[0].derived)); + assert_ptr_equal(mod_imp->identities[0].derived[0], &mod->identities[2]); + assert_non_null(mod->identities[0].derived); + assert_int_equal(2, LY_ARRAY_COUNT(mod->identities[0].derived)); + assert_ptr_equal(mod->identities[0].derived[0], &mod->identities[2]); + assert_ptr_equal(mod->identities[0].derived[1], &mod->identities[3]); + assert_non_null(mod->identities[1].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived)); + assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[2]); + assert_non_null(mod->identities[2].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[2].derived)); + assert_ptr_equal(mod->identities[2].derived[0], &mod->identities[3]); + + TEST_SCHEMA_OK(1, 0, "c", "identity c2 {base c1;} identity c1;", mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived)); + assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[0]); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} identity i1;}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\".", "/inv:{identity='i1'}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself.", "/inv:{identity='i1'}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}", + "Identity \"i1\" is indirectly derived from itself.", "/inv:{identity='i3'}"); + + /* base in non-implemented module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module base {namespace \"urn\"; prefix b; identity i1; identity i2 {base i1;}}"); + TEST_SCHEMA_OK(0, 0, "ident", "import base {prefix b;} identity ii {base b:i1;}", mod); + + /* default value from non-implemented module */ + TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} leaf l {type identityref {base b:i1;} default b:i2;}", + "Invalid default - value does not fit the type (Invalid identityref \"b:i2\" value" + " - identity found in non-implemented module \"base\".).", "/ident2:l"); + + /* default value in typedef from non-implemented module */ + TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} typedef t1 {type identityref {base b:i1;} default b:i2;}" + "leaf l {type t1;}", "Invalid default - value does not fit the type (Invalid" + " identityref \"b:i2\" value - identity found in non-implemented module \"base\".).", "/ident2:l"); + + /* + * printing + */ + + /* + * cleanup + */ +} + +static void +test_feature(void **state) +{ + struct lys_module *mod; + const struct lysp_feature *f; + + /* + * parsing YANG + */ + + TEST_STMT_DUP(1, 0, "feature f", "description", "a", "b", 1); + TEST_STMT_DUP(1, 0, "feature f", "reference", "a", "b", 1); + TEST_STMT_DUP(1, 0, "feature f", "status", "current", "obsolete", 1); + + /* full content */ + TEST_SCHEMA_OK(1, 0, "featureone", + "feature test {description text;reference \'another text\';status current; if-feature x; if-feature y; featureone:ext;}" + "extension ext; feature x; feature y;", mod); + assert_non_null(mod->parsed->features); + assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->features)); + + /* invalid substatement */ + TEST_STMT_SUBSTM_ERR(0, "feature", "organization", "XXX"); + + /* + * parsing YIN + */ + /* max subelems */ + TEST_SCHEMA_OK(0, 1, "featureone-yin", "<feature name=\"feature-name\">" + "<if-feature name=\"iff\"/>" + "<status value=\"deprecated\"/>" + "<description><text>desc</text></description>" + "<reference><text>ref</text></reference>" + "<myext:ext xmlns:myext=\"urn:libyang:test:featureone-yin\"/>" + "</feature><extension name=\"ext\"/><feature name=\"iff\"/>", mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features)); + assert_string_equal(mod->parsed->features[0].name, "feature-name"); + assert_string_equal(mod->parsed->features[0].dsc, "desc"); + assert_true(mod->parsed->features[0].flags & LYS_STATUS_DEPRC); + assert_string_equal(mod->parsed->features[0].iffeatures[0].str, "iff"); + assert_string_equal(mod->parsed->features[0].ref, "ref"); + assert_string_equal(mod->parsed->features[0].exts[0].name, "myext:ext"); + + /* min subelems */ + TEST_SCHEMA_OK(0, 1, "featuretwo-yin", "<feature name=\"feature-name\"/>", mod) + assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->features)); + assert_string_equal(mod->parsed->features[0].name, "feature-name"); + + /* invalid substatement */ + TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<feature name=\"feature-name\"><organization><text>org</text></organization></feature>", + "Unexpected sub-element \"organization\" of \"feature\" element.", NULL, 1); + + /* + * compiling + */ + + TEST_SCHEMA_OK(1, 0, "a", "feature f1 {description test1;reference test2;status current;} feature f2; feature f3;\n" + "feature orfeature {if-feature \"f1 or f2\";}\n" + "feature andfeature {if-feature \"f1 and f2\";}\n" + "feature f6 {if-feature \"not f1\";}\n" + "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}\n" + "feature f8 {if-feature \"f1 or f2 or f3 or orfeature or andfeature\";}\n" + "feature f9 {if-feature \"not not f1\";}", mod); + assert_non_null(mod->parsed->features); + assert_int_equal(9, LY_ARRAY_COUNT(mod->parsed->features)); + + /* all features are disabled by default */ + LY_ARRAY_FOR(mod->parsed->features, struct lysp_feature, f) { + assert_false(f->flags & LYS_FENABLED); + } + + /* some invalid expressions */ + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature f1;}", + "Invalid value \"f1\" of if-feature - unable to find feature \"f1\".", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f and';}", + "Invalid value \"f and\" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature 'or';}", + "Invalid value \"or\" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature '(f1';}", + "Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f1)';}", + "Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature ---;}", + "Invalid value \"---\" of if-feature - unable to find feature \"---\".", NULL, 0); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1; feature f2{if-feature 'not f1';}", + "Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", NULL, 0); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} feature f1;}"); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f2;} feature f2 {if-feature f1;}", + "Feature \"f1\" is indirectly referenced from itself.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f1;}", + "Feature \"f1\" is referenced from itself.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f {if-feature ();}", + "Invalid value \"()\" of if-feature - number of features in expression does not match the required number of operands for the operations.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1(';}", + "Invalid value \"f1(\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'and f1';}", + "Invalid value \"and f1\" of if-feature - missing feature/expression before \"and\" operation.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not ';}", + "Invalid value \"f1 not \" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not not ';}", + "Invalid value \"f1 not not \" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2; feature f {if-feature 'or f1 f2';}", + "Invalid value \"or f1 f2\" of if-feature - missing feature/expression before \"or\" operation.", NULL, 0); + + /* + * printing + */ + + /* + * cleanup + */ +} + +static void +test_extension_argument(void **state) +{ + struct lys_module *mod; + const char *mod_def_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " extension e {\n" + " argument name;\n" + " }\n\n" + " a:e \"aaa\";\n" + "}\n"; + const char *mod_def_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <extension name=\"e\">\n" + " <argument name=\"name\"/>\n" + " </extension>\n" + " <a:e name=\"aaa\"/>\n" + "</module>\n"; + const char *mod_test_yin, *mod_test_yang; + char *printed; + + mod_test_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " import a {\n" + " prefix a;\n" + " }\n\n" + " a:e \"xxx\";\n" + "}\n"; + mod_test_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); +} + +static void +test_extension_argument_element(void **state) +{ + struct lys_module *mod; + const char *mod_def_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " extension e {\n" + " argument name {\n" + " yin-element true;\n" + " }\n" + " }\n\n" + " a:e \"aaa\";\n" + "}\n"; + const char *mod_def_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <extension name=\"e\">\n" + " <argument name=\"name\">\n" + " <yin-element value=\"true\"/>\n" + " </argument>\n" + " </extension>\n" + " <a:e>\n" + " <a:name>aaa</a:name>\n" + " </a:e>\n" + "</module>\n"; + const char *mod_test_yin, *mod_test_yang; + char *printed; + + mod_test_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " import a {\n" + " prefix a;\n" + " }\n\n" + " a:e \"xxx\";\n" + "}\n"; + mod_test_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n" + " <a:e>\n" + " <a:name>xxx</a:name>\n" + " </a:e>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* invalid */ + mod_test_yang = "module x { namespace \"urn:x\"; prefix x; import a { prefix a; } a:e; }"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e/>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e>\n" + " <x:name>xxx</x:name>\n" + " </a:e>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.", + NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e>\n" + " <a:value>xxx</a:value>\n" + " </a:e>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.", + NULL, 0); + +} + +static void +test_extension_compile(void **state) +{ + struct lys_module *mod; + struct lysc_ctx cctx = {0}; + struct lysp_ext_instance ext_p = {0}; + struct lysp_ext_substmt *substmtp; + struct lysp_stmt child = {0}; + struct lysc_ext_instance ext_c = {0}; + struct lysc_ext_substmt *substmt; + LY_ERR rc = LY_SUCCESS; + + /* current module, whatever */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang"); + assert_true(mod); + + /* compile context */ + cctx.ctx = UTEST_LYCTX; + cctx.cur_mod = mod; + cctx.pmod = mod->parsed; + cctx.path_len = 1; + cctx.path[0] = '/'; + + /* parsed ext instance */ + lydict_insert(UTEST_LYCTX, "pref:my-ext", 0, &ext_p.name); + ext_p.format = LY_VALUE_JSON; + ext_p.parent_stmt = LY_STMT_MODULE; + + LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_p.substmts, substmtp, rc, cleanup); + + substmtp->stmt = LY_STMT_ERROR_MESSAGE; + substmtp->storage = (uintptr_t)(void *)&ext_p.parsed; + /* fake parse */ + lydict_insert(UTEST_LYCTX, "my error", 0, (const char **)&ext_p.parsed); + + /* compiled ext instance */ + ext_c.parent_stmt = ext_p.parent_stmt; + LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_c.substmts, substmt, rc, cleanup); + + substmt->stmt = LY_STMT_ERROR_MESSAGE; + substmt->storage = (uintptr_t)(void *)&ext_c.compiled; + + /* + * error-message + */ + ext_p.child = &child; + lydict_insert(UTEST_LYCTX, "error-message", 0, &child.stmt); + lydict_insert(UTEST_LYCTX, "my error", 0, &child.arg); + child.format = LY_VALUE_JSON; + child.kw = LY_STMT_ERROR_MESSAGE; + + /* compile */ + assert_int_equal(LY_SUCCESS, lyplg_ext_compile_extension_instance(&cctx, &ext_p, &ext_c)); + + /* check */ + assert_string_equal(ext_c.compiled, "my error"); + +cleanup: + lydict_remove(UTEST_LYCTX, ext_p.name); + lydict_remove(UTEST_LYCTX, child.stmt); + lydict_remove(UTEST_LYCTX, child.arg); + LY_ARRAY_FREE(ext_p.substmts); + lydict_remove(UTEST_LYCTX, ext_p.parsed); + LY_ARRAY_FREE(ext_c.substmts); + lydict_remove(UTEST_LYCTX, ext_c.compiled); + if (rc) { + fail(); + } +} + +static void +test_ext_recursive(void **state) +{ + const char *mod_base_yang, *mod_imp_yang, *mod_base_yin, *mod_imp_yin; + + mod_imp_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " extension use-in {\n" + " argument name {\n" + " b:arg-type {\n" + " type string;\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " b:occurence \"*\";\n" + " }\n" + "\n" + " extension substatement {\n" + " argument name {\n" + " b:arg-type {\n" + " type string;\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " b:occurence \"*\";\n" + " b:substatement \"b:occurence\";\n" + " }\n" + "\n" + " extension arg-type {\n" + " b:use-in \"argument\";\n" + " b:substatement \"type\" {\n" + " b:occurence \"1\";\n" + " }\n" + " b:substatement \"default\";\n" + " }\n" + "\n" + " extension occurence {\n" + " argument value {\n" + " b:arg-type {\n" + " type enumeration {\n" + " enum \"?\";\n" + " enum \"*\";\n" + " enum \"+\";\n" + " enum \"1\";\n" + " }\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " }\n" + "}\n"; + + mod_imp_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + + mod_base_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " import b {\n" + " prefix b;\n" + " }\n" + "\n" + " extension abstract {\n" + " b:use-in \"identity\";\n" + " }\n" + "\n" + " identity mount-id;\n" + "\n" + " identity yang-lib-id {\n" + " base mount-id;\n" + " a:abstract;\n" + " }\n" + "}\n"; + + mod_base_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n\n" + " <extension name=\"e\">\n" + " <argument name=\"name\"/>\n" + " </extension>\n\n" + " <a:e name=\"aaa\"/>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yang, LYS_IN_YANG, NULL)); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yin, LYS_IN_YIN, NULL)); +} + +static void +test_lysc_path(void **state) +{ + const struct lysc_node *node; + char *path; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b;" + "container a {" + " list l {" + " key \"k l m\";" + " leaf k {type string;}" + " leaf l {type string;}" + " leaf m {type string;}" + " leaf n {type string;}" + " }" + "}}", LYS_IN_YANG, NULL)); + + node = lys_find_path(UTEST_LYCTX, NULL, "/b:a/l", 0); + path = lysc_path(node, LYSC_PATH_DATA_PATTERN, NULL, 0); + assert_string_equal(path, "/b:a/l[k='%s'][l='%s'][m='%s']"); + free(path); + node = lys_find_path(UTEST_LYCTX, NULL, "/b:a/l/n", 0); + path = lysc_path(node, LYSC_PATH_DATA_PATTERN, NULL, 0); + assert_string_equal(path, "/b:a/l[k='%s'][l='%s'][m='%s']/n"); + free(path); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_getnext), + UTEST(test_date), + UTEST(test_revisions), + UTEST(test_collision_typedef), + UTEST(test_collision_grouping), + UTEST(test_collision_identity), + UTEST(test_collision_feature), + UTEST(test_accessible_tree), + UTEST(test_includes), + UTEST(test_key_order), + UTEST(test_disabled_enum), + UTEST(test_identity), + UTEST(test_feature), + UTEST(test_extension_argument), + UTEST(test_extension_argument_element), + UTEST(test_extension_compile), + UTEST(test_ext_recursive), + UTEST(test_lysc_path), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c new file mode 100644 index 0000000..3d4fbf7 --- /dev/null +++ b/tests/utests/schema/test_tree_schema_compile.c @@ -0,0 +1,4110 @@ +/** + * @file test_tree_schema_compile.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions from parser_yang.c + * + * Copyright (c) 2018 - 2023 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.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "path.h" +#include "plugins_types.h" +#include "schema_compile.h" +#include "xpath.h" + +static int +setup(void **state) +{ + UTEST_SETUP; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS)); + + return 0; +} + +static void +test_imp_free_data(void *model_data, void *UNUSED(user_data)) +{ + free(model_data); +} + +static LY_ERR +test_imp_clb(const char *mod_name, const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + char *nl; + + if ((nl = strchr(user_data, '\n'))) { + /* more modules */ + if (!strncmp((char *)user_data + 7, mod_name, strlen(mod_name))) { + *module_data = strndup(user_data, nl - (char *)user_data); + *format = LYS_IN_YANG; + *free_module_data = test_imp_free_data; + } else { + *module_data = strdup(nl + 1); + *format = LYS_IN_YANG; + *free_module_data = test_imp_free_data; + } + } else { + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + } + return LY_SUCCESS; +} + +static void +test_module(void **state) +{ + const char *str, *feats[] = {"invalid", NULL}; + struct ly_in *in; + struct lys_module *mod = NULL; + struct lysp_feature *f; + struct lysc_iffeature *iff; + struct lys_glob_unres unres = {0}; + + str = "module test {namespace urn:test; prefix t;" + "feature f1;feature f2 {if-feature f1;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + assert_int_equal(0, mod->implemented); + assert_int_equal(LY_EINVAL, lys_set_implemented(mod, feats)); + CHECK_LOG_CTX("Feature \"invalid\" not found in module \"test\".", NULL, 0); + assert_int_equal(LY_SUCCESS, lys_set_implemented(mod, NULL)); + assert_non_null(mod->compiled); + assert_string_equal("test", mod->name); + assert_string_equal("urn:test", mod->ns); + assert_string_equal("t", mod->prefix); + /* features */ + assert_non_null(mod->parsed->features); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features)); + f = &mod->parsed->features[1]; + assert_non_null(f->iffeatures); + assert_int_equal(1, LY_ARRAY_COUNT(f->iffeatures)); + iff = &f->iffeatures_c[0]; + assert_non_null(iff->expr); + assert_non_null(iff->features); + assert_int_equal(1, LY_ARRAY_COUNT(iff->features)); + assert_ptr_equal(&mod->parsed->features[0], iff->features[0]); + + /* submodules cannot be compiled directly */ + str = "submodule test {belongs-to xxx {prefix x;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); + + /* data definition name collision in top level */ + str = "module aa {namespace urn:aa;prefix aa; leaf a {type string;} container a{presence x;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EEXIST, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Duplicate identifier \"/aa:a\" of data definition/RPC/action/notification statement.", "/aa:a", 0); +} + +static void +test_submodule(void **state) +{ + char *str; + + /* extension in a submodule */ + str = "submodule a-submod {yang-version 1.1; belongs-to a-mod {prefix a;}" + " extension ext2 {argument arg;}" + " typedef INTERFACE_NAME {type string; a:ext2 \"interface\";}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module a-mod {namespace urn:a-mod; prefix a; include a-submod;" + "identity baseid;" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); +} + +static void +test_name_collisions(void **state) +{ + const char *yang_data; + + /* top-level */ + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " leaf c {type empty;}" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " notification c;" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " rpc c;" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " choice ch {" + " leaf c {type string;}" + " case c2 {" + " leaf aa {type empty;}" + " }" + " }" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:ch/c/c", 0); + + /* nested */ + yang_data = "module a {namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "container a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "notification a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "action a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + /* grouping */ +} + +static void +test_node_container(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;container c;}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, cont->nodetype); + assert_string_equal("c", cont->name); + assert_true(cont->flags & LYS_CONFIG_W); + assert_true(cont->flags & LYS_STATUS_CURR); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;container c {config false; status deprecated; container child;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data)); + assert_true(cont->flags & LYS_CONFIG_R); + assert_true(cont->flags & LYS_STATUS_DEPRC); + assert_non_null((cont = (struct lysc_node_container *)cont->child)); + assert_int_equal(LYS_CONTAINER, cont->nodetype); + assert_true(cont->flags & LYS_CONFIG_R); + assert_true(cont->flags & LYS_STATUS_DEPRC); + assert_string_equal("child", cont->name); +} + +static void +test_node_leaflist(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + struct lysc_node_leaflist *ll; + struct lysc_node_leaf *l; + const char *dflt; + uint8_t dynamic; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "typedef mytype {type union {type leafref {path ../target;} type string;}}" + "leaf-list ll1 {type union {type decimal64 {fraction-digits 2;} type mytype;}}" + "leaf-list ll2 {type leafref {path ../target;}}" + "leaf target {type int8;}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}" + "leaf-list ll1 {type mytype;default 1; default 1; config false;}" + "leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_non_null(ll->dflts); + assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference (typedef's default does not reference own type) */ + assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("1", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("1", dflt = ll->dflts[1]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_DFLT | LYS_SET_CONFIG, ll->flags); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data->next)); + assert_non_null(ll->dflts); + assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference */ + assert_int_equal(1, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("10", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags); + + /* ordered-by is ignored (with verbose message) for state data, RPC/action output parameters and notification content */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;" + "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG, ll->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e;" + "rpc oper {output {leaf-list ll {type string; ordered-by user;}}}}", LYS_IN_YANG, &mod)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f;" + "notification event {leaf-list ll {type string; ordered-by user;}}}", LYS_IN_YANG, &mod)); + + /* forward reference in default */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;" + "leaf ref {type instance-identifier {require-instance true;} default \"/g:g[.='val']\";}" + "leaf-list g {type string;}}", LYS_IN_YANG, &mod)); + assert_non_null(l = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("ref", l->name); + assert_non_null(l->dflt); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "/bb:ll", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;" + "leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_non_null(ll->dflts); + assert_int_equal(3, LY_ARRAY_COUNT(ll->dflts)); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;" + "leaf-list ll {type string; default one;default two;default one;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Configuration leaf-list has multiple defaults of the same value \"one\".", "/dd:ll", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;" + "leaf ref {type instance-identifier {require-instance true;} default \"/ee:g\";}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Invalid instance-identifier \"/ee:g\" value - semantic error: Not found node \"g\" in path.).", "/ee:ref", 0); +} + +static void +test_node_list(void **state) +{ + struct lys_module *mod; + struct lysc_node_list *list; + struct lysc_node *child; + struct ly_in *in; + const char *data = + "module a {namespace urn:a;prefix a;feature f;" + "list l1 {key \"x y\"; ordered-by user; leaf y{type string;if-feature f;} leaf x {type string; when 1;}}" + "list l2 {config false;leaf value {type string;}}}"; + const char *feats[] = {"f", NULL}; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats, &mod)); + ly_in_free(in, 0); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_non_null(list->child); + assert_string_equal("x", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_string_equal("y", list->child->next->name); + assert_true(list->child->next->flags & LYS_KEY); + assert_non_null(list->child); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, list->flags); + assert_true(list->child->flags & LYS_KEY); + assert_true(list->child->next->flags & LYS_KEY); + list = (struct lysc_node_list *)mod->compiled->data->next; + assert_non_null(list); + assert_non_null(list->child); + assert_false(list->child->flags & LYS_KEY); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG | LYS_KEYLESS, list->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;" + "list l {key a; unique \"a c/b:b\"; unique \"c/e d\";" + "leaf a {type string; default x;} leaf d {type string;config false;}" + "container c {leaf b {type string;}leaf e{type string;config false;}}}}", + LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_string_equal("a", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_null(((struct lysc_node_leaf *)list->child)->dflt); + assert_non_null(list->uniques); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques)); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0])); + assert_string_equal("a", list->uniques[0][0]->name); + assert_true(list->uniques[0][0]->flags & LYS_UNIQUE); + assert_string_equal("b", list->uniques[0][1]->name); + assert_true(list->uniques[0][1]->flags & LYS_UNIQUE); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[1])); + assert_string_equal("e", list->uniques[1][0]->name); + assert_true(list->uniques[1][0]->flags & LYS_UNIQUE); + assert_string_equal("d", list->uniques[1][1]->name); + assert_true(list->uniques[1][1]->flags & LYS_UNIQUE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;" + "list l {key a;leaf a {type empty;}}}", LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_string_equal("a", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_int_equal(LY_TYPE_EMPTY, ((struct lysc_node_leaf *)list->child)->type->basetype); + + /* keys order */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;" + "list l {key \"d b c\";leaf a {type string;} leaf b {type string;} leaf c {type string;} leaf d {type string;}}}", LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_non_null(child = list->child); + assert_string_equal("d", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("b", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("c", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("a", child->name); + assert_false(child->flags & LYS_KEY); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing key in list representing configuration data.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;" + "list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("List's key must not have any \"when\" statement.", "/bb:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;" + "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Key \"x\" is disabled.", "/cc:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;" + "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Key of a configuration list must not be a state leaf.", "/dd:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;" + "list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/ee:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;" + "list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("The list's key \"x\" not found.", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;" + "list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Unique's descendant-schema-nodeid \"y\" refers to leaf-list node instead of a leaf.", "/gg:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;" + "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Unique statement \"x y\" refers to leaves with different config type.", "/hh:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;" + "list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".", "/ii:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;" + "list l {key x; unique c/x;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.", "/jj:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;" + "list l {key x; unique c^y;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.", "/kk:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;" + "list l {key \"x y x\";leaf x {type string;}leaf y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicated key identifier \"x\".", "/ll:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;" + "list l {key x;leaf x {type empty;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("List key of the \"empty\" type is allowed only in YANG 1.1 modules.", "/mm:l/x", 0); +} + +static void +test_node_choice(void **state) +{ + struct lys_module *mod; + struct lysc_node_choice *ch; + struct lysc_node_case *cs; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;feature f;" + "choice ch {default a:b; when \"true()\"; case a {leaf a1 {type string;}leaf a2 {type string;}}" + "leaf b {type string;}}}", LYS_IN_YANG, &mod)); + ch = (struct lysc_node_choice *)mod->compiled->data; + assert_non_null(ch); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, ch->flags); + assert_int_equal(1, LY_ARRAY_COUNT(ch->when)); + assert_null(ch->when[0]->context); + cs = ch->cases; + assert_non_null(cs); + assert_string_equal("a", cs->name); + assert_ptr_equal(ch, cs->parent); + assert_non_null(cs->child); + assert_string_equal("a1", cs->child->name); + assert_non_null(cs->child->next); + assert_string_equal("a2", cs->child->next->name); + assert_ptr_equal(cs, cs->child->parent); + cs = (struct lysc_node_case *)cs->next; + assert_non_null(cs); + assert_string_equal("b", cs->name); + assert_int_equal(LYS_STATUS_CURR | LYS_SET_DFLT | LYS_CONFIG_W, cs->flags); + assert_ptr_equal(ch, cs->parent); + assert_non_null(cs->child); + assert_string_equal("b", cs->child->name); + assert_ptr_equal(cs, cs->child->parent); + assert_ptr_equal(ch->dflt, cs); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/aa:ch/a/x\" of data definition/RPC/action/notification statement.", "/aa:ch/x/x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa;" + "choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/aa2:ch/a/y\" of data definition/RPC/action/notification statement.", "/aa2:ch/b/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;" + "choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:ch/a\" of case statement.", "/bb:ch/a", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb2 {namespace urn:bb2;prefix bb;" + "choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb2:ch/b\" of case statement.", "/bb2:ch/b", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ca {namespace urn:ca;prefix ca;" + "choice ch {default c;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Default case \"c\" not found.", "/ca:ch", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cb {namespace urn:cb;prefix cb; import a {prefix a;}" + "choice ch {default a:a;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Default case \"a:a\" not found.", "/cb:ch", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;" + "choice ch {default a;case a {leaf x {mandatory true;type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Mandatory node \"x\" under the default case \"a\".", "/cc:ch", 0); + /* TODO check with mandatory nodes from augment placed into the case */ +} + +static void +test_node_anydata(void **state) +{ + struct lys_module *mod; + struct lysc_node_anydata *any; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;" + "anydata any {config false;mandatory true;}}", LYS_IN_YANG, &mod)); + any = (struct lysc_node_anydata *)mod->compiled->data; + assert_non_null(any); + assert_int_equal(LYS_ANYDATA, any->nodetype); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_SET_CONFIG, any->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;" + "anyxml any;}", LYS_IN_YANG, &mod)); + any = (struct lysc_node_anydata *)mod->compiled->data; + assert_non_null(any); + assert_int_equal(LYS_ANYXML, any->nodetype); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, any->flags); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;anydata any;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); +} + +static void +test_action(void **state) +{ + struct lys_module *mod; + const struct lysc_node_action *rpc; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "rpc a {input {leaf x {type int8;} leaf y {type int8;}} output {leaf result {type int16;}}}}", LYS_IN_YANG, &mod)); + rpc = mod->compiled->rpcs; + assert_non_null(rpc); + assert_null(rpc->next); + assert_int_equal(LYS_RPC, rpc->nodetype); + assert_int_equal(LYS_STATUS_CURR, rpc->flags); + assert_string_equal("a", rpc->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {" + "action b {input {leaf x {type int8;} leaf y {type int8;}}" + "output {must \"result > 25\"; must \"/top\"; leaf result {type int16;}}}}" + "augment /top/b/output {leaf result2 {type string;}}}", LYS_IN_YANG, &mod)); + rpc = lysc_node_actions(mod->compiled->data); + assert_non_null(rpc); + assert_null(rpc->next); + assert_int_equal(LYS_ACTION, rpc->nodetype); + assert_int_equal(LYS_STATUS_CURR, rpc->flags); + assert_string_equal("b", rpc->name); + assert_null(rpc->input.musts); + assert_int_equal(2, LY_ARRAY_COUNT(rpc->output.musts)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {action x;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} rpc x;}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:x\" of data definition/RPC/action/notification statement.", "/bb:x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} action y;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/cc:c/y\" of data definition/RPC/action/notification statement.", "/cc:c/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:c/z\" of data definition/RPC/action/notification statement.", "/dd:c/z", 0); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}"); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/ee:w\" of data definition/RPC/action/notification statement.", "/ee:w", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}" + "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside another RPC/action.", "/ff:{augment='/test/input/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}" + "augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/gg:{augment='/test/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}" + "grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/hh:test/a/{uses='grp'}/invalid", 0); +} + +static void +test_notification(void **state) +{ + struct lys_module *mod; + const struct lysc_node_notif *notif; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "notification a1 {leaf x {type int8;}} notification a2;}", LYS_IN_YANG, &mod)); + notif = mod->compiled->notifs; + assert_non_null(notif); + assert_non_null(notif->next); + assert_null(notif->next->next); + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("a1", notif->name); + assert_non_null(notif->child); + assert_string_equal("x", notif->child->name); + notif = notif->next; + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("a2", notif->name); + assert_null(notif->child); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {" + "notification b1 {leaf x {type int8;}} notification b2 {must \"/top\";}}}", LYS_IN_YANG, &mod)); + notif = lysc_node_notifs(mod->compiled->data); + assert_non_null(notif); + assert_non_null(notif->next); + assert_null(notif->next->next); + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("b1", notif->name); + assert_non_null(notif->child); + assert_string_equal("x", notif->child->name); + notif = notif->next; + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("b2", notif->name); + assert_null(notif->child); + assert_int_equal(1, LY_ARRAY_COUNT(notif->musts)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:x\" of data definition/RPC/action/notification statement.", "/bb:x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/cc:c/y\" of data definition/RPC/action/notification statement.", "/cc:c/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:c/z\" of data definition/RPC/action/notification statement.", "/dd:c/z", 0); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}"); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/ee:w\" of data definition/RPC/action/notification statement.", "/ee:w", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}" + "augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/ff:{augment='/test/input/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}" + "augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside another notification.", "/gg:{augment='/test/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}" + "grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/hh:test/input/a/{uses='grp'}/invalid", 0); +} + +/** + * actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types, + * do not test the expression itself + */ +static void +test_type_range(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type int16 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT16, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(-32768, ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type int32 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT32, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type int64 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT64, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type uint8 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type uint16 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT16, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type uint32 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT32, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;leaf l {type uint64 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT64, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type uint8 {range 10..100;}}" + "typedef mytype2 {type mytype;} leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;" + "typedef mytype {type uint8 {range 1..100{description \"one to hundred\";reference A;}}}" + "leaf l {type mytype {range 1..10 {description \"one to ten\";reference B;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_string_equal("one to ten", ((struct lysc_type_num *)type)->range->dsc); + assert_string_equal("B", ((struct lysc_type_num *)type)->range->ref); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(1, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); +} + +static void +test_type_length(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type binary {length min {error-app-tag errortag;error-message error;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_string_equal("errortag", ((struct lysc_type_bin *)type)->length->eapptag); + assert_string_equal("error", ((struct lysc_type_bin *)type)->length->emsg); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type binary {length max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type binary {length min..max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type binary {length 5;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type binary {length 1..10;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type binary {length 1..10|20..30;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(20, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type binary {length \"16 | 32\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length \"10\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"50\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"10..30|60..100\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(60, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"10..80\";}}leaf ll {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(80, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(2, type->refcount); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + /* invalid values */ + assert_int_equal(LY_EDENIED, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf l {type binary {length -10;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - value \"-10\" does not fit the type limitations.", "/aa:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type binary {length 18446744073709551616;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/bb:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;leaf l {type binary {length \"max .. 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data after max keyword (.. 10).", "/cc:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf l {type binary {length 50..10;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/dd:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type binary {length \"50 | 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type binary {length \"x\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data (x).", "/ff:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type binary {length \"50 | min\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data before min keyword (50 | ).", "/gg:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;leaf l {type binary {length \"| 50\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected beginning of the expression (| 50).", "/hh:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;leaf l {type binary {length \"10 ..\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).", "/ii:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;leaf l {type binary {length \".. 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected \"..\" without a lower bound.", "/jj:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;leaf l {type binary {length \"10 |\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression (10 |).", "/kk:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module kl {namespace urn:kl;prefix kl;leaf l {type binary {length \"10..20 | 15..30\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (15).", "/kl:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length 11;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (11) is not equally or more limiting.", "/ll:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 1..11;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.", "/mm:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 20..110;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.", + "/nn:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 20..30|110..120;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.", + "/oo:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;typedef mytype {type binary {length 10..11;}}" + "leaf l {type mytype {length 15;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15) is not equally or more limiting.", "/pp:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;typedef mytype {type binary {length 10..20|30..40;}}" + "leaf l {type mytype {length 15..35;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.", "/qq:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length 10..35;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.", "/rr:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;leaf l {type binary {pattern '[0-9]*';}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid type restrictions for binary type.", "/ss:l", 0); +} + +static void +test_type_pattern(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;leaf l {type string {" + "pattern .* {error-app-tag errortag;error-message error;}" + "pattern [0-9].*[0-9] {modifier invert-match;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_str *)type)->patterns); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns)); + assert_string_equal("errortag", ((struct lysc_type_str *)type)->patterns[0]->eapptag); + assert_string_equal("error", ((struct lysc_type_str *)type)->patterns[0]->emsg); + assert_string_equal(".*", ((struct lysc_type_str *)type)->patterns[0]->expr); + assert_int_equal(0, ((struct lysc_type_str *)type)->patterns[0]->inverted); + assert_null(((struct lysc_type_str *)type)->patterns[1]->eapptag); + assert_null(((struct lysc_type_str *)type)->patterns[1]->emsg); + assert_string_equal("[0-9].*[0-9]", ((struct lysc_type_str *)type)->patterns[1]->expr); + assert_int_equal(1, ((struct lysc_type_str *)type)->patterns[1]->inverted); + + /* TODO check some data "^Å™$" */ +} + +static void +test_type_enum(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;feature f; leaf l {type enumeration {" + "enum automin; enum min {value -2147483648;}enum one {if-feature f; value 1;}" + "enum two; enum seven {value 7;}enum eight;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(5, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("automin", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("min", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_enum *)type)->enums[1].value); + assert_string_equal("two", ((struct lysc_type_enum *)type)->enums[2].name); + assert_int_equal(2, ((struct lysc_type_enum *)type)->enums[2].value); + assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[3].name); + assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[3].value); + assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[4].name); + assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[4].value); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b;feature f; typedef mytype {type enumeration {" + "enum 11; enum min {value -2147483648;}enum x$&;" + "enum two; enum seven {value 7;}enum eight;}} leaf l { type mytype {enum seven;enum eight;}}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[1].value); + + const char *new_module = "module moc_c {yang-version 1.1; namespace urn:moc_c;prefix moc_c;feature f; typedef mytype {type enumeration {" + "enum first{value -270;} enum second; enum third {value -400;} enum fourth;}} leaf l { type mytype;}}"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod)); + + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(4, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(-270, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(-269, ((struct lysc_type_enum *)type)->enums[1].value); + assert_string_equal("third", ((struct lysc_type_enum *)type)->enums[2].name); + assert_int_equal(-400, ((struct lysc_type_enum *)type)->enums[2].value); + assert_string_equal("fourth", ((struct lysc_type_enum *)type)->enums[3].name); + assert_int_equal(-268, ((struct lysc_type_enum *)type)->enums[3].value); + + new_module = "module moc_d {yang-version 1.1; namespace urn:moc_d;prefix moc_d;feature f; typedef mytype {type enumeration {" + "enum first; enum second;}} leaf l { type mytype;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod)); + + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(1, ((struct lysc_type_enum *)type)->enums[1].value); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type enumeration {" + "enum one {if-feature f;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one {value -2147483649;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-2147483649\" of \"value\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one {value 2147483648;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"2147483648\" of \"value\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one; enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"one\" of enum statement.", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum '';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not be zero-length.", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum ' x';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\" x\").", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum 'x ';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\"x \").", NULL, 1); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum 'inva\nlid';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Control characters in enum name should be avoided (\"inva\nlid\", character number 5).", NULL, 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type enumeration;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing enum substatement for enumeration type.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum two;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - derived type adds new item \"two\".", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum one {value 1;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.", "/dd:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type enumeration {enum x {value 2147483647;}enum y;}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.", + "/ee:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type enumeration {enum x {value 1;}enum y {value 1;}}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - value 1 collide in items \"y\" and \"x\".", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;typedef mytype {type enumeration;}" + "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing enum substatement for enumeration type mytype.", "/gg:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh; typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Enumeration type can be subtyped only in YANG 1.1 modules.", "/hh:l", 0); +} + +static void +test_type_dec64(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type decimal64 {" + "fraction-digits 2;range min..max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_dec *)type)->range->parts[0].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;typedef mytype {type decimal64 {" + "fraction-digits 2;range '3.14 | 5.1 | 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].max_64); + assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].min_64); + assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].max_64); + assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].min_64); + assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;typedef mytype {type decimal64 {" + "fraction-digits 2;range '1 .. 65535';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(100, ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(6553500, ((struct lysc_type_dec *)type)->range->parts[0].max_64); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"0\" of \"fraction-digits\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"fraction-digits\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Value \"19\" is out of \"fraction-digits\" bounds.", NULL, 1); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ab {namespace urn:ab;prefix ab; typedef mytype {type decimal64;}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type mytype.", "/ab:l", 0); + + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;" + "range '3.142';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.", "/bb:l", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;" + "range '4 | 3.14';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (3.14).", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; typedef mytype {type decimal64 {fraction-digits 2;}}" + "leaf l {type mytype {fraction-digits 3;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.", "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module de {namespace urn:de;prefix de; typedef mytype {type decimal64 {fraction-digits 2;}}" + "typedef mytype2 {type mytype {fraction-digits 3;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.", + "/de:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {" + "fraction-digits 18;range '-10 .. 0';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - invalid value \"-10000000000000000000\".", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {" + "fraction-digits 18;range '0 .. 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - invalid value \"10000000000000000000\".", "/ee:l", 0); +} + +static void +test_type_instanceid(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;typedef mytype {type instance-identifier {require-instance false;}}" + "leaf l1 {type instance-identifier {require-instance true;}}" + "leaf l2 {type mytype;} leaf l3 {type instance-identifier;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(0, ((struct lysc_type_instanceid *)type)->require_instance); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {require-instance yes;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"yes\" of \"require-instance\".", NULL, 1); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {fraction-digits 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type restrictions for instance-identifier type.", "/aa:l", 0); +} + +static ly_bool +identity_isderived(const struct lysc_ident *base, const char *der) +{ + LY_ARRAY_COUNT_TYPE u; + + LY_ARRAY_FOR(base->derived, u) { + if (!strcmp(base->derived[u]->name, der)) { + return 1; + } + if (identity_isderived(base->derived[u], der)) { + return 1; + } + } + return 0; +} + +static ly_bool +contains_derived_identity(struct ly_ctx *ctx, char *module_name, + char *revision, char *identity_name, char *derived_name) +{ + LY_ARRAY_COUNT_TYPE u = 0; + struct lys_module *mod; + struct lysc_ident *identity = NULL; + + if (!(mod = ly_ctx_get_module(ctx, module_name, revision))) { + return 0; + } + + LY_ARRAY_FOR(mod->identities, u) { + if (!strcmp(identity_name, mod->identities[u].name)) { + identity = &mod->identities[u]; + break; + } + } + if (!identity) { + return 0; + } + + return identity_isderived(identity, derived_name); +} + +static void +test_identity(void **state) +{ + char *str; + const char *feats[2] = {NULL, NULL}; + struct lyd_node *tree; + const char *data; + +#define RESET_CTX(CTX) \ + ly_ctx_destroy(UTEST_LYCTX); \ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + + /* It does not matter whether the base identity is in implemented + * module or not. + */ + + /* Implemented module's identity expand base identity located in unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a {prefix a;}" + "identity id2 {base a:baseid;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_false(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + RESET_CTX(UTEST_LYCTX); + + /* Unimplemented module (c) expand base identity located in unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}\n" + "module c {namespace urn:c; prefix c; import a {prefix a;}" + "identity id3 {base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a {prefix a;} import c {prefix c;}" + "identity id2 {base a:baseid;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:c\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id3\" value - identity found in non-implemented module \"c\".", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* Unimplemented module expand base identity located in implemented module. */ + str = "module b {namespace urn:b; prefix b;" + "identity baseid;" + "identity id2 {base baseid;}" + "leaf lf {type identityref {base baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a; import b {prefix b;}" + "identity id1 {base b:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + /* load (but don't implement) module (a) into context by module (c) */ + str = "module c {namespace urn:c; prefix c; import a {prefix a;}}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* Transitivity of derived identity through unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}\n" + "module c {namespace urn:c; prefix c; import a {prefix a;}" + "identity id3 {base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import c {prefix c;} import a {prefix a;}" + "identity id2 {base c:id3;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\">id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + RESET_CTX(UTEST_LYCTX); + + /* The base reference must not refer to a non-existent module, + * even if the module is not implemented. + */ + str = "module b {namespace urn:b; prefix b;" + "identity ident { base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + /* load (but don't implement) module (b) into context by module (c) */ + str = "module c {namespace urn:c; prefix c; import b {prefix b;}}"; + UTEST_INVALID_MODULE(str, LYS_IN_YANG, NULL, LY_EVALID); + RESET_CTX(UTEST_LYCTX); + + /* Tests in which multiple revisions are available and the import + * does not specify an exact revision. + */ + + /* The old revision was soon implemented + * and therefore its "baseid" is used. + */ + str = "module a {namespace urn:a; prefix a;" + "revision \"2014-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a;" + "revision \"2015-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b;" + "import a {prefix a;}" + "identity baseref { base a:baseid;}" + "identity id1 { base baseref;}" + "identity id2 { base baseref;}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref")); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id1")); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id2")); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Even if a newer revision has been implemented, the old and + * unimplemented one will be used because it has already been + * imported. Therefore, if the user wants to use multiple revisions, + * he must choose one and implement it as soon as possible. + */ + str = "module a {namespace urn:a; prefix a;" + "revision \"2014-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b;" + "import a {prefix a;}" + "identity baseref { base a:baseid;}" + "identity id1 { base baseref;}" + "identity id2 { base baseref;}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a;" + "revision \"2015-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_log_level(LY_LLVRB); + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + CHECK_LOG_LASTMSG("Implemented module \"a@2015-05-08\" was not and will not " + "be imported if the revision-date is missing in the import " + "statement. Instead, the revision \"2014-05-08\" is imported."); + ly_log_level(LY_LLWRN); + /* Data is inserted only to implemented revision. */ + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref")); + assert_false(contains_derived_identity(UTEST_LYCTX, "a", "2015-05-08", "baseid", "baseref")); + RESET_CTX(UTEST_LYCTX); + + /* Identity testing with if-features. */ + + /* The if-feature has no effect if the module is imported. */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "feature f;" + "identity baseid { if-feature \"f\";}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a { prefix a;}" + "identity id1 { base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Even if the identity in the implemented module is disabled, + * it can be used as a base. + */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "feature f;" + "identity baseid { if-feature \"f\";}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module b {namespace urn:b; prefix b; import a { prefix a;}" + "identity id1 { base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Identity derivation cannot be instantiated if it is disabled. + * Conversely, if the identity is enabled, it can be instantiated. + */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}" + "feature f2;" + "feature f3;" + "identity id1 { base a:baseid;}" + "identity id2 { if-feature \"f2\"; base a:baseid;}" + "identity id3 { if-feature \"f3\"; base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + feats[0] = "f2"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\">id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"id3\" value - identity is disabled by if-feature.", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* The derived identities are enabled and disabled in submodule. */ + str = "submodule asub {yang-version 1.1; belongs-to a {prefix a;}" + "feature f2;" + "feature f3;" + "identity id1 { base a:baseid;}" + "identity id2 { if-feature \"f2\"; base a:baseid;}" + "identity id3 { if-feature \"f3\"; base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module a {namespace urn:a; prefix a; include asub;" + "identity baseid;" + "}"; + feats[0] = "f2"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL); + str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id3\" value - identity is disabled by if-feature.", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + +#undef RESET_CTX +} + +static void +test_type_identityref(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;identity i; identity j; identity k {base i;}" + "typedef mytype {type identityref {base i;}}" + "leaf l1 {type mytype;} leaf l2 {type identityref {base a:k; base j;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("i", ((struct lysc_type_identityref *)type)->bases[0]->name); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name); + assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import a {prefix a;}" + "leaf l {type identityref {base a:k; base a:j;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name); + assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type identityref;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing base substatement for identityref type.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; typedef mytype {type identityref;}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing base substatement for identityref type mytype.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; identity i; typedef mytype {type identityref {base i;}}" + "leaf l {type mytype {base i;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid base substatement for the type not directly derived from identityref built-in type.", + "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; identity i; typedef mytype {type identityref {base i;}}" + "typedef mytype2 {type mytype {base i;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.", + "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; identity i; identity j;" + "leaf l {type identityref {base i;base j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Multiple bases in identityref type are allowed only in YANG 1.1 modules.", "/ee:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; identity i;leaf l {type identityref {base j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Unable to find base (j) of identityref.", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type identityref {base x:j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid prefix used for base (x:j) of identityref.", "/gg:l", 0); +} + +static void +test_type_leafref(void **state) +{ + char *str; + struct lys_module *mod; + struct lysc_type *type; + const char *path; + struct lyxp_expr *expr; + + /* lys_path_parse() */ + path = "invalid_path"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"invalid_path\"), expected \"..\".", NULL, 0); + + path = ".."; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "..["; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath token \"[\" (\"[\"), expected \"Operator(Path)\".", NULL, 0); + + path = "../"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "/"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "../../pref:id/xxx[predicate]/invalid!!!"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"invalid\" is supposed to be a function call.", NULL, 0); + + path = "/absolute/prefix:path"; + assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + assert_int_equal(4, expr->used); + assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[0]); + assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[1]); + assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[2]); + assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[3]); + lyxp_expr_free(UTEST_LYCTX, expr); + + /* complete leafref paths */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;" + "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}" + "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/a:target1", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "a", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/a/target2", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mytype {type leafref {path /b:target;}}" + "typedef mytype2 {type mytype;} typedef mytype3 {type leafref {path /target;}} leaf ref {type mytype2;}" + "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type string;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* prefixes are reversed to check using correct context of the path! */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix b; import b {prefix c;}" + "typedef mytype3 {type c:mytype {require-instance false;}}" + "leaf ref1 {type b:mytype3;}leaf ref2 {type c:mytype2;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* non-prefixed nodes in path are supposed to be from the module where the leafref type is instantiated */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; import b {prefix b;}" + "leaf ref {type b:mytype3;}leaf target {type int8;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/target", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* conditional leafrefs */ + str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;" + "leaf ref1 {type leafref {path /target;}}" + "leaf target {if-feature 'f1'; type boolean;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Target of leafref \"ref1\" cannot be referenced because it is disabled.", "/e:ref1", 0); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/e:ref1", 0); + + str = "module en {yang-version 1.1;namespace urn:en;prefix en;feature f1;" + "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}" + "leaf target {type boolean;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + + str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;" + "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/e:ref1", 0); + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module cl {namespace urn:cl;prefix cl;feature f1;" + "leaf f {type string; if-feature 'f1';}" + "leaf g {type leafref {path \"/cl:f\";}}" + "leaf h {type uint16; default 1;}}"); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module im {namespace urn:im;prefix im;import cl {prefix cl;}" + "leaf ref {must \"/cl:h > 0\"; type uint16;}}", LYS_IN_YANG, &mod)); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + CHECK_LOG_CTX("Target of leafref \"g\" cannot be referenced because it is disabled.", "/cl:g", 0); + CHECK_LOG_CTX("Not found node \"f\" in path.", "/cl:g", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;" + "list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}" + "container default-address{leaf ifname{type leafref{ path \"../../interface/name\";}}" + "leaf address {type leafref{ path \"../../interface[ name = current()/../ifname ]/address/ip\";}}}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)(*lysc_node_child_p(mod->compiled->data->prev))->prev)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("../../interface[ name = current()/../ifname ]/address/ip", + ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;" + "leaf source {type leafref {path \"/endpoint-parent[id=current()/../field]/endpoint/name\";}}" + "leaf field {type int32;}list endpoint-parent {key id;leaf id {type int32;}" + "list endpoint {key name;leaf name {type string;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/endpoint-parent[id=current()/../field]/endpoint/name", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + + /* leafref to imported (not yet implemented) module */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h-imp {namespace urn:h-imp;prefix h-imp;" + "leaf l {type string;}}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module h {namespace urn:h;prefix h;import h-imp {prefix hi;}" + "leaf h {type uint16;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import h {prefix h;}" + "leaf i {type leafref {path /h:h;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "h")); + assert_int_equal(1, mod->implemented); + assert_non_null(mod->compiled->data); + assert_string_equal("h", mod->compiled->data->name); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j; leaf j {type string;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}" + "leaf i {type leafref {path \"/ilist[name = current()/../j:j]/value\";}}" + "list ilist {key name; leaf name {type string;} leaf value {type uint16;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "j")); + assert_int_equal(1, mod->implemented); + assert_non_null(mod->compiled->data); + assert_string_equal("j", mod->compiled->data->name); + + /* leafref with a default value */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;" + "leaf source {type leafref {path \"../target\";}default true;}" + "leaf target {type boolean;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("../target", ((struct lysc_type_leafref *)type)->path->expr); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_int_equal(LY_TYPE_BOOL, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->realtype->basetype); + assert_int_equal(1, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->boolean); + + /* union reference */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;" + "typedef s-ref {type union {type leafref {path '/str';}}}" + "leaf str {type string {length \"1..16\" {error-message \"Custom message\";}}}" + "leaf ref1 {type s-ref;}" + "leaf ref2 {type s-ref;}}", LYS_IN_YANG, NULL)); + + /* invalid paths */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"invalid\" in path.", "/aa:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Too many parent references in path.", "/bb:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"a\" found (prefix format schema stored mapping).", "/cc:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}" + "container a {leaf target2 {type uint8;}} leaf ref1 {type leafref {" + "path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("List predicate defined for container \"a\" in path.", "/dd:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;\n container a {leaf target2 {type uint8;}}\n" + "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ee\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"a\" is supposed to be a function call.", NULL, 3); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.", "/ff:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8;" + "status deprecated;}} leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".", + "/gg:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;" + "leaf ref1 {type leafref;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/hh:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;typedef mytype {type leafref;}" + "leaf ref1 {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing path substatement for leafref type mytype.", "/ii:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;" + "leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.", + "/kk:ref", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;" + "leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.", + "/ll:ref", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}" + "leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.", + "/mm:ref", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{\n path \"/interface[name is current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"nn\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 0x69 ('i'), perhaps \"name\" is supposed to be a function call.", NULL, 5); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{\n path \"/interface[name=current()/../ifname/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"oo\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 5); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}" + "leaf ifname{type leafref{ path \"../interface/name\";}}" + "leaf address {type leafref{ path \"/interface[x:name=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).", "/pp:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}" + "leaf ifname{type leafref{ path \"../interface/name\";}}" + "leaf address {type leafref{ path \"/interface[id=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"id\" in path.", "/qq:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name=current() / .. / ifname][name=current()/../test]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"rr\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate predicate key \"name\" in path.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = ../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ss\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"FunctionName\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module tt {namespace urn:tt;prefix tt;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"tt\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"Operator(Path)\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module uu {namespace urn:uu;prefix uu;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/..ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"uu\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 'i'[31] of expression '/interface[name = current()/..ifname]/ip'.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module vv {namespace urn:vv;prefix vv;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"vv\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"ifname]/ip\"), expected \"..\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ww {namespace urn:ww;prefix ww;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/../]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ww\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]/ip\"), expected \"NameTest\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module xx {namespace urn:xx;prefix xx;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/../#node]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module yy {namespace urn:yy;prefix yy;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[name=current()/../x:ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).", "/yy:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zz {namespace urn:zz;prefix zz;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[name=current()/../xxx]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"xxx\" in path.", "/zz:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zza {namespace urn:zza;prefix zza;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}container c;\n" + "leaf address {type leafref{ path \"/interface[name=current()/../c]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leaf expected instead of container \"c\" in leafref predicate in path.", "/zza:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzb {namespace urn:zzb;prefix zzb;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}container c;}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[c=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Key expected instead of container \"c\" in path.", "/zzb:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzc {namespace urn:zzc;prefix zzc;\n" + "leaf source {type leafref {path \"../target\";}default true;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/zzc:source", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzd {namespace urn:zzd;prefix zzd;\n" + "leaf source {type leafref {path \"../target\";}default true;}\n" + "leaf target {type uint8;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid type uint8 value \"true\".).", "/zzd:source", 0); + + /* circular chain */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aaa {namespace urn:aaa;prefix aaa;\n" + "leaf ref1 {type leafref {path /ref2;}}\n" + "leaf ref2 {type leafref {path /ref3;}}\n" + "leaf ref3 {type leafref {path /ref4;}}\n" + "leaf ref4 {type leafref {path /ref1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/ref1\" - circular chain of leafrefs detected.", "/aaa:ref4", 0); +} + +static void +test_type_empty(void **state) +{ + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "leaf l {type empty; default x;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}" + "leaf l {type mytype;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).", "/bb:l", 0); +} + +static void +test_type_union(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a; typedef mybasetype {type string;}" + "typedef mytype {type union {type int8; type mybasetype;}}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(2, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b; typedef mybasetype {type string;}" + "typedef mytype {type union {type int8; type mybasetype;}}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c; typedef mybasetype {type string;}" + "typedef mytype {type union {type leafref {path ../target;} type mybasetype;}}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}" + "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type int8;}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype); + + /* invalid unions */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;typedef mytype {type union;}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing type substatement for union type mytype.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type union;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing type substatement for union type.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;typedef mytype {type union{type int8; type string;}}" + "leaf l {type mytype {type string;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type substatement for the type not directly derived from union built-in type.", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;typedef mytype {type union{type int8; type string;}}" + "typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.", + "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type union{type mytype; type string;}}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;typedef mytype {type mytype2;}" + "typedef mytype2 {type mytype;} leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ef:l", 0); +} + +static void +test_type_dflt(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + struct lysc_node_leaf *leaf; + uint8_t dynamic; + + /* default is not inherited from union's types */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mybasetype;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type mybasetype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference (typedf's default does not reference own type)*/ + assert_int_equal(LY_TYPE_STRING, type->basetype); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type mybasetype; default goodbye;units yyy;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("yyy", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; typedef mybasetype {type string;default hello;units xxx;}" + "typedef mytype {type mybasetype;}leaf l1 {type mytype; default goodbye;units yyy;}" + "leaf l2 {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("yyy", leaf->units); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data->next; + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; typedef mybasetype {type string;}" + "typedef mytype {type mybasetype; default hello;units xxx;}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(4, type->refcount); /* 3x type reference, 1x default value's reference (typedef's default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + /* mandatory leaf does not takes default value from type */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;typedef mytype {type string; default hello;units xxx;}" + "leaf l {type mytype; mandatory true;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_STRING, type->basetype); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_string_equal("xxx", ((struct lysc_node_leaf *)mod->compiled->data)->units); +} + +static void +test_type_exts(void **state) +{ + const char *schema1, *schema2, *schema3, *schema4; + struct lys_module *mod; + const struct lysc_node *snode; + struct lysc_type *type; + struct lysc_type_union *type_u; + + schema1 = "module my-extensions {\n" + " namespace \"urn:my-extensions\";\n" + " prefix my-ext;\n" + "\n" + " extension shortdesc {\n" + " argument shortdsc;\n" + " }\n" + "}\n"; + schema2 = "module module-inet {\n" + " yang-version 1.1;\n" + " namespace \"urn:module-inet\";\n" + " prefix mod-inet;\n" + "\n" + " import ietf-inet-types {\n" + " prefix inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " typedef domain-name {\n" + " type inet:domain-name {\n" + " my-ext:shortdesc \"<host-name>\";\n" + " }\n" + " }\n" + "\n" + " typedef ipv4-address {\n" + " type inet:ipv4-address-no-zone {\n" + " my-ext:shortdesc \"<A.B.C.D>\";\n" + " }\n" + " }\n" + " typedef my-enum {\n" + " type enumeration {\n" + " enum one;\n" + " enum two;\n" + " enum three;\n" + " }\n" + " }\n" + "}\n"; + schema3 = "module module-a {\n" + " yang-version 1.1;\n" + " namespace \"urn:module-a\";\n" + " prefix mod-a;\n" + "\n" + " import module-inet {\n" + " prefix mod-inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " typedef server-address {\n" + " type union {\n" + " type mod-inet:ipv4-address {\n" + " my-ext:shortdesc \"<ipv4-address>\";\n" + " }\n" + " type mod-inet:domain-name {\n" + " my-ext:shortdesc \"<fqdn>\";\n" + " }\n" + " }\n" + " }\n" + "}\n"; + schema4 = "module main-module {\n" + " yang-version 1.1;\n" + " namespace \"urn:main-module\";\n" + " prefix main;\n" + "\n" + " import module-a {\n" + " prefix mod-a;\n" + " }\n" + "\n" + " import module-inet {\n" + " prefix mod-inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " container config {\n" + " leaf server {\n" + " type mod-a:server-address {\n" + " my-ext:shortdesc \"<server-address>\";\n" + " }\n" + " }\n" + "\n" + " leaf hostname {\n" + " type union {\n" + " type mod-inet:domain-name;\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "\n" + " leaf my-leaf {\n" + " type mod-inet:my-enum {\n" + " my-ext:shortdesc \"my enum\";\n" + " }\n" + " }\n" + "}\n"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema1, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema2, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema3, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema4, LYS_IN_YANG, &mod)); + + /* server */ + snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/server", 0); + assert_non_null(snode); + + type = ((struct lysc_node_leaf *)snode)->type; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 1); + assert_string_equal(type->exts[0].argument, "<server-address>"); + type_u = (struct lysc_type_union *)type; + assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2); + + type = type_u->types[0]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 2); + assert_string_equal(type->exts[0].argument, "<A.B.C.D>"); + assert_string_equal(type->exts[1].argument, "<ipv4-address>"); + + type = type_u->types[1]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 2); + assert_string_equal(type->exts[0].argument, "<host-name>"); + assert_string_equal(type->exts[1].argument, "<fqdn>"); + + /* hostname */ + snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/hostname", 0); + assert_non_null(snode); + type = ((struct lysc_node_leaf *)snode)->type; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 0); + type_u = (struct lysc_type_union *)type; + assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2); + + type = type_u->types[0]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 1); + assert_string_equal(type->exts[0].argument, "<host-name>"); + + type = type_u->types[1]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 0); +} + +static void +test_status(void **state) +{ + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "container c {status deprecated; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"deprecated\" status of parent \"c\".", "/aa:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;" + "container c {status obsolete; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/bb:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;" + "container c {status obsolete; leaf l {status deprecated; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"deprecated\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/cc:c/l", 0); + + /* just a warning */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:dd;prefix d;" + "container c {leaf l {status obsolete; type string;}}" + "container d {leaf m {when \"../../c/l\"; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition \"../../c/l\" may be referencing deprecated node \"l\".", NULL, 0); +} + +static void +test_grouping(void **state) +{ + /* result ok, but a warning about not used locally scoped grouping printed */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; grouping grp1 {leaf a1 {type string;}}" + "container a {leaf x {type string;} grouping grp2 {leaf a2 {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Locally scoped grouping \"grp2\" not used.", NULL, 0); + + /* result ok - when statement or leafref target must be checked only at the place where the grouping is really instantiated */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; grouping grp {" + "leaf ref {type leafref {path \"../name\";}}" + "leaf cond {type string; when \"../name = 'specialone'\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* invalid - error in a non-instantiated grouping */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "grouping grp {leaf x {type leafref;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:{grouping='grp'}/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "container a {grouping grp {leaf x {type leafref;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:a/{grouping='grp'}/x", 0); + CHECK_LOG_CTX("Locally scoped grouping \"grp\" not used.", NULL, 0); + + /* config check */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module z1 {namespace urn:z1;prefix z1;" + "container root;}\n" + "module z2 {namespace urn:z2;prefix z2;" + "grouping leafs_group {" + " leaf name {type string; config true;}" + " leaf value {type uint32; config true;}" + "}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z3 {namespace urn:z3;prefix z3;" + "import z1 {prefix z1;} import z2 {prefix z2;}" + "grouping grp_a_top {leaf a1 {type int8;}}" + "grouping list_group {" + " list mylist {key \"name\"; unique \"value\"; uses z2:leafs_group;}" + "}" + "augment /z1:root { uses list_group;} }", LYS_IN_YANG, NULL)); + + /* identity */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y1 {namespace urn:y1;prefix y1;" + "identity base_identity;" + "identity id1 {base \"base_identity\";}" + "grouping attrs_group {" + " leaf name {type identityref {base \"base_identity\";} default \"id1\";}" + "}}", LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y2 {namespace urn:y2;prefix y2;" + "import y1 {prefix y1;}" + "container root {uses y1:attrs_group;}}", LYS_IN_YANG, NULL)); +} + +static void +test_uses(void **state) +{ + struct lys_module *mod; + const struct lysc_node *parent, *child; + const struct lysc_node_container *cont; + const struct lysc_node_leaf *leaf; + const struct lysc_node_choice *choice; + const struct lysc_node_case *cs; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;} feature f;" + "grouping grp {leaf x {type mytype;} leaf y {type string; if-feature f;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;import grp {prefix g;}" + "grouping grp_a_top {leaf a1 {type int8;}}" + "container a {uses grp_a; uses grp_a_top; uses g:grp; grouping grp_a {leaf a2 {type uint8;}}}}", LYS_IN_YANG, &mod)); + assert_non_null((parent = mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, parent->nodetype); + assert_non_null((child = ((struct lysc_node_container *)parent)->child)); + assert_string_equal("a2", child->name); + assert_ptr_equal(mod, child->module); + assert_non_null((child = child->next)); + assert_string_equal("a1", child->name); + assert_ptr_equal(mod, child->module); + assert_non_null((child = child->next)); + assert_string_equal("x", child->name); + assert_ptr_equal(mod, child->module); + assert_null((child = child->next)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {when 1; type string;} leaf c {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;include bsub;uses grp {when 2;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("b", leaf->name); + assert_int_equal(2, LY_ARRAY_COUNT(leaf->when)); + assert_int_equal(1, leaf->when[0]->refcount); + assert_non_null(leaf->when[0]->context); + assert_string_equal("b", leaf->when[0]->context->name); + assert_int_equal(2, leaf->when[1]->refcount); + assert_null(leaf->when[1]->context); + + leaf = (struct lysc_node_leaf *)leaf->next; + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("c", leaf->name); + assert_int_equal(1, LY_ARRAY_COUNT(leaf->when)); + assert_int_equal(2, leaf->when[0]->refcount); + assert_null(leaf->when[0]->context); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;" + "grouping grp {leaf l {type string;}leaf k {type string; status obsolete;}}" + "uses grp {status deprecated;}}", LYS_IN_YANG, &mod)); + assert_int_equal(LYS_LEAF, mod->compiled->data->nodetype); + assert_string_equal("l", mod->compiled->data->name); + assert_true(LYS_STATUS_DEPRC & mod->compiled->data->flags); + assert_int_equal(LYS_LEAF, mod->compiled->data->next->nodetype); + assert_string_equal("k", mod->compiled->data->next->name); + assert_true(LYS_STATUS_OBSLT & mod->compiled->data->next->flags); + CHECK_LOG_CTX(NULL, NULL, 0); /* no warning about inheriting deprecated flag from uses */ + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; grouping grp {container g;}" + "container top {uses grp {augment g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + assert_non_null(child = lysc_node_child(mod->compiled->data)); + assert_string_equal("g", child->name); + assert_non_null(child = lysc_node_child(child)); + assert_string_equal("x", child->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e; grouping grp {action g { description \"super g\";}}" + "container top {action e; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + cont = (const struct lysc_node_container *)mod->compiled->data; + assert_non_null(cont->actions); + assert_non_null(cont->actions->next); + assert_null(cont->actions->next->next); + assert_string_equal("e", cont->actions->next->name); + assert_string_equal("g", cont->actions->name); + assert_string_equal("ultra g", cont->actions->dsc); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f; grouping grp {notification g { description \"super g\";}}" + "container top {notification f; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + cont = (const struct lysc_node_container *)mod->compiled->data; + assert_non_null(cont->notifs); + assert_non_null(cont->notifs->next); + assert_null(cont->notifs->next->next); + assert_string_equal("f", cont->notifs->next->name); + assert_string_equal("g", cont->notifs->name); + assert_string_equal("ultra g", cont->notifs->dsc); + + /* empty grouping */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g; grouping grp; uses grp;}", LYS_IN_YANG, &mod)); + assert_null(mod->compiled->data); + + /* choice in uses */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1;namespace urn:h;prefix h; grouping grp {choice gch {case gc1 { leaf y { type string;}} case gc2 {leaf z {type string;}}}}" + "choice ch {case one { leaf x {type string;}} case two { uses grp;}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + choice = (const struct lysc_node_choice *)mod->compiled->data; + assert_string_equal("ch", choice->name); + cs = choice->cases; + assert_non_null(cs); + assert_string_equal("one", cs->name); + assert_non_null(cs->child); + assert_string_equal("x", cs->child->name); + + cs = (struct lysc_node_case *)cs->next; + assert_non_null(cs); + assert_string_equal("two", cs->name); + assert_non_null(cs->child); + assert_string_equal("gch", cs->child->name); + + /* top-level uses with augment and refine */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i; grouping grp {container g;}" + "uses grp {augment g {leaf x {type int8;}} refine g {description \"dsc\";}}}", + LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + child = mod->compiled->data; + assert_string_equal("g", child->name); + cont = (const struct lysc_node_container *)child; + assert_string_equal("dsc", cont->dsc); + assert_non_null(child = lysc_node_child(child)); + assert_string_equal("x", child->name); + + /* unique */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j;" + "grouping grp {list l {key \"k\"; unique \"l\"; leaf k {type string;} leaf l {type string;}}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}" + "container a {uses j:grp;}}", LYS_IN_YANG, NULL)); + + /* if-features */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;" + "feature f;" + "grouping grp {container g; leaf l{type string;}}" + "uses grp {if-feature f;}}", + LYS_IN_YANG, &mod)); + assert_null(mod->compiled->data); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "/aa:{uses='missinggrp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;uses grp;" + "grouping grp {leaf a{type string;}uses grp1;}" + "grouping grp1 {leaf b {type string;}uses grp2;}" + "grouping grp2 {leaf c {type string;}uses grp;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Grouping \"grp\" references itself through a uses statement.", + "/bb:{uses='grp'}/{uses='grp1'}/{uses='grp2'}/{uses='grp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;uses a:missingprefix;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid prefix used for grouping \"a:missingprefix\" reference.", "/cc:{uses='a:missingprefix'}", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;grouping grp{leaf a{type string;}}" + "leaf a {type string;}uses grp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:a\" of data definition/RPC/action/notification statement.", + "/dd:{uses='grp'}/dd:a", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;grouping grp {leaf l {type string; status deprecated;}}" + "uses grp {status obsolete;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".", + "/ee:{uses='grp'}/ee:l", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;grouping grp {leaf l {type string;}}" + "leaf l {type int8;}uses grp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/ff:l\" of data definition/RPC/action/notification statement.", + "/ff:{uses='grp'}/ff:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module fg {namespace urn:fg;prefix fg;grouping grp {leaf m {type string;}}" + "uses grp;leaf m {type int8;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/fg:m\" of data definition/RPC/action/notification statement.", "/fg:m", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}" + "leaf g {type string;}" + "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"/g\" - name test expected instead of \"/\".", + "/gg:top/{uses='grp'}/{augment='/g'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1;namespace urn:hh;prefix hh;" + "grouping grp {notification g { description \"super g\";}}" + "container top {notification h; uses grp {refine h {description \"ultra h\";}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Refine(s) target node \"h\" in grouping \"grp\" was not found.", "/hh:top/{uses='grp'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module ii {yang-version 1.1;namespace urn:ii;prefix ii;" + "grouping grp {action g { description \"super g\";}}" + "container top {action i; uses grp {refine i {description \"ultra i\";}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Refine(s) target node \"i\" in grouping \"grp\" was not found.", "/ii:top/{uses='grp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {yang-version 1.1;namespace urn:jj;prefix jj;" + "grouping grp {leaf j { when \"1\"; type invalid;}}" + "container top {uses grp;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Referenced type \"invalid\" not found.", "/jj:top/{uses='grp'}/j", 0); +} + +static void +test_refine(void **state) +{ + struct lys_module *mod; + struct lysc_node *parent, *child; + struct lysc_node_leaf *leaf; + struct lysc_node_leaflist *llist; + uint8_t dynamic; + struct ly_in *in; + const char *data, *feats1[] = {"f", NULL}, *feats2[] = {"fa", NULL}; + + data = "module grp {yang-version 1.1;namespace urn:grp;prefix g; feature f;typedef mytype {type string; default cheers!;}" + "grouping grp {container c {leaf l {type mytype; default goodbye;}" + "leaf-list ll {type mytype; default goodbye; max-elements 6;}" + "choice ch {default ca; leaf ca {type int8;}leaf cb{type uint8;}}" + "leaf x {type mytype; mandatory true; must 1;}" + "anydata a {mandatory false; if-feature f; description original; reference original;}" + "container c {config false; leaf l {type string;}}}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats1, NULL)); + + data = "module a {yang-version 1.1;namespace urn:a;prefix a;import grp {prefix g;}feature fa;" + "uses g:grp {refine c/l {default hello; config false;}" + "refine c/ll {default hello;default world;}" + "refine c/ch {default cb;config true; if-feature fa;}" + "refine c/x {mandatory false; must ../ll;description refined; reference refined;}" + "refine c/a {mandatory true; must 1; description refined; reference refined;}" + "refine c/ll {max-elements 5;}" + "refine c/c {config true;presence indispensable;}}}"; + ly_in_memory(in, data); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats2, &mod)); + ly_in_free(in, 0); + + assert_non_null((parent = mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, parent->nodetype); + assert_string_equal("c", parent->name); + assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)parent)->child)); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("l", leaf->name); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_R, leaf->flags & LYS_CONFIG_MASK); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(LYS_LEAFLIST, llist->nodetype); + assert_string_equal("ll", llist->name); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("world", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(5, llist->max); + assert_non_null(child = llist->next); + assert_int_equal(LYS_CHOICE, child->nodetype); + assert_string_equal("ch", child->name); + assert_string_equal("cb", ((struct lysc_node_choice *)child)->dflt->name); + assert_true(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->dflt->flags); + assert_false(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->cases[0].flags); + assert_non_null(leaf = (struct lysc_node_leaf *)child->next); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("x", leaf->name); + assert_false(LYS_MAND_TRUE & leaf->flags); + assert_string_equal("cheers!", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf->musts); + assert_int_equal(2, LY_ARRAY_COUNT(leaf->musts)); + assert_string_equal("refined", leaf->dsc); + assert_string_equal("refined", leaf->ref); + assert_non_null(child = leaf->next); + assert_int_equal(LYS_ANYDATA, child->nodetype); + assert_string_equal("a", child->name); + assert_true(LYS_MAND_TRUE & child->flags); + assert_non_null(((struct lysc_node_anydata *)child)->musts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_anydata *)child)->musts)); + assert_string_equal("refined", child->dsc); + assert_string_equal("refined", child->ref); + assert_non_null(child = child->next); + assert_int_equal(LYS_CONTAINER, child->nodetype); + assert_string_equal("c", child->name); + assert_true(LYS_PRESENCE & child->flags); + assert_true(LYS_CONFIG_W & child->flags); + assert_true(LYS_CONFIG_W & ((struct lysc_node_container *)child)->child->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import grp {prefix g;}" + "uses g:grp {status deprecated; refine c/x {default hello; mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)mod->compiled->data)->child->prev->prev->prev)); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("x", leaf->name); + assert_false(LYS_MAND_TRUE & leaf->flags); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;" + "grouping alg {leaf alg2 {type bits {" + "bit ftp;bit h323-q931;bit h323-ras;bit pptp;bit rtsp;bit sip-tcp;bit sip-udp;bit tftp;bit dns-udp;}}}" + "container conf {uses alg {refine alg2 {default \"dns-udp\";}}}}", LYS_IN_YANG, &mod)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;import grp {prefix g;}" + "uses g:grp {refine c {default hello;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of container node - it is not possible to replace \"default\" property.", + "/aa:{uses='g:grp'}/aa:c/{refine='c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import grp {prefix g;}" + "uses g:grp {refine c/l {default hello; default world;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf with too many (2) default properties.", "/bb:{uses='g:grp'}/bb:c/l/{refine='c/l'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;import grp {prefix g;}" + "uses g:grp {refine c/ll {default hello; default world;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.", + "/cc:{uses='g:grp'}/cc:c/ll/{refine='c/ll'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;import grp {prefix g;}" + "uses g:grp {refine c/ll {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf-list node - it is not possible to replace \"mandatory\" property.", + "/dd:{uses='g:grp'}/dd:c/ll/{refine='c/ll'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;import grp {prefix g;}" + "uses g:grp {refine c/l {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ee:{uses='g:grp'}/ee:c/l", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/ee:{uses='g:grp'}/ee:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;import grp {prefix g;}" + "uses g:grp {refine c/ch {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ef:{uses='g:grp'}/ef:c/ch", 0); + CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "/ef:{uses='g:grp'}/ef:c/ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}" + "uses g:grp {refine c/ch/ca/ca {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Mandatory node \"ca\" under the default case \"ca\".", "/ff:{uses='g:grp'}/ff:c/ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}" + "uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg:{uses='g:grp'}/gg:c/x", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/gg:{uses='g:grp'}/gg:c/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import grp {prefix g;}" + "uses g:grp {refine c/c/l {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/hh:{uses='g:grp'}/hh:c/c/l", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/hh:{uses='g:grp'}/hh:c/c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;grouping grp {leaf l {type string; status deprecated;}}" + "uses grp {status obsolete;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".", + "/ii:{uses='grp'}/ii:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;import grp {prefix g;}" + "uses g:grp {refine c/x {presence nonsence;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"presence\" property.", + "/jj:{uses='g:grp'}/jj:c/x/{refine='c/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;import grp {prefix g;}" + "uses g:grp {refine c/ch {must 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of choice node - it is not possible to add \"must\" property.", + "/kk:{uses='g:grp'}/kk:c/ch/{refine='c/ch'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;import grp {prefix g;}" + "uses g:grp {refine c/x {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"min-elements\" property.", + "/ll:{uses='g:grp'}/ll:c/x/{refine='c/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;import grp {prefix g;}" + "uses g:grp {refine c/ll {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm:{uses='g:grp'}/mm:c/ll", 0); + CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "/mm:{uses='g:grp'}/mm:c/ll", 0); +} + +static void +test_augment(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + const struct lysc_node_choice *ch; + const struct lysc_node_case *c; + const struct lysc_node_container *cont; + const struct lysc_node_action *rpc; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a; typedef atype {type string;}" + "container top {leaf a {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}" + "leaf b {type a:atype;}}", LYS_IN_YANG, &mod)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module c {namespace urn:c;prefix c; import a {prefix a;}" + "augment /a:top { container c {leaf c {type a:atype;}}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;import a {prefix a;} import c {prefix c;}" + "augment /a:top/c:c { leaf d {type a:atype;} leaf c {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"))); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "b")); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "c")); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "d")); + assert_non_null(node = mod->compiled->data); + assert_string_equal(node->name, "top"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "a"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "c"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "d"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;choice ch {leaf a {type string;}}" + "augment /ch/c {when 1; leaf lc2 {type uint16;}}" + "augment /ch { when 1; leaf b {type int8;} case c {leaf lc1 {type uint8;}}}}", LYS_IN_YANG, &mod)); + assert_non_null((ch = (const struct lysc_node_choice *)mod->compiled->data)); + assert_null(mod->compiled->data->next); + assert_string_equal("ch", ch->name); + + assert_non_null(c = (const struct lysc_node_case *)ch->cases); + assert_string_equal("a", c->name); + assert_null(c->when); + assert_string_equal("a", c->child->name); + + assert_non_null(c = (const struct lysc_node_case *)c->next); + assert_string_equal("b", c->name); + assert_non_null(c->when); + assert_string_equal("b", c->child->name); + + assert_non_null(c = (const struct lysc_node_case *)c->next); + assert_string_equal("c", c->name); + assert_non_null(c->when); + assert_string_equal("lc1", ((const struct lysc_node_case *)c)->child->name); + assert_null(lysc_node_when(((const struct lysc_node_case *)c)->child)); + assert_string_equal("lc2", ((const struct lysc_node_case *)c)->child->next->name); + assert_non_null(lysc_node_when(((const struct lysc_node_case *)c)->child->next)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;grouping g {leaf a {type string;}}" + "container c;" + "augment /c {uses g;}}", LYS_IN_YANG, &mod)); + assert_non_null(node = lysc_node_child(mod->compiled->data)); + assert_string_equal(node->name, "a"); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule gsub {belongs-to g {prefix g;}" + "augment /c {container sub;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;include gsub; container c;" + "augment /c/sub {leaf main {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + assert_string_equal("c", mod->compiled->data->name); + assert_non_null(node = ((struct lysc_node_container *)mod->compiled->data)->child); + assert_string_equal("sub", node->name); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("main", node->name); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module himp {namespace urn:hi;prefix hi;container top; rpc func;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;import himp {prefix hi;}container top;" + "augment /hi:top {container p {presence XXX; leaf x {mandatory true;type string;}}}" + "augment /hi:top {list ll {key x;leaf x {type string;}leaf y {mandatory true; type string;}}}" + "augment /hi:top {leaf l {type string; mandatory true; config false;}}" + "augment /top {leaf l {type string; mandatory true;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("l", node->name); + assert_true(node->flags & LYS_MAND_TRUE); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp")); + assert_non_null(node = mod->compiled->data); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("p", node->name); + assert_non_null(node = node->next); + assert_string_equal("l", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("ll", node->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import himp {prefix hi;}" + "augment /hi:func/hi:input {leaf x {type string;}}" + "augment /hi:func/hi:output {leaf y {type string;}}}", LYS_IN_YANG, NULL)); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp")); + assert_non_null(rpc = mod->compiled->rpcs); + assert_null(rpc->next); + assert_non_null(rpc->input.child); + assert_string_equal("x", rpc->input.child->name); + assert_null(rpc->input.child->next); + assert_non_null(rpc->output.child); + assert_string_equal("y", rpc->output.child->name); + assert_null(rpc->output.child->next); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;yang-version 1.1; container root;" + "grouping grp {notification grp-notif;}" + "augment /root {uses grp;}}", LYS_IN_YANG, &mod)); + assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data); + assert_null(cont->child); + assert_non_null(cont->notifs); + assert_null(cont->notifs->next); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k; prefix k;yang-version 1.1;" + "feature f;" + "container c {if-feature f; leaf a {type string;}}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l; prefix l; yang-version 1.1;" + "import k {prefix k;}" + "augment /k:c {leaf b {type string;}}" + "leaf c {when \"/k:c/l:b\"; type string;}}", LYS_IN_YANG, NULL)); + /* no xpath warning expected */ + CHECK_LOG_CTX(NULL, NULL, 0); + assert_null(mod->compiled->data); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;yang-version 1.1;" + "feature f;" + "container root{container cont{if-feature f;}}" + "augment /root/cont {if-feature f; leaf l{type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data); + assert_null(cont->child); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}" + "augment /x/ {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "/aa:{augment='/x/'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}" + "augment /x {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/x\" from module \"aa\" was not found.", "/aa:{augment='/x'}", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; container c {leaf a {type string;}}" + "augment /c {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:c/a\" of data definition/RPC/action/notification statement.", "/bb:{augment='/c'}/a", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c {leaf a {type string;}}" + "augment /c/a {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/c/a\" from module \"cc\" was not found.", "/cc:{augment='/c/a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; container c {leaf a {type string;}}" + "augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid augment of container node which is not allowed to contain case node \"b\".", "/dd:{augment='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; import himp {prefix hi;}" + "augment /hi:top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid augment adding mandatory node \"c\" without making it conditional via when statement.", + "/ee:{augment='/hi:top'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; container top;" + "augment ../top {leaf x {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"../top\" - \"/\" expected instead of \"..\".", "/ff:{augment='../top'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; rpc func;" + "augment /func {leaf x {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/func\" from module \"gg\" was not found.", "/gg:{augment='/func'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import himp {prefix hi;}" + "augment /hi:func/input {leaf x {type string;}}" + "augment /hi:func/output {leaf y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Augment target node \"/hi:func/input\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/input'}", 0); + CHECK_LOG_CTX("Augment target node \"/hi:func/output\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/output'}", 0); +} + +static void +test_deviation(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + const struct lysc_node_list *list; + const struct lysc_node_leaflist *llist; + const struct lysc_node_leaf *leaf; + const char *str; + uint8_t dynamic; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a;" + "container top {leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "choice ch {default c; case b {leaf b{type string;}} case a {leaf a{type string;} leaf x {type string;}}" + " case c {leaf c{type string;}}}" + "rpc func1 { input { leaf x {type int8;}} output {leaf y {type int8;}}}" + "rpc func2;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}" + "deviation /a:top/a:b {deviate not-supported;}" + "deviation /a:ch/a:a/a:x {deviate not-supported;}" + "deviation /a:ch/a:c {deviate not-supported;}" + "deviation /a:ch/a:b {deviate not-supported;}" + "deviation /a:ch/a:a/a:a {deviate not-supported;}" + "deviation /a:ch {deviate replace {default a;}}" + "deviation /a:func1/a:input {deviate not-supported;}" + "deviation /a:func1/a:output {deviate not-supported;}" + "deviation /a:func2 {deviate not-supported;}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"))); + assert_non_null(node = mod->compiled->data); + assert_string_equal(node->name, "top"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "a"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + assert_null(node = node->next); + assert_non_null(node = mod->compiled->data->next); + assert_string_equal("ch", node->name); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(((struct lysc_node_choice *)node)->cases); + assert_null(((struct lysc_node_choice *)node)->cases->next); + assert_non_null(mod->compiled->rpcs); + assert_null(mod->compiled->rpcs->next); + assert_null(mod->compiled->rpcs->input.child); + assert_null(mod->compiled->rpcs->output.child); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mytype {type string; units kilometers;}" + "leaf c1 {type mytype;} leaf c2 {type mytype; units meters;} leaf c3 {type mytype; units meters;}" + "deviation /c1 {deviate add {units meters;}}" + "deviation /c2 {deviate delete {units meters;}}" + "deviation /c3 {deviate replace {units centimeters;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("c1", node->name); + assert_string_equal("meters", ((struct lysc_node_leaf *)node)->units); + assert_non_null(node = node->next); + assert_string_equal("c2", node->name); + assert_string_equal("kilometers", ((struct lysc_node_leaf *)node)->units); + assert_non_null(node = node->next); + assert_string_equal("c3", node->name); + assert_string_equal("centimeters", ((struct lysc_node_leaf *)node)->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; leaf c1 {type string; must 1;}" + "container c2 {presence yes; must 1; must 2;} leaf c3 {type string; must 1; must 3;}" + "deviation /c1 {deviate add {must 3;}}" + "deviation /c2 {deviate delete {must 2;}}" + "deviation /c3 {deviate delete {must 3; must 1;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("c1", node->name); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_node_leaf *)node)->musts)); + assert_string_equal("3", ((struct lysc_node_leaf *)node)->musts[1].cond->expr); + assert_non_null(node = node->next); + assert_string_equal("c2", node->name); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_container *)node)->musts)); + assert_string_equal("1", ((struct lysc_node_container *)node)->musts[0].cond->expr); + assert_non_null(node = node->next); + assert_string_equal("c3", node->name); + assert_null(((struct lysc_node_leaf *)node)->musts); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module e {yang-version 1.1; namespace urn:e;prefix e; typedef mytype {type string; default nothing;}" + "choice a {default aa;leaf aa {type string;} leaf ab {type string;} leaf ac {type string; mandatory true;}}" + "choice b {default ba;leaf ba {type string;} leaf bb {type string;}}" + "leaf c {default hello; type string;}" + "leaf-list d {default hello; default world; type string;}" + "leaf c2 {type mytype;} leaf-list d2 {type mytype;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1; namespace urn:f;prefix f;import e {prefix x;}" + "deviation /x:a {deviate delete {default aa;}}" + "deviation /x:b {deviate delete {default ba;}}" + "deviation /x:c {deviate delete {default hello;}}" + "deviation /x:d {deviate delete {default world;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_null(leaf->dflt); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf = (struct lysc_node_leaf *)llist->next); + assert_string_equal("nothing", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(5, leaf->dflt->realtype->refcount); /* 3x type reference, 2x default value reference (typedef's default does not reference own type) */ + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("nothing", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;import e {prefix x;}" + "deviation /x:b {deviate add {default x:ba;}}" + "deviation /x:c {deviate add {default bye;}}" + "deviation /x:d {deviate add {default all; default people;}}" + "deviation /x:c2 {deviate add {default hi; must 1;}}" + "deviation /x:d2 {deviate add {default hi; default all;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_non_null(leaf->dflt); + assert_string_equal("bye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(3, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("people", llist->dflts[2]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[2], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf = (struct lysc_node_leaf *)llist->next); + assert_non_null(leaf->dflt); + assert_string_equal("hi", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(6, leaf->dflt->realtype->refcount); /* 3x type reference, 3x default value reference + - previous type's default values were replaced by node's default values where d2 now has 2 default values */ + assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts)); + assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts[0].prefixes)); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hi", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1; namespace urn:h;prefix h;import e {prefix x;}" + "deviation /x:b {deviate replace {default x:ba;}}" + "deviation /x:c {deviate replace {default hello;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_non_null(leaf->dflt); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module i {namespace urn:i;prefix i;" + "list l1 {key a; leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "list l2 {key a; unique \"b c\"; unique \"d\"; leaf a {type string;} leaf b {type string;}" + " leaf c {type string;} leaf d {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;import i {prefix i;}" + "augment /i:l1 {leaf j_c {type string;}}" + "deviation /i:l1 {deviate add {unique \"b j:j_c\"; }}" + "deviation /i:l1 {deviate add {unique \"i:c\";}}" + "deviation /i:l2 {deviate delete {unique \"d\"; unique \"b c\";}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "i"))); + assert_non_null(list = (struct lysc_node_list *)mod->compiled->data); + assert_string_equal("l1", list->name); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques)); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0])); + assert_string_equal("b", list->uniques[0][0]->name); + assert_string_equal("j_c", list->uniques[0][1]->name); + assert_int_equal(1, LY_ARRAY_COUNT(list->uniques[1])); + assert_string_equal("c", list->uniques[1][0]->name); + assert_non_null(list = (struct lysc_node_list *)list->next); + assert_string_equal("l2", list->name); + assert_null(list->uniques); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k; leaf a {type string;}" + "container top {leaf x {type string;} leaf y {type string; config false;}}" + "deviation /a {deviate add {config false; }}" + "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("top", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal("x", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("y", node->name); + assert_true(node->flags & LYS_CONFIG_R); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l; leaf a {config false; type string;}" + "container top {leaf x {type string;}}" + "deviation /a {deviate replace {config true;}}" + "deviation /top {deviate replace {config false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true(node->flags & LYS_CONFIG_W); + assert_non_null(node = node->next); + assert_string_equal("top", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal("x", node->name); + assert_true(node->flags & LYS_CONFIG_R); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;" + "container a {leaf a {type string;}}" + "container b {leaf b {mandatory true; type string;}}" + "deviation /a/a {deviate add {mandatory true;}}" + "deviation /b/b {deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_false(node->flags & LYS_MAND_MASK); /* just unset on container */ + assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_FALSE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module n {yang-version 1.1; namespace urn:n;prefix n;" + "leaf a {default test; type string;}" + "leaf b {mandatory true; type string;}" + "deviation /a {deviate add {mandatory true;} deviate delete {default test;}}" + "deviation /b {deviate add {default test;} deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_null(((struct lysc_node_leaf *)node)->dflt); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_non_null(((struct lysc_node_leaf *)node)->dflt); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_FALSE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module o {namespace urn:o;prefix o;" + "leaf-list a {type string;}" + "list b {config false;}" + "leaf-list c {min-elements 1; max-elements 10; type string;}" + "list d {min-elements 10; max-elements 100; config false;}" + "deviation /a {deviate add {min-elements 1; max-elements 10;}}" + "deviation /b {deviate add {min-elements 10; max-elements 100;}}" + "deviation /c {deviate replace {min-elements 10; max-elements 100;}}" + "deviation /d {deviate replace {min-elements 1; max-elements 10;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_int_equal(1, ((struct lysc_node_leaflist *)node)->min); + assert_int_equal(10, ((struct lysc_node_leaflist *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_int_equal(10, ((struct lysc_node_list *)node)->min); + assert_int_equal(100, ((struct lysc_node_list *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("c", node->name); + assert_int_equal(10, ((struct lysc_node_leaflist *)node)->min); + assert_int_equal(100, ((struct lysc_node_leaflist *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("d", node->name); + assert_int_equal(1, ((struct lysc_node_list *)node)->min); + assert_int_equal(10, ((struct lysc_node_list *)node)->max); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module p {yang-version 1.1; namespace urn:p;prefix p; typedef mytype {type int8; default 1;}" + "leaf a {type string; default 10;} leaf-list b {type string;}" + "deviation /a {deviate replace {type mytype;}}" + "deviation /b {deviate replace {type mytype;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("a", leaf->name); + assert_int_equal(LY_TYPE_INT8, leaf->type->basetype); + assert_string_equal("10", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(10, leaf->dflt->uint8); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_string_equal("b", llist->name); + assert_int_equal(LY_TYPE_INT8, llist->type->basetype); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("1", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(1, llist->dflts[0]->uint8); + + /* instance-identifiers with NULL canonical are changed to string types with a canonical value equal to the original value */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module q {yang-version 1.1; namespace urn:q;prefix q; import e {prefix e;}" + "leaf q {type instance-identifier; default \"/e:d2[.='a']\";}" + "leaf-list ql {type instance-identifier; default \"/e:d[.='b']\"; default \"/e:d2[.='c']\";}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module qdev {yang-version 1.1; namespace urn:qdev;prefix qd; import q {prefix q;}" + "deviation /q:q { deviate replace {type string;}}" + "deviation /q:ql { deviate replace {type string;}}}", LYS_IN_YANG, NULL)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_int_equal(LY_TYPE_STRING, leaf->dflt->realtype->basetype); + assert_non_null(leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_CANON, NULL, NULL, NULL)); + assert_string_equal("/e:d2[.='a']", leaf->dflt->_canonical); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype); + assert_string_equal("/e:d[.='b']", llist->dflts[0]->_canonical); + assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype); + assert_string_equal("/e:d2[.='c']", llist->dflts[1]->_canonical); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module r {yang-version 1.1; namespace urn:r;prefix r;" + "typedef mytype {type uint8; default 200;}" + "leaf r {type mytype;} leaf-list lr {type mytype;}" + "deviation /r:r {deviate replace {type string;}}" + "deviation /r:lr {deviate replace {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("r", leaf->name); + assert_null(leaf->dflt); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_string_equal("lr", llist->name); + assert_null(llist->dflts); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module s {yang-version 1.1; namespace urn:s;prefix s;" + "leaf s {type instance-identifier {require-instance true;} default /s:x;}" + "leaf x {type string;} leaf y {type string;}" + "deviation /s:s {deviate replace {default /s:y;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("s", leaf->name); + assert_non_null(leaf->dflt); + assert_non_null(str = leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, mod->parsed, &dynamic, NULL)); + assert_string_equal("/s:y", str); + if (dynamic) { + free((char *)str); + } + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module t {namespace urn:t;prefix t;" + "leaf l {type string;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module u {namespace urn:u;prefix u;import t {prefix t;}" + "identity ident;" + "deviation /t:l {deviate replace {type identityref {base ident;}}}" + "}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "t"))); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l", leaf->name); + assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype); + assert_string_equal("ident", ((struct lysc_type_identityref *)leaf->type)->bases[0]->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module v {namespace urn:v;prefix v;" + "identity ident; identity ident2 { base ident; }" + "}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule w-sub { belongs-to w { prefix w; }" + "import v { prefix v_pref; }" + "leaf l { type string; default \"v_pref:ident2\"; }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module w {namespace urn:w;prefix w;" + "include w-sub;" + "}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module x {namespace urn:x;prefix x;" + "import w { prefix w_pref; } import v { prefix v_pref; }" + "deviation /w_pref:l { deviate replace { type identityref { base v_pref:ident; } } }" + "}", LYS_IN_YANG, NULL)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l", leaf->name); + assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module y {namespace urn:y;prefix y;" + "leaf l1 {type string;}" + "leaf l2 {type string;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z {namespace urn:z;prefix z;" + "import y {prefix y;}" + "deviation \"/y:l1\" {deviate replace {type leafref {path \"/l2\";}}}" + "deviation \"/y:l2\" {deviate replace {type leafref {path \"/z:al2\";}}}" + "leaf al2 {type string;}" + "}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "y"))); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l1", leaf->name); + assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype); + leaf = (struct lysc_node_leaf *)leaf->next; + assert_string_equal("l2", leaf->name); + assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype); + + /* complex dependencies */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-base {namespace urn:m-base;prefix mb;" + "container cont {leaf l {type string;} leaf l2 {type string;}}}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module m-base-aug {namespace urn:m-base-aug;prefix mba;" + "import m-base {prefix mb;}" + "augment /mb:cont {leaf l {type string;} leaf l2 {type string;}}" + "container cont2 {leaf l {type string;}}}" + "\n" + "module m-base-aug2 {namespace urn:m-base-aug2;prefix mba2;" + "import m-base {prefix mb;} import m-base-aug {prefix mba;}" + "augment /mb:cont {leaf augl1 {type string;}}" + "augment /mba:cont2 {leaf augl2 {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-dev {namespace urn:m-dev;prefix md;" + "import m-base-aug {prefix mba;} import m-base-aug2 {prefix mba2;}" + "deviation /mba:cont2/mba2:augl2 {deviate not-supported;}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "m-base-aug"))); + node = mod->compiled->data; + assert_string_equal(node->name, "cont2"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "l"); + assert_null(node->next); + + /* default identity referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a1-imp {namespace urn:a1-imp;prefix a1i;" + "identity id-base;" + "identity id1 {base id-base; status deprecated;}" + "leaf l {type identityref {base \"id-base\";}}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a1 {namespace urn:a1;prefix a1;" + "import a1-imp {prefix a1i;}" + "deviation \"/a1i:l\" {deviate add {default \"a1i:id1\";}}" + "}", LYS_IN_YANG, NULL)); + + /* default instance-identifier referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a2-imp {namespace urn:a2-imp;prefix a2i;" + "leaf l {type instance-identifier;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a2 {namespace urn:a2;prefix a2;" + "import a2-imp {prefix a2i;}" + "deviation \"/a2i:l\" {deviate add {default \"/a2i:k\";}}" + "}", LYS_IN_YANG, NULL)); + + /* must referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a3-imp {namespace urn:a3-imp;prefix a3i;" + "leaf l {type string;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a3 {namespace urn:a3;prefix a3;" + "import a3-imp {prefix a3i;}" + "deviation \"/a3i:l\" {deviate add {must \"string-length(/a3i:k) > 0\";}}" + "}", LYS_IN_YANG, NULL)); + + /* type leafref referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a4-imp {namespace urn:a4-imp;prefix a4i;" + "leaf l {type string;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a4 {namespace urn:a4;prefix a4;" + "import a4-imp {prefix a4i;}" + "deviation \"/a4i:l\" {deviate replace {type leafref {path \"/a4i:k\";}}}" + "}", LYS_IN_YANG, NULL)); + + /* unique referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a5-imp {namespace urn:a5-imp;prefix a5i;" + "list l1 {key \"k\";" + " leaf k {type string;}" + " leaf l {type string; status deprecated;}" + "}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a5 {namespace urn:a5;prefix a5;" + "import a5-imp {prefix a5i;}" + "deviation \"/a5i:l1\" {deviate add {unique \"a5i:l\";}}" + "}", LYS_IN_YANG, NULL)); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}" + "deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Deviation(s) target node \"/a:top/a:z\" from module \"aa1\" was not found.", + "/a:{deviation='/a:top/a:z'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa2;import a {prefix a;}" + "deviation /a:top/a:a {deviate not-supported;}" + "deviation /a:top/a:a {deviate add {default error;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Multiple deviations of \"/a:top/a:a\" with one of them being \"not-supported\".", + "/aa2:{deviation='/a:top/a:a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import a {prefix a;}" + "deviation a:top/a:a {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"a:top/a:a\" - \"/\" expected instead of \"a:top\".", + "/bb:{deviation='a:top/a:a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c;" + "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"units\" property.", + "/cc:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cd {namespace urn:cd;prefix cd; leaf c {type string; units centimeters;}" + "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"units\" property which already exists (with value \"centimeters\").", + "/cd:{deviation='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd1 {namespace urn:dd1;prefix dd1; container c;" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to delete \"units\" property.", + "/dd1:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd2 {namespace urn:dd2;prefix dd2; leaf c {type string;}" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which is not present.", + "/dd2:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd3 {namespace urn:dd3;prefix dd3; leaf c {type string; units centimeters;}" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which does not match the target's property value \"centimeters\".", + "/dd3:{deviation='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee1 {namespace urn:ee1;prefix ee1; container c;" + "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"units\" property.", + "/ee1:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee2 {namespace urn:ee2;prefix ee2; leaf c {type string;}" + "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation replacing \"units\" property \"meters\" which is not present.", + "/ee2:{deviation='/c'}", 0); + + /* the default is already deleted in /e:a byt module f */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff1 {namespace urn:ff1;prefix ff1; import e {prefix e;}" + "deviation /e:a {deviate delete {default x:aa;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"x:aa\" which is not present.", + "/ff1:{deviation='/e:a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff3 {namespace urn:ff3;prefix ff3; import e {prefix e;}" + "deviation /e:b {deviate delete {default e:b;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"e:b\" which does not match the target's property value \"x:ba\".", + "/ff3:{deviation='/e:b'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff5 {namespace urn:ff5;prefix ff5; anyxml a;" + "deviation /a {deviate delete {default x;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to delete \"default\" property.", + "/ff5:{deviation='/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff6 {namespace urn:ff6;prefix ff6; import e {prefix e;}" + "deviation /e:c {deviate delete {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match the target's property value \"hello\".", + "/ff6:{deviation='/e:c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff7 {namespace urn:ff7;prefix ff7; import e {prefix e;}" + "deviation /e:d {deviate delete {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match any of the target's property values.", + "/ff7:{deviation='/e:d'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg1 {namespace urn:gg1;prefix gg1; import e {prefix e;}" + "deviation /e:b {deviate add {default e:a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"x:ba\").", + "/gg1:{deviation='/e:b'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg2 {namespace urn:gg2;prefix gg2; import e {prefix e;}" + "deviation /e:a {deviate add {default x:a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Default case prefix \"x\" not found in imports of \"gg2\".", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg3 {namespace urn:gg3;prefix gg3; import e {prefix e;}" + "deviation /e:a {deviate add {default a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Default case \"a\" not found.", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}" + "deviation /e:c {deviate add {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"hello\").", + "/gg4:{deviation='/e:c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}" + "deviation /e:a {deviate add {default e:ac;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Mandatory node \"ac\" under the default case \"e:ac\".", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg5 {namespace urn:gg5;prefix gg5; leaf x {type string; mandatory true;}" + "deviation /x {deviate add {default error;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg5:{deviation='/x'}", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/gg5:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh1 {yang-version 1.1; namespace urn:hh1;prefix hh1; import e {prefix e;}" + "deviation /e:d {deviate replace {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf-list node - it is not possible to replace \"default\" property.", + "/hh1:{deviation='/e:d'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii1 {namespace urn:ii1;prefix ii1; import i {prefix i;}" + "deviation /i:l1 {deviate delete {unique x;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"x\" which does not match any of the target's property values.", + "/ii1:{deviation='/i:l1'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii2 {namespace urn:ii2;prefix ii2; import i {prefix i;} leaf x { type string;}" + "deviation /i:l2 {deviate delete {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"d\" which does not match any of the target's property values.", + "/ii2:{deviation='/i:l2'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii3 {namespace urn:ii3;prefix ii3; leaf x { type string;}" + "deviation /x {deviate delete {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to delete \"unique\" property.", "/ii3:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii4 {namespace urn:ii4;prefix ii4; leaf x { type string;}" + "deviation /x {deviate add {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to add \"unique\" property.", "/ii4:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj1 {namespace urn:jj1;prefix jj1; choice ch {case a {leaf a{type string;}}}" + "deviation /ch/a {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to add \"config\" property.", "/jj1:{deviation='/ch/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj2 {namespace urn:jj2;prefix jj2; container top {config false; leaf x {type string;}}" + "deviation /top/x {deviate add {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj2:{deviation='/top/x'}", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/jj2:{deviation='/top/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj4 {namespace urn:jj4;prefix jj4; choice ch {case a {leaf a{type string;}}}" + "deviation /ch/a {deviate replace {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to replace \"config\" property.", + "/jj4:{deviation='/ch/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}" + "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj5:top", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/jj5:top/x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}" + "deviation /x {deviate add {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"config\" property which already exists (with value \"config false\").", + "/jj6:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}" + "deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"mandatory\" property.", + "/kk1:{deviation='/top'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}" + "deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"mandatory\" property.", + "/kk2:{deviation='/top'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}" + "deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory true\").", + "/kk4:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll1 {namespace urn:ll1;prefix ll1; leaf x {default test; type string;}" + "deviation /x {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll1:{deviation='/x'}", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/ll1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {yang-version 1.1; namespace urn:ll2;prefix ll2; leaf-list x {default test; type string;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:{deviation='/x'}", 0); + CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "/ll2:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {namespace urn:ll2;prefix ll2; choice ch {default a; leaf a {type string;} leaf b {type string;}}" + "deviation /ch {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:ch", 0); + CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "/ll2:ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm1 {namespace urn:mm1;prefix mm1; leaf-list x {min-elements 10; type string;}" + "deviation /x {deviate add {max-elements 5;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm1:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list min-elements 10 is bigger than max-elements 5.", "/mm1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm2 {namespace urn:mm2;prefix mm2; leaf-list x {max-elements 10; type string;}" + "deviation /x {deviate add {min-elements 20;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm2:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list min-elements 20 is bigger than max-elements 10.", "/mm2:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm3 {namespace urn:mm3;prefix mm3; list x {min-elements 5; max-elements 10; config false;}" + "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm3:{deviation='/x'}", 0); + CHECK_LOG_CTX("List min-elements 5 is bigger than max-elements 1.", "/mm3:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm4 {namespace urn:mm4;prefix mm4; list x {min-elements 5; max-elements 10; config false;}" + "deviation /x {deviate replace {min-elements 20;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm4:{deviation='/x'}", 0); + CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "/mm4:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm5 {namespace urn:mm5;prefix mm5; leaf-list x {type string; min-elements 5;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", + "/mm5:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm6 {namespace urn:mm6;prefix mm6; list x {config false; min-elements 5;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", + "/mm6:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm7 {namespace urn:mm7;prefix mm7; leaf-list x {type string; max-elements 5;}" + "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", + "/mm7:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm8 {namespace urn:mm8;prefix mm8; list x {config false; max-elements 5;}" + "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", + "/mm8:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn1 {namespace urn:nn1;prefix nn1; anyxml x;" + "deviation /x {deviate replace {type string;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to replace \"type\" property.", "/nn1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn2 {namespace urn:nn2;prefix nn2; leaf-list x {type string;}" + "deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/nn2:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/nn2:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo1 {namespace urn:oo1;prefix oo1; leaf x {type uint16; default 300;}" + "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo1:x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo2 {yang-version 1.1;namespace urn:oo2;prefix oo2; leaf-list x {type uint16; default 10; default 300;}" + "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo2:x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo3 {namespace urn:oo3;prefix oo3; leaf x {type uint8;}" + "deviation /x {deviate add {default 300;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo3:x", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp; leaf l { type leafref {path /c/x;}}" + "container c {leaf x {type string;} leaf y {type string;}}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp1 {namespace urn:pp1;prefix pp1; import pp {prefix pp;}" + "deviation /pp:c/pp:x {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Target of leafref \"l\" cannot be referenced because it is disabled.", "/pp:l", 0); + CHECK_LOG_CTX("Not found node \"x\" in path.", "/pp:l", 0); +} + +static void +test_when(void **state) +{ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " when \"/cont/lst[val='25']\";\n" + " type empty;\n" + " }\n" + " list lst {\n" + " key \"k\";\n" + " leaf k {\n" + " type uint8;\n" + " }\n" + " leaf val {\n" + " when /cont2;\n" + " type int32;\n" + " }\n" + " }\n" + " }\n" + " container cont2 {\n" + " presence \"a\";\n" + " when ../cont/l;\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "/a:cont/lst/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " when \"/cont/lst[val='25']\";\n" + " type empty;\n" + " }\n" + " list lst {\n" + " key \"k\";\n" + " leaf k {\n" + " type uint8;\n" + " }\n" + " leaf val {\n" + " when /cont2;\n" + " type int32;\n" + " }\n" + " }\n" + " }\n" + " container cont2 {\n" + " presence \"a\";\n" + " when ../cont/lst/val;\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "/a:cont/lst/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " leaf val {\n" + " type int64;\n" + " when \"../val='25'\";\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " grouping grp {\n" + " leaf val {\n" + " type int64;\n" + " }\n" + " }\n" + " uses grp {\n" + " when \"val='25'\";\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " augment /cont {\n" + " when \"val='25'\";\n" + " leaf val {\n" + " type int64;\n" + " }\n" + " }\n" + " container cont;\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:cont/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " augment /cont {\n" + " when \"aug-cont/aug-l\";\n" + " container aug-cont {\n" + " leaf aug-l {\n" + " type int64;\n" + " }\n" + " }\n" + " }\n" + " container cont;\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node children.", "/a:cont/aug-cont", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container c {\n" + " list l {\n" + " key name;\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " container cond-data {\n" + " when \"/c/l2[name = current()/../name]/val = 'value'\";\n" + " leaf cond-leaf {\n" + " type string;\n" + " default \"default_val\";\n" + " }\n" + " }\n" + " }\n" + " list l2 {\n" + " key name;\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " container c2 {\n" + " leaf val {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module c1 {" + " namespace urn:c1;" + " prefix c1;" + " container my-container {" + " leaf my-type {" + " type string;" + " }" + " }" + "}\n" + "module c2 {" + " namespace \"urn:c2\";" + " prefix c2;" + " grouping my-group {" + " leaf my-leaf {" + " type string;" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module c3 {" + " namespace \"urn:c3\";" + " prefix c3;" + " import c1 {" + " prefix c1;" + " }" + " import c2 {" + " prefix c2;" + " }" + " augment \"/c1:my-container\" {" + " uses c2:my-group {" + " when \"./c1:my-type = '42'\";" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module d1 {" + " namespace urn:d1;" + " prefix d1;" + " container ifm {" + " container interfaces {" + " list interface {" + " key \"name\";" + " leaf name {" + " type string;" + " }" + " container ethernet {" + " container main-interface {" + " container l2-attribute {" + " when \"not(/d1:ifm/d1:interfaces/d1:interface/d1:trunk/d1:members/d1:member[d1:name=current()/../../../d1:name])\";" + " presence \"\";" + " }" + " }" + " }" + " container trunk {" + " container members {" + " list member {" + " key \"name\";" + " leaf name {" + " type string;" + " }" + " }" + " }" + " }" + " }" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module d2 {" + " namespace \"urn:d2\";" + " prefix d2;" + " import d1 {" + " prefix d1;" + " }" + " augment \"/d1:ifm/d1:interfaces/d1:interface/d1:ethernet/d1:main-interface\" {" + " when \"not(d1:l2-attribute)\";" + " container extra-attribute {" + " presence \"\";" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); +} + +static void +test_must(void **state) +{ + /* "*" must not be restricted to any module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module a {" + " namespace urn:a;" + " prefix a;" + " container cont {" + " leaf l {" + " type empty;" + " }" + " list lst {" + " key \"k\";" + " leaf k {" + " type uint8;" + " }" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module a-aug {" + " namespace urn:aa;" + " prefix aa;" + " import a {" + " prefix a;" + " }" + " augment /a:cont {" + " container cont2 {" + " must \"/a:cont/*/a:k\";" + " leaf aug {" + " type empty;" + " }" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); + /* no warnings */ + CHECK_LOG_CTX(NULL, NULL, 0); + + /* must referencing disabled leafref in another module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module b-imp {" + " yang-version 1.1;" + " namespace \"urn:b-imp\";" + " prefix \"bi\";" + "" + " feature feat;" + "" + " grouping band-capabilities {" + " leaf band-number {" + " type uint16;" + " }" + "" + " container sub-band-info {" + " when \"../band-number = '46'\";" + " if-feature \"bi:feat\";" + " leaf number-of-laa-scarriers {" + " type uint8;" + " }" + " }" + " }" + "" + " container module-capability {" + " list band-capabilities {" + " key band-number;" + " config false;" + " uses band-capabilities;" + " }" + " container rw-sub-band-info {" + " if-feature \"bi:feat\";" + " leaf rw-number-of-laa-scarriers {" + " type leafref {" + " path \"/module-capability/band-capabilities/sub-band-info/number-of-laa-scarriers\";" + " require-instance false;" + " }" + " }" + " }" + " }" + "}"); + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module b {" + " yang-version 1.1;" + " namespace \"urn:b\";" + " prefix \"b\";" + "" + " import b-imp {" + " prefix \"bi\";" + " }" + "" + " container laa-config {" + " must \"number-of-laa-scarriers <= /bi:module-capability/bi:rw-sub-band-info/bi:rw-number-of-laa-scarriers\";" + " }" + "}", + LYS_IN_YANG, NULL)); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + + CHECK_LOG_CTX("Schema node \"number-of-laa-scarriers\" not found; in expr \"number-of-laa-scarriers\" " + "with context node \"/b:laa-config\".", NULL, 0); +} + +static void +test_unique_disabled(void **state) +{ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module list-unique {" + " namespace urn:lu;" + " prefix lu;" + " feature f;" + " list l {" + " key \"k\";" + " unique \"v\";" + " leaf k {" + " type string;" + " }" + " leaf v {" + " if-feature f;" + " type string;" + " }" + " }" + " list l2 {" + " key \"k\";" + " unique \"v1 v2\";" + " leaf k {" + " type string;" + " }" + " leaf v1 {" + " if-feature f;" + " type string;" + " }" + " leaf v2 {" + " type string;" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_module, setup), + UTEST(test_submodule, setup), + UTEST(test_name_collisions, setup), + UTEST(test_type_length, setup), + UTEST(test_type_range, setup), + UTEST(test_type_pattern, setup), + UTEST(test_type_enum, setup), + UTEST(test_type_dec64, setup), + UTEST(test_type_instanceid, setup), + UTEST(test_identity, setup), + UTEST(test_type_identityref, setup), + UTEST(test_type_leafref, setup), + UTEST(test_type_empty, setup), + UTEST(test_type_union, setup), + UTEST(test_type_dflt, setup), + UTEST(test_type_exts, setup), + UTEST(test_status, setup), + UTEST(test_node_container, setup), + UTEST(test_node_leaflist, setup), + UTEST(test_node_list, setup), + UTEST(test_node_choice, setup), + UTEST(test_node_anydata, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_grouping, setup), + UTEST(test_uses, setup), + UTEST(test_refine, setup), + UTEST(test_augment, setup), + UTEST(test_deviation, setup), + UTEST(test_when, setup), + UTEST(test_must, setup), + UTEST(test_unique_disabled, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_yang.c b/tests/utests/schema/test_yang.c new file mode 100644 index 0000000..67f9747 --- /dev/null +++ b/tests/utests/schema/test_yang.c @@ -0,0 +1,1758 @@ +/** + * @file test_yang.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for YANG module parser and printer + * + * Copyright (c) 2018 - 2022 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 <stdio.h> +#include <string.h> + +#include "in_internal.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "schema_compile.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_free.h" + +/* originally static functions from parser_yang.c and parser_yin.c */ +LY_ERR buf_add_char(struct ly_ctx *ctx, struct ly_in *in, size_t len, char **buf, size_t *buf_len, size_t *buf_used); +LY_ERR buf_store_char(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, + size_t *word_len, char **word_b, size_t *buf_len, uint8_t need_buf, uint8_t *prefix); +LY_ERR get_keyword(struct lysp_yang_ctx *ctx, enum ly_stmt *kw, char **word_p, size_t *word_len); +LY_ERR get_argument(struct lysp_yang_ctx *ctx, enum yang_arg arg, + uint16_t *flags, char **word_p, char **word_b, size_t *word_len); +LY_ERR skip_comment(struct lysp_yang_ctx *ctx, uint8_t comment); + +LY_ERR parse_action(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_action **actions); +LY_ERR parse_any(struct lysp_yang_ctx *ctx, enum ly_stmt kw, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_augment(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_augment **augments); +LY_ERR parse_case(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_container(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates); +LY_ERR parse_deviation(struct lysp_yang_ctx *ctx, struct lysp_deviation **deviations); +LY_ERR parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_grp **groupings); +LY_ERR parse_choice(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_leaf(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_leaflist(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_list(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts); +LY_ERR parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts); +LY_ERR parse_module(struct lysp_yang_ctx *ctx, struct lysp_module *mod); +LY_ERR parse_notif(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_notif **notifs); +LY_ERR parse_submodule(struct lysp_yang_ctx *ctx, struct lysp_submodule *submod); +LY_ERR parse_uses(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_when(struct lysp_yang_ctx *ctx, struct lysp_when **when_p); +LY_ERR parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct lysp_type_enum *enm); + +struct lysp_yang_ctx *YCTX; +struct lysf_ctx fctx; + +struct ly_in in = {0}; + +#define YCTX_INIT \ + in.line = 1; \ + YCTX->in = ∈ \ + LOG_LOCINIT(UTEST_LYCTX, NULL, NULL, NULL, &in) + +static int +setup(void **state) +{ + struct lysp_module *pmod; + + UTEST_SETUP; + + /* allocate parser context */ + YCTX = calloc(1, sizeof(*YCTX)); + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + YCTX->format = LYS_IN_YANG; + ly_set_new(&YCTX->parsed_mods); + + /* allocate new parsed module */ + pmod = calloc(1, sizeof *pmod); + ly_set_add(YCTX->parsed_mods, pmod, 1, NULL); + + /* allocate new module */ + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->ctx = UTEST_LYCTX; + pmod->mod->parsed = pmod; + + /* initilize and use the global easily available and customizable input handler */ + in.line = 1; + YCTX->in = ∈ + ly_log_location(NULL, NULL, NULL, &in); + + fctx.ctx = PARSER_CTX(YCTX); + fctx.mod = pmod->mod; + + return 0; +} + +static int +teardown(void **state) +{ + lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0); + ly_log_location_revert(0, 0, 0, 1); + + ly_set_free(YCTX->parsed_mods, NULL); + ly_set_erase(&YCTX->ext_inst, NULL); + free(YCTX); + YCTX = NULL; + + lysf_ctx_erase(&fctx); + + UTEST_TEARDOWN; + + return 0; +} + +#define TEST_DUP_GENERIC(PREFIX, MEMBER, VALUE1, VALUE2, FUNC, RESULT, LINE, CLEANUP) \ + in.current = PREFIX MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, FUNC(YCTX, RESULT)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, LINE);\ + CLEANUP +static void +test_helpers(void **state) +{ + char *buf, *p; + size_t len, size; + uint8_t prefix = 0; + + /* storing into buffer */ + in.current = "abcd"; + buf = NULL; + size = len = 0; + assert_int_equal(LY_SUCCESS, buf_add_char(NULL, &in, 2, &buf, &size, &len)); + assert_int_not_equal(0, size); + assert_int_equal(2, len); + assert_string_equal("cd", in.current); + assert_false(strncmp("ab", buf, 2)); + free(buf); + buf = NULL; + + /* invalid first characters */ + len = 0; + in.current = "2invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = ".invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = "-invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + /* invalid following characters */ + len = 3; /* number of characters read before the str content */ + in.current = "!"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = ":"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + UTEST_LOG_CTX_CLEAN; + /* valid colon for prefixed identifiers */ + len = size = 0; + p = NULL; + prefix = 0; + in.current = "x:id"; + assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 0, &prefix)); + assert_int_equal(1, len); + assert_null(buf); + assert_string_equal(":id", in.current); + assert_int_equal('x', p[len - 1]); + assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + assert_int_equal(2, len); + assert_string_equal("id", in.current); + assert_int_equal(':', p[len - 1]); + free(buf); + prefix = 0; + + /* checking identifiers */ + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, NULL)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, '#', 1, NULL)); + CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", NULL, 1); + + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'a', 1, &prefix)); + assert_int_equal(0, prefix); + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + assert_int_equal(1, prefix); + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + CHECK_LOG_CTX("Invalid identifier first character ':' (0x003a).", NULL, 1); + assert_int_equal(1, prefix); + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'b', 0, &prefix)); + assert_int_equal(2, prefix); + /* second colon is invalid */ + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); +} + +#define TEST_GET_ARGUMENT_SUCCESS(INPUT_TEXT, CTX, ARG_TYPE, EXPECT_WORD, EXPECT_LEN, EXPECT_CURRENT, EXPECT_LINE)\ + {\ + const char * text = INPUT_TEXT;\ + in.line = 1;\ + in.current = text;\ + assert_int_equal(LY_SUCCESS, get_argument(CTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len));\ + assert_string_equal(word, EXPECT_WORD);\ + assert_int_equal(len, EXPECT_LEN);\ + assert_string_equal(EXPECT_CURRENT, in.current);\ + assert_int_equal(EXPECT_LINE, in.line);\ + } + +static void +test_comments(void **state) +{ + char *word, *buf; + size_t len; + + TEST_GET_ARGUMENT_SUCCESS(" // this is a text of / one * line */ comment\nargument;", + YCTX, Y_STR_ARG, "argument;", 8, ";", 2); + assert_null(buf); + + TEST_GET_ARGUMENT_SUCCESS("/* this is a \n * text // of / block * comment */\"arg\" + \"ume\" \n + \n \"nt\";", + YCTX, Y_STR_ARG, "argument", 8, ";", 4); + assert_ptr_equal(buf, word); + free(word); + + in.line = 1; + in.current = " this is one line comment on last line"; + assert_int_equal(LY_SUCCESS, skip_comment(YCTX, 1)); + assert_true(in.current[0] == '\0'); + + in.line = 1; + in.current = " this is a not terminated comment x"; + assert_int_equal(LY_EVALID, skip_comment(YCTX, 2)); + CHECK_LOG_CTX("Unexpected end-of-input, non-terminated comment.", NULL, 1); + assert_true(in.current[0] == '\0'); +} + +static void +test_arg(void **state) +{ + char *word, *buf; + size_t len; + + /* missing argument */ + in.current = ";"; + assert_int_equal(LY_SUCCESS, get_argument(YCTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len)); + assert_null(word); + + in.current = "{"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"{\", expected an argument.", NULL, 1); + + /* invalid escape sequence */ + in.current = "\"\\s\""; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Double-quoted string unknown special character \'\\s\'.", NULL, 1); + + TEST_GET_ARGUMENT_SUCCESS("\'\\s\'", YCTX, Y_STR_ARG, "\\s\'", 2, "", 1); + + /* invalid character after the argument */ + in.current = "hello\""; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"\"\", expected unquoted string character, optsep, semicolon or opening brace.", NULL, 1); + + in.current = "hello}"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"}\", expected unquoted string character, optsep, semicolon or opening brace.", NULL, 1); + /* invalid identifier-ref-arg-str */ + in.current = "pre:pre:value"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + + in.current = "\"\";"; /* empty identifier is not allowed */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Statement argument is required.", NULL, 1); + + in.current = "\"\";"; /* empty reference identifier is not allowed */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Statement argument is required.", NULL, 1); + + /* slash is not an invalid character */ + TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1); + assert_null(buf); + + /* different quoting */ + TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1); + + TEST_GET_ARGUMENT_SUCCESS("hello ", YCTX, Y_STR_ARG, "hello ", 5, " ", 1); + + TEST_GET_ARGUMENT_SUCCESS("hello/*comment*/\n", YCTX, Y_STR_ARG, "hello/*comment*/\n", 5, "\n", 1); + + TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\\t\\\"\\\\\";", YCTX, Y_STR_ARG, "hello\n\t\"\\", 9, ";", 1); + free(buf); + + YCTX->indent = 14; + /* - space and tabs before newline are stripped out + * - space and tabs after newline (indentation) are stripped out + */ + TEST_GET_ARGUMENT_SUCCESS("\"hello \t\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n world!", 14, "", 2); + free(buf); + +/* In contrast to previous, the backslash-escaped tabs are expanded after trimming, so they are preserved */ + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello \\t\n\t\\t world!\"", YCTX, Y_STR_ARG, "hello \t\n\t world!", 16, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + /* Do not handle whitespaces after backslash-escaped newline as indentation */ + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n\t\t world!", 15, "", 1); + assert_ptr_equal(word, buf); + free(buf); + + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello\n \tworld!\"", YCTX, Y_STR_ARG, "hello\nworld!", 12, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + TEST_GET_ARGUMENT_SUCCESS("\'hello\'", YCTX, Y_STR_ARG, "hello'", 5, "", 1); + + TEST_GET_ARGUMENT_SUCCESS("\"hel\" +\t\n\"lo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + in.line = 1; + in.current = "\"hel\" +\t\nlo"; /* unquoted the second part */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Both string parts divided by '+' must be quoted.", NULL, 2); + + TEST_GET_ARGUMENT_SUCCESS("\'he\'\t\n+ \"llo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2); + free(buf); + + TEST_GET_ARGUMENT_SUCCESS(" \t\n\"he\"+\'llo\'", YCTX, Y_STR_ARG, "hello", 5, "", 2); + free(buf); + + /* missing argument */ + in.line = 1; + in.current = ";"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \";\", expected an argument.", NULL, 1); +} + +#define TEST_STMS_SUCCESS(INPUT_TEXT, CTX, ACTION, EXPECT_WORD)\ + in.current = INPUT_TEXT;\ + assert_int_equal(LY_SUCCESS, get_keyword(CTX, &kw, &word, &len));\ + assert_int_equal(ACTION, kw);\ + assert_int_equal(strlen(EXPECT_WORD), len);\ + assert_true(0 == strncmp(EXPECT_WORD, word, len)) + +static void +test_stmts(void **state) +{ + const char *p; + enum ly_stmt kw; + char *word; + size_t len; + + in.current = "\n// comment\n\tinput\t{"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_INPUT, kw); + assert_int_equal(5, len); + assert_string_equal("input\t{", word); + assert_string_equal("\t{", in.current); + + in.current = "\t /* comment */\t output\n\t{"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_OUTPUT, kw); + assert_int_equal(6, len); + assert_string_equal("output\n\t{", word); + assert_string_equal("\n\t{", in.current); + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_SYNTAX_LEFT_BRACE, kw); + assert_int_equal(1, len); + assert_string_equal("{", word); + assert_string_equal("", in.current); + + in.current = "/input { "; /* invalid slash */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid identifier first character '/'.", NULL, 4); + + in.current = "not-a-statement-nor-extension { "; /* invalid identifier */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid character sequence \"not-a-statement-nor-extension\", expected a keyword.", NULL, 4); + + in.current = "path;"; /* missing sep after the keyword */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid character sequence \"path;\", expected a keyword followed by a separator.", NULL, 4); + + TEST_STMS_SUCCESS("action ", YCTX, LY_STMT_ACTION, "action"); + + TEST_STMS_SUCCESS("anydata ", YCTX, LY_STMT_ANYDATA, "anydata"); + TEST_STMS_SUCCESS("anyxml ", YCTX, LY_STMT_ANYXML, "anyxml"); + TEST_STMS_SUCCESS("argument ", YCTX, LY_STMT_ARGUMENT, "argument"); + TEST_STMS_SUCCESS("augment ", YCTX, LY_STMT_AUGMENT, "augment"); + TEST_STMS_SUCCESS("base ", YCTX, LY_STMT_BASE, "base"); + TEST_STMS_SUCCESS("belongs-to ", YCTX, LY_STMT_BELONGS_TO, "belongs-to"); + TEST_STMS_SUCCESS("bit ", YCTX, LY_STMT_BIT, "bit"); + TEST_STMS_SUCCESS("case ", YCTX, LY_STMT_CASE, "case"); + TEST_STMS_SUCCESS("choice ", YCTX, LY_STMT_CHOICE, "choice"); + TEST_STMS_SUCCESS("config ", YCTX, LY_STMT_CONFIG, "config"); + TEST_STMS_SUCCESS("contact ", YCTX, LY_STMT_CONTACT, "contact"); + TEST_STMS_SUCCESS("container ", YCTX, LY_STMT_CONTAINER, "container"); + TEST_STMS_SUCCESS("default ", YCTX, LY_STMT_DEFAULT, "default"); + TEST_STMS_SUCCESS("description ", YCTX, LY_STMT_DESCRIPTION, "description"); + TEST_STMS_SUCCESS("deviate ", YCTX, LY_STMT_DEVIATE, "deviate"); + TEST_STMS_SUCCESS("deviation ", YCTX, LY_STMT_DEVIATION, "deviation"); + TEST_STMS_SUCCESS("enum ", YCTX, LY_STMT_ENUM, "enum"); + TEST_STMS_SUCCESS("error-app-tag ", YCTX, LY_STMT_ERROR_APP_TAG, "error-app-tag"); + TEST_STMS_SUCCESS("error-message ", YCTX, LY_STMT_ERROR_MESSAGE, "error-message"); + TEST_STMS_SUCCESS("extension ", YCTX, LY_STMT_EXTENSION, "extension"); + TEST_STMS_SUCCESS("feature ", YCTX, LY_STMT_FEATURE, "feature"); + TEST_STMS_SUCCESS("fraction-digits ", YCTX, LY_STMT_FRACTION_DIGITS, "fraction-digits"); + TEST_STMS_SUCCESS("grouping ", YCTX, LY_STMT_GROUPING, "grouping"); + TEST_STMS_SUCCESS("identity ", YCTX, LY_STMT_IDENTITY, "identity"); + TEST_STMS_SUCCESS("if-feature ", YCTX, LY_STMT_IF_FEATURE, "if-feature"); + TEST_STMS_SUCCESS("import ", YCTX, LY_STMT_IMPORT, "import"); + TEST_STMS_SUCCESS("include ", YCTX, LY_STMT_INCLUDE, "include"); + TEST_STMS_SUCCESS("input{", YCTX, LY_STMT_INPUT, "input"); + TEST_STMS_SUCCESS("key ", YCTX, LY_STMT_KEY, "key"); + TEST_STMS_SUCCESS("leaf ", YCTX, LY_STMT_LEAF, "leaf"); + TEST_STMS_SUCCESS("leaf-list ", YCTX, LY_STMT_LEAF_LIST, "leaf-list"); + TEST_STMS_SUCCESS("length ", YCTX, LY_STMT_LENGTH, "length"); + TEST_STMS_SUCCESS("list ", YCTX, LY_STMT_LIST, "list"); + TEST_STMS_SUCCESS("mandatory ", YCTX, LY_STMT_MANDATORY, "mandatory"); + TEST_STMS_SUCCESS("max-elements ", YCTX, LY_STMT_MAX_ELEMENTS, "max-elements"); + TEST_STMS_SUCCESS("min-elements ", YCTX, LY_STMT_MIN_ELEMENTS, "min-elements"); + TEST_STMS_SUCCESS("modifier ", YCTX, LY_STMT_MODIFIER, "modifier"); + TEST_STMS_SUCCESS("module ", YCTX, LY_STMT_MODULE, "module"); + TEST_STMS_SUCCESS("must ", YCTX, LY_STMT_MUST, "must"); + TEST_STMS_SUCCESS("namespace ", YCTX, LY_STMT_NAMESPACE, "namespace"); + TEST_STMS_SUCCESS("notification ", YCTX, LY_STMT_NOTIFICATION, "notification"); + TEST_STMS_SUCCESS("ordered-by ", YCTX, LY_STMT_ORDERED_BY, "ordered-by"); + TEST_STMS_SUCCESS("organization ", YCTX, LY_STMT_ORGANIZATION, "organization"); + TEST_STMS_SUCCESS("output ", YCTX, LY_STMT_OUTPUT, "output"); + TEST_STMS_SUCCESS("path ", YCTX, LY_STMT_PATH, "path"); + TEST_STMS_SUCCESS("pattern ", YCTX, LY_STMT_PATTERN, "pattern"); + TEST_STMS_SUCCESS("position ", YCTX, LY_STMT_POSITION, "position"); + TEST_STMS_SUCCESS("prefix ", YCTX, LY_STMT_PREFIX, "prefix"); + TEST_STMS_SUCCESS("presence ", YCTX, LY_STMT_PRESENCE, "presence"); + TEST_STMS_SUCCESS("range ", YCTX, LY_STMT_RANGE, "range"); + TEST_STMS_SUCCESS("reference ", YCTX, LY_STMT_REFERENCE, "reference"); + TEST_STMS_SUCCESS("refine ", YCTX, LY_STMT_REFINE, "refine"); + TEST_STMS_SUCCESS("require-instance ", YCTX, LY_STMT_REQUIRE_INSTANCE, "require-instance"); + TEST_STMS_SUCCESS("revision ", YCTX, LY_STMT_REVISION, "revision"); + TEST_STMS_SUCCESS("revision-date ", YCTX, LY_STMT_REVISION_DATE, "revision-date"); + TEST_STMS_SUCCESS("rpc ", YCTX, LY_STMT_RPC, "rpc"); + TEST_STMS_SUCCESS("status ", YCTX, LY_STMT_STATUS, "status"); + TEST_STMS_SUCCESS("submodule ", YCTX, LY_STMT_SUBMODULE, "submodule"); + TEST_STMS_SUCCESS("type ", YCTX, LY_STMT_TYPE, "type"); + TEST_STMS_SUCCESS("typedef ", YCTX, LY_STMT_TYPEDEF, "typedef"); + TEST_STMS_SUCCESS("unique ", YCTX, LY_STMT_UNIQUE, "unique"); + TEST_STMS_SUCCESS("units ", YCTX, LY_STMT_UNITS, "units"); + TEST_STMS_SUCCESS("uses ", YCTX, LY_STMT_USES, "uses"); + TEST_STMS_SUCCESS("value ", YCTX, LY_STMT_VALUE, "value"); + TEST_STMS_SUCCESS("when ", YCTX, LY_STMT_WHEN, "when"); + TEST_STMS_SUCCESS("yang-version ", YCTX, LY_STMT_YANG_VERSION, "yang-version"); + TEST_STMS_SUCCESS("yin-element ", YCTX, LY_STMT_YIN_ELEMENT, "yin-element"); + TEST_STMS_SUCCESS(";config false;", YCTX, LY_STMT_SYNTAX_SEMICOLON, ";"); + assert_string_equal("config false;", in.current); + TEST_STMS_SUCCESS("{ config false;", YCTX, LY_STMT_SYNTAX_LEFT_BRACE, "{"); + assert_string_equal(" config false;", in.current); + TEST_STMS_SUCCESS("}", YCTX, LY_STMT_SYNTAX_RIGHT_BRACE, "}"); + assert_string_equal("", in.current); + + /* geenric extension */ + in.current = p = "nacm:default-deny-write;"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_EXTENSION_INSTANCE, kw); + assert_int_equal(23, len); + assert_ptr_equal(p, word); +} + +static void +test_minmax(void **state) +{ + uint16_t flags = 0; + uint32_t value = 0; + struct lysp_ext_instance *ext = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + in.current = " 1invalid; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"1invalid\" of \"min-elements\".", NULL, 1); + + flags = value = 0; + in.current = " -1; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"-1\" of \"min-elements\".", NULL, 1); + + /* implementation limit */ + flags = value = 0; + in.current = " 4294967296; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Value \"4294967296\" is out of \"min-elements\" bounds.", NULL, 1); + + flags = value = 0; + in.current = " 1 {config true;} ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"min-elements\".", NULL, 1); + + in.current = " 1invalid; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"1invalid\" of \"max-elements\".", NULL, 1); + + flags = value = 0; + in.current = " -1; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"-1\" of \"max-elements\".", NULL, 1); + + /* implementation limit */ + flags = value = 0; + in.current = " 4294967296; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Value \"4294967296\" is out of \"max-elements\" bounds.", NULL, 1); + + flags = value = 0; + in.current = " 1 {config true;} ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"max-elements\".", NULL, 1); +} + +static void +test_valid_module(void **state) +{ + struct lys_module *mod; + char *printed; + const char *links_yang = + "module links {\n" + " yang-version 1.1;\n" + " namespace \"urn:module2\";\n" + " prefix mod2;\n" + "\n" + " identity just-another-identity;\n" + "\n" + " leaf one-leaf {\n" + " type string;\n" + " }\n" + "\n" + " list list-for-augment {\n" + " key keyleaf;\n" + "\n" + " leaf keyleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf just-leaf {\n" + " type int32;\n" + " }\n" + " }\n" + "\n" + " leaf rleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf-list llist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 100;\n" + " ordered-by user;\n" + " }\n" + "\n" + " grouping rgroup {\n" + " leaf rg1 {\n" + " type string;\n" + " }\n" + "\n" + " leaf rg2 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + const char *statements_yang = + "module statements {\n" + " yang-version 1.1;\n" + " namespace \"urn:module\";\n" + " prefix mod;\n" + "\n" + " import links {\n" + " prefix mod2;\n" + " }\n" + "\n" + " extension ext;\n" + "\n" + " identity random-identity {\n" + " base mod2:just-another-identity;\n" + " base another-identity;\n" + " }\n" + "\n" + " identity another-identity {\n" + " base mod2:just-another-identity;\n" + " }\n" + "\n" + " typedef percent {\n" + " type uint8 {\n" + " range \"0 .. 100\";\n" + " }\n" + " units \"percent\";\n" + " }\n" + "\n" + " list list1 {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf x {\n" + " type string;\n" + " }\n" + " leaf y {\n" + " type string;\n" + " }\n" + " }\n" + " container ice-cream-shop {\n" + " container employees {\n" + " when \"/list1/x\";\n" + " list employee {\n" + " key \"id\";\n" + " unique \"name\";\n" + " config true;\n" + " min-elements 0 {\n" + " mod:ext;\n" + " }\n" + " max-elements unbounded;\n" + " leaf id {\n" + " type uint64;\n" + " mandatory true;\n" + " }\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf age {\n" + " type uint32;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " container random {\n" + " grouping group {\n" + " leaf g1 {\n" + " type percent;\n" + " mandatory false;\n" + " }\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " }\n" + " choice switch {\n" + " case a {\n" + " leaf aleaf {\n" + " type string;\n" + " default \"aaa\";\n" + " }\n" + " }\n" + " case c {\n" + " leaf cleaf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " anyxml xml-data;\n" + " anydata any-data;\n" + " leaf-list leaflist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 20;\n" + " }\n" + " uses group;\n" + " uses mod2:rgroup;\n" + " leaf lref {\n" + " type leafref {\n" + " path \"/mod2:one-leaf\";\n" + " }\n" + " }\n" + " leaf iref {\n" + " type identityref {\n" + " base mod2:just-another-identity;\n" + " }\n" + " }\n" + " }\n" + "\n" + " augment \"/random\" {\n" + " leaf aug-leaf {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " notification notif;\n" + "\n" + " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\" {\n" + " deviate not-supported {\n" + " mod:ext;\n" + " }\n" + " }\n" + " deviation \"/mod:list1\" {\n" + " deviate add {\n" + " mod:ext;\n" + " must \"1\";\n" + " must \"2\";\n" + " unique \"x\";\n" + " unique \"y\";\n" + " config true;\n" + " min-elements 1;\n" + " max-elements 2;\n" + " }\n" + " }\n" + " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee\" {\n" + " deviate delete {\n" + " unique \"name\";\n" + " }\n" + " }\n" + " deviation \"/mod:random/mod:leaflist\" {\n" + " deviate replace {\n" + " type uint32;\n" + " min-elements 10;\n" + " max-elements 15;\n" + " }\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, &mod); + lys_print_mem(&printed, mod, LYS_OUT_YANG, 0); + assert_string_equal(printed, statements_yang); + free(printed); +} + +static struct lysp_module * +mod_renew(struct lysp_yang_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_module *pmod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + pmod = calloc(1, sizeof *pmod); + ctx->parsed_mods->objs[0] = pmod; + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->parsed = pmod; + pmod->mod->ctx = ly_ctx; + + ctx->in->line = 1; + fctx.mod = pmod->mod; + + return pmod; +} + +static struct lysp_submodule * +submod_renew(struct lysp_yang_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_submodule *submod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + submod = calloc(1, sizeof *submod); + ctx->parsed_mods->objs[0] = submod; + submod->mod = calloc(1, sizeof *submod->mod); + lydict_insert(ly_ctx, "name", 0, &submod->mod->name); + submod->mod->parsed = (struct lysp_module *)submod; + submod->mod->ctx = ly_ctx; + + fctx.mod = submod->mod; + + return submod; +} + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_module(void **state) +{ + struct lysp_module *mod = NULL; + struct lysp_submodule *submod = NULL; + struct lys_module *m; + struct lysp_yang_ctx *ctx_p; + + mod = mod_renew(YCTX); + + /* missing mandatory substatements */ + in.current = " name {}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + assert_string_equal("name", mod->mod->name); + CHECK_LOG_CTX("Missing mandatory keyword \"namespace\" as a child of \"module\".", NULL, 1); + + mod = mod_renew(YCTX); + in.current = " name {namespace urn:name;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + assert_string_equal("urn:name", mod->mod->ns); + CHECK_LOG_CTX("Missing mandatory keyword \"prefix\" as a child of \"module\".", NULL, 1); + mod = mod_renew(YCTX); + + in.current = " name {namespace urn:name;prefix \"n\";}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_string_equal("n", mod->mod->prefix); + mod = mod_renew(YCTX); + +#define SCHEMA_BEGINNING " name {yang-version 1.1;namespace urn:x;prefix \"x\";" +#define SCHEMA_BEGINNING2 " name {namespace urn:x;prefix \"x\";" +#define TEST_NODE(NODETYPE, INPUT, NAME) \ + in.current = SCHEMA_BEGINNING INPUT; \ + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \ + assert_non_null(mod->data); \ + assert_int_equal(NODETYPE, mod->data->nodetype); \ + assert_string_equal(NAME, mod->data->name); \ + mod = mod_renew(YCTX); +#define TEST_GENERIC(INPUT, TARGET, TEST) \ + in.current = SCHEMA_BEGINNING INPUT; \ + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \ + assert_non_null(TARGET); \ + TEST; \ + mod = mod_renew(YCTX); +#define TEST_DUP(MEMBER, VALUE1, VALUE2, LINE) \ + TEST_DUP_GENERIC(SCHEMA_BEGINNING, MEMBER, VALUE1, VALUE2, \ + parse_module, mod, LINE, mod = mod_renew(YCTX)) + + /* duplicated namespace, prefix */ + TEST_DUP("namespace", "y", "z", 1); + TEST_DUP("prefix", "y", "z", 1); + TEST_DUP("contact", "a", "b", 1); + TEST_DUP("description", "a", "b", 1); + TEST_DUP("organization", "a", "b", 1); + TEST_DUP("reference", "a", "b", 1); + + /* not allowed in module (submodule-specific) */ + in.current = SCHEMA_BEGINNING "belongs-to master {prefix m;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid keyword \"belongs-to\" as a child of \"module\".", NULL, 1); + mod = mod_renew(YCTX); + + /* anydata */ + TEST_NODE(LYS_ANYDATA, "anydata test;}", "test"); + /* anyxml */ + TEST_NODE(LYS_ANYXML, "anyxml test;}", "test"); + /* augment */ + TEST_GENERIC("augment /somepath;}", mod->augments, + assert_string_equal("/somepath", mod->augments[0].nodeid)); + /* choice */ + TEST_NODE(LYS_CHOICE, "choice test;}", "test"); + /* contact 0..1 */ + TEST_GENERIC("contact \"firstname\" + \n\t\" surname\";}", mod->mod->contact, + assert_string_equal("firstname surname", mod->mod->contact)); + /* container */ + TEST_NODE(LYS_CONTAINER, "container test;}", "test"); + /* description 0..1 */ + TEST_GENERIC("description \'some description\';}", mod->mod->dsc, + assert_string_equal("some description", mod->mod->dsc)); + /* deviation */ + TEST_GENERIC("deviation /somepath {deviate not-supported;}}", mod->deviations, + assert_string_equal("/somepath", mod->deviations[0].nodeid)); + /* extension */ + TEST_GENERIC("extension test;}", mod->extensions, + assert_string_equal("test", mod->extensions[0].name)); + /* feature */ + TEST_GENERIC("feature test;}", mod->features, + assert_string_equal("test", mod->features[0].name)); + /* grouping */ + TEST_GENERIC("grouping grp;}", mod->groupings, + assert_string_equal("grp", mod->groupings[0].name)); + /* identity */ + TEST_GENERIC("identity test;}", mod->identities, + assert_string_equal("test", mod->identities[0].name)); + /* import */ + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module zzz { namespace urn:zzz; prefix z;}"); + TEST_GENERIC("import zzz {prefix z;}}", mod->imports, + assert_string_equal("zzz", mod->imports[0].name)); + + /* import - prefix collision */ + in.current = SCHEMA_BEGINNING "import zzz {prefix x;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Prefix \"x\" already used as module prefix.", NULL, 1); + mod = mod_renew(YCTX); + + in.current = SCHEMA_BEGINNING "import zzz {prefix y;}import zzz {prefix y;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Prefix \"y\" already used to import \"zzz\" module.", NULL, 1); + + mod = mod_renew(YCTX); + ly_log_location_revert(0, 0, 0, 1); + + in.current = "module name10 {yang-version 1.1;namespace urn:name10;prefix \"n10\";import zzz {prefix y;}import zzz {prefix z;}}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Single revision of the module \"zzz\" imported twice.", NULL, 0); + + /* include */ + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module xxx { namespace urn:xxx; prefix x;}"); + in.current = "module" SCHEMA_BEGINNING "include xxx;}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"xxx\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0); + CHECK_LOG_CTX("Input data contains module in situation when a submodule is expected.", NULL, 0); + + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to wrong-name {prefix w;}}"); + in.current = "module" SCHEMA_BEGINNING "include xxx;}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); + UTEST_LOG_CTX_CLEAN; + + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to name {prefix x;}}"); + TEST_GENERIC("include xxx;}", mod->includes, + assert_string_equal("xxx", mod->includes[0].name)); + + /* leaf */ + TEST_NODE(LYS_LEAF, "leaf test {type string;}}", "test"); + /* leaf-list */ + TEST_NODE(LYS_LEAFLIST, "leaf-list test {type string;}}", "test"); + /* list */ + TEST_NODE(LYS_LIST, "list test {key a;leaf a {type string;}}}", "test"); + /* notification */ + TEST_GENERIC("notification test;}", mod->notifs, + assert_string_equal("test", mod->notifs[0].name)); + /* organization 0..1 */ + TEST_GENERIC("organization \"CESNET a.l.e.\";}", mod->mod->org, + assert_string_equal("CESNET a.l.e.", mod->mod->org)); + /* reference 0..1 */ + TEST_GENERIC("reference RFC7950;}", mod->mod->ref, + assert_string_equal("RFC7950", mod->mod->ref)); + /* revision */ + TEST_GENERIC("revision 2018-10-12;}", mod->revs, + assert_string_equal("2018-10-12", mod->revs[0].date)); + /* rpc */ + TEST_GENERIC("rpc test;}", mod->rpcs, + assert_string_equal("test", mod->rpcs[0].name)); + /* typedef */ + TEST_GENERIC("typedef test{type string;}}", mod->typedefs, + assert_string_equal("test", mod->typedefs[0].name)); + /* uses */ + TEST_NODE(LYS_USES, "uses test;}", "test"); + /* yang-version */ + in.current = SCHEMA_BEGINNING2 "\n\tyang-version 10;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid value \"10\" of \"yang-version\".", NULL, 0); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version 1;yang-version 1.1;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Duplicate keyword \"yang-version\".", NULL, 0); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version 1;}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_int_equal(1, mod->version); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version \"1.1\";}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_int_equal(2, mod->version); + mod = mod_renew(YCTX); + + in.current = "module " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after module, expected end-of-input.", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + in.current = "prefix " SCHEMA_BEGINNING "}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + in.current = "module " SCHEMA_BEGINNING "leaf enum {type enumeration {enum seven { position 7;}}}}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Invalid keyword \"position\" as a child of \"enum\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + /* extensions */ + TEST_GENERIC("prefix:test;}", mod->exts, + assert_string_equal("prefix:test", mod->exts[0].name); + assert_int_equal(LY_STMT_MODULE, mod->exts[0].parent_stmt)); + mod = mod_renew(YCTX); + + /* invalid substatement */ + in.current = SCHEMA_BEGINNING "must false;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"module\".", NULL, 0); + + /* submodule */ + submod = submod_renew(YCTX); + + /* missing mandatory substatements */ + in.current = " subname {}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Missing mandatory keyword \"belongs-to\" as a child of \"submodule\".", NULL, 0); + assert_string_equal("subname", submod->name); + + submod = submod_renew(YCTX); + + in.current = " subname {belongs-to name {prefix x;}}"; + assert_int_equal(LY_SUCCESS, parse_submodule(YCTX, submod)); + assert_string_equal("name", submod->mod->name); + submod = submod_renew(YCTX); + +#undef SCHEMA_BEGINNING +#define SCHEMA_BEGINNING " subname {belongs-to name {prefix x;}" + + /* duplicated namespace, prefix */ + in.current = " subname {belongs-to name {prefix x;}belongs-to module1;belongs-to module2;} ..."; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Duplicate keyword \"belongs-to\".", NULL, 0); + submod = submod_renew(YCTX); + + /* not allowed in submodule (module-specific) */ + in.current = SCHEMA_BEGINNING "namespace \"urn:z\";}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Invalid keyword \"namespace\" as a child of \"submodule\".", NULL, 0); + submod = submod_renew(YCTX); + in.current = SCHEMA_BEGINNING "prefix m;}}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Invalid keyword \"prefix\" as a child of \"submodule\".", NULL, 0); + submod = submod_renew(YCTX); + + in.current = "submodule " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}"; + assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod)); + CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after submodule, expected end-of-input.", NULL, 1); + lysp_yang_ctx_free(ctx_p); + + in.current = "prefix " SCHEMA_BEGINNING "}"; + assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod)); + CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + submod = submod_renew(YCTX); + +#undef TEST_GENERIC +#undef TEST_NODE +#undef TEST_DUP +#undef SCHEMA_BEGINNING +} + +static void +test_deviation(void **state) +{ + struct lysp_deviation *d = NULL; + + /* invalid cardinality */ + TEST_DUP_GENERIC(" test {deviate not-supported;", "description", "a", "b", parse_deviation, &d, 1, ); + TEST_DUP_GENERIC(" test {deviate not-supported;", "reference", "a", "b", parse_deviation, &d, 1, ); + + /* missing mandatory substatement */ + in.current = " test {description text;}"; + assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d)); + CHECK_LOG_CTX("Missing mandatory keyword \"deviate\" as a child of \"deviation\".", NULL, 1); + + /* invalid substatement */ + in.current = " test {deviate not-supported; status obsolete;}"; + assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d)); + CHECK_LOG_CTX("Invalid keyword \"status\" as a child of \"deviation\".", NULL, 1); +} + +static void +test_deviate(void **state) +{ + struct lysp_deviate *d = NULL; + + /* invalid cardinality */ + TEST_DUP_GENERIC("add {", "config", "true", "false", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "mandatory", "true", "false", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "max-elements", "1", "2", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "min-elements", "1", "2", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "units", "kilometers", "miles", parse_deviate, &d, 1, ); + + /* invalid substatements */ +#define TEST_NOT_SUP(DEV, STMT, VALUE) \ + in.current = " "DEV" {"STMT" "VALUE";}..."; \ + assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d)); \ + CHECK_LOG_CTX("Deviate \""DEV"\" does not support keyword \""STMT"\".", NULL, 1); + + TEST_NOT_SUP("not-supported", "units", "meters"); + TEST_NOT_SUP("not-supported", "must", "1"); + TEST_NOT_SUP("not-supported", "unique", "x"); + TEST_NOT_SUP("not-supported", "default", "a"); + TEST_NOT_SUP("not-supported", "config", "true"); + TEST_NOT_SUP("not-supported", "mandatory", "true"); + TEST_NOT_SUP("not-supported", "min-elements", "1"); + TEST_NOT_SUP("not-supported", "max-elements", "2"); + TEST_NOT_SUP("not-supported", "type", "string"); + TEST_NOT_SUP("add", "type", "string"); + TEST_NOT_SUP("delete", "config", "true"); + TEST_NOT_SUP("delete", "mandatory", "true"); + TEST_NOT_SUP("delete", "min-elements", "1"); + TEST_NOT_SUP("delete", "max-elements", "2"); + TEST_NOT_SUP("delete", "type", "string"); + TEST_NOT_SUP("replace", "must", "1"); + TEST_NOT_SUP("replace", "unique", "a"); + + in.current = " nonsence; ..."; + assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d)); + CHECK_LOG_CTX("Invalid value \"nonsence\" of \"deviate\".", NULL, 1);\ + assert_null(d); +#undef TEST_NOT_SUP +} + +static void +test_container(void **state) +{ + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "cont {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node**)&c)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)c); c = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("presence", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "cont {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; leaf l {type string;}" + "leaf-list ll {type string;} list li;must 'expr';notification not; presence true; reference test;status current;typedef t {type int8;}uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LYSP_NODE(c, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "cont", 0, LYS_CONTAINER, 0, "test", 1); + assert_non_null(c->actions); + assert_non_null(c->child); + assert_non_null(c->groupings); + assert_non_null(c->musts); + assert_non_null(c->notifs); + assert_string_equal("true", c->presence); + assert_non_null(c->typedefs); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + + /* invalid */ + in.current = " cont {augment /root;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid keyword \"augment\" as a child of \"container\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + in.current = " cont {nonsence true;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid character sequence \"nonsence\", expected a keyword.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + in.current = " cont {action x;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - " + "the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; +} + +static void +test_leaf(void **state) +{ + struct lysp_node_leaf *l = NULL; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node**)&l)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("default", "x", "y"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("type", "int8", "uint8"); + TEST_DUP("units", "text1", "text2"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without mandatory which is mutual exclusive with default */ + in.current = "l {config false;default \"xxx\";description test;if-feature f;" + "must 'expr';reference test;status current;type string; units yyy;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "l", 0, LYS_LEAF, 0, "test", 1); + assert_string_equal("xxx", l->dflt.str); + assert_string_equal("yyy", l->units); + assert_string_equal("string", l->type.name); + assert_non_null(l->musts); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* full content - now with mandatory */ + in.current = "l {mandatory true; type string;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, NULL, 0, LYS_MAND_TRUE, 0, "l", 0, LYS_LEAF, 0, NULL, 0); + assert_string_equal("string", l->type.name); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* invalid */ + in.current = " l {description \"missing type\";} ..."; + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + in.current = "l { type iid { path qpud wrong {"; + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Invalid character sequence \"wrong\", expected a keyword.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; +} + +static void +test_leaflist(void **state) +{ + struct lysp_node_leaflist *ll = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "ll {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node**)&ll)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)ll); ll = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("max-elements", "10", "20"); + TEST_DUP("min-elements", "10", "20"); + TEST_DUP("ordered-by", "user", "system"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("type", "int8", "uint8"); + TEST_DUP("units", "text1", "text2"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without min-elements which is mutual exclusive with default */ + in.current = "ll {config false;default \"xxx\"; default \"yyy\";description test;if-feature f;" + "max-elements 10;must 'expr';ordered-by user;reference test;" + "status current;type string; units zzz;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LYSP_NODE(ll, "test", 1, 0x446, 1, "ll", 0, LYS_LEAFLIST, 0, "test", 1); + assert_non_null(ll->dflts); + assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("xxx", ll->dflts[0].str); + assert_string_equal("yyy", ll->dflts[1].str); + assert_string_equal("zzz", ll->units); + assert_int_equal(10, ll->max); + assert_int_equal(0, ll->min); + assert_string_equal("string", ll->type.name); + assert_non_null(ll->musts); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_MAX, ll->flags); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + /* full content - now with min-elements */ + in.current = "ll {min-elements 10; type string;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LYSP_NODE(ll, NULL, 0, 0x200, 0, "ll", 0, LYS_LEAFLIST, 0, NULL, 0); + assert_string_equal("string", ll->type.name); + assert_int_equal(0, ll->max); + assert_int_equal(10, ll->min); + assert_int_equal(LYS_SET_MIN, ll->flags); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + /* invalid */ + in.current = " ll {description \"missing type\";} ..."; + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf-list\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 - default statement is not allowed */ + in.current = " ll {default xx; type string;} ..."; + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"leaf-list\" - the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; +} + +static void +test_list(void **state) +{ + struct lysp_node_list *l = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node**)&l)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("key", "one", "two"); + TEST_DUP("max-elements", "10", "20"); + TEST_DUP("min-elements", "10", "20"); + TEST_DUP("ordered-by", "user", "system"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "l {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; key l; leaf l {type string;}" + "leaf-list ll {type string;} list li;max-elements 10; min-elements 1;must 'expr';notification not; ordered-by system; reference test;" + "status current;typedef t {type int8;}unique xxx;unique yyy;uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_list(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_MAX | LYS_SET_MIN, 1, "l", + 0, LYS_LIST, 0, "test", 1); + assert_string_equal("l", l->key); + assert_non_null(l->uniques); + assert_int_equal(2, LY_ARRAY_COUNT(l->uniques)); + assert_string_equal("xxx", l->uniques[0].str); + assert_string_equal("yyy", l->uniques[1].str); + assert_int_equal(10, l->max); + assert_int_equal(1, l->min); + assert_non_null(l->musts); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* invalid content */ + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + in.current = "l {action x;} ..."; + assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"list\" - the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; +} + +static void +test_choice(void **state) +{ + struct lysp_node_choice *ch = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "ch {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_choice(YCTX, NULL, (struct lysp_node**)&ch)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)ch); ch = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("default", "a", "b"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without default due to a collision with mandatory */ + in.current = "ch {anydata any;anyxml anyxml; case c;choice ch;config false;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;mandatory true;reference test;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch)); + CHECK_LYSP_NODE(ch, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "ch", 0, LYS_CHOICE, 0, "test", 1); + lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL; + + /* full content - the default missing from the previous node */ + in.current = "ch {default c;case c;} ..."; + assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch)); + CHECK_LYSP_NODE(ch, NULL, 0, 0, 0, "ch", 0, LYS_CHOICE, 0, NULL, 0); + assert_string_equal("c", ch->dflt.str); + lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL; +} + +static void +test_case(void **state) +{ + struct lysp_node_case *cs = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "cs {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node**)&cs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)cs); cs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "cs {anydata any;anyxml anyxml; choice ch;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;reference test;status current;uses grp;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_case(YCTX, NULL, (struct lysp_node **)&cs)); + CHECK_LYSP_NODE(cs, "test", 1, LYS_STATUS_CURR, 1, "cs", 0, LYS_CASE, 0, "test", 1); + lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL; + + /* invalid content */ + in.current = "cs {config true} ..."; + assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node **)&cs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"case\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL; +} + +static void +test_any(void **state, enum ly_stmt kw) +{ + struct lysp_node_anydata *any = NULL; + + if (kw == LY_STMT_ANYDATA) { + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + } else { + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + } + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_any(YCTX, kw, NULL, (struct lysp_node**)&any)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)any); any = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "any {config true;description test;if-feature f;mandatory true;must 'expr';reference test;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_any(YCTX, kw, NULL, (struct lysp_node **)&any)); + // CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, TYPE, PARENT, REF, WHEN) + uint16_t node_type = kw == LY_STMT_ANYDATA ? LYS_ANYDATA : LYS_ANYXML; + + CHECK_LYSP_NODE(any, "test", 1, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "any", 0, node_type, 0, "test", 1); + assert_non_null(any->musts); + lysp_node_free(&fctx, (struct lysp_node *)any); any = NULL; +} + +static void +test_anydata(void **state) +{ + test_any(state, LY_STMT_ANYDATA); +} + +static void +test_anyxml(void **state) +{ + test_any(state, LY_STMT_ANYXML); +} + +static void +test_grouping(void **state) +{ + struct lysp_node_grp *grp = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, &grp->node); grp = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "grp {action x;anydata any;anyxml anyxml; choice ch;container c;description test;grouping g;leaf l {type string;}" + "leaf-list ll {type string;} list li;notification not;reference test;status current;typedef t {type int8;}uses g;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_grouping(YCTX, NULL, &grp)); + assert_non_null(grp); + assert_int_equal(LYS_GROUPING, grp->nodetype); + assert_string_equal("grp", grp->name); + assert_string_equal("test", grp->dsc); + assert_non_null(grp->exts); + assert_string_equal("test", grp->ref); + assert_null(grp->parent); + assert_int_equal(LYS_STATUS_CURR, grp->flags); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, &grp->node); + grp = NULL; + + /* invalid content */ + in.current = "grp {config true} ..."; + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"grouping\".", NULL, 1); + lysp_node_free(&fctx, &grp->node); + grp = NULL; + + in.current = "grp {must 'expr'} ..."; + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); + CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"grouping\".", NULL, 1); + lysp_node_free(&fctx, &grp->node); + grp = NULL; +} + +static void +test_action(void **state) +{ + struct lysp_node_action *rpcs = NULL; + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)rpcs); rpcs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("input", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a"); + TEST_DUP("output", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "top;"; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + in.current = "func {description test;grouping grp;if-feature f;reference test;status current;typedef mytype {type int8;} m:ext;" + "input {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}" + " list li; must 1; typedef mytypei {type int8;} uses grp; m:ext;}" + "output {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}" + " list li; must 1; typedef mytypeo {type int8;} uses grp; m:ext;}} ..."; + assert_int_equal(LY_SUCCESS, parse_action(YCTX, (struct lysp_node *)c, &rpcs)); + assert_non_null(rpcs); + assert_int_equal(LYS_ACTION, rpcs->nodetype); + assert_string_equal("func", rpcs->name); + assert_string_equal("test", rpcs->dsc); + assert_non_null(rpcs->exts); + assert_non_null(rpcs->iffeatures); + assert_string_equal("test", rpcs->ref); + assert_non_null(rpcs->groupings); + assert_non_null(rpcs->typedefs); + assert_int_equal(LYS_STATUS_CURR, rpcs->flags); + /* input */ + assert_int_equal(rpcs->input.nodetype, LYS_INPUT); + assert_non_null(rpcs->input.groupings); + assert_non_null(rpcs->input.exts); + assert_non_null(rpcs->input.musts); + assert_non_null(rpcs->input.typedefs); + assert_non_null(rpcs->input.child); + /* output */ + assert_int_equal(rpcs->output.nodetype, LYS_OUTPUT); + assert_non_null(rpcs->output.groupings); + assert_non_null(rpcs->output.exts); + assert_non_null(rpcs->output.musts); + assert_non_null(rpcs->output.typedefs); + assert_non_null(rpcs->output.child); + + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL; + + /* invalid content */ + in.current = "func {config true} ..."; + assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"rpc\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL; + + lysp_node_free(&fctx, (struct lysp_node *)c); +} + +static void +test_notification(void **state) +{ + struct lysp_node_notif *notifs = NULL; + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, ¬ifs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)notifs); notifs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "top;"; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + in.current = "ntf {anydata a1; anyxml a2; choice ch; container c; description test; grouping grp; if-feature f; leaf l {type int8;}" + "leaf-list ll {type int8;} list li; must 1; reference test; status current; typedef mytype {type int8;} uses grp; m:ext;}"; + assert_int_equal(LY_SUCCESS, parse_notif(YCTX, (struct lysp_node *)c, ¬ifs)); + assert_non_null(notifs); + assert_int_equal(LYS_NOTIF, notifs->nodetype); + assert_string_equal("ntf", notifs->name); + assert_string_equal("test", notifs->dsc); + assert_non_null(notifs->exts); + assert_non_null(notifs->iffeatures); + assert_string_equal("test", notifs->ref); + assert_non_null(notifs->groupings); + assert_non_null(notifs->typedefs); + assert_non_null(notifs->musts); + assert_non_null(notifs->child); + assert_int_equal(LYS_STATUS_CURR, notifs->flags); + + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL; + + /* invalid content */ + in.current = "ntf {config true} ..."; + assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, ¬ifs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"notification\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL; + + lysp_node_free(&fctx, (struct lysp_node *)c); +} + +static void +test_uses(void **state) +{ + struct lysp_node_uses *u = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_uses(YCTX, NULL, (struct lysp_node**)&u)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)u); u = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "grpref {augment some/node;description test;if-feature f;reference test;refine some/other/node;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_uses(YCTX, NULL, (struct lysp_node **)&u)); + CHECK_LYSP_NODE(u, "test", 1, LYS_STATUS_CURR, 1, "grpref", 0, LYS_USES, 0, "test", 1); + assert_non_null(u->augments); + assert_non_null(u->refines); + lysp_node_free(&fctx, (struct lysp_node *)u); u = NULL; +} + +static void +test_augment(void **state) +{ + struct lysp_node_augment *a = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_augment(YCTX, NULL, &a)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "/target/nodeid {action x; anydata any;anyxml anyxml; case cs; choice ch;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;notification not;reference test;status current;uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_augment(YCTX, NULL, &a)); + assert_non_null(a); + assert_int_equal(LYS_AUGMENT, a->nodetype); + assert_string_equal("/target/nodeid", a->nodeid); + assert_string_equal("test", a->dsc); + assert_non_null(a->exts); + assert_non_null(a->iffeatures); + assert_string_equal("test", a->ref); + assert_non_null(a->when); + assert_null(a->parent); + assert_int_equal(LYS_STATUS_CURR, a->flags); + lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL; +} + +static void +test_when(void **state) +{ + struct lysp_when *w = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + in.current = "l { description text1;description text2;} ..."; + assert_int_equal(LY_EVALID, parse_when(YCTX, &w)); + assert_null(w); + CHECK_LOG_CTX("Duplicate keyword \"description\".", NULL, 1); + + in.current = "l { reference 1;reference 2;} ..."; + assert_int_equal(LY_EVALID, parse_when(YCTX, &w)); + assert_null(w); + CHECK_LOG_CTX("Duplicate keyword \"reference\".", NULL, 1); +} + +static void +test_value(void **state) +{ + struct lysp_type_enum enm; + + in.current = "-0;"; + memset(&enm, 0, sizeof enm); + assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_VALUE, &enm), LY_SUCCESS); + + in.current = "-0;"; + memset(&enm, 0, sizeof enm); + assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_POSITION, &enm), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-0\" of \"position\".", NULL, 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_helpers, setup, teardown), + UTEST(test_comments, setup, teardown), + UTEST(test_arg, setup, teardown), + UTEST(test_stmts, setup, teardown), + UTEST(test_minmax, setup, teardown), + UTEST(test_valid_module, setup, teardown), + UTEST(test_module, setup, teardown), + UTEST(test_deviation, setup, teardown), + UTEST(test_deviate, setup, teardown), + UTEST(test_container, setup, teardown), + UTEST(test_leaf, setup, teardown), + UTEST(test_leaflist, setup, teardown), + UTEST(test_list, setup, teardown), + UTEST(test_choice, setup, teardown), + UTEST(test_case, setup, teardown), + UTEST(test_anydata, setup, teardown), + UTEST(test_anyxml, setup, teardown), + UTEST(test_action, setup, teardown), + UTEST(test_notification, setup, teardown), + UTEST(test_grouping, setup, teardown), + UTEST(test_uses, setup, teardown), + UTEST(test_augment, setup, teardown), + UTEST(test_when, setup, teardown), + UTEST(test_value, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_yin.c b/tests/utests/schema/test_yin.c new file mode 100644 index 0000000..329fcda --- /dev/null +++ b/tests/utests/schema/test_yin.c @@ -0,0 +1,3589 @@ +/** + * @file test_yin.c + * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for YIN parser and printer + * + * Copyright (c) 2015 - 2022 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 <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "in.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "schema_compile.h" +#include "tree.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" +#include "xml.h" +#include "xpath.h" + +/* copied from parser_yin.c */ +enum yin_argument { + YIN_ARG_UNKNOWN = 0, /**< parsed argument can not be matched with any supported yin argument keyword */ + YIN_ARG_NAME, /**< argument name */ + YIN_ARG_TARGET_NODE, /**< argument target-node */ + YIN_ARG_MODULE, /**< argument module */ + YIN_ARG_VALUE, /**< argument value */ + YIN_ARG_TEXT, /**< argument text */ + YIN_ARG_CONDITION, /**< argument condition */ + YIN_ARG_URI, /**< argument uri */ + YIN_ARG_DATE, /**< argument data */ + YIN_ARG_TAG, /**< argument tag */ + YIN_ARG_NONE /**< empty (special value) */ +}; + +struct yin_subelement { + enum ly_stmt type; /**< type of keyword */ + void *dest; /**< meta infromation passed to responsible function (mostly information about where parsed subelement should be stored) */ + uint16_t flags; /**< describes constraints of subelement can be set to YIN_SUBELEM_MANDATORY, YIN_SUBELEM_UNIQUE, YIN_SUBELEM_FIRST, YIN_SUBELEM_VER2, and YIN_SUBELEM_DEFAULT_TEXT */ +}; + +struct import_meta { + const char *prefix; /**< module prefix. */ + struct lysp_import **imports; /**< imports to add to. */ +}; + +struct yin_argument_meta { + uint16_t *flags; /**< Argument flags */ + const char **argument; /**< Argument value */ +}; + +struct tree_node_meta { + struct lysp_node *parent; /**< parent node */ + struct lysp_node **nodes; /**< linked list of siblings */ +}; + +struct include_meta { + const char *name; /**< Module/submodule name. */ + struct lysp_include **includes; /**< [Sized array](@ref sizedarrays) of parsed includes to add to. */ +}; + +struct inout_meta { + struct lysp_node *parent; /**< Parent node. */ + struct lysp_node_action_inout *inout_p; /**< inout_p Input/output pointer to write to. */ +}; + +struct minmax_dev_meta { + uint32_t *lim; /**< min/max value to write to. */ + uint16_t *flags; /**< min/max flags to write to. */ + struct lysp_ext_instance **exts; /**< extension instances to add to. */ +}; + +#define YIN_SUBELEM_MANDATORY 0x01 +#define YIN_SUBELEM_UNIQUE 0x02 +#define YIN_SUBELEM_FIRST 0x04 +#define YIN_SUBELEM_VER2 0x08 + +#define YIN_SUBELEM_PARSED 0x80 + +/* prototypes of static functions */ +enum yin_argument yin_match_argument_name(const char *name, size_t len); + +LY_ERR yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info, size_t subelem_info_size, + const void *parent, enum ly_stmt parent_stmt, const char **text_content, struct lysp_ext_instance **exts); +LY_ERR yin_validate_value(struct lysp_yin_ctx *ctx, enum yang_arg val_type); +enum ly_stmt yin_match_keyword(struct lysp_yin_ctx *ctx, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, enum ly_stmt parrent); + +LY_ERR yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, + LY_ARRAY_COUNT_TYPE parent_stmt_index, struct lysp_ext_instance **exts); +LY_ERR yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent, struct lysp_stmt **element); +LY_ERR yin_parse_mod(struct lysp_yin_ctx *ctx, struct lysp_module *mod); +LY_ERR yin_parse_submod(struct lysp_yin_ctx *ctx, struct lysp_submodule *submod); + +/* wrapping element used for mocking has nothing to do with real module structure */ +#define ELEMENT_WRAPPER_START "<status xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" +#define ELEMENT_WRAPPER_END "</status>" + +#define TEST_1_CHECK_LYSP_EXT_INSTANCE(NODE, INSUBSTMT)\ + CHECK_LYSP_EXT_INSTANCE((NODE), NULL, 1, INSUBSTMT, 0, "myext:c-define", LY_VALUE_XML) + +struct lysp_yin_ctx *YCTX; +struct lysf_ctx fctx; + +static int +setup_ctx(void **state) +{ + struct lysp_module *pmod; + + /* allocate parser context */ + YCTX = calloc(1, sizeof(*YCTX)); + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + YCTX->format = LYS_IN_YIN; + ly_set_new(&YCTX->parsed_mods); + + /* allocate new parsed module */ + pmod = calloc(1, sizeof *pmod); + ly_set_add(YCTX->parsed_mods, pmod, 1, NULL); + + /* allocate new module */ + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->ctx = UTEST_LYCTX; + pmod->mod->parsed = pmod; + + return 0; +} + +static int +setup(void **state) +{ + UTEST_SETUP; + + setup_ctx(state); + + fctx.ctx = UTEST_LYCTX; + fctx.mod = PARSER_CUR_PMOD(YCTX)->mod; + + return 0; +} + +static int +teardown_ctx(void **UNUSED(state)) +{ + lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0); + lysp_yin_ctx_free(YCTX); + YCTX = NULL; + + return 0; +} + +static int +teardown(void **state) +{ + teardown_ctx(state); + + lysf_ctx_erase(&fctx); + + UTEST_TEARDOWN; + + return 0; +} + +#define RESET_STATE \ + ly_in_free(UTEST_IN, 0); \ + UTEST_IN = NULL; \ + teardown_ctx(state); \ + setup_ctx(state) + +static void +test_yin_match_keyword(void **state) +{ + const char *prefix; + size_t prefix_len; + + /* create mock yin namespace in xml context */ + ly_in_new_memory("<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" />", &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + prefix = YCTX->xmlctx->prefix; + prefix_len = YCTX->xmlctx->prefix_len; + + assert_int_equal(yin_match_keyword(YCTX, "anydatax", strlen("anydatax"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "asdasd", strlen("asdasd"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "", 0, prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "anydata", strlen("anydata"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYDATA); + assert_int_equal(yin_match_keyword(YCTX, "anyxml", strlen("anyxml"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYXML); + assert_int_equal(yin_match_keyword(YCTX, "argument", strlen("argument"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ARGUMENT); + assert_int_equal(yin_match_keyword(YCTX, "augment", strlen("augment"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_AUGMENT); + assert_int_equal(yin_match_keyword(YCTX, "base", strlen("base"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BASE); + assert_int_equal(yin_match_keyword(YCTX, "belongs-to", strlen("belongs-to"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BELONGS_TO); + assert_int_equal(yin_match_keyword(YCTX, "bit", strlen("bit"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BIT); + assert_int_equal(yin_match_keyword(YCTX, "case", strlen("case"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CASE); + assert_int_equal(yin_match_keyword(YCTX, "choice", strlen("choice"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CHOICE); + assert_int_equal(yin_match_keyword(YCTX, "config", strlen("config"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONFIG); + assert_int_equal(yin_match_keyword(YCTX, "contact", strlen("contact"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTACT); + assert_int_equal(yin_match_keyword(YCTX, "container", strlen("container"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTAINER); + assert_int_equal(yin_match_keyword(YCTX, "default", strlen("default"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEFAULT); + assert_int_equal(yin_match_keyword(YCTX, "description", strlen("description"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DESCRIPTION); + assert_int_equal(yin_match_keyword(YCTX, "deviate", strlen("deviate"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATE); + assert_int_equal(yin_match_keyword(YCTX, "deviation", strlen("deviation"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATION); + assert_int_equal(yin_match_keyword(YCTX, "enum", strlen("enum"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ENUM); + assert_int_equal(yin_match_keyword(YCTX, "error-app-tag", strlen("error-app-tag"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_APP_TAG); + assert_int_equal(yin_match_keyword(YCTX, "error-message", strlen("error-message"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_MESSAGE); + assert_int_equal(yin_match_keyword(YCTX, "extension", strlen("extension"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_EXTENSION); + assert_int_equal(yin_match_keyword(YCTX, "feature", strlen("feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FEATURE); + assert_int_equal(yin_match_keyword(YCTX, "fraction-digits", strlen("fraction-digits"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FRACTION_DIGITS); + assert_int_equal(yin_match_keyword(YCTX, "grouping", strlen("grouping"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_GROUPING); + assert_int_equal(yin_match_keyword(YCTX, "identity", strlen("identity"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IDENTITY); + assert_int_equal(yin_match_keyword(YCTX, "if-feature", strlen("if-feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IF_FEATURE); + assert_int_equal(yin_match_keyword(YCTX, "import", strlen("import"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IMPORT); + assert_int_equal(yin_match_keyword(YCTX, "include", strlen("include"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INCLUDE); + assert_int_equal(yin_match_keyword(YCTX, "input", strlen("input"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INPUT); + assert_int_equal(yin_match_keyword(YCTX, "key", strlen("key"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_KEY); + assert_int_equal(yin_match_keyword(YCTX, "leaf", strlen("leaf"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF); + assert_int_equal(yin_match_keyword(YCTX, "leaf-list", strlen("leaf-list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF_LIST); + assert_int_equal(yin_match_keyword(YCTX, "length", strlen("length"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LENGTH); + assert_int_equal(yin_match_keyword(YCTX, "list", strlen("list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LIST); + assert_int_equal(yin_match_keyword(YCTX, "mandatory", strlen("mandatory"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MANDATORY); + assert_int_equal(yin_match_keyword(YCTX, "max-elements", strlen("max-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MAX_ELEMENTS); + assert_int_equal(yin_match_keyword(YCTX, "min-elements", strlen("min-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MIN_ELEMENTS); + assert_int_equal(yin_match_keyword(YCTX, "modifier", strlen("modifier"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODIFIER); + assert_int_equal(yin_match_keyword(YCTX, "module", strlen("module"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODULE); + assert_int_equal(yin_match_keyword(YCTX, "must", strlen("must"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MUST); + assert_int_equal(yin_match_keyword(YCTX, "namespace", strlen("namespace"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NAMESPACE); + assert_int_equal(yin_match_keyword(YCTX, "notification", strlen("notification"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NOTIFICATION); + assert_int_equal(yin_match_keyword(YCTX, "ordered-by", strlen("ordered-by"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORDERED_BY); + assert_int_equal(yin_match_keyword(YCTX, "organization", strlen("organization"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORGANIZATION); + assert_int_equal(yin_match_keyword(YCTX, "output", strlen("output"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_OUTPUT); + assert_int_equal(yin_match_keyword(YCTX, "path", strlen("path"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATH); + assert_int_equal(yin_match_keyword(YCTX, "pattern", strlen("pattern"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATTERN); + assert_int_equal(yin_match_keyword(YCTX, "position", strlen("position"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_POSITION); + assert_int_equal(yin_match_keyword(YCTX, "prefix", strlen("prefix"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PREFIX); + assert_int_equal(yin_match_keyword(YCTX, "presence", strlen("presence"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PRESENCE); + assert_int_equal(yin_match_keyword(YCTX, "range", strlen("range"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RANGE); + assert_int_equal(yin_match_keyword(YCTX, "reference", strlen("reference"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFERENCE); + assert_int_equal(yin_match_keyword(YCTX, "refine", strlen("refine"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFINE); + assert_int_equal(yin_match_keyword(YCTX, "require-instance", strlen("require-instance"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REQUIRE_INSTANCE); + assert_int_equal(yin_match_keyword(YCTX, "revision", strlen("revision"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION); + assert_int_equal(yin_match_keyword(YCTX, "revision-date", strlen("revision-date"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION_DATE); + assert_int_equal(yin_match_keyword(YCTX, "rpc", strlen("rpc"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RPC); + assert_int_equal(yin_match_keyword(YCTX, "status", strlen("status"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_STATUS); + assert_int_equal(yin_match_keyword(YCTX, "submodule", strlen("submodule"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_SUBMODULE); + assert_int_equal(yin_match_keyword(YCTX, "type", strlen("type"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPE); + assert_int_equal(yin_match_keyword(YCTX, "typedef", strlen("typedef"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPEDEF); + assert_int_equal(yin_match_keyword(YCTX, "unique", strlen("unique"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNIQUE); + assert_int_equal(yin_match_keyword(YCTX, "units", strlen("units"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNITS); + assert_int_equal(yin_match_keyword(YCTX, "uses", strlen("uses"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_USES); + assert_int_equal(yin_match_keyword(YCTX, "value", strlen("value"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_VALUE); + assert_int_equal(yin_match_keyword(YCTX, "when", strlen("when"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_WHEN); + assert_int_equal(yin_match_keyword(YCTX, "yang-version", strlen("yang-version"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YANG_VERSION); + assert_int_equal(yin_match_keyword(YCTX, "yin-element", strlen("yin-element"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YIN_ELEMENT); +} + +static void +test_yin_match_argument_name(void **UNUSED(state)) +{ + assert_int_equal(yin_match_argument_name("", 5), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("qwertyasd", 5), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("conditionasd", 8), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("condition", 9), YIN_ARG_CONDITION); + assert_int_equal(yin_match_argument_name("date", 4), YIN_ARG_DATE); + assert_int_equal(yin_match_argument_name("module", 6), YIN_ARG_MODULE); + assert_int_equal(yin_match_argument_name("name", 4), YIN_ARG_NAME); + assert_int_equal(yin_match_argument_name("tag", 3), YIN_ARG_TAG); + assert_int_equal(yin_match_argument_name("target-node", 11), YIN_ARG_TARGET_NODE); + assert_int_equal(yin_match_argument_name("text", 4), YIN_ARG_TEXT); + assert_int_equal(yin_match_argument_name("uri", 3), YIN_ARG_URI); + assert_int_equal(yin_match_argument_name("value", 5), YIN_ARG_VALUE); +} + +static void +test_yin_parse_content(void **state) +{ + LY_ERR ret = LY_SUCCESS; + const char *data = + "<prefix value=\"a_mod\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <myext:custom xmlns:myext=\"urn:example:extensions\">totally amazing extension</myext:custom>\n" + " <extension name=\"ext\">\n" + " <argument name=\"argname\"></argument>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"></status>\n" + " </extension>\n" + " <text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>\n" + " <if-feature name=\"foo\"></if-feature>\n" + " <when condition=\"condition...\">\n" + " <reference><text>when_ref</text></reference>\n" + " <description><text>when_desc</text></description>\n" + " </when>\n" + " <config value=\"true\"/>\n" + " <error-message>\n" + " <value>error-msg</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-app-tag\"/>\n" + " <units name=\"radians\"></units>\n" + " <default value=\"default-value\"/>\n" + " <position value=\"25\"></position>\n" + " <value value=\"-5\"/>\n" + " <require-instance value=\"true\"></require-instance>\n" + " <range value=\"5..10\" />\n" + " <length value=\"baf\"/>\n" + " <pattern value='pattern'>\n" + " <modifier value='invert-match'/>\n" + " </pattern>\n" + " <enum name=\"yay\">\n" + " </enum>\n" + "</prefix>"; + struct lysp_ext_instance *exts = NULL; + const char *value; + + /* test unique subelem */ + const char *prefix_value; + struct yin_subelement subelems2[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_UNIQUE}}; + + data = ELEMENT_WRAPPER_START + "<prefix value=\"inv_mod\" />" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + ELEMENT_WRAPPER_END; + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems2, 2, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Redefinition of \"text\" sub-element in \"status\" element.", NULL, 1); + lydict_remove(UTEST_LYCTX, prefix_value); + lydict_remove(UTEST_LYCTX, value); + RESET_STATE; + + /* test first subelem */ + data = ELEMENT_WRAPPER_START + "<prefix value=\"inv_mod\" />" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + ELEMENT_WRAPPER_END; + struct yin_subelement subelems3[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_FIRST}}; + + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems3, 2, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Sub-element \"text\" of \"status\" element must be defined as it's first sub-element.", NULL, 1); + lydict_remove(UTEST_LYCTX, prefix_value); + RESET_STATE; + + /* test mandatory subelem */ + data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END; + struct yin_subelement subelems4[1] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE}}; + + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems4, 1, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"status\" element.", NULL, 1); +} + +static void +test_validate_value(void **state) +{ + const char *data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END; + + /* create some XML context */ + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + YCTX->xmlctx->status = LYXML_ELEM_CONTENT; + YCTX->xmlctx->dynamic = 0; + + YCTX->xmlctx->value = "#invalid"; + YCTX->xmlctx->value_len = 8; + assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", NULL, 1); + + YCTX->xmlctx->value = ""; + YCTX->xmlctx->value_len = 0; + assert_int_equal(yin_validate_value(YCTX, Y_STR_ARG), LY_SUCCESS); + + YCTX->xmlctx->value = "pre:b"; + YCTX->xmlctx->value_len = 5; + assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_SUCCESS); + + YCTX->xmlctx->value = "pre:pre:b"; + YCTX->xmlctx->value_len = 9; + assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); +} + +static void +test_valid_module(void **state) +{ + struct lys_module *mod; + char *printed; + const char *links_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"links\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:mod2=\"urn:module2\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:module2\"/>\n" + " <prefix value=\"mod2\"/>\n" + " <identity name=\"just-another-identity\"/>\n" + " <grouping name=\"rgroup\">\n" + " <leaf name=\"rg1\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"rg2\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <leaf name=\"one-leaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <list name=\"list-for-augment\">\n" + " <key value=\"keyleaf\"/>\n" + " <leaf name=\"keyleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"just-leaf\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " </list>\n" + " <leaf name=\"rleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf-list name=\"llist\">\n" + " <type name=\"string\"/>\n" + " <min-elements value=\"0\"/>\n" + " <max-elements value=\"100\"/>\n" + " <ordered-by value=\"user\"/>\n" + " </leaf-list>\n" + "</module>\n"; + const char *statements_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"statements\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:mod=\"urn:module\"\n" + " xmlns:mod2=\"urn:module2\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:module\"/>\n" + " <prefix value=\"mod\"/>\n" + " <import module=\"links\">\n" + " <prefix value=\"mod2\"/>\n" + " </import>\n" + " <extension name=\"ext\"/>\n" + " <identity name=\"random-identity\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " <base name=\"another-identity\"/>\n" + " </identity>\n" + " <identity name=\"another-identity\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " </identity>\n" + " <typedef name=\"percent\">\n" + " <type name=\"uint8\">\n" + " <range value=\"0 .. 100\"/>\n" + " </type>\n" + " <units name=\"percent\"/>\n" + " </typedef>\n" + " <list name=\"list1\">\n" + " <key value=\"a\"/>\n" + " <leaf name=\"a\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"x\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"y\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </list>\n" + " <container name=\"ice-cream-shop\">\n" + " <container name=\"employees\">\n" + " <when condition=\"/list1/x\"/>\n" + " <list name=\"employee\">\n" + " <key value=\"id\"/>\n" + " <unique tag=\"name\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"0\">\n" + " <mod:ext/>\n" + " </min-elements>\n" + " <max-elements value=\"unbounded\"/>\n" + " <leaf name=\"id\">\n" + " <type name=\"uint64\"/>\n" + " <mandatory value=\"true\"/>\n" + " </leaf>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"age\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " </list>\n" + " </container>\n" + " </container>\n" + " <container name=\"random\">\n" + " <grouping name=\"group\">\n" + " <leaf name=\"g1\">\n" + " <type name=\"percent\"/>\n" + " <mandatory value=\"false\"/>\n" + " </leaf>\n" + " <leaf name=\"g2\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <choice name=\"switch\">\n" + " <case name=\"a\">\n" + " <leaf name=\"aleaf\">\n" + " <type name=\"string\"/>\n" + " <default value=\"aaa\"/>\n" + " </leaf>\n" + " </case>\n" + " <case name=\"c\">\n" + " <leaf name=\"cleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </case>\n" + " </choice>\n" + " <anyxml name=\"xml-data\"/>\n" + " <anydata name=\"any-data\"/>\n" + " <leaf-list name=\"leaflist\">\n" + " <type name=\"string\"/>\n" + " <min-elements value=\"0\"/>\n" + " <max-elements value=\"20\"/>\n" + " </leaf-list>\n" + " <uses name=\"group\"/>\n" + " <uses name=\"mod2:rgroup\"/>\n" + " <leaf name=\"lref\">\n" + " <type name=\"leafref\">\n" + " <path value=\"/mod2:one-leaf\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"iref\">\n" + " <type name=\"identityref\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " </type>\n" + " </leaf>\n" + " </container>\n" + " <augment target-node=\"/random\">\n" + " <leaf name=\"aug-leaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </augment>\n" + " <notification name=\"notif\"/>\n" + " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\">\n" + " <deviate value=\"not-supported\">\n" + " <mod:ext/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:list1\">\n" + " <deviate value=\"add\">\n" + " <mod:ext/>\n" + " <must condition=\"1\"/>\n" + " <must condition=\"2\"/>\n" + " <unique tag=\"x\"/>\n" + " <unique tag=\"y\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"1\"/>\n" + " <max-elements value=\"2\"/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee\">\n" + " <deviate value=\"delete\">\n" + " <unique tag=\"name\"/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:random/mod:leaflist\">\n" + " <deviate value=\"replace\">\n" + " <type name=\"uint32\"/>\n" + " <min-elements value=\"10\"/>\n" + " <max-elements value=\"15\"/>\n" + " </deviate>\n" + " </deviation>\n" + "</module>\n"; + + UTEST_ADD_MODULE(links_yin, LYS_IN_YIN, NULL, NULL); + UTEST_ADD_MODULE(statements_yin, LYS_IN_YIN, NULL, &mod); + lys_print_mem(&printed, mod, LYS_OUT_YIN, 0); + assert_string_equal(printed, statements_yin); + free(printed); +} + +static void +test_print_module(void **state) +{ + struct lys_module *mod; + + char *orig = malloc(8096); + + strcpy(orig, + "module all {\n" + " yang-version 1.1;\n" + " namespace \"urn:all\";\n" + " prefix all_mod;\n\n" + " import ietf-yang-types {\n" + " prefix yt;\n" + " revision-date 2013-07-15;\n" + " description\n" + " \"YANG types\";\n" + " reference\n" + " \"RFC reference\";\n" + " }\n\n" + " feature feat1 {\n" + " if-feature \"feat2\";\n" + " status obsolete;\n" + " }\n\n" + " feature feat2;\n" + " feature feat3;\n\n" + " identity ident2 {\n" + " base ident1;\n" + " }\n\n" + " identity ident1;\n\n" + " typedef tdef1 {\n" + " type tdef2 {\n" + " length \"3..9 | 30..40\";\n" + " pattern \"[ac]*\";\n" + " }\n" + " units \"none\";\n" + " default \"aaa\";\n" + " }\n\n" + " typedef tdef2 {\n" + " type string {\n" + " length \"2..10 | 20..50\";\n" + " pattern \"[ab]*\";\n" + " }\n" + " }\n\n" + " grouping group1 {\n" + " leaf leaf1 {\n" + " type int8;\n" + " }\n" + " }\n\n" + " container cont1 {\n" + " leaf leaf2 {\n" + " if-feature \"feat1\";\n" + " type int16;\n" + " status obsolete;\n" + " }\n\n" + " uses group1 {\n" + " if-feature \"feat2\";\n" + " refine \"leaf1\" {\n" + " if-feature \"feat3\";\n" + " must \"24 - 4 = number('20')\";\n" + " default \"25\";\n" + " config true;\n" + " mandatory false;\n" + " description\n" + " \"dsc\";\n" + " reference\n" + " \"none\";\n" + " }\n" + " }\n\n" + " leaf leaf3 {\n" + " type int32;\n" + " }\n\n" + " leaf leaf4 {\n" + " type int64 {\n" + " range \"1000 .. 50000\" {\n" + " error-message\n" + " \"Special error message.\";\n" + " error-app-tag \"special-tag\";\n" + " }\n" + " }\n" + " }\n\n" + " leaf leaf5 {\n" + " type uint8;\n" + " }\n\n" + " leaf leaf6 {\n" + " type uint16;\n" + " }\n\n" + " leaf leaf7 {\n" + " type uint32;\n" + " }\n\n" + " leaf leaf8 {\n" + " type uint64;\n" + " }\n\n" + " choice choic1 {\n" + " default \"leaf9b\";\n" + " leaf leaf9a {\n" + " type decimal64 {\n" + " fraction-digits 9;\n" + " }\n" + " }\n\n" + " leaf leaf9b {\n" + " type boolean;\n" + " default \"false\";\n" + " }\n" + " }\n\n" + " leaf leaf10 {\n" + " type boolean;\n" + " }\n\n"); + strcpy(orig + strlen(orig), + " leaf leaf11 {\n" + " type enumeration {\n" + " enum \"one\";\n" + " enum \"two\";\n" + " enum \"five\" {\n" + " value 5;\n" + " }\n" + " }\n" + " }\n\n" + " leaf leaf12 {\n" + " type bits {\n" + " bit flag0 {\n" + " position 0;\n" + " }\n" + " bit flag1;\n" + " bit flag2 {\n" + " position 2;\n" + " }\n" + " bit flag3 {\n" + " position 3;\n" + " }\n" + " }\n" + " default \"flag0 flag3\";\n" + " }\n\n" + " leaf leaf13 {\n" + " type binary;\n" + " }\n\n" + " leaf leaf14 {\n" + " type leafref {\n" + " path \"/cont1/leaf17\";\n" + " }\n" + " }\n\n" + " leaf leaf15 {\n" + " type empty;\n" + " }\n\n" + " leaf leaf16 {\n" + " type union {\n" + " type instance-identifier {\n" + " require-instance true;\n" + " }\n" + " type int8;\n" + " }\n" + " }\n\n" + " list list1 {\n" + " key \"leaf18\";\n" + " unique \"leaf19\";\n" + " min-elements 1;\n" + " max-elements 20;\n" + " leaf leaf18 {\n" + " type string;\n" + " }\n\n" + " leaf leaf19 {\n" + " type uint32;\n" + " }\n\n" + " anyxml axml1;\n" + " anydata adata1;\n\n" + " action act1 {\n" + " input {\n" + " leaf leaf24 {\n" + " type string;\n" + " }\n" + " }\n\n" + " output {\n" + " leaf leaf25 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n\n" + " notification notif1 {\n" + " leaf leaf26 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n\n" + " leaf-list llist1 {\n" + " type tdef1;\n" + " ordered-by user;\n" + " }\n\n" + " list list2 {\n" + " key \"leaf27 leaf28\";\n" + " leaf leaf27 {\n" + " type uint8;\n" + " }\n\n" + " leaf leaf28 {\n" + " type uint8;\n" + " }\n" + " }\n\n" + " leaf leaf29 {\n" + " type instance-identifier;\n" + " }\n\n" + " container must-deviations-container {\n" + " presence \"Allows deviations on the leaf\";\n" + " leaf leaf30 {\n" + " type string;\n" + " }\n" + " }\n\n" + " leaf leaf23 {\n" + " type empty;\n" + " }\n" + " }\n\n" + " augment \"/cont1\" {\n" + " leaf leaf17 {\n" + " type string;\n" + " }\n" + " }\n\n" + " rpc rpc1 {\n" + " input {\n" + " leaf leaf20 {\n" + " type tdef1;\n" + " }\n" + " }\n\n" + " output {\n" + " container cont2 {\n" + " leaf leaf21 {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " }\n\n" + " container test-when {\n" + " leaf when-check {\n" + " type boolean;\n" + " }\n\n" + " leaf gated-data {\n" + " when \"../when-check = 'true'\";\n" + " type uint16;\n" + " }\n" + " }\n\n" + " extension c-define {\n" + " description\n" + " \"Takes as an argument a name string.\n" + " Makes the code generator use the given name\n" + " in the #define.\";\n" + " argument \"name\";\n" + " }\n" + "}\n"); + + char *ori_res = malloc(8096); + + strcpy(ori_res, + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"all\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:all_mod=\"urn:all\"\n" + " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:all\"/>\n" + " <prefix value=\"all_mod\"/>\n" + " <import module=\"ietf-yang-types\">\n" + " <prefix value=\"yt\"/>\n" + " <revision-date date=\"2013-07-15\"/>\n" + " <description>\n" + " <text>YANG types</text>\n" + " </description>\n" + " <reference>\n" + " <text>RFC reference</text>\n" + " </reference>\n" + " </import>\n" + " <extension name=\"c-define\">\n" + " <argument name=\"name\"/>\n" + " <description>\n" + " <text>Takes as an argument a name string.\n" + "Makes the code generator use the given name\n" + "in the #define.</text>\n" + " </description>\n" + " </extension>\n" + " <feature name=\"feat1\">\n" + " <if-feature name=\"feat2\"/>\n" + " <status value=\"obsolete\"/>\n" + " </feature>\n" + " <feature name=\"feat2\"/>\n" + " <feature name=\"feat3\"/>\n" + " <identity name=\"ident2\">\n" + " <base name=\"ident1\"/>\n" + " </identity>\n" + " <identity name=\"ident1\"/>\n" + " <typedef name=\"tdef1\">\n" + " <type name=\"tdef2\">\n" + " <length value=\"3..9 | 30..40\"/>\n" + " <pattern value=\"[ac]*\"/>\n" + " </type>\n" + " <units name=\"none\"/>\n" + " <default value=\"aaa\"/>\n" + " </typedef>\n" + " <typedef name=\"tdef2\">\n" + " <type name=\"string\">\n" + " <length value=\"2..10 | 20..50\"/>\n" + " <pattern value=\"[ab]*\"/>\n" + " </type>\n" + " </typedef>\n" + " <grouping name=\"group1\">\n" + " <leaf name=\"leaf1\">\n" + " <type name=\"int8\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <container name=\"cont1\">\n" + " <leaf name=\"leaf2\">\n" + " <if-feature name=\"feat1\"/>\n" + " <type name=\"int16\"/>\n" + " <status value=\"obsolete\"/>\n" + " </leaf>\n" + " <uses name=\"group1\">\n" + " <if-feature name=\"feat2\"/>\n" + " <refine target-node=\"leaf1\">\n" + " <if-feature name=\"feat3\"/>\n" + " <must condition=\"24 - 4 = number('20')\"/>\n" + " <default value=\"25\"/>\n" + " <config value=\"true\"/>\n" + " <mandatory value=\"false\"/>\n" + " <description>\n" + " <text>dsc</text>\n" + " </description>\n" + " <reference>\n" + " <text>none</text>\n" + " </reference>\n" + " </refine>\n" + " </uses>\n" + " <leaf name=\"leaf3\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf4\">\n" + " <type name=\"int64\">\n" + " <range value=\"1000 .. 50000\">\n" + " <error-message>\n" + " <value>Special error message.</value>\n" + " </error-message>\n" + " <error-app-tag value=\"special-tag\"/>\n" + " </range>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf5\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf6\">\n" + " <type name=\"uint16\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf7\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf8\">\n" + " <type name=\"uint64\"/>\n" + " </leaf>\n" + " <choice name=\"choic1\">\n" + " <default value=\"leaf9b\"/>\n" + " <leaf name=\"leaf9a\">\n" + " <type name=\"decimal64\">\n" + " <fraction-digits value=\"9\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf9b\">\n" + " <type name=\"boolean\"/>\n" + " <default value=\"false\"/>\n" + " </leaf>\n" + " </choice>\n" + " <leaf name=\"leaf10\">\n" + " <type name=\"boolean\"/>\n" + " </leaf>\n"); + strcpy(ori_res + strlen(ori_res), + " <leaf name=\"leaf11\">\n" + " <type name=\"enumeration\">\n" + " <enum name=\"one\"/>\n" + " <enum name=\"two\"/>\n" + " <enum name=\"five\">\n" + " <value value=\"5\"/>\n" + " </enum>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf12\">\n" + " <type name=\"bits\">\n" + " <bit name=\"flag0\">\n" + " <position value=\"0\"/>\n" + " </bit>\n" + " <bit name=\"flag1\"/>\n" + " <bit name=\"flag2\">\n" + " <position value=\"2\"/>\n" + " </bit>\n" + " <bit name=\"flag3\">\n" + " <position value=\"3\"/>\n" + " </bit>\n" + " </type>\n" + " <default value=\"flag0 flag3\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf13\">\n" + " <type name=\"binary\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf14\">\n" + " <type name=\"leafref\">\n" + " <path value=\"/cont1/leaf17\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf15\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf16\">\n" + " <type name=\"union\">\n" + " <type name=\"instance-identifier\">\n" + " <require-instance value=\"true\"/>\n" + " </type>\n" + " <type name=\"int8\"/>\n" + " </type>\n" + " </leaf>\n" + " <list name=\"list1\">\n" + " <key value=\"leaf18\"/>\n" + " <unique tag=\"leaf19\"/>\n" + " <min-elements value=\"1\"/>\n" + " <max-elements value=\"20\"/>\n" + " <leaf name=\"leaf18\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf19\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " <anyxml name=\"axml1\"/>\n" + " <anydata name=\"adata1\"/>\n" + " <action name=\"act1\">\n" + " <input>\n" + " <leaf name=\"leaf24\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </input>\n" + " <output>\n" + " <leaf name=\"leaf25\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </output>\n" + " </action>\n" + " <notification name=\"notif1\">\n" + " <leaf name=\"leaf26\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </notification>\n" + " </list>\n" + " <leaf-list name=\"llist1\">\n" + " <type name=\"tdef1\"/>\n" + " <ordered-by value=\"user\"/>\n" + " </leaf-list>\n" + " <list name=\"list2\">\n" + " <key value=\"leaf27 leaf28\"/>\n" + " <leaf name=\"leaf27\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf28\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " </list>\n" + " <leaf name=\"leaf29\">\n" + " <type name=\"instance-identifier\"/>\n" + " </leaf>\n" + " <container name=\"must-deviations-container\">\n" + " <presence value=\"Allows deviations on the leaf\"/>\n" + " <leaf name=\"leaf30\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </container>\n" + " <leaf name=\"leaf23\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " </container>\n" + " <container name=\"test-when\">\n" + " <leaf name=\"when-check\">\n" + " <type name=\"boolean\"/>\n" + " </leaf>\n" + " <leaf name=\"gated-data\">\n" + " <when condition=\"../when-check = 'true'\"/>\n" + " <type name=\"uint16\"/>\n" + " </leaf>\n" + " </container>\n" + " <augment target-node=\"/cont1\">\n" + " <leaf name=\"leaf17\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </augment>\n" + " <rpc name=\"rpc1\">\n" + " <input>\n" + " <leaf name=\"leaf20\">\n" + " <type name=\"tdef1\"/>\n" + " </leaf>\n" + " </input>\n" + " <output>\n" + " <container name=\"cont2\">\n" + " <leaf name=\"leaf21\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " </container>\n" + " </output>\n" + " </rpc>\n" + "</module>\n"); + + char *printed; + struct ly_out *out; + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out)); + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YIN, 0, 0)); + assert_int_equal(strlen(ori_res), ly_out_printed(out)); + assert_string_equal(printed, ori_res); + + ly_out_free(out, NULL, 1); + free(orig); + free(ori_res); +} + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YIN; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_print_submodule(void **state) +{ + struct lys_module *mod; + + const char *mod_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a_mod=\"urn:a\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a_mod\"/>\n" + " <include module=\"a-sub\"/>\n" + "</module>\n"; + + char *submod_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"a-sub\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a_mod=\"urn:a\"\n" + " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n" + " <yang-version value=\"1.1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_mod\"/>\n" + " </belongs-to>\n" + " <import module=\"ietf-yang-types\">\n" + " <prefix value=\"yt\"/>\n" + " <revision-date date=\"2013-07-15\"/>\n" + " </import>\n\n" + " <description>\n" + " <text>YANG types</text>\n" + " </description>\n" + " <reference>\n" + " <text>RFC reference</text>\n" + " </reference>\n" + "</submodule>\n"; + + char *printed; + struct ly_out *out; + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod_yin); + + UTEST_ADD_MODULE(mod_yin, LYS_IN_YIN, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_submodule(out, mod->parsed->includes[0].submodule, LYS_OUT_YIN, 0, 0)); + assert_int_equal(strlen(submod_yin), ly_out_printed(out)); + assert_string_equal(printed, submod_yin); + + ly_out_free(out, NULL, 1); +} + +/* helper function to simplify unit test of each element using parse_content function */ +LY_ERR +test_element_helper(void **state, const char *data, void *dest, const char **text, struct lysp_ext_instance **exts) +{ + const char *name, *prefix; + size_t name_len, prefix_len; + LY_ERR ret = LY_SUCCESS; + struct yin_subelement subelems[71] = { + {LY_STMT_ACTION, dest, 0}, + {LY_STMT_ANYDATA, dest, 0}, + {LY_STMT_ANYXML, dest, 0}, + {LY_STMT_ARGUMENT, dest, 0}, + {LY_STMT_AUGMENT, dest, 0}, + {LY_STMT_BASE, dest, 0}, + {LY_STMT_BELONGS_TO, dest, 0}, + {LY_STMT_BIT, dest, 0}, + {LY_STMT_CASE, dest, 0}, + {LY_STMT_CHOICE, dest, 0}, + {LY_STMT_CONFIG, dest, 0}, + {LY_STMT_CONTACT, dest, 0}, + {LY_STMT_CONTAINER, dest, 0}, + {LY_STMT_DEFAULT, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_DESCRIPTION, dest, 0}, + {LY_STMT_DEVIATE, dest, 0}, + {LY_STMT_DEVIATION, dest, 0}, + {LY_STMT_ENUM, dest, 0}, + {LY_STMT_ERROR_APP_TAG, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ERROR_MESSAGE, dest, 0}, + {LY_STMT_EXTENSION, dest, 0}, + {LY_STMT_FEATURE, dest, 0}, + {LY_STMT_FRACTION_DIGITS, dest, 0}, + {LY_STMT_GROUPING, dest, 0}, + {LY_STMT_IDENTITY, dest, 0}, + {LY_STMT_IF_FEATURE, dest, 0}, + {LY_STMT_IMPORT, dest, 0}, + {LY_STMT_INCLUDE, dest, 0}, + {LY_STMT_INPUT, dest, 0}, + {LY_STMT_KEY, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_LEAF, dest, 0}, + {LY_STMT_LEAF_LIST, dest, 0}, + {LY_STMT_LENGTH, dest, 0}, + {LY_STMT_LIST, dest, 0}, + {LY_STMT_MANDATORY, dest, 0}, + {LY_STMT_MAX_ELEMENTS, dest, 0}, + {LY_STMT_MIN_ELEMENTS, dest, 0}, + {LY_STMT_MODIFIER, dest, 0}, + {LY_STMT_MODULE, dest, 0}, + {LY_STMT_MUST, dest, 0}, + {LY_STMT_NAMESPACE, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_NOTIFICATION, dest, 0}, + {LY_STMT_ORDERED_BY, dest, 0}, + {LY_STMT_ORGANIZATION, dest, 0}, + {LY_STMT_OUTPUT, dest, 0}, + {LY_STMT_PATH, dest, 0}, + {LY_STMT_PATTERN, dest, 0}, + {LY_STMT_POSITION, dest, 0}, + {LY_STMT_PREFIX, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_PRESENCE, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_RANGE, dest, 0}, + {LY_STMT_REFERENCE, dest, 0}, + {LY_STMT_REFINE, dest, 0}, + {LY_STMT_REQUIRE_INSTANCE, dest, 0}, + {LY_STMT_REVISION, dest, 0}, + {LY_STMT_REVISION_DATE, dest, 0}, + {LY_STMT_RPC, dest, 0}, + {LY_STMT_STATUS, dest, 0}, + {LY_STMT_SUBMODULE, dest, 0}, + {LY_STMT_TYPE, dest, 0}, + {LY_STMT_TYPEDEF, dest, 0}, + {LY_STMT_UNIQUE, dest, 0}, + {LY_STMT_UNITS, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_USES, dest, 0}, + {LY_STMT_VALUE, dest, 0}, + {LY_STMT_WHEN, dest, 0}, + {LY_STMT_YANG_VERSION, dest, 0}, + {LY_STMT_YIN_ELEMENT, dest, 0}, + {LY_STMT_EXTENSION_INSTANCE, dest, 0}, + {LY_STMT_ARG_TEXT, dest, 0}, + {LY_STMT_ARG_VALUE, dest, 0} + }; + + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + prefix = YCTX->xmlctx->prefix; + prefix_len = YCTX->xmlctx->prefix_len; + name = YCTX->xmlctx->name; + name_len = YCTX->xmlctx->name_len; + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems, 71, NULL, + yin_match_keyword(YCTX, name, name_len, prefix, prefix_len, LY_STMT_NONE), text, exts); + + /* free parser and input */ + lyxml_ctx_free(YCTX->xmlctx); + YCTX->xmlctx = NULL; + ly_in_free(UTEST_IN, 0); + UTEST_IN = NULL; + return ret; +} + +#define EXT_SUBELEM "<myext:c-define name=\"MY_MTU\" xmlns:myext=\"urn:example:extensions\"/>" + +static void +test_enum_elem(void **state) +{ + struct lysp_type type = {0}; + const char *data; + + data = ELEMENT_WRAPPER_START + "<enum name=\"enum-name\">\n" + " <if-feature name=\"feature\" />\n" + " <value value=\"55\" />\n" + " <status value=\"deprecated\" />\n" + " <description><text>desc...</text></description>\n" + " <reference><text>ref...</text></reference>\n" + " " EXT_SUBELEM "\n" + "</enum>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE; + + CHECK_LYSP_TYPE_ENUM(type.enums, "desc...", 1, flags, 1, "enum-name", "ref...", 55); + assert_string_equal(type.enums->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(type.enums->exts, LY_STMT_ENUM); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); + + data = ELEMENT_WRAPPER_START + "<enum name=\"enum-name\"></enum>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_string_equal(type.enums->name, "enum-name"); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); +} + +static void +test_bit_elem(void **state) +{ + struct lysp_type type = {0}; + const char *data; + + data = ELEMENT_WRAPPER_START + "<bit name=\"bit-name\">\n" + " <if-feature name=\"feature\" />\n" + " <position value=\"55\" />\n" + " <status value=\"deprecated\" />\n" + " <description><text>desc...</text></description>\n" + " <reference><text>ref...</text></reference>\n" + EXT_SUBELEM + "</bit>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE; + + CHECK_LYSP_TYPE_ENUM(type.bits, "desc...", 1, flags, 1, "bit-name", "ref...", 55); + assert_string_equal(type.bits->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(type.bits->exts, LY_STMT_BIT); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); + + data = ELEMENT_WRAPPER_START + "<bit name=\"bit-name\"> </bit>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(type.bits, NULL, 0, 0, 0, "bit-name", NULL, 0); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); +} + +static void +test_status_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + /* test invalid value */ + data = ELEMENT_WRAPPER_START "<status value=\"invalid\"></status>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"status\" element. " + "Valid values are \"current\", \"deprecated\" and \"obsolete\".", NULL, 1); +} + +static void +test_yin_element_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<yin-element value=\"invalid\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"yin-element\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_yangversion_elem(void **state) +{ + const char *data; + uint8_t version = 0; + + /* invalid value */ + data = ELEMENT_WRAPPER_START "<yang-version value=\"version\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &version, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"version\" of \"value\" attribute in \"yang-version\" element. " + "Valid values are \"1\" and \"1.1\".", NULL, 1); +} + +static void +test_argument_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + const char *arg; + struct yin_argument_meta arg_meta = {&flags, &arg}; + + /* min subelems */ + data = ELEMENT_WRAPPER_START + "<argument name=\"arg\">" + "</argument>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &arg_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(arg, "arg"); + assert_true(flags == 0); + lydict_remove(UTEST_LYCTX, arg); +} + +static void +test_belongsto_elem(void **state) +{ + const char *data; + struct lysp_submodule submod; + + lydict_insert(UTEST_LYCTX, "module-name", 0, &PARSER_CUR_PMOD(YCTX)->mod->name); + + data = ELEMENT_WRAPPER_START "<belongs-to module=\"module-name\"></belongs-to>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &submod, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"belongs-to\" element.", NULL, 1); +} + +static void +test_config_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<config value=\"false\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS); + assert_true(flags & LYS_CONFIG_R); + flags = 0; + + data = ELEMENT_WRAPPER_START "<config value=\"invalid\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"config\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_default_elem(void **state) +{ + const char *data; + struct lysp_qname val = {0}; + + data = ELEMENT_WRAPPER_START "<default/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of default element.", NULL, 1); +} + +static void +test_err_app_tag_elem(void **state) +{ + const char *data; + const char *val = NULL; + + data = ELEMENT_WRAPPER_START "<error-app-tag/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of error-app-tag element.", NULL, 1); +} + +static void +test_err_msg_elem(void **state) +{ + const char *data; + const char *val = NULL; + + data = ELEMENT_WRAPPER_START "<error-message></error-message>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"value\" of \"error-message\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<error-message invalid=\"text\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Unexpected attribute \"invalid\" of \"error-message\" element.", NULL, 1); +} + +static void +test_fracdigits_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* invalid values */ + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"-1\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"02\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"02\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"1p\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"1p\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"19\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"19\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"999999999999999999\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"999999999999999999\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); +} + +static void +test_iffeature_elem(void **state) +{ + const char *data; + const char **iffeatures = NULL; + + data = ELEMENT_WRAPPER_START "<if-feature/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &iffeatures, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute name of if-feature element.", NULL, 1); + LY_ARRAY_FREE(iffeatures); + iffeatures = NULL; +} + +static void +test_length_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<length value=\"length-str\">\n" + " <error-message><value>err-msg</value></error-message>\n" + " <error-app-tag value=\"err-app-tag\"/>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + EXT_SUBELEM + "</length>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.length, "length-str", "desc", + "err-app-tag", "err-msg", 1, "ref"); + assert_true(type.flags & LYS_SET_LENGTH); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.length->exts[0]), LY_STMT_LENGTH); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START + "<length value=\"length-str\">" + "</length>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.length, "length-str", NULL, + NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + assert_true(type.flags & LYS_SET_LENGTH); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<length></length>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of length element.", NULL, 1); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_modifier_elem(void **state) +{ + const char *data; + const char *pat; + + assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "\006pattern", 8, &pat)); + data = ELEMENT_WRAPPER_START "<modifier value=\"invert\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &pat, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invert\" of \"value\" attribute in \"modifier\" element. " + "Only valid value is \"invert-match\".", NULL, 1); + lydict_remove(UTEST_LYCTX, pat); +} + +static void +test_namespace_elem(void **state) +{ + const char *data; + const char *ns; + + data = ELEMENT_WRAPPER_START "<namespace/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &ns, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute uri of namespace element.", NULL, 1); +} + +static void +test_pattern_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<pattern value=\"super_pattern\">\n" + " <modifier value=\"invert-match\"/>\n" + " <error-message><value>err-msg-value</value></error-message>\n" + " <error-app-tag value=\"err-app-tag-value\"/>\n" + " <description><text>"pattern-desc"</text></description>\n" + " <reference><text>pattern-ref</text></reference>\n" + EXT_SUBELEM + "</pattern>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_true(type.flags & LYS_SET_PATTERN); + CHECK_LYSP_RESTR(type.patterns, "\x015super_pattern", "\"pattern-desc\"", + "err-app-tag-value", "err-msg-value", 1, "pattern-ref"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.patterns->exts[0]), LY_STMT_PATTERN); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<pattern value=\"pattern\"> </pattern>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.patterns, "\x006pattern", NULL, NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_value_position_elem(void **state) +{ + const char *data; + struct lysp_type_enum en = {0}; + + /* valid values */ + data = ELEMENT_WRAPPER_START "<value value=\"-55\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, -55); + memset(&en, 0, sizeof(en)); + + data = ELEMENT_WRAPPER_START "<value value=\"0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + data = ELEMENT_WRAPPER_START "<value value=\"-0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + /* valid positions */ + data = ELEMENT_WRAPPER_START "<position value=\"0\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + /* invalid values */ + data = ELEMENT_WRAPPER_START "<value value=\"99999999999999999999999\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"99999999999999999999999\" of \"value\" attribute in \"value\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<value value=\"1k\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"1k\" of \"value\" attribute in \"value\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<value value=\"\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"value\" element.", NULL, 1); + + /*invalid positions */ + data = ELEMENT_WRAPPER_START "<position value=\"-5\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-5\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"-0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-0\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"99999999999999999999\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"99999999999999999999\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"position\" element.", NULL, 1); +} + +static void +test_prefix_elem(void **state) +{ + const char *data; + const char *value = NULL; + + data = ELEMENT_WRAPPER_START "<prefix value=\"pref\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &value, NULL, NULL), LY_SUCCESS); + assert_string_equal(value, "pref"); + lydict_remove(UTEST_LYCTX, value); +} + +static void +test_range_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<range value=\"range-str\">\n" + " <error-message><value>err-msg</value></error-message>\n" + " <error-app-tag value=\"err-app-tag\" />\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + EXT_SUBELEM + "</range>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.range, "range-str", "desc", + "err-app-tag", "err-msg", 1, "ref"); + assert_true(type.flags & LYS_SET_RANGE); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.range->exts[0]), LY_STMT_RANGE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<range value=\"range-str\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.range, "range-str", NULL, + NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_reqinstance_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + data = ELEMENT_WRAPPER_START "<require-instance value=\"true\">" EXT_SUBELEM "</require-instance>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_int_equal(type.require_instance, 1); + assert_true(type.flags & LYS_SET_REQINST); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_REQUIRE_INSTANCE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<require-instance value=\"false\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_int_equal(type.require_instance, 0); + assert_true(type.flags & LYS_SET_REQINST); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<require-instance value=\"invalid\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + memset(&type, 0, sizeof(type)); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"require-instance\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_revision_date_elem(void **state) +{ + const char *data; + char rev[LY_REV_SIZE]; + + data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-01-01\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_SUCCESS); + assert_string_equal(rev, "2000-01-01"); + + data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-50-05\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"2000-50-05\" of \"revision-date\".", NULL, 1); +} + +static void +test_unique_elem(void **state) +{ + const char *data; + const char **values = NULL; + + data = ELEMENT_WRAPPER_START "<unique tag=\"tag\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS); + assert_string_equal(*values, "tag"); + lydict_remove(UTEST_LYCTX, *values); + LY_ARRAY_FREE(values); + values = NULL; +} + +static void +test_units_elem(void **state) +{ + const char *data; + const char *values = NULL; + + data = ELEMENT_WRAPPER_START "<units name=\"name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS); + assert_string_equal(values, "name"); + lydict_remove(UTEST_LYCTX, values); + values = NULL; +} + +static void +test_yin_text_value_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<text>text</text>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "text"); + lydict_remove(UTEST_LYCTX, val); + + data = "<error-message xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <value>text</value> </error-message>"; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "text"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<text></text>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal("", val); + lydict_remove(UTEST_LYCTX, val); +} + +static void +test_type_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<type name=\"type-name\">\n" + " <base name=\"base-name\"/>\n" + " <bit name=\"bit\"/>\n" + " <enum name=\"enum\"/>\n" + " <fraction-digits value=\"2\"/>\n" + " <length value=\"length\"/>\n" + " <path value=\"/path\"/>\n" + " <pattern value=\"pattern\"/>\n" + " <range value=\"range\" />\n" + " <require-instance value=\"true\"/>\n" + " <type name=\"sub-type-name\"/>\n" + EXT_SUBELEM + "</type>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_string_equal(type.name, "type-name"); + assert_string_equal(*type.bases, "base-name"); + assert_string_equal(type.bits->name, "bit"); + assert_string_equal(type.enums->name, "enum"); + assert_int_equal(type.fraction_digits, 2); + CHECK_LYSP_RESTR(type.length, "length", NULL, + NULL, NULL, 0, NULL); + assert_string_equal(type.path->expr, "/path"); + CHECK_LYSP_RESTR(type.patterns, "\006pattern", NULL, + NULL, NULL, 0, NULL); + CHECK_LYSP_RESTR(type.range, "range", NULL, + NULL, NULL, 0, NULL); + assert_int_equal(type.require_instance, 1); + assert_string_equal(type.types->name, "sub-type-name"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_TYPE); + assert_true(type.flags & LYS_SET_BASE); + assert_true(type.flags & LYS_SET_BIT); + assert_true(type.flags & LYS_SET_ENUM); + assert_true(type.flags & LYS_SET_FRDIGITS); + assert_true(type.flags & LYS_SET_LENGTH); + assert_true(type.flags & LYS_SET_PATH); + assert_true(type.flags & LYS_SET_PATTERN); + assert_true(type.flags & LYS_SET_RANGE); + assert_true(type.flags & LYS_SET_REQINST); + assert_true(type.flags & LYS_SET_TYPE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<type name=\"type-name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_max_elems_elem(void **state) +{ + const char *data; + struct lysp_node_list list = {0}; + struct lysp_refine refine = {0}; + + data = "<refine xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"10\"/> </refine>"; + assert_int_equal(test_element_helper(state, data, &refine, NULL, NULL), LY_SUCCESS); + assert_int_equal(refine.max, 10); + assert_true(refine.flags & LYS_SET_MAX); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"0\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"0\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"-10\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-10\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"k\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"k\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"u12\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"u12\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); +} + +static void +test_min_elems_elem(void **state) +{ + const char *data; + struct lysp_node_leaflist llist = {0}; + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"-5\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Value \"-5\" of \"value\" attribute in \"min-elements\" element is out of bounds.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"99999999999999999\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Value \"99999999999999999\" of \"value\" attribute in \"min-elements\" element is out of bounds.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"5k\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"5k\" of \"value\" attribute in \"min-elements\" element.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"05\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"05\" of \"value\" attribute in \"min-elements\" element.", NULL, 1); +} + +static void +test_ordby_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<ordered-by value=\"user\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS); + assert_true(flags & LYS_ORDBY_USER); + + data = ELEMENT_WRAPPER_START "<ordered-by value=\"inv\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"inv\" of \"value\" attribute in \"ordered-by\" element. " + "Valid values are \"system\" and \"user\".", NULL, 1); +} + +static void +test_any_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_anydata *parsed = NULL; + uint16_t flags; + + /* anyxml max subelems */ + data = ELEMENT_WRAPPER_START + "<anyxml name=\"any-name\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</anyxml>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "any-name", 0, LYS_ANYXML, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYXML); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* anydata max subelems */ + data = ELEMENT_WRAPPER_START + "<anydata name=\"any-name\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</anydata>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "any-name", 0, LYS_ANYDATA, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYDATA); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + node_meta.parent = (void *)0x10; + data = ELEMENT_WRAPPER_START "<anydata name=\"any-name\"> </anydata>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + assert_ptr_equal(parsed->parent, node_meta.parent); + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "any-name", 0, LYS_ANYDATA, 1, NULL, 0); + lysp_node_free(&fctx, siblings); +} + +static void +test_leaf_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_leaf *parsed = NULL; + uint16_t flags; + + /* max elements */ + data = ELEMENT_WRAPPER_START + "<leaf name=\"leaf\">\n" + " <config value=\"true\" />\n" + " <default value=\"def-val\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaf *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "leaf", 0, LYS_LEAF, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF); + assert_string_equal(parsed->musts->arg.str, "must-cond"); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + assert_string_equal(parsed->dflt.str, "def-val"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min elements */ + data = ELEMENT_WRAPPER_START "<leaf name=\"leaf\"> <type name=\"type\"/> </leaf>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaf *)siblings; + assert_string_equal(parsed->name, "leaf"); + assert_string_equal(parsed->type.name, "type"); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_leaf_list_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_leaflist *parsed = NULL; + uint16_t flags; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <default value=\"def-val0\"/>\n" + " <default value=\"def-val1\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <max-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MAX; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + assert_string_equal(parsed->dflts[0].str, "def-val0"); + assert_string_equal(parsed->dflts[1].str, "def-val1"); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->max, 5); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <min-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->min, 5); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <max-elements value=\"15\"/>\n" + " <min-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN | LYS_SET_MAX; + CHECK_LYSP_NODE(parsed, "desc", 0, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->min, 5); + assert_int_equal(parsed->max, 15); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <type name=\"type\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + assert_string_equal(parsed->name, "llist"); + assert_string_equal(parsed->type.name, "type"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* invalid combinations */ + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <max-elements value=\"5\"/>\n" + " <min-elements value=\"15\"/>\n" + " <type name=\"type\"/>" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid combination of min-elements and max-elements: min value 15 is bigger than the max value 5.", + NULL, 4); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <default value=\"def-val1\"/>\n" + " <min-elements value=\"15\"/>\n" + " <type name=\"type\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid combination of sub-elemnts \"min-elements\" and \"default\" in \"leaf-list\" element.", NULL, 5); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"type\" of \"leaf-list\" element.", NULL, 1); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_presence_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<presence value=\"presence-val\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "presence-val"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<presence/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of presence element.", NULL, 1); +} + +static void +test_key_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<key value=\"key-value\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "key-value"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<key/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of key element.", NULL, 1); +} + +static void +test_uses_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_uses *parsed = NULL; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<uses name=\"uses-name\">\n" + " <when condition=\"cond\" />\n" + " <if-feature name=\"feature\" />\n" + " <status value=\"obsolete\" />\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <refine target-node=\"target\"/>\n" + " <augment target-node=\"target\" />\n" + EXT_SUBELEM + "</uses>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_uses *)&siblings[0]; + CHECK_LYSP_NODE(parsed, "desc", 1, LYS_STATUS_OBSLT, 1, + "uses-name", 0, LYS_USES, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_string_equal(parsed->refines->nodeid, "target"); + assert_string_equal(parsed->augments->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_USES); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<uses name=\"uses-name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(siblings[0].name, "uses-name"); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_list_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_list *parsed = NULL; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<list name=\"list-name\">\n" + " <when condition=\"when\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <key value=\"key\"/>\n" + " <unique tag=\"utag\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"10\"/>\n" + " <ordered-by value=\"user\"/>\n" + " <status value=\"deprecated\"/>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + " <action name=\"action\"/>\n" + " <grouping name=\"grp\"/>\n" + " <notification name=\"notf\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_list *)&siblings[0]; + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "cont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(parsed->child->next->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + uint16_t flags = LYS_ORDBY_USER | LYS_STATUS_DEPRC | LYS_CONFIG_W | LYS_SET_MIN; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "list-name", 0, LYS_LIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when", NULL, 0, NULL); + assert_string_equal(parsed->groupings->name, "grp"); + assert_string_equal(parsed->actions->name, "action"); + assert_int_equal(parsed->groupings->nodetype, LYS_GROUPING); + assert_string_equal(parsed->notifs->name, "notf"); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->key, "key"); + assert_int_equal(parsed->min, 10); + assert_string_equal(parsed->typedefs->name, "tpdf"); + assert_string_equal(parsed->uniques->str, "utag"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LIST); + lysp_node_free(&fctx, siblings); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<list name=\"list-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_list *)&siblings[0]; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "list-name", 0, LYS_LIST, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_notification_elem(void **state) +{ + const char *data; + struct lysp_node_notif *notifs = NULL; + struct tree_node_meta notif_meta = {NULL, (struct lysp_node **)¬ifs}; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<notification name=\"notif-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <must condition=\"cond\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + " <grouping name=\"grp\"/>\n" + EXT_SUBELEM + "</notification>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, ¬if_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(notifs->name, "notif-name"); + assert_string_equal(notifs->child->name, "anyd"); + assert_int_equal(notifs->child->nodetype, LYS_ANYDATA); + assert_string_equal(notifs->child->next->name, "anyx"); + assert_int_equal(notifs->child->next->nodetype, LYS_ANYXML); + assert_string_equal(notifs->child->next->next->name, "leaf"); + assert_int_equal(notifs->child->next->next->nodetype, LYS_LEAF); + assert_string_equal(notifs->child->next->next->next->name, "llist"); + assert_int_equal(notifs->child->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(notifs->child->next->next->next->next->name, "sub-list"); + assert_int_equal(notifs->child->next->next->next->next->nodetype, LYS_LIST); + assert_true(notifs->flags & LYS_STATUS_DEPRC); + assert_string_equal(notifs->groupings->name, "grp"); + assert_int_equal(notifs->groupings->nodetype, LYS_GROUPING); + assert_string_equal(notifs->child->next->next->next->next->next->name, "uses-name"); + assert_int_equal(notifs->child->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(notifs->child->next->next->next->next->next->next->name, "cont"); + assert_int_equal(notifs->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER); + assert_int_equal(notifs->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(notifs->child->next->next->next->next->next->next->next->name, "choice"); + assert_null(notifs->child->next->next->next->next->next->next->next->next); + assert_string_equal(notifs->iffeatures[0].str, "iff"); + assert_string_equal(notifs->musts->arg.str, "cond"); + assert_int_equal(notifs->nodetype, LYS_NOTIF); + assert_null(notifs->parent); + assert_string_equal(notifs->ref, "ref"); + assert_string_equal(notifs->typedefs->name, "tpdf"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(notifs->exts[0]), LY_STMT_NOTIFICATION); + lysp_node_free(&fctx, (struct lysp_node *)notifs); + notifs = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<notification name=\"notif-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, ¬if_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(notifs->name, "notif-name"); + lysp_node_free(&fctx, (struct lysp_node *)notifs); + notifs = NULL; +} + +static void +test_grouping_elem(void **state) +{ + const char *data; + struct lysp_node_grp *grps = NULL; + struct tree_node_meta grp_meta = {NULL, (struct lysp_node **)&grps}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<grouping name=\"grp-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <notification name=\"notf\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <action name=\"act\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</grouping>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(grps->name, "grp-name"); + assert_string_equal(grps->child->name, "anyd"); + assert_string_equal(grps->child->next->name, "anyx"); + assert_string_equal(grps->child->next->next->name, "leaf"); + assert_string_equal(grps->child->next->next->next->name, "llist"); + assert_string_equal(grps->child->next->next->next->next->name, "list"); + assert_string_equal(grps->dsc, "desc"); + assert_true(grps->flags & LYS_STATUS_CURR); + assert_string_equal(grps->groupings->name, "sub-grp"); + assert_int_equal(grps->nodetype, LYS_GROUPING); + assert_string_equal(grps->notifs->name, "notf"); + assert_null(grps->parent); + assert_string_equal(grps->ref, "ref"); + assert_string_equal(grps->typedefs->name, "tpdf"); + assert_string_equal(grps->actions->name, "act"); + assert_string_equal(grps->child->next->next->next->next->next->name, "uses-name"); + assert_int_equal(grps->child->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(grps->child->next->next->next->next->next->next->name, "cont"); + assert_int_equal(grps->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(grps->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(grps->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(grps->exts[0]), LY_STMT_GROUPING); + lysp_node_free(&fctx, &grps->node); + grps = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<grouping name=\"grp-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(grps->name, "grp-name"); + lysp_node_free(&fctx, &grps->node); + grps = NULL; +} + +static void +test_container_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_container *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<container name=\"cont-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <config value=\"true\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"sub-grp\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <notification name=\"notf\"/>\n" + " <presence value=\"presence\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <when condition=\"when-cond\"/>\n" + " <action name=\"act\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</container>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_container *)siblings; + uint16_t flags = LYS_CONFIG_W | LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "cont-name", 0, LYS_CONTAINER, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->presence, "presence"); + assert_string_equal(parsed->typedefs->name, "tpdf"); + assert_string_equal(parsed->groupings->name, "sub-grp"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + assert_string_equal(parsed->notifs->name, "notf"); + assert_string_equal(parsed->actions->name, "act"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CONTAINER); + lysp_node_free(&fctx, siblings); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<container name=\"cont-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_container *)siblings; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "cont-name", 0, LYS_CONTAINER, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_case_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_case *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<case name=\"case-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <when condition=\"when-cond\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</case>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_case *)siblings; + uint16_t flags = LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "case-name", 0, LYS_CASE, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CASE); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<case name=\"case-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_case *)siblings; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "case-name", 0, LYS_CASE, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_choice_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_choice *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<choice name=\"choice-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <case name=\"sub-case\"/>\n" + " <choice name=\"choice\"/>\n" + " <config value=\"true\"/>\n" + " <container name=\"subcont\"/>\n" + " <default value=\"def\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <mandatory value=\"true\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</choice>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_choice *)siblings; + uint16_t flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "choice-name", 0, LYS_CHOICE, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "sub-case"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CASE); + assert_string_equal(parsed->child->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(parsed->child->next->next->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CHOICE); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<choice name=\"choice-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_choice *)siblings; + assert_string_equal(parsed->name, "choice-name"); + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "choice-name", 0, LYS_CHOICE, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_inout_elem(void **state) +{ + const char *data; + struct lysp_node_action_inout inout = {0}; + struct inout_meta inout_meta = {NULL, &inout}; + + /* max subelements */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<input>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</input>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_INPUT, 0, 1); + CHECK_LYSP_RESTR(inout.musts, "cond", NULL, NULL, NULL, 0, NULL); + assert_string_equal(inout.typedefs->name, "tpdf"); + assert_string_equal(inout.groupings->name, "sub-grp"); + assert_string_equal(inout.child->name, "anyd"); + assert_int_equal(inout.child->nodetype, LYS_ANYDATA); + assert_string_equal(inout.child->next->name, "anyx"); + assert_int_equal(inout.child->next->nodetype, LYS_ANYXML); + assert_string_equal(inout.child->next->next->name, "choice"); + assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE); + assert_string_equal(inout.child->next->next->next->name, "subcont"); + assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(inout.child->next->next->next->next->name, "leaf"); + assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(inout.child->next->next->next->next->next->name, "llist"); + assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(inout.child->next->next->next->next->next->next->name, "list"); + assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(inout.child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_INPUT); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* max subelements */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<output>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</output>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_OUTPUT, 0, 1); + assert_string_equal(inout.musts->arg.str, "cond"); + assert_string_equal(inout.typedefs->name, "tpdf"); + assert_string_equal(inout.groupings->name, "sub-grp"); + assert_string_equal(inout.child->name, "anyd"); + assert_int_equal(inout.child->nodetype, LYS_ANYDATA); + assert_string_equal(inout.child->next->name, "anyx"); + assert_int_equal(inout.child->next->nodetype, LYS_ANYXML); + assert_string_equal(inout.child->next->next->name, "choice"); + assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE); + assert_string_equal(inout.child->next->next->next->name, "subcont"); + assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(inout.child->next->next->next->next->name, "leaf"); + assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(inout.child->next->next->next->next->next->name, "llist"); + assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(inout.child->next->next->next->next->next->next->name, "list"); + assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(inout.child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_OUTPUT); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<input><leaf name=\"l\"><type name=\"empty\"/></leaf></input>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + data = ELEMENT_WRAPPER_START "<output><leaf name=\"l\"><type name=\"empty\"/></leaf></output>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* invalid combinations */ + data = ELEMENT_WRAPPER_START "<input name=\"test\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_EVALID); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + CHECK_LOG_CTX("Unexpected attribute \"name\" of \"input\" element.", NULL, 1); + memset(&inout, 0, sizeof inout); +} + +static void +test_action_elem(void **state) +{ + const char *data; + struct lysp_node_action *actions = NULL; + struct tree_node_meta act_meta = {NULL, (struct lysp_node **)&actions}; + uint16_t flags; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<action name=\"act\">\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"grouping\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <input><uses name=\"uses-name\"/></input>\n" + " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM + "</action>" + ELEMENT_WRAPPER_END; + /* there must be parent for action */ + act_meta.parent = (void *)1; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + act_meta.parent = NULL; + flags = LYS_STATUS_DEPRC; + CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\ + 1, 0, 0, 0,\ + 1, 0,\ + "act", LYS_ACTION, \ + 1, 0, 0, 1,\ + 1, 0,\ + 1, "ref", 1); + + assert_string_equal(actions->iffeatures[0].str, "iff"); + assert_string_equal(actions->typedefs->name, "tpdf"); + assert_string_equal(actions->groupings->name, "grouping"); + assert_string_equal(actions->output.musts->arg.str, "cond"); + assert_string_equal(actions->input.child->name, "uses-name"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_ACTION); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; + + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<rpc name=\"act\">\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"grouping\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <input><uses name=\"uses-name\"/></input>\n" + " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM + "</rpc>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + flags = LYS_STATUS_DEPRC; + CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\ + 1, 0, 0, 0,\ + 1, 0,\ + "act", LYS_RPC, \ + 1, 0, 0, 1,\ + 1, 0,\ + 0, "ref", 1); + + assert_string_equal(actions->iffeatures[0].str, "iff"); + assert_string_equal(actions->typedefs->name, "tpdf"); + assert_string_equal(actions->groupings->name, "grouping"); + assert_string_equal(actions->input.child->name, "uses-name"); + assert_string_equal(actions->output.musts->arg.str, "cond"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_RPC); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<action name=\"act\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(actions->name, "act"); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; +} + +static void +test_augment_elem(void **state) +{ + const char *data; + struct lysp_node_augment *augments = NULL; + struct tree_node_meta aug_meta = {NULL, (struct lysp_node **)&augments}; + + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<augment target-node=\"target\">\n" + " <action name=\"action\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <case name=\"case\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <notification name=\"notif\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <uses name=\"uses\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</augment>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(augments->nodeid, "target"); + assert_null(augments->parent); + assert_int_equal(augments->nodetype, LYS_AUGMENT); + assert_true(augments->flags & LYS_STATUS_CURR); + assert_string_equal(augments->dsc, "desc"); + assert_string_equal(augments->ref, "ref"); + assert_string_equal(augments->when->cond, "when-cond"); + assert_string_equal(augments->iffeatures[0].str, "iff"); + assert_string_equal(augments->child->name, "anyd"); + assert_int_equal(augments->child->nodetype, LYS_ANYDATA); + assert_string_equal(augments->child->next->name, "anyx"); + assert_int_equal(augments->child->next->nodetype, LYS_ANYXML); + assert_string_equal(augments->child->next->next->name, "case"); + assert_int_equal(augments->child->next->next->nodetype, LYS_CASE); + assert_string_equal(augments->child->next->next->next->name, "choice"); + assert_int_equal(augments->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(augments->child->next->next->next->next->name, "subcont"); + assert_int_equal(augments->child->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(augments->child->next->next->next->next->next->name, "leaf"); + assert_int_equal(augments->child->next->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(augments->child->next->next->next->next->next->next->name, "llist"); + assert_int_equal(augments->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(augments->child->next->next->next->next->next->next->next->name, "list"); + assert_int_equal(augments->child->next->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(augments->child->next->next->next->next->next->next->next->next->name, "uses"); + assert_int_equal(augments->child->next->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(augments->child->next->next->next->next->next->next->next->next->next); + assert_string_equal(augments->actions->name, "action"); + assert_string_equal(augments->notifs->name, "notif"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(augments->exts[0]), LY_STMT_AUGMENT); + lysp_node_free(&fctx, (struct lysp_node *)augments); + augments = NULL; + + data = ELEMENT_WRAPPER_START "<augment target-node=\"target\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(augments->nodeid, "target"); + lysp_node_free(&fctx, (struct lysp_node *)augments); + augments = NULL; +} + +static void +test_deviate_elem(void **state) +{ + const char *data; + struct lysp_deviate *deviates = NULL; + + /* invalid arguments */ + data = ELEMENT_WRAPPER_START "<deviate value=\"\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"invalid\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"ad\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"ad\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"adds\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"adds\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START + "<deviate value=\"not-supported\">\n" + " <must condition=\"c\"/>\n" + "</deviate>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Deviate of this type doesn't allow \"must\" as it's sub-element.", NULL, 2); +} + +static void +test_deviation_elem(void **state) +{ + const char *data; + struct lysp_deviation *deviations = NULL; + + /* invalid */ + data = ELEMENT_WRAPPER_START "<deviation target-node=\"target\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviations, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"deviate\" of \"deviation\" element.", NULL, 1); +} + +static struct lysp_module * +mod_renew(struct lysp_yin_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_module *pmod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + pmod = calloc(1, sizeof *pmod); + ctx->parsed_mods->objs[0] = pmod; + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->parsed = pmod; + pmod->mod->ctx = ly_ctx; + + fctx.mod = pmod->mod; + + return pmod; +} + +static void +test_module_elem(void **state) +{ + const char *data; + struct lysp_module *lysp_mod = mod_renew(YCTX); + + /* max subelems */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <include module=\"b-mod\"/>\n" + " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n" + " <organization><text>org</text></organization>\n" + " <contact><text>contact</text></contact>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <revision date=\"2019-02-02\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"cont\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <augment target-node=\"target\"/>\n" + " <deviation target-node=\"target\">\n" + " <deviate value=\"not-supported\"/>\n" + " </deviation>\n" + " <extension name=\"ext\"/>\n" + " <feature name=\"feature\"/>\n" + " <grouping name=\"grp\"/>\n" + " <identity name=\"ident-name\"/>\n" + " <notification name=\"notf\"/>\n" + " <rpc name=\"rpc-name\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM "\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS); + assert_string_equal(lysp_mod->mod->name, "mod"); + assert_string_equal(lysp_mod->revs[0].date, "2019-02-02"); + assert_string_equal(lysp_mod->mod->ns, "ns"); + assert_string_equal(lysp_mod->mod->prefix, "pref"); + assert_null(lysp_mod->mod->filepath); + assert_string_equal(lysp_mod->mod->org, "org"); + assert_string_equal(lysp_mod->mod->contact, "contact"); + assert_string_equal(lysp_mod->mod->dsc, "desc"); + assert_string_equal(lysp_mod->mod->ref, "ref"); + assert_int_equal(lysp_mod->version, LYS_VERSION_1_1); + CHECK_LYSP_IMPORT(lysp_mod->imports, NULL, 0, "a-mod", + "imp-pref", NULL, ""); + assert_string_equal(lysp_mod->includes->name, "b-mod"); + assert_string_equal(lysp_mod->extensions->name, "ext"); + assert_string_equal(lysp_mod->features->name, "feature"); + assert_string_equal(lysp_mod->identities->name, "ident-name"); + assert_string_equal(lysp_mod->typedefs->name, "tpdf"); + assert_string_equal(lysp_mod->groupings->name, "grp"); + assert_string_equal(lysp_mod->data->name, "anyd"); + assert_int_equal(lysp_mod->data->nodetype, LYS_ANYDATA); + assert_string_equal(lysp_mod->data->next->name, "anyx"); + assert_int_equal(lysp_mod->data->next->nodetype, LYS_ANYXML); + assert_string_equal(lysp_mod->data->next->next->name, "choice"); + assert_int_equal(lysp_mod->data->next->next->nodetype, LYS_CHOICE); + assert_string_equal(lysp_mod->data->next->next->next->name, "cont"); + assert_int_equal(lysp_mod->data->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(lysp_mod->data->next->next->next->next->name, "leaf"); + assert_int_equal(lysp_mod->data->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(lysp_mod->data->next->next->next->next->next->name, "llist"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(lysp_mod->data->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(lysp_mod->data->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(lysp_mod->data->next->next->next->next->next->next->next->next); + assert_string_equal(lysp_mod->augments->nodeid, "target"); + assert_string_equal(lysp_mod->rpcs->name, "rpc-name"); + assert_string_equal(lysp_mod->notifs->name, "notf"); + assert_string_equal(lysp_mod->deviations->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_mod->exts[0]), LY_STMT_MODULE); + + /* min subelems */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_mod = mod_renew(YCTX); + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <yang-version value=\"1.1\"/>\n" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS); + assert_string_equal(lysp_mod->mod->name, "mod"); + + /* incorrect subelem order */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_mod = mod_renew(YCTX); + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <feature name=\"feature\"/>\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <yang-version value=\"1.1\"/>\n" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_EVALID); + CHECK_LOG_CTX("Invalid order of module\'s sub-elements \"namespace\" can\'t appear after \"feature\".", NULL, 3); +} + +static struct lysp_submodule * +submod_renew(struct lysp_yin_ctx *ctx, const char *belongs_to) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_submodule *submod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + submod = calloc(1, sizeof *submod); + ctx->parsed_mods->objs[0] = submod; + submod->mod = calloc(1, sizeof *submod->mod); + lydict_insert(ly_ctx, belongs_to, 0, &submod->mod->name); + submod->mod->parsed = (struct lysp_module *)submod; + submod->mod->ctx = ly_ctx; + + fctx.mod = submod->mod; + + return submod; +} + +static void +test_submodule_elem(void **state) +{ + const char *data; + struct lysp_submodule *lysp_submod = submod_renew(YCTX, "module-name"); + + /* max subelements */ + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <yang-version value=\"1.1\"/>\n" + " <belongs-to module=\"module-name\">\n" + " <prefix value=\"pref\"/>\n" + " </belongs-to>\n" + " <include module=\"b-mod\"/>\n" + " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n" + " <organization><text>org</text></organization>\n" + " <contact><text>contact</text></contact>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <revision date=\"2019-02-02\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"cont\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <augment target-node=\"target\"/>\n" + " <deviation target-node=\"target\">\n" + " <deviate value=\"not-supported\"/>\n" + " </deviation>\n" + " <extension name=\"ext\"/>\n" + " <feature name=\"feature\"/>\n" + " <grouping name=\"grp\"/>\n" + " <identity name=\"ident-name\"/>\n" + " <notification name=\"notf\"/>\n" + " <rpc name=\"rpc-name\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM "\n" + "</submodule>\n"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (mod) are not necessary.", + NULL, 0); + assert_string_equal(lysp_submod->name, "mod"); + assert_string_equal(lysp_submod->revs[0].date, "2019-02-02"); + assert_string_equal(lysp_submod->prefix, "pref"); + assert_null(lysp_submod->filepath); + assert_string_equal(lysp_submod->org, "org"); + assert_string_equal(lysp_submod->contact, "contact"); + assert_string_equal(lysp_submod->dsc, "desc"); + assert_string_equal(lysp_submod->ref, "ref"); + assert_int_equal(lysp_submod->version, LYS_VERSION_1_1); + CHECK_LYSP_IMPORT(lysp_submod->imports, NULL, 0, "a-mod", + "imp-pref", NULL, ""); + assert_string_equal(lysp_submod->includes->name, "b-mod"); + assert_string_equal(lysp_submod->extensions->name, "ext"); + assert_string_equal(lysp_submod->features->name, "feature"); + assert_string_equal(lysp_submod->identities->name, "ident-name"); + assert_string_equal(lysp_submod->typedefs->name, "tpdf"); + assert_string_equal(lysp_submod->groupings->name, "grp"); + assert_string_equal(lysp_submod->data->name, "anyd"); + assert_int_equal(lysp_submod->data->nodetype, LYS_ANYDATA); + assert_string_equal(lysp_submod->data->next->name, "anyx"); + assert_int_equal(lysp_submod->data->next->nodetype, LYS_ANYXML); + assert_string_equal(lysp_submod->data->next->next->name, "choice"); + assert_int_equal(lysp_submod->data->next->next->nodetype, LYS_CHOICE); + assert_string_equal(lysp_submod->data->next->next->next->name, "cont"); + assert_int_equal(lysp_submod->data->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(lysp_submod->data->next->next->next->next->name, "leaf"); + assert_int_equal(lysp_submod->data->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(lysp_submod->data->next->next->next->next->next->name, "llist"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(lysp_submod->data->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(lysp_submod->data->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(lysp_submod->data->next->next->next->next->next->next->next->next); + assert_string_equal(lysp_submod->augments->nodeid, "target"); + assert_string_equal(lysp_submod->rpcs->name, "rpc-name"); + assert_string_equal(lysp_submod->notifs->name, "notf"); + assert_string_equal(lysp_submod->deviations->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_submod->exts[0]), LY_STMT_SUBMODULE); + + /* min subelemnts */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_submod = submod_renew(YCTX, "module-name"); + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS); + assert_string_equal(lysp_submod->prefix, "pref"); + assert_int_equal(lysp_submod->version, LYS_VERSION_1_0); + + /* incorrect subelem order */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_submod = submod_renew(YCTX, "module-name"); + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n" + " <yang-version value=\"1\"/>\n" + " <reference><text>ref</text></reference>\n" + " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_EVALID); + CHECK_LOG_CTX("Invalid order of submodule's sub-elements \"belongs-to\" can't appear after \"reference\".", NULL, 4); +} + +static void +test_yin_parse_module(void **state) +{ + const char *data; + struct lys_module *mod; + struct lysp_yin_ctx *yin_ctx = NULL; + struct ly_in *in = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"a\"> \n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:tests:extensions:metadata:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <import module=\"ietf-yang-metadata\">\n" + " <prefix value=\"md\"/>\n" + " </import>\n" + " <feature name=\"f\"/>\n" + " <md:annotation name=\"x\">\n" + " <description>\n" + " <text>test</text>\n" + " </description>\n" + " <reference>\n" + " <text>test</text>\n" + " </reference>\n" + " <if-feature name=\"f\"/>\n" + " <status value=\"current\"/>\n" + " <type name=\"uint8\"/>\n" + " <units name=\"meters\"/>\n" + " </md:annotation>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + assert_null(mod->parsed->exts->child->next->child); + assert_string_equal(mod->parsed->exts->child->next->arg, "test"); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\"" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"" + " xmlns:foo=\"urn:example:foo\"" + " xmlns:myext=\"urn:example:extensions\">\n" + + " <yang-version value=\"1\"/>\n" + + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + + " <import module=\"example-extensions\">\n" + " <prefix value=\"myext\"/>\n" + " </import>\n" + + " <list name=\"interface\">\n" + " <key value=\"name\"/>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"mtu\">\n" + " <type name=\"uint32\"/>\n" + " <description>\n" + " <text>The MTU of the interface.</text>\n" + " </description>\n" + " <myext:c-define name=\"MY_MTU\"/>\n" + " </leaf>\n" + " </list>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<submodule name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" + "</submodule>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EINVAL); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + "</module>\n" + "<module>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EVALID); + CHECK_LOG_CTX("Trailing garbage \"<module>\" after module, expected end-of-input.", NULL, 6); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; +} + +static void +test_yin_parse_submodule(void **state) +{ + const char *data; + struct lysp_yin_ctx *yin_ctx = NULL; + struct lysp_submodule *submod = NULL; + struct ly_in *in; + + lydict_insert(UTEST_LYCTX, "a", 0, &PARSER_CUR_PMOD(YCTX)->mod->name); + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\"" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"" + " xmlns:a=\"urn:a\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + " <include module=\"atop\"/>\n" + " <feature name=\"fox\"/>\n" + " <notification name=\"bar-notif\">\n" + " <if-feature name=\"bar\"/>\n" + " </notification>\n" + " <notification name=\"fox-notif\">\n" + " <if-feature name=\"fox\"/>\n" + " </notification>\n" + " <augment target-node=\"/a_pref:top\">\n" + " <if-feature name=\"bar\"/>\n" + " <container name=\"bar-sub\"/>\n" + " </augment>\n" + " <augment target-node=\"/top\">\n" + " <container name=\"bar-sub2\"/>\n" + " </augment>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"inval\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EINVAL); + CHECK_LOG_CTX("Input data contains module when a submodule is expected.", NULL, 0); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EVALID); + CHECK_LOG_CTX("Trailing garbage \"<submodule name...\" after submodule, expected end-of-input.", NULL, 8); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; +} + +int +main(void) +{ + + const struct CMUnitTest tests[] = { + UTEST(test_yin_match_keyword, setup, teardown), + UTEST(test_yin_parse_content, setup, teardown), + UTEST(test_validate_value, setup, teardown), + UTEST(test_valid_module), + UTEST(test_print_module), + UTEST(test_print_submodule), + + UTEST(test_yin_match_argument_name), + cmocka_unit_test_setup_teardown(test_enum_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_bit_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_status_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_element_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yangversion_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_argument_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_belongsto_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_config_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_default_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_err_app_tag_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_err_msg_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_fracdigits_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_iffeature_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_length_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_modifier_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_namespace_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_pattern_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_value_position_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_prefix_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_range_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_reqinstance_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_revision_date_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_unique_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_units_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_text_value_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_type_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_max_elems_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_min_elems_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_ordby_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_any_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_leaf_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_leaf_list_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_presence_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_key_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_uses_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_list_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_notification_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_grouping_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_container_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_case_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_choice_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_inout_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_action_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_augment_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_deviate_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_deviation_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_module_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_submodule_elem, setup, teardown), + + cmocka_unit_test_setup_teardown(test_yin_parse_module, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_parse_submodule, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c new file mode 100644 index 0000000..efdc8f1 --- /dev/null +++ b/tests/utests/types/binary.c @@ -0,0 +1,359 @@ +/** + * @file binary.c + * @author Michal VaÅ¡ko <mvasko@cesnet.cz> + * @brief test for built-in binary type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_plugin_store(void **state) +{ + const char *val, *dec_val; + unsigned char bin_val[2]; + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value value = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BINARY]); + struct lysc_type *lysc_type, *lysc_type2; + LY_ERR ly_ret; + const char *schema; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("a", "leaf l {type binary;} leaf k {type binary {length 4..8;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + lysc_type2 = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + + /* check proper type */ + assert_string_equal("libyang 2 - binary, version 1", type->id); + + /* check store XML double pad */ + val = "YWhveQ=="; + dec_val = "ahoy"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* single pad */ + val = "YWhveWo="; + dec_val = "ahoyj"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* no pad */ + val = "YWhveWoy"; + dec_val = "ahoyj2"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* binary data */ + val = "q80="; + bin_val[0] = 0xab; + bin_val[1] = 0xcd; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, bin_val, 2, + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* newlines after every 64 chars */ + val = "MIIEAzCCAuugAwIBAgIURc4sipHvJSlNrQIhRhZilBvV4RowDQYJKoZIhvcNAQEL\n" + "BQAwgZAxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBNb3JhdmlhMQ0wCwYD\n" + "VQQHDARCcm5vMRgwFgYDVQQKDA9DRVNORVQgei5zLnAuby4xDDAKBgNVBAsMA1RN\n" + "QzETMBEGA1UEAwwKZXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs\n" + "ZS5vcmcwHhcNMjEwOTAzMTAyMTAxWhcNMzEwOTAxMTAyMTAxWjCBkDELMAkGA1UE\n" + "BhMCQ1oxFjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xGDAW\n" + "BgNVBAoMD0NFU05FVCB6LnMucC5vLjEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApl\n" + "eGFtcGxlIENBMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLm9yZzCCASIwDQYJ\n" + "KoZIhvcNAQEBBQADggEPADCCAQoCggEBAN4Ld3JDDocyy9KXNJhEUPeZpQW3UdUN\n" + "Xloeh5n/bxasgThkBuQ7oF/nKyVUe517U1CJA993ZIc0jhIWThAnqXkz70DX5EZ7\n" + "ancPd01MidA6T8k1RYYJWr+vyIRYYBYzK7LSnU6wMWqPTgzZB+KMWwb065ooLEB5\n" + "XwqAeTIMPLRqM1Galewl4ZSuRJnrXxRjfF3AWNyC9dZw6wIg8cppvoLdBGQiFJQf\n" + "9SgiVy+HyedAytFEixqKAAIgQUJwhCgbEd6jGFbeaL8HT4MFp1VmaaUBQMkZj/Gn\n" + "KBwCk5BEMu76EN1pzHc4Dd6DabQNGCnsqOPe31yhQGmNFy9R6zNnWZMCAwEAAaNT\n" + "MFEwHQYDVR0OBBYEFM7w/pO8vk5oowvWPoCKo0RW/JcnMB8GA1UdIwQYMBaAFM7w\n" + "/pO8vk5oowvWPoCKo0RW/JcnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n" + "BQADggEBAG/xfYuRKnCyiwYC/K7kAjHmCNnLCr1mx8P1ECsSJPme3OThDTNeGf8i\n" + "N2952tGmMFDa+DaAwPc6Gt3cWTb/NYMTLWlt2yj5rJAcLXxIU0SMafBf+F7E/R8A\n" + "b/HDDjs0pQaJ0EJhQJVkMdfj3Wq9l0dJT5iEBUrUQflDufiMdEJEIGKZh86MgzEL\n" + "bcn1QX8dlLc91M2OifWStqLzXPicG+jjuoPUceC0flMQDb2qx03sxvJKfYfS5ArA\n" + "CqvdWyXLoP7DI9THJrMI/vBHJKpl4Wtmsh2OLn9VHauFMzPSGke5GwjXCpbXGepj\n" + "9qWN8Gd/FWgSDH2OBvZ6aHdB1pPjN9k="; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* empty value */ + val = ""; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, "", "", 0); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* short value */ + val = "YQ=="; + dec_val = "a"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* length check */ + val = "Zm91cg=="; + dec_val = "four"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type2); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type2); + type->free(UTEST_LYCTX, &value); + + val = "ZWlnaHQwMTI="; + dec_val = "eight012"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type2); + type->free(UTEST_LYCTX, &value); + + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, dec_val, strlen(dec_val), + 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val)); + assert_ptr_equal(value.realtype, lysc_type2); + type->free(UTEST_LYCTX, &value); + + /* + * ERROR TESTS + */ + val = "q80."; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + assert_string_equal(err->msg, "Invalid Base64 character '.'."); + ly_err_free(err); + + val = "q80"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + assert_string_equal(err->msg, "Base64 encoded value length must be divisible by 4."); + ly_err_free(err); + + val = "MTIz"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type2, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + assert_string_equal(err->msg, "Unsatisfied length - string \"MTIz\" length is not allowed."); + ly_err_free(err); + + /* LYPLG_TYPE_STORE_ONLY test */ + val = "MTIz"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type2, val, strlen(val), + LYPLG_TYPE_STORE_ONLY, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + type->free(UTEST_LYCTX, &value); + ly_err_free(err); +} + +static void +test_plugin_print(void **state) +{ + const char *schema, *val; + struct lyd_value value = {0}; + struct lys_module *mod; + struct lysc_type *lysc_type; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BINARY]); + struct ly_err_item *err = NULL; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* Testing empty value. */ + val = ""; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + assert_string_equal("", value.realtype->plugin->print(UTEST_LYCTX, &(value), LY_VALUE_CANON, NULL, NULL, NULL)); + type->free(UTEST_LYCTX, &value); +} + +static void +test_plugin_duplicate(void **state) +{ + const char *schema, *val; + struct lyd_value value = {0}, dup; + struct lys_module *mod; + struct lysc_type *lysc_type; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BINARY]); + struct ly_err_item *err = NULL; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* Testing empty value. */ + val = ""; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &value, &dup)); + CHECK_LYD_VALUE(dup, BINARY, "", "", 0); + type->free(UTEST_LYCTX, &value); + type->free(UTEST_LYCTX, &dup); +} + +static void +test_plugin_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BINARY]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("a", "leaf-list ll {type binary;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data)->type; + + /* v1 < v2, v2 > v1, v1 == v1 */ + v1 = "YWhveQ=="; /* ahoy */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "YWhveg=="; /* ahoz */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* v2 is shorter */ + v1 = "YWhveQ=="; /* ahoj */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "YWhv"; /* aho */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +static void +test_data_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", "leaf port {type binary;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "port", ""); + TEST_SUCCESS_LYB("lyb", "port", "YWhveQ=="); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_plugin_store), + UTEST(test_plugin_print), + UTEST(test_plugin_duplicate), + UTEST(test_plugin_sort), + UTEST(test_data_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c new file mode 100644 index 0000000..3555377 --- /dev/null +++ b/tests/utests/types/bits.c @@ -0,0 +1,1121 @@ +/** + * @file bits.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for int8 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_ERROR_JSON(MOD_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_type_bits *lysc_type; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero;\nbit one;" + " bit ten{position 10;}\tbit \"eleven\"; bit last{position 4294967295; }}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 5); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, 0, "last", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 5, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T1", "leaf port {type bits { bit _ten {position 10;} bit _ten-one;" + " bit _two {position 2;} bit ten_end...;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "_two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "_ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "_ten-one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "ten_end...", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); + + /* TEST MODULE SUBTYPE */ + schema = MODULE_CREATE_YANG("T2", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "twelve", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YANG("T3", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}" + "leaf port {type my_type {" + " bit ten {position 10;} bit two;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 2); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 2, 0, 0, 0, 0x02, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* + * TEST ERROR + */ + /* test change bit possition */ + schema = MODULE_CREATE_YANG("TERR_0", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit \"twelve\";}}" + "leaf port {type my_type {" + " bit ten {position 11;} bit two;}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", + "/TERR_0:port", 0); + + /* add new bit */ + schema = MODULE_CREATE_YANG("TERR_1", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}" + "leaf port {type my_type {" + " bit ten {position 10;} bit two; bit test;}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port", 0); + + /* different max value => autoadd index */ + schema = MODULE_CREATE_YANG("TERR_2", "leaf port {type bits {" + " bit first {position -1;} bit second;" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"position\".", NULL, 5); + + /* different max value => autoadd index */ + schema = MODULE_CREATE_YANG("TERR_3", "leaf port {type bits {" + " bit first {position 4294967295;} bit second;" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.", + "/TERR_3:port", 0); + + schema = MODULE_CREATE_YANG("TERR_4", "leaf port {type bits {" + " bit first {position 10;} bit \"\";" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL, 0); + CHECK_LOG_CTX("Statement argument is required.", NULL, 5); + + /* wrong character */ + schema = MODULE_CREATE_YANG("TERR_5", "leaf port {type bits {" + " bit first {position 10;} bit abcd^;" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier character '^' (0x005e).", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_6", "leaf port {type bits {" + " bit hi; bit hi;" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"hi\" of bit statement.", NULL, 5); + + /* wrong character */ + schema = MODULE_CREATE_YANG("TERR_7", "leaf port {type bits {" + " bit first {position 10;} bit \"ab&cd\";" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier character '&' (0x0026).", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_8", "leaf port {type bits {" + " bit first {position 10;} bit \"4abcd\";" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier first character '4' (0x0034).", NULL, 5); + + schema = MODULE_CREATE_YANG("TERR_9", "leaf port {type bits;}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Missing bit substatement for bits type.", "/TERR_9:port", 0); + + /* new features of YANG 1.1 in YANG 1.0 */ + schema = "module TERR_10 {" + " namespace \"urn:tests:TERR_10\";" + " prefix pref;" + " feature f;" + " leaf l {type bits {" + " bit one {if-feature f;}" + " }}" + "}"; + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_10\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"if-feature\" as a child of \"bit\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + + schema = "module TERR_11 {" + " namespace \"urn:tests:TERR_10\";" + " prefix pref;" + " typedef mytype {type bits {bit one;}}" + " leaf l {type mytype {bit one;}}" + "}"; + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Bits type can be subtyped only in YANG 1.1 modules.", "/TERR_11:l", 0); + + /* feature is not present */ + schema = MODULE_CREATE_YANG("IF_0", "feature f;" + "leaf port {type bits { bit zero;\nbit one;" + " bit ten{if-feature f; position 10;}\tbit eleven;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_int_equal(LY_ENOT, lys_feature_value (mod, "f")); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 3); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); + + /* feature is present */ + schema = MODULE_CREATE_YANG("IF_1", "feature f;" + "leaf port {type bits { bit zero;\nbit one;" + " bit ten{position 10; if-feature f;}\tbit eleven;}}"); + const char *IF_1_FEATURES[] = {"f", NULL}; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, IF_1_FEATURES, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_type_bits *lysc_type; + + schema = MODULE_CREATE_YIN("T0", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"zero\"/> <bit name=\"one\"/>" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"eleven\"/>" + " <bit name=\"last\"> <position value=\"4294967295\"/> </bit>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 5); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, 0, "last", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 5, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T1", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"_ten\"> <position value=\"10\"/> </bit> <bit name=\"_ten-one\"/>" + " <bit name=\"_two\"> <position value=\"2\"/> </bit> <bit name=\"ten_end...\"/>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "_two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "_ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "_ten-one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "ten_end...", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); + + /* TEST MODULE SUBTYPE */ + schema = MODULE_CREATE_YIN("T2", + "<typedef name=\"my_type\"> <type name=\"bits\">" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"eleven\"/> <bit name=\"two\"> <position value=\"2\"/> </bit>" + " <bit name=\"twelve\"/>" + "</type> </typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"/></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "twelve", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("T3", "<typedef name=\"my_type\"> <type name=\"bits\">" + " <bit name=\"ten\"> <position value=\"10\"/></bit>" + " <bit name=\"eleven\"/> <bit name=\"two\"> <position value=\"2\"/> </bit>" + " <bit name=\"twelve\"/>" + "</type></typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"two\"/>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + lysc_type = (struct lysc_type_bits *) lysc_leaf->type; + CHECK_LYSC_TYPE_BITS(lysc_type, 0, 2); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 2, 0, 0, 0, 0x02, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* + * TEST ERROR + */ + /* test change bit possition */ + schema = MODULE_CREATE_YIN("TERR_0", "<typedef name=\"my_type\"> <type name=\"bits\">" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"eleven\"/>" + " <bit name=\"two\"> <position value=\"2\"/> </bit>" + " <bit name=\"twelve\"/>" + "</type></typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <bit name=\"ten\"> <position value=\"11\"/> </bit> <bit name=\"two\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", + "/TERR_0:port", 0); + + /* add new bit */ + schema = MODULE_CREATE_YIN("TERR_1", + "<typedef name=\"my_type\"> <type name=\"bits\">" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"eleven\"/>" + " <bit name=\"two\"> <position value=\"2\"/> </bit>" + " <bit name=\"twelve\"/>" + "</type></typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <bit name=\"ten\"> <position value=\"10\"/> </bit>" + " <bit name=\"two\"/>" + " <bit name=\"test\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port", 0); + + /* different max value => autoadd index */ + schema = MODULE_CREATE_YIN("TERR_2", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"first\"> <position value=\"-1\"> </bit>" + " <bit name=\"second\">" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"position\" element.", NULL, 8); + + /* different max value => autoadd index */ + schema = MODULE_CREATE_YIN("TERR_3", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"first\"> <position value=\"4294967295\"/> </bit>" + " <bit name=\"second\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.", + "/TERR_3:port", 0); + + schema = MODULE_CREATE_YIN("TERR_4", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\" ahoj \"> <position value=\"20\"/> </bit>" + " <bit name=\"second\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier first character ' ' (0x0020).", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_5", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"ah oj\"> <position value=\"20\"/> </bit>" + " <bit name=\"second\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier character ' ' (0x0020).", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_6", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"hi\"/> " + " <bit name=\"hi\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"hi\" of bit statement.", NULL, 8); + + schema = MODULE_CREATE_YIN("TERR_7", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"4ahoj\"> <position value=\"20\"/> </bit>" + " <bit name=\"second\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid identifier first character '4' (0x0034).", NULL, 8); + + /* TEST EMPTY NAME*/ + schema = MODULE_CREATE_YIN("TERR_8", + "<leaf name=\"port\"> <type name=\"bits\">" + " <bit name=\"\"> <position value=\"20\"/> </bit>" + " <bit name=\"second\"/>" + "</type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL, 0); + CHECK_LOG_CTX("Empty identifier is not allowed.", NULL, 8); +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", + "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;}}" + "leaf port {type my_type {" + " bit ten {position 10;} bit two;}}"); + + schema_yin = MODULE_CREATE_YIN("PRINT0", + " <typedef name=\"my_type\">\n" + " <type name=\"bits\">\n" + " <bit name=\"ten\">\n" + " <position value=\"10\"/>\n" + " </bit>\n" + " <bit name=\"eleven\"/>\n" + " <bit name=\"two\">\n" + " <position value=\"2\"/>\n" + " </bit>\n" + " <bit name=\"twelve\"/>\n" + " </type>\n" + " </typedef>\n" + " <leaf name=\"port\">\n" + " <type name=\"my_type\">\n" + " <bit name=\"ten\">\n" + " <position value=\"10\"/>\n" + " </bit>\n" + " <bit name=\"two\"/>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " typedef my_type {\n" + " type bits {\n" + " bit ten {\n" + " position 10;\n" + " }\n" + " bit eleven;\n" + " bit two {\n" + " position 2;\n" + " }\n" + " bit twelve;\n" + " }\n" + " }\n\n" + " leaf port {\n" + " type my_type {\n" + " bit ten {\n" + " position 10;\n" + " }\n" + " bit two;\n" + " }\n" + " }\n"); + + schema_yin = MODULE_CREATE_YIN("PRINT1", + " <typedef name=\"my_type\">\n" + " <type name=\"bits\">\n" + " <bit name=\"ten\">\n" + " <position value=\"10\"/>\n" + " </bit>\n" + " <bit name=\"eleven\"/>\n" + " <bit name=\"two\">\n" + " <position value=\"2\"/>\n" + " </bit>\n" + " <bit name=\"twelve\"/>\n" + " </type>\n" + " </typedef>\n" + " <leaf name=\"port\">\n" + " <type name=\"my_type\">\n" + " <bit name=\"ten\">\n" + " <position value=\"10\"/>\n" + " </bit>\n" + " <bit name=\"two\"/>\n" + " </type>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); +} + +static void +test_data_xml(void **state) +{ + + const char *schema; + struct lyd_node *tree; + const char *data; + + /* xml test */ + schema = MODULE_CREATE_YANG("T0", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;" + " bit _test-end...;}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("T0", "ten two twelve", BITS, "two ten twelve", "two", "ten", "twelve"); + TEST_SUCCESS_XML("T0", "ten\ntwo\ttwelve", BITS, "two ten twelve", "two", "ten", "twelve"); + TEST_SUCCESS_XML("T0", "ten two", BITS, "two ten", "two", "ten"); + TEST_SUCCESS_XML("T0", "_test-end...", BITS, "_test-end...", "_test-end..."); + TEST_SUCCESS_XML("T0", "twelve\nten\ttwo \n eleven", BITS, "two ten eleven twelve", + "two", "ten", "eleven", "twelve"); + TEST_SUCCESS_XML("T0", "", BITS, ""); + TEST_SUCCESS_XML("T0", "\n\t", BITS, ""); + + TEST_ERROR_XML("T0", "twelvea"); + CHECK_LOG_CTX("Invalid bit \"twelvea\".", "/T0:port", 1); + TEST_ERROR_XML("T0", "twelve t"); + CHECK_LOG_CTX("Invalid bit \"t\".", "/T0:port", 1); + TEST_ERROR_XML("T0", "ELEVEN"); + CHECK_LOG_CTX("Invalid bit \"ELEVEN\".", "/T0:port", 1); + + /* empty value */ + data = "<port xmlns=\"urn:tests:T0\"/>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, BITS, ""); + lyd_free_all(tree); + +} + +static void +test_data_json(void **state) +{ + const char *schema; + + /* variable for default value test */ + + /* xml test */ + schema = MODULE_CREATE_YANG("T0", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;" + " bit _test-end...;}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_JSON("T0", "ten two twelve", BITS, "two ten twelve", "two", "ten", "twelve"); + TEST_SUCCESS_JSON("T0", "ten\\ntwo\\ttwelve", BITS, "two ten twelve", "two", "ten", "twelve"); + TEST_SUCCESS_JSON("T0", "ten two", BITS, "two ten", "two", "ten"); + TEST_SUCCESS_JSON("T0", "_test-end...", BITS, "_test-end...", "_test-end..."); + TEST_SUCCESS_JSON("T0", "twelve\\nten\\ttwo \\n eleven", BITS, "two ten eleven twelve", + "two", "ten", "eleven", "twelve"); + TEST_SUCCESS_JSON("T0", "", BITS, ""); + TEST_SUCCESS_JSON("T0", "\\n\\t", BITS, ""); + + TEST_ERROR_JSON("T0", "twelvea"); + CHECK_LOG_CTX("Invalid character sequence \"twelvea}\", expected a JSON value.", NULL, 1); + TEST_ERROR_JSON("T0", "twelve t"); + CHECK_LOG_CTX("Invalid character sequence \"twelve t}\", expected a JSON value.", NULL, 1); + TEST_ERROR_JSON("T0", "ELEVEN"); + CHECK_LOG_CTX("Invalid character sequence \"ELEVEN}\", expected a JSON value.", NULL, 1); +} + +static void +test_data_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", "typedef my_type{type bits {" + " bit ten {position 10;} bit eleven; bit two {position 2;} bit twelve;" + " bit _test-end...;}}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "port", "ten twelve"); + TEST_SUCCESS_LYB("lyb", "port", "two"); + TEST_SUCCESS_LYB("lyb", "port", ""); +} + +static void +test_diff(void **state) +{ + const char *schema; + struct lyd_node *model_1, *model_2; + struct lyd_node *diff; + const char *expected_string; + const char *data_1; + const char *data_2; + const char *diff_expected; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;" + " bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>"; + data_2 = "<port xmlns=\"urn:tests:T0\"> one</port>"; + diff_expected = "<port xmlns=\"urn:tests:T0\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\"" + " yang:orig-default=\"false\" yang:orig-value=\"two three\">one</port>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2) + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* create data from diff */ + diff_expected = "<port xmlns=\"urn:tests:T0\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\"" + " yang:orig-default=\"false\" yang:orig-value=\"two three \"></port>"; + CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff) + data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + expected_string = "<port xmlns=\"urn:tests:T0\"/>"; + + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + lyd_free_all(model_1); + lyd_free_all(diff); + + /* create data from diff */ + diff_expected = "<port xmlns=\"urn:tests:T0\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"replace\"" + " yang:orig-default=\"false\" yang:orig-value=\"two three\"> one </port>"; + CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff) + data_1 = "<port xmlns=\"urn:tests:T0\"> two three </port>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + expected_string = "<port xmlns=\"urn:tests:T0\">one</port>"; + + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + lyd_free_all(model_1); + lyd_free_all(diff); + +} + +static void +test_print(void **state) +{ + const char *schema; + const char *expected_string; + struct lyd_node *model_1; + const char *data; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;" + " bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* print zero bits */ + data = "<port xmlns=\"urn:tests:T0\"></port>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + /* XML */ + expected_string = "<port xmlns=\"urn:tests:T0\"/>"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* JSON */ + expected_string = "{\"T0:port\":\"\"}"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* free */ + lyd_free_all(model_1); + + /* print one bit */ + data = "<port xmlns=\"urn:tests:T0\"> two </port>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + /* XML */ + expected_string = "<port xmlns=\"urn:tests:T0\">two</port>"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* JSON */ + expected_string = "{\"T0:port\":\"two\"}"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* free */ + lyd_free_all(model_1); + + /* print two bits */ + data = "<port xmlns=\"urn:tests:T0\">three two </port>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1) + /* XML */ + expected_string = "<port xmlns=\"urn:tests:T0\">two three</port>"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* JSON */ + expected_string = "{\"T0:port\":\"two three\"}"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + /* free */ + lyd_free_all(model_1); +} + +static void +test_plugin_store(void **state) +{ + const char *val_text = NULL; + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value value = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BITS]); + struct lysc_type *lysc_type; + struct lysc_type lysc_type_test; + LY_ERR ly_ret; + char *alloc; + const char *schema; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type bits { bit zero; bit one;" + " bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* check proper type */ + assert_string_equal("libyang 2 - bits, version 1", type->id); + + /* check store + */ + val_text = ""; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BITS, ""); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "zero one two"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BITS, "zero one two", "zero", "one", "two"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "zero two"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, BITS, "zero two", "zero", "two"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "\n "; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, BITS, ""); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* + * minor tests + * dynamic alocated input text + */ + val_text = "two"; + alloc = (char *)malloc(strlen(val_text) + 1); + memcpy(alloc, val_text, strlen(val_text) + 1); + ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc, strlen(val_text), + LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + alloc = NULL; + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, BITS, "two", "two"); + type->free(UTEST_LYCTX, &value); + + /* wrong lysc_type of value */ + lysc_type_test = *lysc_type; + lysc_type_test.basetype = LY_TYPE_INT8; + val_text = "two"; + ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EINVAL, ly_ret); + ly_err_free(err); + + /* + * ERROR TESTS + */ + val_text = "two"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = "two two"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); +} + +static void +test_plugin_compare(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BITS]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + const char *schema; + /* different type */ + const char *diff_type_text = "20"; + struct lyd_value diff_type_val; + struct lysc_type *diff_type; + /* Value which are going to be created to tested */ + const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "typedef my_int_type { type bits { bit zero; bit one;" + " bit two; bit three;}}" + "leaf p1 {type my_int_type;}" + "leaf p2 {type my_int_type;}" + "leaf p3 {type my_int_type{bit three; bit zero;}}" + "leaf p4 {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* + * BASIC TEST; + */ + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[0]), &(values[0]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[1]), &(values[3]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[0]), &(values[1]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[3]), &(values[4]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[2]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[2]), &(values[5]))); + + /* + * SAME TYPE but different node + */ + diff_type_text = val_init[2]; + diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &diff_type_val, &(values[2]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[1]))); + type->free(UTEST_LYCTX, &(diff_type_val)); + + /* delete values */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_sort(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BITS]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + struct lyd_value val1 = {0}, val2 = {0}; + + schema = MODULE_CREATE_YANG("T0", "leaf-list ll { type bits { bit zero; bit one; bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* 1000 < 1001 */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, "three", strlen("three"), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, "three one", strlen("three one"), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* 0011 == 0011 */ + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, "zero one", strlen("zero one"), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, "one zero", strlen("one zero"), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +static void +test_plugin_print(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BITS]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + const char *schema; + /* Value which are going to be created to tested */ + const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", + "leaf p1 { type bits { bit zero; bit one;" + " bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print value */ + ly_bool dynamic = 0; + + assert_string_equal("", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("three", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("zero two", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("zero", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("three", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL)); + + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_dup(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_BITS]); + struct lysc_type *lysc_type; + const char *schema; + LY_ERR ly_ret; + /* Value which are going to be tested */ + const char *val_init[] = {"", "two zero", "three", "zero two", "zero", "three one two zero"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", + "leaf p1 { type bits { bit zero; bit one;" + " bit two; bit three;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print duplicate value */ + struct lyd_value dup_value; + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, ""); + assert_ptr_equal(dup_value.realtype, values[0].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, "zero two", "zero", "two"); + assert_ptr_equal(dup_value.realtype, values[1].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, "three", "three"); + assert_ptr_equal(dup_value.realtype, values[2].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, "zero two", "zero", "two"); + assert_ptr_equal(dup_value.realtype, values[3].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[4]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, "zero", "zero"); + assert_ptr_equal(dup_value.realtype, values[4].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[5]), &dup_value)); + CHECK_LYD_VALUE(dup_value, BITS, "zero one two three", "zero", "one", "two", "three"); + assert_ptr_equal(dup_value.realtype, values[5].realtype); + type->free(UTEST_LYCTX, &dup_value); + + /* error tests */ + assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value)); + + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + UTEST(test_data_xml), + UTEST(test_data_json), + UTEST(test_data_lyb), + UTEST(test_diff), + UTEST(test_print), + + UTEST(test_plugin_store), + UTEST(test_plugin_compare), + UTEST(test_plugin_sort), + UTEST(test_plugin_print), + UTEST(test_plugin_dup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/boolean.c b/tests/utests/types/boolean.c new file mode 100644 index 0000000..2aba30f --- /dev/null +++ b/tests/utests/types/boolean.c @@ -0,0 +1,109 @@ +/** + * @file boolean.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "typedef tboolean {type boolean;}" + "leaf l1 {type boolean;}" + "leaf l2 {type tboolean;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "l1", "true", BOOL, "true", 1); + + TEST_SUCCESS_XML("defs", "l1", "false", BOOL, "false", 0); + + TEST_SUCCESS_XML("defs", "l2", "false", BOOL, "false", 0); + + /* invalid value */ + TEST_ERROR_XML("defs", "l1", "unsure"); + CHECK_LOG_CTX("Invalid boolean value \"unsure\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", " true"); + CHECK_LOG_CTX("Invalid boolean value \" true\".", "/defs:l1", 1); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf bool {type boolean;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "bool", "true"); + TEST_SUCCESS_LYB("lyb", "bool", "false"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/decimal64.c b/tests/utests/types/decimal64.c new file mode 100644 index 0000000..fdd118a --- /dev/null +++ b/tests/utests/types/decimal64.c @@ -0,0 +1,130 @@ +/** + * @file decimal64.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +#define TEST_SUCCESS_PARSE_STORE_ONLY_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STORE_ONLY, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + lyd_free_all(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf l1 {type decimal64 {fraction-digits 1; range 1.5..10;}}" + "leaf l2 {type decimal64 {fraction-digits 18;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "l1", "\n +8 \t\n ", DEC64, "8.0", 80); + TEST_SUCCESS_XML("defs", "l1", "8.00", DEC64, "8.0", 80); + + TEST_SUCCESS_XML("defs", "l2", "-9.223372036854775808", DEC64, "-9.223372036854775808", + INT64_C(-9223372036854775807) - INT64_C(1)); + TEST_SUCCESS_XML("defs", "l2", "9.223372036854775807", DEC64, "9.223372036854775807", INT64_C(9223372036854775807)); + + TEST_ERROR_XML("defs", "l1", "\n 15 \t\n "); + CHECK_LOG_CTX("Unsatisfied range - value \"15.0\" is out of the allowed range.", "/defs:l1", 3); + + TEST_ERROR_XML("defs", "l1", "\n 0 \t\n "); + CHECK_LOG_CTX("Unsatisfied range - value \"0.0\" is out of the allowed range.", "/defs:l1", 3); + + TEST_ERROR_XML("defs", "l1", "xxx"); + CHECK_LOG_CTX("Invalid 1. character of decimal64 value \"xxx\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", ""); + CHECK_LOG_CTX("Invalid empty decimal64 value.", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", "8.5 xxx"); + CHECK_LOG_CTX("Invalid 6. character of decimal64 value \"8.5 xxx\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", "8.55 xxx"); + CHECK_LOG_CTX("Value \"8.55\" of decimal64 type exceeds defined number (1) of fraction digits.", "/defs:l1", 1); + + /* LYPLG_TYPE_STORE_ONLY test */ + TEST_SUCCESS_PARSE_STORE_ONLY_XML("defs", "l1", "\n 15 \t\n "); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "dec64", "8.00"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/empty.c b/tests/utests/types/empty.c new file mode 100644 index 0000000..39b1179 --- /dev/null +++ b/tests/utests/types/empty.c @@ -0,0 +1,106 @@ +/** + * @file empty.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "typedef tempty {type empty;}" + "leaf l1 {type empty;}" + "leaf l2 {type tempty;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "l1", "", EMPTY, ""); + + TEST_SUCCESS_XML("defs", "l2", "", EMPTY, ""); + + /* invalid value */ + TEST_ERROR_XML("defs", "l1", "x"); + CHECK_LOG_CTX("Invalid empty value length 1.", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", " "); + CHECK_LOG_CTX("Invalid empty value length 1.", "/defs:l1", 1); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf empty {type empty;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "empty", ""); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/enumeration.c b/tests/utests/types/enumeration.c new file mode 100644 index 0000000..8ff1c8c --- /dev/null +++ b/tests/utests/types/enumeration.c @@ -0,0 +1,140 @@ +/** + * @file enumeration.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "feature f; leaf l1 {type enumeration {enum white; enum yellow {if-feature f;}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "l1", "white", ENUM, "white", "white"); + + /* disabled feature */ + TEST_ERROR_XML("defs", "l1", "yellow"); + CHECK_LOG_CTX("Invalid enumeration value \"yellow\".", "/defs:l1", 1); + + /* leading/trailing whitespaces */ + TEST_ERROR_XML("defs", "l1", " white"); + CHECK_LOG_CTX("Invalid enumeration value \" white\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", "white\n"); + CHECK_LOG_CTX("Invalid enumeration value \"white\n\".", "/defs:l1", 2); + + /* invalid value */ + TEST_ERROR_XML("defs", "l1", "black"); + CHECK_LOG_CTX("Invalid enumeration value \"black\".", "/defs:l1", 1); +} + +static void +test_plugin_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_ENUM]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + schema = MODULE_CREATE_YANG("sort", "leaf l1 {type enumeration {enum white; enum yellow; enum black;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + v1 = "white"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "black"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", "leaf l1 {type enumeration {enum white; enum yellow; enum black;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "l1", "white"); + TEST_SUCCESS_LYB("lyb", "l1", "black"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_data_xml), + UTEST(test_plugin_sort), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/identityref.c b/tests/utests/types/identityref.c new file mode 100644 index 0000000..16c638e --- /dev/null +++ b/tests/utests/types/identityref.c @@ -0,0 +1,134 @@ +/** + * @file identityref.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_ERROR_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema, *schema2; + struct lyd_node *tree; + const char *data; + + /* xml test */ + schema = "module ident-base {" + " yang-version 1.1;" + " namespace \"urn:tests:ident-base\";" + " prefix ib;" + " identity ident-base;" + " identity ident-imp {base ident-base;}" + "}"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + schema2 = "module defs {" + " yang-version 1.1;" + " namespace \"urn:tests:defs\";" + " prefix d;" + " import ident-base {prefix ib;}" + " identity ident1 {base ib:ident-base;}" + " leaf l1 {type identityref {base ib:ident-base;}}" + "}"; + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + + /* local ident, XML/JSON print */ + data = "<l1 xmlns=\"urn:tests:defs\">ident1</l1>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "defs:ident1", "ident1"); + CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK); + CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident1\"}", LYD_JSON, LYD_PRINT_SHRINK); + lyd_free_all(tree); + + /* foreign ident, XML/JSON print */ + data = "<l1 xmlns=\"urn:tests:defs\" xmlns:ib=\"urn:tests:ident-base\">ib:ident-imp</l1>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "ident-base:ident-imp", "ident-imp"); + CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK); + CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident-base:ident-imp\"}", LYD_JSON, LYD_PRINT_SHRINK); + lyd_free_all(tree); + + /* invalid value */ + TEST_ERROR_XML("defs", "", "l1", "fast-ethernet"); + CHECK_LOG_CTX("Invalid identityref \"fast-ethernet\" value - identity not found in module \"defs\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:defs\"", "l1", "x:slow-ethernet"); + CHECK_LOG_CTX("Invalid identityref \"x:slow-ethernet\" value - identity not found in module \"defs\".", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:ident-base\"", "l1", "x:ident-base"); + CHECK_LOG_CTX("Invalid identityref \"x:ident-base\" value - identity not derived from the base \"ident-base:ident-base\".", + "/defs:l1", 1); + + TEST_ERROR_XML("defs", "xmlns:x=\"urn:tests:unknown\"", "l1", "x:ident-base"); + CHECK_LOG_CTX("Invalid identityref \"x:ident-base\" value - unable to map prefix to YANG schema.", "/defs:l1", 1); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "identity idbase;" + "identity ident {base idbase;}" + "leaf lf {type identityref {base idbase;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "lf", "ident"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/inet_types.c b/tests/utests/types/inet_types.c new file mode 100644 index 0000000..402cade --- /dev/null +++ b/tests/utests/types/inet_types.c @@ -0,0 +1,337 @@ +/** + * @file inet_types.c + * @author Michal VaÅ¡ko <mvasko@cesnet.cz> + * @brief test for ietf-inet-types values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + " import ietf-inet-types {\n" \ + " prefix inet;\n" \ + " }\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_PARSE_STORE_ONLY_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STORE_ONLY, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 4, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_PARSE_STORE_ONLY_XML(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STORE_ONLY, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("a", + "leaf l {type inet:ip-address;}" + "leaf l2 {type inet:ipv6-address;}" + "leaf l3 {type inet:ip-address-no-zone;}" + "leaf l4 {type inet:ipv6-address-no-zone;}" + "leaf l5 {type inet:ip-prefix;}" + "leaf l6 {type inet:ipv4-prefix;}" + "leaf l7 {type inet:ipv6-prefix;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* ip-address */ + TEST_SUCCESS_XML("a", "l", "192.168.0.1", UNION, "192.168.0.1", STRING, "192.168.0.1"); + TEST_SUCCESS_XML("a", "l", "192.168.0.1%12", UNION, "192.168.0.1%12", STRING, "192.168.0.1%12"); + TEST_SUCCESS_XML("a", "l", "2008:15:0:0:0:0:feAC:1", UNION, "2008:15::feac:1", STRING, "2008:15::feac:1"); + + /* ipv6-address */ + TEST_SUCCESS_XML("a", "l2", "FAAC:21:011:Da85::87:daaF%1", STRING, "faac:21:11:da85::87:daaf%1"); + + /* ip-address-no-zone */ + TEST_SUCCESS_XML("a", "l3", "127.0.0.1", UNION, "127.0.0.1", STRING, "127.0.0.1"); + TEST_SUCCESS_XML("a", "l3", "0:00:000:0000:000:00:0:1", UNION, "::1", STRING, "::1"); + + /* ipv6-address-no-zone */ + TEST_SUCCESS_XML("a", "l4", "A:B:c:D:e:f:1:0", STRING, "a:b:c:d:e:f:1:0"); + + /* ip-prefix */ + TEST_SUCCESS_XML("a", "l5", "158.1.58.4/1", UNION, "128.0.0.0/1", STRING, "128.0.0.0/1"); + TEST_SUCCESS_XML("a", "l5", "158.1.58.4/24", UNION, "158.1.58.0/24", STRING, "158.1.58.0/24"); + TEST_SUCCESS_XML("a", "l5", "2000:A:B:C:D:E:f:a/16", UNION, "2000::/16", STRING, "2000::/16"); + + /* ipv4-prefix */ + TEST_SUCCESS_XML("a", "l6", "0.1.58.4/32", STRING, "0.1.58.4/32"); + TEST_SUCCESS_XML("a", "l6", "12.1.58.4/8", STRING, "12.0.0.0/8"); + + /* ipv6-prefix */ + TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/112", STRING, "::c:d:e:f:0/112"); + TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/110", STRING, "::c:d:e:c:0/110"); + TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/96", STRING, "::c:d:e:0:0/96"); + TEST_SUCCESS_XML("a", "l7", "::C:D:E:f:a/55", STRING, "::/55"); +} + +static void +test_data_basic_plugins_only_xml(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("a", "leaf l {type inet:ipv4-address;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* Stored via ipv4-address plugin */ + TEST_SUCCESS_XML("a", "l", "192.168.0.1", STRING, "192.168.0.1"); + TEST_ERROR_XML("a", "l", "192.168.0.333"); + TEST_ERROR_PARSE_STORE_ONLY_XML("a", "l", "192.168.0.333"); + + /* Recreate context to get rid of all plugins */ + ly_ctx_destroy(UTEST_LYCTX); + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_BUILTIN_PLUGINS_ONLY, &UTEST_LYCTX)); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* Stored via string plugin */ + TEST_SUCCESS_XML("a", "l", "192.168.0.1", STRING, "192.168.0.1"); + TEST_ERROR_XML("a", "l", "192.168.0.333"); + CHECK_LOG_CTX("Unsatisfied pattern - \"192.168.0.333\" does not conform to \"" + "(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9]" + "[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(%[\\p{N}\\p{L}]+)?\".", "/a:l", 1); + TEST_SUCCESS_PARSE_STORE_ONLY_XML("a", "l", "192.168.0.333", STRING, "192.168.0.333"); +} + +static void +test_data_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf l {type inet:ip-address;}" + "leaf l2 {type inet:ipv6-address;}" + "leaf l3 {type inet:ip-address-no-zone;}" + "leaf l4 {type inet:ipv6-address-no-zone;}" + "leaf l5 {type inet:ip-prefix;}" + "leaf l6 {type inet:ipv4-prefix;}" + "leaf l7 {type inet:ipv6-prefix;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_LYB("lyb", "l", "192.168.0.1"); + TEST_SUCCESS_LYB("lyb", "l2", "FAAC:21:011:Da85::87:daaF%1"); + TEST_SUCCESS_LYB("lyb", "l3", "127.0.0.1"); + TEST_SUCCESS_LYB("lyb", "l4", "A:B:c:D:e:f:1:0"); + TEST_SUCCESS_LYB("lyb", "l5", "158.1.58.4/1"); + TEST_SUCCESS_LYB("lyb", "l6", "12.1.58.4/8"); + TEST_SUCCESS_LYB("lyb", "l7", "::C:D:E:f:a/112"); +} + +static void +test_plugin_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_UNION]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + schema = MODULE_CREATE_YANG("a", + "leaf l {type inet:ip-address;}" + "leaf l2 {type inet:ipv6-address;}" + "leaf l3 {type inet:ip-address-no-zone;}" + "leaf l4 {type inet:ipv6-address-no-zone;}" + "leaf l5 {type inet:ipv4-prefix;}" + "leaf l6 {type inet:ipv6-prefix;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + /* ipv4-address */ + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + v1 = "192.168.0.1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "192.168.0.2"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + v1 = "192.168.0.1%1A"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "192.168.0.1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* ipv6-address */ + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data->next)->type; + type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + + v1 = "2008:15:0:0:0:0:feAC:1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2008:15::feac:2"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + v1 = "FAAC:21:011:Da85::87:daaF%1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "FAAC:21:011:Da85::87:daaF%14"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* ipv4-address-no-zone */ + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data->next->next)->type; + type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_UNION]); + v1 = "127.0.0.1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "127.0.1.1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* ipv6-address-no-zone */ + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data->next->next->next)->type; + type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + v1 = "A:B:c:D:e:f:1:1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "A:B:c:D:e:f:1:0"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* ipv4-prefix */ + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data->next->next->next->next)->type; + v1 = "0.1.58.4/32"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "0.1.58.4/16"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* ipv6-prefix */ + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data->next->next->next->next->next)->type; + v1 = "::C:D:E:f:a/96"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "::C:D:E:f:a/112"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_data_lyb), + UTEST(test_plugin_sort), + UTEST(test_data_basic_plugins_only_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/instanceid.c b/tests/utests/types/instanceid.c new file mode 100644 index 0000000..ce5a24e --- /dev/null +++ b/tests/utests/types/instanceid.c @@ -0,0 +1,292 @@ +/** + * @file instanceid.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \ + assert_null(tree); \ + } + +#define LYB_CHECK_START \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; + +#define LYB_CHECK_END \ + { \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME1, DATA1, NODE_NAME2, DATA2) \ + LYB_CHECK_START \ + data = "<" NODE_NAME1 " xmlns=\"urn:tests:" MOD_NAME "\">" DATA1 "</" NODE_NAME1 ">" \ + "<xdf:" NODE_NAME2 " xmlns:xdf=\"urn:tests:" MOD_NAME "\">/xdf:" DATA2 "</xdf:" NODE_NAME2 ">"; \ + LYB_CHECK_END \ + +#define TEST_SUCCESS_LYB2(MOD_NAME, NODE_NAME, DATA) \ + { \ + LYB_CHECK_START \ + data = "<" NODE_NAME " xmlns:aa=\"urn:tests:lyb2\" xmlns=\"urn:tests:" MOD_NAME "\">/aa:" DATA "</" NODE_NAME ">"; \ + LYB_CHECK_END \ + } + +static void +test_data_xml(void **state) +{ + const char *schema, *schema2; + const enum ly_path_pred_type val1[] = {0, 0}; + const enum ly_path_pred_type val2[] = {LY_PATH_PREDTYPE_LIST, 0}; + const enum ly_path_pred_type val3[] = {LY_PATH_PREDTYPE_LEAFLIST}; + const enum ly_path_pred_type val4[] = {LY_PATH_PREDTYPE_LIST, 0}; + const enum ly_path_pred_type val5[] = {LY_PATH_PREDTYPE_LIST, 0}; + const enum ly_path_pred_type val6[] = {LY_PATH_PREDTYPE_LIST, 0}; + + /* xml test */ + schema = MODULE_CREATE_YANG("mod", "container cont {leaf l2 {type empty;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + schema2 = MODULE_CREATE_YANG("defs", "identity ident; identity ident-der1 {base ident;} identity ident-der2 {base ident;}" + "leaf l1 {type instance-identifier {require-instance true;}}" + "leaf l2 {type instance-identifier {require-instance false;}}" + "container cont {leaf l {type empty;}}" + "list list {key \"id\"; leaf id {type string;} leaf value {type string;}}" + "leaf-list llist {type uint32;}" + "list list-inst {key \"id\"; leaf id {type instance-identifier;} leaf value {type string;}}" + "list list-ident {key \"id\"; leaf id {type identityref {base ident;}} leaf value {type string;}}" + "list list2 {key \"id id2\"; leaf id {type string;} leaf id2 {type string;}}" + "list list-keyless {config false; leaf value {type string;}}"); + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML2("<cont xmlns=\"urn:tests:defs\"><l/></cont>", "defs", "xmlns:xdf=\"urn:tests:defs\"", "l1", + "/xdf:cont/xdf:l", INST, "/defs:cont/l", val1); + + TEST_SUCCESS_XML2("<list xmlns=\"urn:tests:defs\"><id>a</id></list><list xmlns=\"urn:tests:defs\"><id>b</id></list>", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:list[xdf:id='b']/xdf:id", INST, + "/defs:list[id='b']/id", val2); + + TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:llist[.='1']", INST, "/defs:llist[.='1']", val3); + + TEST_SUCCESS_XML2("<list-inst xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">/b:llist[.='1']</id>" + "<value>x</value></list-inst>" + "<list-inst xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">/b:llist[.='2']</id>" + "<value>y</value></list-inst>" + "<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>", + "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list-inst[a:id=\"/a:llist[.='1']\"]/a:value", + INST, "/defs:list-inst[id=\"/defs:llist[.='1']\"]/value", val4); + + TEST_SUCCESS_XML2("<list-ident xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">b:ident-der1</id>" + "<value>x</value></list-ident>" + "<list-ident xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">b:ident-der2</id>" + "<value>y</value></list-ident>", + "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list-ident[a:id='a:ident-der1']/a:value", + INST, "/defs:list-ident[id='defs:ident-der1']/value", val5); + + TEST_SUCCESS_XML2("<list2 xmlns=\"urn:tests:defs\"><id>defs:xxx</id><id2>x</id2></list2>" + "<list2 xmlns=\"urn:tests:defs\"><id>a:xxx</id><id2>y</id2></list2>", + "defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list2[a:id='a:xxx'][a:id2='y']/a:id2", + INST, "/defs:list2[id='a:xxx'][id2='y']/id2", val6); + + /* syntax/semantic errors */ + TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>a</id></list>" + "<list xmlns=\"urn:tests:defs\"><id>b</id><value>x</value></list>", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:list[2]/xdf:value", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[2]/xdf:value\" value - semantic error: " + "Positional predicate defined for configuration list \"list\" in path.", "/defs:l1", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont/t:1l", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont/t:1l\" value - syntax error: Invalid character 't'[9] of expression '/t:cont/t:1l'.", + "/defs:l1", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont:t:1l", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont:t:1l\" value - syntax error: Invalid character ':'[8] of expression '/t:cont:t:1l'.", + "/defs:l1", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:cont/xdf:invalid/xdf:path", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:cont/xdf:invalid/xdf:path\" value - semantic error: Not found node \"invalid\" in path.", + "/defs:l1", 1); + + /* non-existing instances, instance-identifier is here in JSON format because it is already in internal + * representation without canonical prefixes */ + TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>", + "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2", LY_ENOTFOUND); + CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/mod:cont/l2\" value - required instance not found.", + "/defs:l1", 0, "instance-required"); + + TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist>", + "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:llist[.='2']", LY_ENOTFOUND); + CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:llist[.='2']\" value - required instance not found.", + "/defs:l1", 0, "instance-required"); + + TEST_ERROR_XML2("<list2 xmlns=\"urn:tests:defs\"><id>a</id><id2>a</id2></list2>" + "<list2 xmlns=\"urn:tests:defs\"><id>c</id><id2>b</id2></list2>" + "<llist xmlns=\"urn:tests:defs\">a</llist>" + "<llist xmlns=\"urn:tests:defs\">b</llist>", + "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:list2[a:id='a'][a:id2='a']/a:id", LY_ENOTFOUND); + CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:list2[id='a'][id2='a']/id\" value - required instance not found.", + "/defs:l1", 0, "instance-required"); + + TEST_ERROR_XML2("<list2 xmlns=\"urn:tests:defs\"><id>a</id><id2>a</id2></list2>" + "<list2 xmlns=\"urn:tests:defs\"><id>c</id><id2>b</id2></list2>" + "<llist xmlns=\"urn:tests:defs\">1</llist>" + "<llist xmlns=\"urn:tests:defs\">2</llist>", + "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:llist[.='3']", LY_ENOTFOUND); + CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:llist[.='3']\" value - required instance not found.", + "/defs:l1", 0, "instance-required"); + + TEST_ERROR_XML2("", + "defs", "xmlns:a=\"urn:tests:defs\"", "l1", "/a:list-keyless[3]", LY_ENOTFOUND); + CHECK_LOG_CTX_APPTAG("Invalid instance-identifier \"/defs:list-keyless[3]\" value - required instance not found.", + "/defs:l1", 0, "instance-required"); + + /* more errors */ + TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>", + "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[1", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1\" value - syntax error: Unexpected XPath expression end.", + "/defs:l1", 1); + + TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>", + "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont[1]", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont[1]\" value - semantic error: Positional predicate defined for container \"cont\" in path.", + "/defs:l1", 1); + + TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>", + "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "[1]", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error: Unexpected XPath token \"[\" (\"[1]\"), expected \"Operator(Path)\".", + "/defs:l1", 1); + + TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>", + "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[l2='1']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[l2='1']\" value - syntax error: Prefix missing for \"l2\" in path.", + "/defs:l1", 1); + + TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>", + "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[m:l2='1']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[m:l2='1']\" value - semantic error: List predicate defined for leaf \"l2\" in path.", + "/defs:l1", 1); + + TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>", + "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[4]", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[4]\" value - semantic error: Positional predicate defined for configuration leaf-list \"llist\" in path.", + "/defs:l1", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[6]", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[6]\" value - semantic error: No module connected with the prefix \"t\" found (prefix format XML prefixes).", + "/defs:l2", 1); + + TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:value='x']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:value='x']\" value - semantic error: Key expected instead of leaf \"value\" in path.", + "/defs:l2", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[.='x']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[.='x']\" value - semantic error: Leaf-list predicate defined for list \"list\" in path.", + "/defs:l2", 1); + + TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist>", + "defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[.='x']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='x']\" value - semantic error: Invalid type uint32 value \"x\".", + "/defs:l1", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[1][2]", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1][2]\" value - syntax error: Unparsed characters \"[2]\" left at the end of path.", + "/defs:l2", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[.='a'][.='b']", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='a'][.='b']\" value - syntax error: Unparsed characters \"[.='b']\" left at the end of path.", + "/defs:l2", 1); + + TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value\" value - syntax error: Duplicate predicate key \"id\" in path.", + "/defs:l2", 1); + + TEST_ERROR_XML2("", + "defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list2[xdf:id='1']/xdf:value", LY_EVALID); + CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list2[xdf:id='1']/xdf:value\" value - semantic error: Predicate missing for a key of list \"list2\" in path.", + "/defs:l2", 1); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf-list leaflisttarget {type string;}" + "leaf inst {type instance-identifier {require-instance true;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "leaflisttarget", "1", "inst", "leaflisttarget[.='1']"); + + /* ietf-netconf-acm node-instance-identifier type */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + schema = MODULE_CREATE_YANG("lyb2", + "import ietf-netconf-acm {prefix acm;}" + "leaf-list ll {type string;}" + "leaf nii {type acm:node-instance-identifier;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB2("lyb2", "nii", "ll[. = 'some_string']"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_plugin_lyb), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/instanceid_keys.c b/tests/utests/types/instanceid_keys.c new file mode 100644 index 0000000..34a449f --- /dev/null +++ b/tests/utests/types/instanceid_keys.c @@ -0,0 +1,76 @@ +/** + * @file instanceid_keys.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for yang instance-identifier-keys type + * + * Copyright (c) 2022 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML_NS1(MOD_NAME, NODE_NAME, PREFIX, NS, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" xmlns:" PREFIX "=\"" NS "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "import yang {prefix y;} leaf l1 {type y:instance-identifier-keys;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML_NS1("defs", "l1", "px", "urn:tests:defs", "[px:key='val']", STRING, "[defs:key='val']"); + + TEST_ERROR_XML("defs", "l1", "black"); + CHECK_LOG_CTX("Invalid first character 'b', list key predicates expected.", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", "[this is not a valid xpath]"); + CHECK_LOG_CTX("Invalid character 0x69 ('i'), perhaps \"this\" is supposed to be a function call.", "/defs:l1", 1); + + TEST_ERROR_XML("defs", "l1", "[px:key='val']"); + CHECK_LOG_CTX("Failed to resolve prefix \"px\".", "/defs:l1", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/int16.c b/tests/utests/types/int16.c new file mode 100644 index 0000000..96828e0 --- /dev/null +++ b/tests/utests/types/int16.c @@ -0,0 +1,74 @@ +/** + * @file int16.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for int16 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int16 {range -20..-10;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", "100"); + CHECK_LOG_CTX("Unsatisfied range - value \"100\" is out of the allowed range.", "/defs:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/int32.c b/tests/utests/types/int32.c new file mode 100644 index 0000000..fa2b341 --- /dev/null +++ b/tests/utests/types/int32.c @@ -0,0 +1,74 @@ +/** + * @file int32.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for int32 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int32;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", "0x01"); + CHECK_LOG_CTX("Invalid type int32 value \"0x01\".", "/defs:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/int64.c b/tests/utests/types/int64.c new file mode 100644 index 0000000..0406673 --- /dev/null +++ b/tests/utests/types/int64.c @@ -0,0 +1,80 @@ +/** + * @file int64.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for int32 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int64;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", ""); + CHECK_LOG_CTX("Invalid type int64 empty value.", "/defs:port", 1); + + TEST_ERROR_XML("defs", " "); + CHECK_LOG_CTX("Invalid type int64 empty value.", "/defs:port", 1); + + TEST_ERROR_XML("defs", "-10 xxx"); + CHECK_LOG_CTX("Invalid type int64 value \"-10 xxx\".", "/defs:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/int8.c b/tests/utests/types/int8.c new file mode 100644 index 0000000..dff29ef --- /dev/null +++ b/tests/utests/types/int8.c @@ -0,0 +1,1762 @@ +/** + * @file int8.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for int8 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_ERROR_JSON(MOD_NAME, DATA) \ + { \ + struct lyd_node *tree; \ + const char *data = "{\"" MOD_NAME ":port\":" DATA "}"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_range *range; + + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 127\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 50); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T0 */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type int8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST MODULE T1 */ + schema = MODULE_CREATE_YANG("T1", "leaf port {type int8 {range \"0 .. 50 |51 .. 60\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 50); + assert_int_equal(range->parts[1].min_64, 51); + assert_int_equal(range->parts[1].max_64, 60); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 |51 .. 60", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T1 */ + schema = MODULE_CREATE_YANG("T2", "leaf port {type int8 {range \"20\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, 20); + assert_int_equal(range->parts[0].max_64, 20); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "20", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T3 */ + schema = MODULE_CREATE_YANG("T3", "leaf port {type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, -60); + assert_int_equal(range->parts[1].min_64, -1); + assert_int_equal(range->parts[1].max_64, 1); + assert_int_equal(range->parts[2].min_64, 60); + assert_int_equal(range->parts[2].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "-128 .. -60 | -1 .. 1 | 60 .. 127", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T4 */ + schema = MODULE_CREATE_YANG("T4", "leaf port {type int8 {range \"1 .. 1\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, 1); + assert_int_equal(range->parts[0].max_64, 1); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "1 .. 1", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T4 */ + schema = MODULE_CREATE_YANG("T5", "leaf port {type int8 {range \"7\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, 7); + assert_int_equal(range->parts[0].max_64, 7); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "7", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T4 */ + schema = MODULE_CREATE_YANG("T6", "leaf port {type int8 {range \"min .. max\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST ERROR -60 .. 0 | 0 .. 127 */ + schema = MODULE_CREATE_YANG("ERR0", "leaf port {type int8 {range \"-60 .. 0 | 0 .. 127\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST); + CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/ERR0:port", 0); + + /* TEST ERROR 0 .. 128 */ + schema = MODULE_CREATE_YANG("ERR1", "leaf port {type int8 {range \"0 .. 128\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/ERR1:port", 0); + + /* TEST ERROR -129 .. 126 */ + schema = MODULE_CREATE_YANG("ERR2", "leaf port {type int8 {range \"-129 .. 0\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR2:port", 0); + + /* TEST ERROR 0 */ + schema = MODULE_CREATE_YANG("ERR3", "leaf port {type int8 {range \"-129\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR3:port", 0); + + /* + * TEST MODULE SUBTYPE + */ + schema = MODULE_CREATE_YANG("TS0", + "typedef my_int_type {" + " type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}" + "}" + "leaf my_leaf {type my_int_type; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, -60); + assert_int_equal(range->parts[1].min_64, -1); + assert_int_equal(range->parts[1].max_64, 1); + assert_int_equal(range->parts[2].min_64, 60); + assert_int_equal(range->parts[2].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_int_type", 0, 0, 1, 0, 0, 0); + + /* TEST SUBTYPE RANGE */ + schema = MODULE_CREATE_YANG("TS1", + "typedef my_int_type {" + " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 127\";}" + "}" + "leaf my_leaf {type my_int_type {range \"min .. -60\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, -100); + assert_int_equal(range->parts[0].max_64, -60); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -60", NULL, NULL, NULL, 0, NULL); + + /* TEST SUBTYPE RANGE */ + schema = MODULE_CREATE_YANG("TS2", + "typedef my_int_type {" + " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 120\";}" + "}" + "leaf my_leaf {type my_int_type {range \"70 .. max\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, 70); + assert_int_equal(range->parts[0].max_64, 120); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "70 .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST SUBTYPE RANGE */ + schema = MODULE_CREATE_YANG("TS3", + "typedef my_int_type {" + " type int8 {range \"-100 .. -60 | -1 .. 1 | 60 .. 127\";}" + "}" + "leaf my_leaf {type my_int_type {range \"-1 .. 1\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, -1); + assert_int_equal(range->parts[0].max_64, 1); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "-1 .. 1", NULL, NULL, NULL, 0, NULL); + + /* TEST SUBTYPE RANGE */ + schema = MODULE_CREATE_YANG("TS4", + "typedef my_int_type {" + " type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}" + "}" + "leaf my_leaf {type my_int_type { " + " range \"min .. -60 | -1 .. 1 | 60 .. max\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, -60); + assert_int_equal(range->parts[1].min_64, -1); + assert_int_equal(range->parts[1].max_64, 1); + assert_int_equal(range->parts[2].min_64, 60); + assert_int_equal(range->parts[2].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -60 | -1 .. 1 | 60 .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST SUBTYPE ERROR min .. max */ + schema = MODULE_CREATE_YANG("TS_ERR0", + "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}" + "leaf my_leaf {type my_int_type {range \"min .. max\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.", + "/TS_ERR0:my_leaf", 0); + + /* TEST SUBTYPE ERROR -80 .. 80 */ + schema = MODULE_CREATE_YANG("TS_ERR1", + "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}" + " leaf my_leaf {type my_int_type {range \"-80 .. 80\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-80 .. 80) is not equally or more limiting.", + "/TS_ERR1:my_leaf", 0); + + /* TEST SUBTYPE ERROR 0 .. max */ + schema = MODULE_CREATE_YANG("TS_ERR2", + "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}" + "leaf my_leaf {type my_int_type {range \"0 .. max\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (0 .. max) is not equally or more limiting.", + "/TS_ERR2:my_leaf", 0); + + /* TEST SUBTYPE ERROR -2 .. 2 */ + schema = MODULE_CREATE_YANG("TS_ERR3", + "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}" + "leaf my_leaf {type my_int_type {range \"-2 .. 2\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-2 .. 2) is not equally or more limiting.", + "/TS_ERR3:my_leaf", 0); + + /* TEST SUBTYPE ERROR -2 .. 2 */ + schema = MODULE_CREATE_YANG("TS_ERR4", + "typedef my_int_type { type int8 {range \"-128 .. -60 | -1 .. 1 | 60 .. 127\";}}" + "leaf my_leaf {type my_int_type {range \"-100 .. -90 | 100 .. 128\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", + "/TS_ERR4:my_leaf", 0); + + /* + * TEST DEFAULT VALUE + */ + schema = MODULE_CREATE_YANG("DF0", + "leaf port {" + " type int8 {range \"0 .. 50 | 127\";}" + " default \"20\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "20", 20); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 50); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "20"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL); + + /* TEST DEFAULT VALUE */ + schema = MODULE_CREATE_YANG("DF1", "leaf port {type int8 {range \"0 .. 50 | 127\";}" + "default \"127\"; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "127", 127); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 50); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "127"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL); + + /* TEST DEFAULT VALUE ERROR */ + schema = MODULE_CREATE_YANG("TD_ERR0", + "leaf port {" + " type int8 {range \"0 .. 50 | 127\";}" + " default \"128\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"128\" is out of type int8 min/max bounds.).", + "/TD_ERR0:port", 0); + + /* TEST DEFAULT VALUE ERROR */ + schema = MODULE_CREATE_YANG("TD_ERR1", + "leaf port {" + " type int8 {range \"0 .. 50 | 127\";}" + " default \"-1\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"-1\" is out of the allowed range.).", + "/TD_ERR1:port", 0); + + /* TEST DEFAULT VALUE ERROR */ + schema = MODULE_CREATE_YANG("TD_ERR2", + "leaf port {" + " type int8 {range \"0 .. 50 | 127\";}" + " default \"60\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"60\" is out of the allowed range.).", + "/TD_ERR2:port", 0); + + /* TEST DEFAULT VALUE ERROR */ + schema = MODULE_CREATE_YANG("TD_ERR3", + "typedef my_int_type { type int8 {range \"60 .. 127\";} default \"127\";}" + "leaf my_leaf {type my_int_type {range \"70 .. 80\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"127\" is out of the allowed range.).", + "/TD_ERR3:my_leaf", 0); + + /* TEST DEFAULT HEXADECIMAL */ + schema = MODULE_CREATE_YANG("DF_HEX0", + "leaf port {" + " type int8;" + " default \"0xf\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "0xf"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT HEXADECIMAL */ + schema = MODULE_CREATE_YANG("DF_HEX1", + "leaf port {" + " type int8;" + " default \"-0xf\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-0xf"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT HEXADECIMAL */ + schema = MODULE_CREATE_YANG("DF_HEXI0", + "leaf port {" + " type int8 {range \"0 .. 50 | 127\";}" + " default \"+0x7F\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "127", 127); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 50); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+0x7F"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 50 | 127", NULL, NULL, NULL, 0, NULL); + + /* TEST DEFAULT HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YANG("DF_HEX2", + "leaf port {" + " type int8;" + " default \"0xff\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0xff\" is out of type int8 min/max bounds.).", + "/DF_HEX2:port", 0); + + /* TEST DEFAULT HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YANG("DF_HEX3", + "leaf port {" + " type int8;" + " default \"-0x81\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0x81\" is out of type int8 min/max bounds.).", + "/DF_HEX3:port", 0); + + /* TEST DEFAULT HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YANG("DF_HEX4", + "leaf port {" + " type int8;" + " default \"0x80\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0x80\" is out of type int8 min/max bounds.).", + "/DF_HEX4:port", 0); + + /* TEST DEFAULT VALUE OCTAL */ + schema = MODULE_CREATE_YANG("DF_OCT0", + "leaf port {" + " type int8;" + " default \"017\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "017"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE OCTAL */ + schema = MODULE_CREATE_YANG("DF_OCT1", + "leaf port {" + " type int8;" + " default \"-017\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-017"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE OCTAL */ + schema = MODULE_CREATE_YANG("DF_OCTI0", + "leaf port {" + " type int8;" + " default \"+017\";" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+017"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE OCTAL ERROR*/ + schema = MODULE_CREATE_YANG("DF_OCT2", + "leaf port {" + " type int8;" + " default \"0377\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0377\" is out of type int8 min/max bounds.).", + "/DF_OCT2:port", 0); + + /* TEST DEFAULT VALUE OCTAL ERROR*/ + schema = MODULE_CREATE_YANG("DF_OCT3", + "leaf port {" + " type int8;" + " default \"-0201\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0201\" is out of type int8 min/max bounds.).", + "/DF_OCT3:port", 0); + + /* TEST DEFAULT VALUE OCTAL ERROR*/ + schema = MODULE_CREATE_YANG("DF_OCT4", + "leaf port {" + " type int8;" + " default \"0200\";" + "}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0200\" is out of type int8 min/max bounds.).", + "/DF_OCT4:port", 0); +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_range *range; + + /* TEST T0 */ + schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"int8\"/> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST T1 */ + schema = MODULE_CREATE_YIN("T1", + "<leaf name=\"port\"> " + " <type name=\"int8\"> <range value = \"0 .. 10\"/> </type>" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_64, 0); + assert_int_equal(range->parts[0].max_64, 10); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "0 .. 10", NULL, NULL, NULL, 0, NULL); + + /* TEST T1 */ + schema = MODULE_CREATE_YIN("T2", + "<leaf name=\"port\"> " + " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, -127); + assert_int_equal(range->parts[0].max_64, 10); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "-127 .. 10 | max", NULL, NULL, NULL, 0, NULL); + + /* TEST T2 */ + schema = MODULE_CREATE_YIN("T3", + "<leaf name=\"port\"> " + " <type name=\"int8\"> <range value =\"min .. 10 | 11 .. 12 | 30\"/> </type>" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, 10); + assert_int_equal(range->parts[1].min_64, 11); + assert_int_equal(range->parts[1].max_64, 12); + assert_int_equal(range->parts[2].min_64, 30); + assert_int_equal(range->parts[2].max_64, 30); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. 10 | 11 .. 12 | 30", NULL, NULL, NULL, 0, NULL); + + /* TEST ERROR -60 .. 0 | 0 .. 127 */ + schema = MODULE_CREATE_YIN("TE0", + "<leaf name=\"port\"> " + " <type name=\"int8\"> <range value = \"min .. 0 | 0 .. 12\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST); + CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/TE0:port", 0); + + /* TEST ERROR 0 .. 128 */ + schema = MODULE_CREATE_YIN("TE1", + "<leaf name=\"port\">" + " <type name=\"int8\"> <range value = \"0 .. 128\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/TE1:port", 0); + + /* TEST ERROR -129 .. 126 */ + schema = MODULE_CREATE_YIN("TE2", + "<leaf name=\"port\"> " + " <type name=\"int8\"> <range value =\"-129 .. 126\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/TE2:port", 0); + + /* TEST YIN */ + schema = MODULE_CREATE_YIN("TS0", + "<typedef name= \"my_int_type\">" + " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>" + "</typedef>" + "<leaf name=\"my_leaf\"> <type name=\"my_int_type\"/> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "my_leaf", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, -127); + assert_int_equal(range->parts[0].max_64, 10); + assert_int_equal(range->parts[1].min_64, 127); + assert_int_equal(range->parts[1].max_64, 127); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "my_leaf", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_int_type", 0, 0, 1, 0, 0, 0); + + /* TEST YIN */ + schema = MODULE_CREATE_YIN("TS1", + "<typedef name= \"my_int_type\">" + " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_int_type\"> <range value =" + " \"min .. -30 | 100 .. max\"/> </type> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, -127); + assert_int_equal(range->parts[0].max_64, -30); + assert_int_equal(range->parts[1].min_64, 100); + assert_int_equal(range->parts[1].max_64, 100); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "my_int_type", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. -30 | 100 .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST ERROR */ + schema = MODULE_CREATE_YIN("TS_ERR1", + "<typedef name= \"my_int_type\">" + " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>" + "</typedef>" + "<leaf name=\"port\">" + " <type name=\"my_int_type\"> <range value = \"min .. max\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.", + "/TS_ERR1:port", 0); + + /* TEST ERROR */ + schema = MODULE_CREATE_YIN("TS_ERR2", + "<typedef name= \"my_int_type\">" + " <type name=\"int8\"> <range value = \"-127 .. 10 | 90 .. 100\"/> </type>" + "</typedef>" + "<leaf name=\"port\">" + " <type name=\"my_int_type\"> <range value = \"5 .. 11\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid range restriction - the derived restriction (5 .. 11) is not equally or more limiting.", + "/TS_ERR2:port", 0); + + /* TEST DEFAULT VALUE */ + schema = MODULE_CREATE_YIN("DF0", + "<leaf name=\"port\">" + " <default value=\"12\" />" + " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 1); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "12", 12); + range = ((struct lysc_type_num *)lysc_leaf->type)->range; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_64, -128); + assert_int_equal(range->parts[0].max_64, 0); + assert_int_equal(range->parts[1].min_64, 1); + assert_int_equal(range->parts[1].max_64, 12); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "12"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x80, 0, 0, "int8", 0, 0, 1, 1, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.range, "min .. 0 | 1 .. 12", NULL, NULL, NULL, 0, NULL); + + /* TEST ERROR TD0 */ + schema = MODULE_CREATE_YIN("TD_ERR0", + "<leaf name=\"port\">" + " <default value=\"128\" />" + " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"128\" is out of type int8 min/max bounds.).", + "/TD_ERR0:port", 0); + + /* TEST ERROR TD1 */ + schema = MODULE_CREATE_YIN("TD_ERR1", + "<leaf name=\"port\">" + " <default value=\"13\" />" + " <type name=\"int8\"> <range value = \"min .. 0 | 1 .. 12\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"13\" is out of the allowed range.).", + "/TD_ERR1:port", 0); + + /* TEST ERROR TD1 */ + schema = MODULE_CREATE_YIN("TD_ERR3", + "<typedef name= \"my_int_type\">" + " <default value=\"10\" />" + " <type name=\"int8\"> <range value = \"-127 .. 10 | max\"/> </type>" + "</typedef>" + "<leaf name=\"my_leaf\">" + " <type name=\"my_int_type\">" + " <range value = \"-127 .. -80\"/> </type>" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied range - value \"10\" is out of the allowed range.).", + "/TD_ERR3:my_leaf", 0); + + /* TEST DEFAULT VALUE HEXADECIMAL */ + schema = MODULE_CREATE_YIN("DF_HEX0", + "<leaf name=\"port\">" + " <default value=\"+0xf\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+0xf"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE HEXADECIMAL */ + schema = MODULE_CREATE_YIN("DF_HEX1", + "<leaf name=\"port\">" + " <default value=\"-0xf\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-0xf"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YIN("DF_HEX2", + "<leaf name=\"port\">" + " <default value=\"0xff\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0xff\" is out of type int8 min/max bounds.).", + "/DF_HEX2:port", 0); + + /* TEST DEFAULT VALUE HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YIN("DF_HEX2", + "<leaf name=\"port\">" + " <default value=\"-0x81\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0x81\" is out of type int8 min/max bounds.).", + "/DF_HEX2:port", 0); + + /* TEST DEFAULT VALUE HEXADECIMAL ERROR */ + schema = MODULE_CREATE_YIN("DF_HEX4", + "<leaf name=\"port\">" + " <default value=\"0x80\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0x80\" is out of type int8 min/max bounds.).", + "/DF_HEX4:port", 0); + + /* TEST DEFAULT VALUE OCTAL */ + schema = MODULE_CREATE_YIN("DF_OCT0", + "<leaf name=\"port\">" + " <default value=\"+017\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "15", 15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "+017"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE OCTAL */ + schema = MODULE_CREATE_YIN("DF_OCT1", + "<leaf name=\"port\">" + " <default value=\"-017\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *)mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x205, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, 1); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_INT8, 0, 0); + CHECK_LYD_VALUE(*(lysc_leaf->dflt), INT8, "-15", -15); + lysp_leaf = (void *)mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, "-017"); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "int8", 0, 0, 1, 0, 0, 0); + + /* TEST DEFAULT VALUE OCTAL ERROR */ + schema = MODULE_CREATE_YIN("DF_OCT2", + "<leaf name=\"port\">" + " <default value=\"-0201\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"-0201\" is out of type int8 min/max bounds.).", + "/DF_OCT2:port", 0); + + /* TEST DEFAULT VALUE OCTAL ERROR */ + schema = MODULE_CREATE_YIN("DF_OCT3", + "<leaf name=\"port\">" + " <default value=\"0200\" />" + " <type name=\"int8\" />" + "</leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Value \"0200\" is out of type int8 min/max bounds.).", + "/DF_OCT3:port", 0); +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", + " description \"desc\";\n" + "leaf port {type int8 {range \"0 .. 50 | 127\";} default \"20\";}"); + schema_yin = MODULE_CREATE_YIN("PRINT0", + "\n" + " <description>\n" + " <text>desc</text>\n" + " </description>\n" + " <leaf name=\"port\">\n" + " <type name=\"int8\">\n" + " <range value=\"0 .. 50 | 127\"/>\n" + " </type>\n" + " <default value=\"20\"/>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " description\n" + " \"desc\";\n\n" + " leaf port {\n" + " type int8 {\n" + " range \"0 .. 50 | 127\";\n" + " }\n" + " default \"20\";\n" + " }\n"); + schema_yin = MODULE_CREATE_YIN("PRINT1", + "<description>" + " <text>desc</text>" + "</description>" + "<leaf name=\"port\">" + " <type name=\"int8\">" + " <range value=\"0 .. 50 | 127\"/>" + " </type>" + "<default value=\"20\"/>" + "</leaf>"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT2", + " description \"desc\";\n" + "leaf port {type int8;}"); + schema_yin = MODULE_CREATE_YIN("PRINT2", + "\n" + " <description>\n" + " <text>desc</text>\n" + " </description>\n" + " <leaf name=\"port\">\n" + " <type name=\"int8\"/>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT3", + "\n" + " leaf port {\n" + " type int8;\n" + " }\n"); + schema_yin = MODULE_CREATE_YIN("PRINT3", + "<leaf name=\"port\">" + " <type name=\"int8\"/>" + "</leaf>"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); +} + +static void +test_data_xml(void **state) +{ + const char *schema; + struct lyd_node *tree; + const char *data; + /* variable for default value test */ + struct lysc_node_container *lysc_root; + struct lyd_node_inner *lyd_root; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 105\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "+50", INT8, "50", 50); + TEST_SUCCESS_XML("defs", "50", INT8, "50", 50); + TEST_SUCCESS_XML("defs", "105", INT8, "105", 105); + TEST_SUCCESS_XML("defs", "0", INT8, "0", 0); + TEST_SUCCESS_XML("defs", "-0", INT8, "0", 0); + TEST_ERROR_XML("defs", "-1"); + CHECK_LOG_CTX("Unsatisfied range - value \"-1\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_XML("defs", "51"); + CHECK_LOG_CTX("Unsatisfied range - value \"51\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_XML("defs", "106"); + CHECK_LOG_CTX("Unsatisfied range - value \"106\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_XML("defs", "104"); + CHECK_LOG_CTX("Unsatisfied range - value \"104\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_XML("defs", "60"); + CHECK_LOG_CTX("Unsatisfied range - value \"60\" is out of the allowed range.", "/defs:port", 1); + + schema = MODULE_CREATE_YANG("T0", "leaf port {type int8; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_XML("T0", "-128", INT8, "-128", -128); + TEST_SUCCESS_XML("T0", "-100", INT8, "-100", -100); + TEST_SUCCESS_XML("T0", "0", INT8, "0", 0); + TEST_SUCCESS_XML("T0", "10", INT8, "10", 10); + TEST_SUCCESS_XML("T0", "50", INT8, "50", 50); + TEST_SUCCESS_XML("T0", "127", INT8, "127", 127); + /* leading zeros */ + TEST_SUCCESS_XML("T0", "-015", INT8, "-15", -15); + TEST_SUCCESS_XML("T0", "015", INT8, "15", 15); + TEST_ERROR_XML("T0", "-129"); + CHECK_LOG_CTX("Value \"-129\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_XML("T0", "128"); + CHECK_LOG_CTX("Value \"128\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_XML("T0", "256"); + CHECK_LOG_CTX("Value \"256\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_XML("T0", "1024"); + CHECK_LOG_CTX("Value \"1024\" is out of type int8 min/max bounds.", "/T0:port", 1); + + /* + * default value + */ + schema = MODULE_CREATE_YANG("T1", + "container cont {\n" + " leaf port {type int8 {range \"0 .. 50 | 105\";} default \"20\";}" + "}"); + /* check using default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "<cont xmlns=\"urn:tests:" "T1" "\">" "</cont>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *)tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 1, 0, 0, 1, 1, + INT8, "20", 20);\ + lyd_free_all(tree); + + /* check rewriting default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "<cont xmlns=\"urn:tests:T1\">" "<port> 30 </port>" "</cont>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *)tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 0, 0, 0, 1, 1, + INT8, "30", 30); + lyd_free_all(tree); + + /* + * specific error + */ + schema = MODULE_CREATE_YANG("T2", "leaf port {type int8 {range \"0 .. 50 | 105\" {" + " error-app-tag \"range-violation\";" + " error-message \"invalid range of value\";" + "}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("T2", "120"); + CHECK_LOG_CTX_APPTAG("invalid range of value", "/T2:port", 1, "range-violation"); +} + +static void +test_data_json(void **state) +{ + const char *schema; + /* value for default test */ + struct lysc_node_container *lysc_root; + struct lyd_node_inner *lyd_root; + const char *data; + struct lyd_node *tree; + + /* parsing json data */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 105\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_JSON("defs", "50", INT8, "50", 50); + TEST_SUCCESS_JSON("defs", "50", INT8, "50", 50); + TEST_SUCCESS_JSON("defs", "105", INT8, "105", 105); + TEST_SUCCESS_JSON("defs", "0", INT8, "0", 0); + TEST_SUCCESS_JSON("defs", "-0", INT8, "0", 0); + TEST_ERROR_JSON("defs", "-1"); + CHECK_LOG_CTX("Unsatisfied range - value \"-1\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_JSON("defs", "51"); + CHECK_LOG_CTX("Unsatisfied range - value \"51\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_JSON("defs", "106"); + CHECK_LOG_CTX("Unsatisfied range - value \"106\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_JSON("defs", "104"); + CHECK_LOG_CTX("Unsatisfied range - value \"104\" is out of the allowed range.", "/defs:port", 1); + TEST_ERROR_JSON("defs", "60"); + CHECK_LOG_CTX("Unsatisfied range - value \"60\" is out of the allowed range.", "/defs:port", 1); + + schema = MODULE_CREATE_YANG("T0", "leaf port {type int8; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_JSON("T0", "-128", INT8, "-128", -128); + TEST_SUCCESS_JSON("T0", "-100", INT8, "-100", -100); + TEST_SUCCESS_JSON("T0", "0", INT8, "0", 0); + TEST_SUCCESS_JSON("T0", "10", INT8, "10", 10); + TEST_SUCCESS_JSON("T0", "50", INT8, "50", 50); + TEST_SUCCESS_JSON("T0", "127", INT8, "127", 127); + /* leading zeros */ + TEST_ERROR_JSON("T0", "015"); + CHECK_LOG_CTX("Invalid character sequence \"15}\", expected a JSON object-end or next item.", NULL, 1); + TEST_ERROR_JSON("T0", "-015"); + CHECK_LOG_CTX("Invalid character sequence \"15}\", expected a JSON object-end or next item.", NULL, 1); + TEST_ERROR_JSON("defs", "+50"); + CHECK_LOG_CTX("Invalid character sequence \"+50}\", expected a JSON value.", NULL, 1); + TEST_ERROR_JSON("T0", "-129"); + CHECK_LOG_CTX("Value \"-129\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_JSON("T0", "128"); + CHECK_LOG_CTX("Value \"128\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_JSON("T0", "256"); + CHECK_LOG_CTX("Value \"256\" is out of type int8 min/max bounds.", "/T0:port", 1); + TEST_ERROR_JSON("T0", "1024"); + CHECK_LOG_CTX("Value \"1024\" is out of type int8 min/max bounds.", "/T0:port", 1); + + /* + * default value + */ + schema = MODULE_CREATE_YANG("T1", + "container cont {\n" + " leaf port {type int8 {range \"0 .. 50 | 105\";} default \"20\";}" + "}"); + /* check using default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "{\"T1:cont\":{}}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *)tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 1, 0, 0, 1, 1, + INT8, "20", 20);\ + lyd_free_all(tree); + + /* check rewriting default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "{\"T1:cont\":{\":port\":30}}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *)tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_root->child, 0, 0, 0, 1, 1, + INT8, "30", 30); + lyd_free_all(tree); + +} + +static void +test_data_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", "leaf port {type int8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "port", "-128"); + TEST_SUCCESS_LYB("lyb", "port", "0"); + TEST_SUCCESS_LYB("lyb", "port", "1"); + TEST_SUCCESS_LYB("lyb", "port", "127"); +} + +static void +test_diff(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50 | 120\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + struct lyd_node *model_1, *model_2; + struct lyd_node *diff; + const char *expected_string; + const char *data_1 = "<port xmlns=\"urn:tests:defs\"> 5 </port>"; + const char *data_2 = "<port xmlns=\"urn:tests:defs\"> 6 </port>"; + const char *diff_expected = "<port xmlns=\"urn:tests:defs\" " + "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" " + "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">" + "6</port>"; + + LYD_TREE_CREATE(data_1, model_1); + LYD_TREE_CREATE(data_2, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* create data from diff */ + diff_expected = "<port xmlns=\"urn:tests:defs\" " + "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" " + "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">" + "120</port>"; + LYD_TREE_CREATE(diff_expected, diff); + data_1 = "<port xmlns=\"urn:tests:defs\"> 5 </port>"; + LYD_TREE_CREATE(data_1, model_1); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + expected_string = "<port xmlns=\"urn:tests:defs\">120</port>"; + + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + lyd_free_all(model_1); + lyd_free_all(diff); + + /* + * check creating data out of range + */ + diff_expected = "<port xmlns=\"urn:tests:defs\" " + "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" " + "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"5\">" + "121</port>"; + CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1); + CHECK_LOG_CTX("Unsatisfied range - value \"121\" is out of the allowed range.", "/defs:port", 1); + + /* + * diff from default value + */ + data_1 = "<cont xmlns=\"urn:tests:T0\"></cont>"; + data_2 = "<cont xmlns=\"urn:tests:T0\"> <port> 6 </port> </cont>"; + diff_expected = "<cont xmlns=\"urn:tests:T0\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"create\"><port>6</port></cont>"; + + schema = MODULE_CREATE_YANG("T0", + "container cont {\n" + " leaf port {type int8; default \"20\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + LYD_TREE_CREATE(data_1, model_1); + LYD_TREE_CREATE(data_2, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(diff); + + lyd_free_all(model_1); + lyd_free_all(model_2); +} + +static void +test_print(void **state) +{ + const char *schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"0 .. 50\";}}"); + const char *expected_string; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + struct lyd_node *model_1; + const char *data_1 = "<port xmlns=\"urn:tests:defs\"> 50 </port>"; + + LYD_TREE_CREATE(data_1, model_1); + + /* XML */ + expected_string = "<port xmlns=\"urn:tests:defs\">50</port>"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + /* JSON */ + expected_string = "{\"defs:port\":50}"; + CHECK_LYD_STRING_PARAM(model_1, expected_string, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + lyd_free_all(model_1); +} + +static void +test_plugin_store(void **state) +{ + const char *val_text = NULL; + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value value = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_INT8]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + char *alloc; + const char *schema; + struct lysc_type lysc_type_test; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8 {range \"-50 .. 50\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* check proper type */ + assert_string_equal("libyang 2 - integers, version 1", type->id); + + /* check store + * options = LY_TYPE_STORE_IMPLEMENT | LY_TYPE_STORE_DYNAMIC + * hint = LYD_VALHINT_DECNUM, LYD_VALHINT_HEXNUM, LYD_VALHINT_OCTNUM + */ + val_text = "20"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, INT8, "20", 20); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "-20"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, INT8, "-20", -20); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "0xf"; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "15", 15); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "1B"; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "27", 27); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "-0xf"; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "-15", -15); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "027"; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_OCTNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "23", 23); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "-027"; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_OCTNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "-23", -23); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* + * minor tests + * dynamic alocated input text + */ + val_text = "0xa"; + alloc = (char *)malloc(strlen(val_text) + 1); + + memcpy(alloc, val_text, strlen(val_text) + 1); + ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc, strlen(val_text), + LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + alloc = NULL; + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, INT8, "10", 10); + type->free(UTEST_LYCTX, &value); + + /* wrong lysc_type of value */ + lysc_type_test = *lysc_type; + lysc_type_test.basetype = LY_TYPE_UINT8; + val_text = "20"; + ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EINT, ly_ret); + UTEST_LOG_CTX_CLEAN; + + /* + * ERROR TESTS + */ + val_text = ""; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = ""; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, 1, + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = "10 b"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = "a"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + /* LYPLG_TYPE_STORE_ONLY test */ + val_text = "-60"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + LYPLG_TYPE_STORE_ONLY, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, + &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + type->free(UTEST_LYCTX, &value); + ly_err_free(err); + + UTEST_LOG_CTX_CLEAN; +} + +static void +test_plugin_compare(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_INT8]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + const char *schema; + /* different type */ + const char *diff_type_text = "20"; + struct lyd_value diff_type_val; + struct lysc_type *diff_type; + /* Value which are going to be created to tested */ + const char *val_init[] = {"20", "30", "-30", "0", "-0", "20"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "typedef my_int_type {type int8; }" + "leaf p1 {type my_int_type;}" + "leaf p2 {type my_int_type;}" + "leaf p3 {type my_int_type{range \"0 .. 50\";}}" + "leaf p4 {type uint8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* + * BASIC TEST; + */ + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[0]), &(values[0]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[0]), &(values[5]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[0]), &(values[1]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[2]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[3]), &(values[4]))); + + /* + * SAME TYPE but different node + */ + diff_type_text = "20"; + diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &diff_type_val, &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[1]))); + type->free(UTEST_LYCTX, &(diff_type_val)); + + /* + * derivated type add some limitations + */ + diff_type_text = "20"; + diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type; + ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[1]))); + type->free(UTEST_LYCTX, &(diff_type_val)); + + /* + * different type (UINT8) + */ + diff_type_text = "20"; + diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next->next)->type; + ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[1]))); + type->free(UTEST_LYCTX, &(diff_type_val)); + + /* delete values */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_print(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_INT8]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + const char *schema; + /* Value which are going to be created to tested */ + const char *val_init[] = {"20", "0x4A", "-f", "0", "-0", "-20"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type int8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print value */ + ly_bool dynamic = 0; + + assert_string_equal("32", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("74", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("-15", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("0", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("0", type->print(UTEST_LYCTX, &(values[4]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("-32", type->print(UTEST_LYCTX, &(values[5]), LY_VALUE_XML, NULL, &dynamic, NULL)); + + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_dup(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_INT8]); + struct lysc_type *lysc_type[2]; + const char *schema; + LY_ERR ly_ret; + /* Value which are going to be tested */ + const char *val_init[] = {"20", "0x4A", "-f", "0", "-0x80", "-20"}; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type int8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type[0] = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + schema = MODULE_CREATE_YANG("T1", + "typedef my_int_type {" + " type int8 {range \"-100 .. 100\";} default 20;" + "}" + "leaf port {type my_int_type; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type[1] = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + /* CREATE VALUES */ + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type[it % 2], val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print duplicate value */ + struct lyd_value dup_value; + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "32", 0x20); + assert_ptr_equal(dup_value.realtype, values[0].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "74", 0x4a); + assert_ptr_equal(dup_value.realtype, values[1].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "-15", -0xf); + assert_ptr_equal(dup_value.realtype, values[2].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "0", 0x0); + assert_ptr_equal(dup_value.realtype, values[3].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[4]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "-128", -0x80); + assert_ptr_equal(dup_value.realtype, values[4].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[5]), &dup_value)); + CHECK_LYD_VALUE(dup_value, INT8, "-32", -0x20); + assert_ptr_equal(dup_value.realtype, values[5].realtype); + type->free(UTEST_LYCTX, &dup_value); + + /* error tests */ + assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value)); + + for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + UTEST(test_data_xml), + UTEST(test_data_json), + UTEST(test_data_lyb), + UTEST(test_diff), + UTEST(test_print), + + UTEST(test_plugin_store), + UTEST(test_plugin_compare), + UTEST(test_plugin_print), + UTEST(test_plugin_dup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/leafref.c b/tests/utests/types/leafref.c new file mode 100644 index 0000000..002763c --- /dev/null +++ b/tests/utests/types/leafref.c @@ -0,0 +1,326 @@ +/** + * @file leafref.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \ + {\ + struct lyd_node *tree; \ + const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME1, DATA1, NODE_NAME2, DATA2) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME1 " xmlns=\"urn:tests:" MOD_NAME "\"><name>" DATA1 "</name></" NODE_NAME1 ">" \ + "<" NODE_NAME2 " xmlns=\"urn:tests:" MOD_NAME "\">" DATA2 "</" NODE_NAME2 ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema, *schema2, *schema3, *data; + struct lyd_node *tree; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}" + "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}" + "leaf str-norestr {type string;}" + "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}" + "container cont {leaf leaftarget {type empty;}" + " list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}}" + " leaf-list leaflisttarget {type uint8; max-elements 5;}}" + "leaf-list leaflisttarget {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + schema2 = MODULE_CREATE_YANG("leafrefs", "import defs {prefix t;}" + "container c { container x {leaf x {type string;}} list l {" + " key \"id value\"; leaf id {type string;} leaf value {type string;}" + " leaf lr1 {type leafref {path \"../../../t:str-norestr\"; require-instance true;}}" + " leaf lr2 {type leafref {path \"../../l[id=current()/../../../t:str-norestr]\" +" + " \"[value=current()/../../../t:str-norestr]/value\"; require-instance true;}}" + " leaf lr3 {type leafref {path \"/t:list[t:id=current ( )/../../x/x]/t:targets\";}}" + "}}"); + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML2("<leaflisttarget xmlns=\"urn:tests:defs\">x</leaflisttarget>" + "<leaflisttarget xmlns=\"urn:tests:defs\">y</leaflisttarget>", + "defs", "xmlns:a=\"urn:tests:defs\"", "a:lref", "y", STRING, "y"); + + TEST_SUCCESS_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>" + "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>" + "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>", + "defs", "xmlns:a=\"urn:tests:defs\"", "a:lref2", "y", STRING, "y"); + + data = "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>" + "<c xmlns=\"urn:tests:leafrefs\"><l><id>x</id><value>x</value><lr1>y</lr1></l></c>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_child(lyd_child(tree->next->next)->next)->next->next, + 0, 0, 0, 1, 1, STRING, "y"); + lyd_free_all(tree); + + data = "<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>" + "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>c</targets><targets>d</targets></list>" + "<c xmlns=\"urn:tests:leafrefs\"><x><x>y</x></x>" + "<l><id>x</id><value>x</value><lr3>c</lr3></l></c>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *)lyd_child(lyd_child(tree->next->next->next)->next)->next->next, + 0, 0, 0, 1, 1, STRING, "c"); + lyd_free_all(tree); + + schema3 = MODULE_CREATE_YANG("simple", "leaf l1 {type leafref {path \"../target\";}}" + "leaf target {type string;}"); + UTEST_ADD_MODULE(schema3, LYS_IN_YANG, NULL, NULL); + + data = "<l1 xmlns=\"urn:tests:simple\">"*"'</l1>" + "<target xmlns=\"urn:tests:simple\">"*"'</target>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_all(tree); + + data = "<l1 xmlns=\"urn:tests:simple\">"*'"</l1>" + "<target xmlns=\"urn:tests:simple\">"*'"</target>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_all(tree); + + /* invalid value */ + TEST_ERROR_XML2("<leaflisttarget xmlns=\"urn:tests:defs\">x</leaflisttarget>", + "defs", "", "lref", "y", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"y\" - no target instance \"/leaflisttarget\" with the same value.", + "/defs:lref", 0, "instance-required"); + + TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>" + "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>" + "<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>", + "defs", "", "lref2", "b", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - " + "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.", + "/defs:lref2", 0, "instance-required"); + + TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>x</id><targets>a</targets><targets>b</targets></list>" + "<list xmlns=\"urn:tests:defs\"><id>y</id><targets>x</targets><targets>y</targets></list>", + "defs", "", "lref2", "b", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - " + "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.", + "/defs:lref2", 0, "instance-required"); + + TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>", + "defs", "", "lref2", "b", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"b\" - " + "no target instance \"../list[id = current()/../str-norestr]/targets\" with the same value.", + "/defs:lref2", 0, "instance-required"); + + TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">y</str-norestr>", + "leafrefs", "", "c", "<l><id>x</id><value>x</value><lr1>a</lr1></l>", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"a\" - no target instance \"../../../t:str-norestr\" with the same value.", + "/leafrefs:c/l[id='x'][value='x']/lr1", 0, "instance-required"); + + TEST_ERROR_XML2("<str-norestr xmlns=\"urn:tests:defs\">z</str-norestr>", + "leafrefs", "", "c", "<l><id>y</id><value>y</value></l><l><id>x</id><value>x</value><lr2>z</lr2></l>", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"z\" - no target instance \"../../l[id=current()/../../../t:str-norestr]" + "[value=current()/../../../t:str-norestr]/value\" with the same value.", + "/leafrefs:c/l[id='x'][value='x']/lr2", 0, "instance-required"); + + TEST_ERROR_XML2("", + "defs", "", "lref", "%n", LY_EVALID); + CHECK_LOG_CTX_APPTAG("Invalid leafref value \"%n\" - no target instance \"/leaflisttarget\" with the same value.", + "/defs:lref", 0, "instance-required"); +} + +static void +test_data_json(void **state) +{ + const char *schema, *data; + struct lyd_node *tree; + + /* json test */ + schema = MODULE_CREATE_YANG("simple", "leaf l1 {type leafref {path \"../target\";}}" + "leaf target {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "{" + " \"simple:l1\":\"\\\"*\\\"'\"," + " \"simple:target\":\"\\\"*\\\"'\"" + "}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_all(tree); + + data = "{" + " \"simple:l1\":\"\\\"*'\\\"\"," + " \"simple:target\":\"\\\"*'\\\"\"" + "}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_all(tree); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "list lst {key \"name\"; leaf name {type string;}}" + "leaf lref {type leafref {path \"../lst/name\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "lst", "key_str", "lref", "key_str"); +} + +static void +test_plugin_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_LEAFREF]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + schema = MODULE_CREATE_YANG("simple", + "leaf l1 {" + " type leafref {" + " require-instance false;" + " path \"../target\";" + " }" + "}" + "leaf target {" + " type string;" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + + v1 = "str1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "str2"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +static void +test_data_xpath_json(void **state) +{ + const char *schema, *data; + struct lyd_node *tree; + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED); + + /* json xpath test */ + schema = MODULE_CREATE_YANG("xp_test", + "list l1 {key t1;" + "leaf t1 {type uint8;}" + "list l2 {key t2;" + "leaf t2 {type uint8;}" + "leaf-list l3 {type uint8;}" + "}}" + "leaf r1 {type leafref {path \"../l1/t1\";}}" + "leaf r2 {type leafref {path \"deref(../r1)/../l2/t2\";}}" + "leaf r3 {type leafref {path \"deref(../r2)/../l3\";}}"); + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "{" + " \"xp_test:l1\":[{\"t1\": 1,\"l2\":[{\"t2\": 2,\"l3\":[3]}]}]," + " \"xp_test:r1\": 1," + " \"xp_test:r2\": 2," + " \"xp_test:r3\": 3" + "}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_all(tree); +} + +static void +test_xpath_invalid_schema(void **state) +{ + const char *schema1, *schema2; + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED); + schema1 = MODULE_CREATE_YANG("xp_test", + "list l1 {key t1;" + "leaf t1 {type uint8;}" + "list l2 {key t2;" + "leaf t2 {type uint8;}" + "leaf-list l3 {type uint8;}" + "}}" + "leaf r1 {type leafref {path \"deref(../l1)/../l2/t2\";}}"); + + UTEST_INVALID_MODULE(schema1, LYS_IN_YANG, NULL, LY_EVALID) + CHECK_LOG_CTX("The deref function target node \"l1\" is not leaf nor leaflist", "/xp_test:r1", 0); + + schema2 = MODULE_CREATE_YANG("xp_test", + "list l1 {key t1;" + "leaf t1 {type uint8;}" + "list l2 {key t2;" + "leaf t2 {type uint8;}" + "leaf-list l3 {type uint8;}" + "}}" + "leaf r1 {type uint8;}" + "leaf r2 {type leafref {path \"deref(../r1)/../l2/t2\";}}"); + + UTEST_INVALID_MODULE(schema2, LYS_IN_YANG, NULL, LY_EVALID) + CHECK_LOG_CTX("The deref function target node \"r1\" is not leafref", "/xp_test:r2", 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_data_json), + UTEST(test_plugin_lyb), + UTEST(test_plugin_sort), + UTEST(test_data_xpath_json), + UTEST(test_xpath_invalid_schema) + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c new file mode 100644 index 0000000..2f002b7 --- /dev/null +++ b/tests/utests/types/string.c @@ -0,0 +1,1379 @@ +/** + * @file string.c + * @author Radek IÅ¡a <isa@cesnet.cz> + * @brief test for string values + * + * Copyright (c) 2021 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" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...)\ + {\ + struct lyd_node *tree;\ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>";\ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);\ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0);\ + CHECK_LYD_NODE_TERM((struct lyd_node_term *) tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__);\ + lyd_free_all(tree);\ + } + +#define TEST_SUCCESS_JSON(MOD_NAME, DATA, TYPE, ...)\ + {\ + struct lyd_node *tree;\ + const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}";\ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);\ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0);\ + CHECK_LYD_NODE_TERM((struct lyd_node_term *) tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__);\ + lyd_free_all(tree);\ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA)\ + {\ + struct lyd_node *tree;\ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>";\ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);\ + assert_null(tree);\ + } + +#define TEST_ERROR_JSON(MOD_NAME, DATA)\ + {\ + struct lyd_node *tree;\ + const char *data = "{\"" MOD_NAME ":port\":\"" DATA "\"}";\ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);\ + assert_null(tree);\ + } + +static void +test_schema_yang(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_pattern *pattern; + struct lysc_range *range; + + /* TEST BASE STRING */ + schema = MODULE_CREATE_YANG("base", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_STRING, 0, 0); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "string", 0, 0, 1, 0, 0, 0); + + /* TEST MODULE T0 */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type string" + "{length \"10 .. max\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_true(range->parts[0].max_u64 == 18446744073709551615ull); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T1 */ + schema = MODULE_CREATE_YANG("T1", "leaf port {type string" + "{length \"min .. 20 | 50\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 0); + assert_int_equal(range->parts[0].max_u64, 20); + assert_int_equal(range->parts[1].min_u64, 50); + assert_int_equal(range->parts[1].max_u64, 50); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 20 | 50", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T2 */ + schema = MODULE_CREATE_YANG("T2", "leaf port {type string" + "{length \"10 .. 20 | 50 .. 100 | 255\";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_int_equal(range->parts[0].max_u64, 20); + assert_int_equal(range->parts[1].min_u64, 50); + assert_int_equal(range->parts[1].max_u64, 100); + assert_int_equal(range->parts[2].min_u64, 255); + assert_int_equal(range->parts[2].max_u64, 255); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. 20 | 50 .. 100 | 255", NULL, NULL, NULL, 0, NULL); + + /* SUBTYPE MODULE T2 */ + schema = MODULE_CREATE_YANG("TS0", + "typedef my_type {" + " type string {length \"10 .. 20 | 50 .. 100 | 255\";}" + "}" + "leaf port {type my_type {length \"min .. 15 | max\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_int_equal(range->parts[0].max_u64, 15); + assert_int_equal(range->parts[1].min_u64, 255); + assert_int_equal(range->parts[1].max_u64, 255); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "my_type", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 15 | max", NULL, NULL, NULL, 0, NULL); + + /* ERROR TESTS NEGATIVE VALUE */ + schema = MODULE_CREATE_YANG("ERR0", "leaf port {type string {length \"-1 .. 20\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port", 0); + + schema = MODULE_CREATE_YANG("ERR1", "leaf port {type string {length \"100 .. 18446744073709551616\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port", 0); + + schema = MODULE_CREATE_YANG("ERR2", "leaf port {type string {length \"10 .. 20 | 20 .. 30\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port", 0); + + schema = MODULE_CREATE_YANG("ERR3", + "typedef my_type {" + " type string;" + "}" + "leaf port {type my_type {length \"-1 .. 15\";}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port", 0); + + /* + * PATTERN + */ + schema = MODULE_CREATE_YANG("TPATTERN_0", "leaf port {type string" + "{pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*';}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL); + + schema = MODULE_CREATE_YANG("TPATTERN_1", "leaf port {type string{" + " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*' ;" + " pattern 'abc.*' {modifier invert-match;}" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 2, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[1]), "\x15" "abc.*", NULL, NULL, NULL, 0, NULL); + + schema = MODULE_CREATE_YANG("TPATTERN_2", + "typedef my_type {" + " type string{" + " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.]*' ;" + " pattern 'abc.*' {modifier invert-match;}" + "}}" + "leaf port {type my_type {pattern 'bcd.*';}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 3); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[2]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "bcd.*", 0, 0x0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "bcd.*", NULL, NULL, NULL, 0, NULL); + + /* + * TEST pattern error + */ + schema = MODULE_CREATE_YANG("TPATTERN_ERR_0", "leaf port {type string {" + "pattern '[a-zA-Z_[a-zA-Z0-9\\-_.*';" + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Regular expression \"[a-zA-Z_[a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).", + "/TPATTERN_ERR_0:port", 0); + + schema = MODULE_CREATE_YANG("TDEFAULT_0", + "typedef my_type {" + " type string{" + " pattern \"[a-zA-Z_][a-zA-Z0-9\\\\-_.]*\";" + " length \"2 .. 5 | 10\";" + " }" + " default \"a1i-j\";" + "}" + "leaf port {type my_type;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j"); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 2); + assert_int_equal(range->parts[0].max_u64, 5); + assert_int_equal(range->parts[1].min_u64, 10); + assert_int_equal(range->parts[1].max_u64, 10); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + /* TEST pattern backslash + * The '[' character is escaped, thus character group is broken. + */ + + schema = MODULE_CREATE_YANG("TPATTERN_BC_ERR_1", "leaf port {type string {" + "pattern '\\[a]b';" /* pattern '\[a]b'; */ + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').", + "/TPATTERN_BC_ERR_1:port", 0); + + schema = MODULE_CREATE_YANG("TPATTERN_BC_ERR_2", "leaf port {type string {" + "pattern \"\\\\[a]b\";" /* pattern "\\[a]b"; */ + "}}"); + UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').", + "/TPATTERN_BC_ERR_2:port", 0); + + /* PATTERN AND LENGTH */ + schema = MODULE_CREATE_YANG("TPL_0", + "typedef my_type {" + " type string{" + " length \"2 .. 10\";" + " }" + "}" + "leaf port {type my_type{ pattern \"[a-zA-Z_][a-zA-Z0-9\\\\-_.]*\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_u64, 2); + assert_int_equal(range->parts[0].max_u64, 10); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", + NULL, NULL, NULL, 0, NULL); +} + +static void +test_schema_yin(void **state) +{ + const char *schema; + struct lys_module *mod; + struct lysc_node_leaf *lysc_leaf; + struct lysp_node_leaf *lysp_leaf; + struct lysc_pattern *pattern; + struct lysc_range *range; + + /* TEST BASE STRING */ + schema = MODULE_CREATE_YIN("base", "<leaf name=\"port\"> <type name=\"string\"/> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_NUM((struct lysc_type_num *)lysc_leaf->type, LY_TYPE_STRING, 0, 0); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "string", 0, 0, 1, 0, 0, 0); + + /* TEST MODULE T0 */ + schema = MODULE_CREATE_YIN("T0", "<leaf name=\"port\"> <type name=\"string\">" + "<length value=\"10 .. max\"/>" + "</type> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 1, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_true(range->parts[0].max_u64 == 18446744073709551615ull); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. max", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T1 */ + schema = MODULE_CREATE_YIN("T1", "<leaf name=\"port\"> <type name=\"string\">" + " <length value=\"min .. 20 | 50\"/>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 0); + assert_int_equal(range->parts[0].max_u64, 20); + assert_int_equal(range->parts[1].min_u64, 50); + assert_int_equal(range->parts[1].max_u64, 50); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 20 | 50", NULL, NULL, NULL, 0, NULL); + + /* TEST MODULE T2 */ + schema = MODULE_CREATE_YIN("T2", "<leaf name=\"port\"> <type name=\"string\">" + "<length value=\"10 .. 20 | 50 .. 100 | 255\"/>" + "</type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 3, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_int_equal(range->parts[0].max_u64, 20); + assert_int_equal(range->parts[1].min_u64, 50); + assert_int_equal(range->parts[1].max_u64, 100); + assert_int_equal(range->parts[2].min_u64, 255); + assert_int_equal(range->parts[2].max_u64, 255); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "string", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "10 .. 20 | 50 .. 100 | 255", NULL, NULL, NULL, 0, NULL); + + /* SUBTYPE MODULE T2 */ + schema = MODULE_CREATE_YIN("TS0", + "<typedef name=\"my_type\">" + " <type name=\"string\"> <length value=\"10 .. 20 | 50 .. 100 | 255\"/> </type>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\">" + " <length value=\"min .. 15 | max\"/>" + "</type> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 0); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 10); + assert_int_equal(range->parts[0].max_u64, 15); + assert_int_equal(range->parts[1].min_u64, 255); + assert_int_equal(range->parts[1].max_u64, 255); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x10, 0, 1, "my_type", 0, 0, 1, 0, 0, 0); + CHECK_LYSP_RESTR(lysp_leaf->type.length, "min .. 15 | max", NULL, NULL, NULL, 0, NULL); + + /* ERROR TESTS NEGATIVE VALUE */ + schema = MODULE_CREATE_YIN("ERR0", "<leaf name=\"port\"> <type name=\"string\">" + "<length value =\"-1 .. 20\"/> </type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port", 0); + + schema = MODULE_CREATE_YIN("ERR1", "<leaf name=\"port\"> <type name=\"string\">" + "<length value=\"100 .. 18446744073709551616\"/>" + "</type> </leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port", 0); + + schema = MODULE_CREATE_YIN("ERR2", "<leaf name=\"port\">" + "<type name=\"string\"> <length value=\"10 .. 20 | 20 .. 30\"/>" + "</type> </leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port", 0); + + schema = MODULE_CREATE_YIN("ERR3", + "<typedef name=\"my_type\"> <type name=\"string\"/> </typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"> <length value=\"-1 .. 15\"/>" + "</type> </leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED); + CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port", 0); + + /* + * PATTERN + */ + schema = MODULE_CREATE_YIN("TPATTERN_0", "<leaf name=\"port\"> <type name=\"string\">" + "<pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>" + "</type> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL); + + schema = MODULE_CREATE_YIN("TPATTERN_1", "<leaf name=\"port\"> <type name=\"string\">" + " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>" + " <pattern value=\"abc.*\"> <modifier value=\"invert-match\"/> </pattern>" + "</type> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 2); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "string", 0, 2, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "[a-zA-Z_][a-zA-Z0-9\\-_.]*", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[1]), "\x15" "abc.*", NULL, NULL, NULL, 0, NULL); + + schema = MODULE_CREATE_YIN("TPATTERN_2", + "<typedef name=\"my_type\">" + " <type name=\"string\">" + " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>" + " <pattern value=\"abc.*\"> <modifier value=\"invert-match\"/> </pattern>" + "</type> </typedef>" + "<leaf name=\"port\"><type name=\"my_type\"> <pattern value=\"bcd.*\"/> </type></leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 3); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[1]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "abc.*", 0, 0x1, NULL); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[2]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "bcd.*", 0, 0x0, NULL); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x40, 0, 0, "my_type", 0, 1, 1, 0, 0, 0); + CHECK_LYSP_RESTR(&(lysp_leaf->type.patterns[0]), "\x6" "bcd.*", NULL, NULL, NULL, 0, NULL); + + /* + * TEST pattern error + * */ + schema = MODULE_CREATE_YIN("TPATTERN_ERR_0", + "<leaf name=\"port\"> <type name=\"string\">" + " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.*\"/>" + "</type> </leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Regular expression \"[a-zA-Z_][a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).", + "/TPATTERN_ERR_0:port", 0); + + /* + * DEFAUT VALUE + */ + schema = MODULE_CREATE_YIN("TDEFAULT_0", + "<typedef name=\"my_type\">" + " <type name=\"string\">" + " <pattern value=\"[a-zA-Z_][a-zA-Z0-9\\-_.]*\"/>" + " <length value=\"2 .. 5 | 10\"/>" + " </type>" + " <default value=\"a1i-j\"/>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j"); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 1, 1); + pattern = ((struct lysc_type_str *)lysc_leaf->type)->patterns[0]; + CHECK_LYSC_PATTERN(pattern, NULL, NULL, NULL, "[a-zA-Z_][a-zA-Z0-9\\-_.]*", 0, 0, NULL); + range = ((struct lysc_type_str *)lysc_leaf->type)->length; + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 2); + assert_int_equal(range->parts[0].max_u64, 5); + assert_int_equal(range->parts[1].min_u64, 10); + assert_int_equal(range->parts[1].max_u64, 10); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("TDEFAULT_1", + "<typedef name=\"my_type\">" + " <type name=\"string\">" + " </type>" + " <default value=\"a1i-j<\"/>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>"); + UTEST_ADD_MODULE(schema, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + lysc_leaf = (void *) mod->compiled->data; + CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, "a1i-j<"); + CHECK_LYSC_TYPE_STR((struct lysc_type_str *)lysc_leaf->type, 0, 0, 0); + CHECK_LYSC_RANGE(range, NULL, NULL, NULL, 0, 2, NULL); + assert_int_equal(range->parts[0].min_u64, 2); + assert_int_equal(range->parts[0].max_u64, 5); + assert_int_equal(range->parts[1].min_u64, 10); + assert_int_equal(range->parts[1].max_u64, 10); + lysp_leaf = (void *) mod->parsed->data; + CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); + CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0x0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); + + schema = MODULE_CREATE_YIN("TDEFAULT_2", + "<typedef name=\"my_type\">" + " <type name=\"string\">" + " <length value=\"2\"/>" + " </type>" + " <default value=\"a1i-j<\"/>" + "</typedef>" + "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied length - string \"a1i-j<\" length is not allowed.).", + "/TDEFAULT_2:port", 0); + + schema = MODULE_CREATE_YIN("TDEFAULT_3", + "<typedef name=\"my_type\">" + " <default value=\"a1i-j<\"/>" + " <type name=\"string\">" + "</type> </typedef>" + "<leaf name=\"port\"><type name=\"my_type\"> <pattern value=\"bcd.*\"/> </type></leaf>"); + UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied pattern - \"a1i-j<\" does not conform to \"bcd.*\".).", + "/TDEFAULT_3:port", 0); + +} + +static void +test_schema_print(void **state) +{ + const char *schema_yang, *schema_yin; + char *printed; + struct lys_module *mod; + + /* test print yang to yin */ + schema_yang = MODULE_CREATE_YANG("PRINT0", + "leaf port {type string {" + "length \"min .. 20 | 50\";" + "pattern \"p.*\\\\\\\\\";" + "pattern 'p4.*' {modifier invert-match;}" + "}default \"p\\\"<\\\\\";}"); + schema_yin = MODULE_CREATE_YIN("PRINT0", + " <leaf name=\"port\">\n" + " <type name=\"string\">\n" + " <length value=\"min .. 20 | 50\"/>\n" + " <pattern value=\"p.*\\\\\"/>\n" + " <pattern value=\"p4.*\">\n" + " <modifier value=\"invert-match\"/>\n" + " </pattern>\n" + " </type>\n" + " <default value=\"p"<\\\"/>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yang, LYS_IN_YANG, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, schema_yin); + free(printed); + + /* test print yin to yang */ + schema_yang = MODULE_CREATE_YANG("PRINT1", + "\n" + " leaf port {\n" + " type string {\n" + " length \"min .. 20 | 50\";\n" + " pattern \"p.*\\\\\\\\\";\n" + " pattern \"p4.*\" {\n" + " modifier invert-match;\n" + " }\n" + " }\n" + " default \"p\\\"<\\\\\";\n" + " }\n"); + schema_yin = MODULE_CREATE_YIN("PRINT1", + " <leaf name=\"port\">\n" + " <type name=\"string\">\n" + " <length value=\"min .. 20 | 50\"/>\n" + " <pattern value=\"p.*\\\\\"/>\n" + " <pattern value=\"p4.*\">\n" + " <modifier value=\"invert-match\"/>\n" + " </pattern>\n" + " </type>\n" + " <default value=\"p"<\\\"/>\n" + " </leaf>\n"); + + UTEST_ADD_MODULE(schema_yin, LYS_IN_YIN, NULL, &mod); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, schema_yang); + free(printed); +} + +static void +test_data_xml(void **state) +{ + const char *schema; + struct lyd_node *tree; + const char *data; + struct lysc_node_container *lysc_root; + struct lyd_node_inner *lyd_root; + + /* NO RESTRICTION TESTS */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + /* space on start and new line */ + TEST_SUCCESS_XML("T0", " 50 \n\t 64", STRING, " 50 \n\t 64"); + /* nuber as string */ + TEST_SUCCESS_XML("T0", "50", STRING, "50"); + TEST_SUCCESS_XML("T0", "+250", STRING, "+250"); + /* references */ + TEST_SUCCESS_XML("T0", """, STRING, "\""); + TEST_SUCCESS_XML("T0", "|&|", STRING, "|&|"); + TEST_SUCCESS_XML("T0", "'", STRING, "'"); + TEST_SUCCESS_XML("T0", "<", STRING, "<"); + TEST_SUCCESS_XML("T0", ">", STRING, ">"); + TEST_SUCCESS_XML("T0", "ЯЯ", STRING, "ЯЯ"); + /* special characters */ + TEST_SUCCESS_XML("T0", "\"", STRING, "\""); + TEST_SUCCESS_XML("T0", "'", STRING, "'"); + TEST_SUCCESS_XML("T0", ">", STRING, ">"); + TEST_SUCCESS_XML("T0", "", STRING, ""); + TEST_SUCCESS_XML("T0", "&<lt;", STRING, "&<lt;"); + /* CDATA IS NOT SUPPORTED + * TEST_SUCCESS_XML("T2", "<![CDATA[<greeting>Hello, world! & Wecome</greeting>]]>", STRING, + * "<greeting>Hello, world! & Wecome</greeting>"); + * COMMENT IN MIDDLE OF TEXT IS NOT SUPPORTED + * TEST_SUCCESS_XML("T2", "this isn't <!--' this is comment '-->comment", + * STRING, "this isn't comment"); + */ + + /* error */ + TEST_ERROR_XML("T0", "< df"); + CHECK_LOG_CTX("Child element \"df\" inside a terminal node \"port\" found.", "/T0:port", 1); + TEST_ERROR_XML("T0", "&text;"); + CHECK_LOG_CTX("Entity reference \"&text;</po\" not supported, only predefined references allowed.", NULL, 1); + TEST_ERROR_XML("T0", "\"\""); + CHECK_LOG_CTX("Invalid character reference \"\"</port\" (0x00000008).", NULL, 1); + + /* TEST INVERTED PATTERN ADN LENGTH */ + schema = MODULE_CREATE_YANG("T1", "leaf port {type string {" + " length \"5 .. 10 | 20\";" + " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.<]*' ;" + " pattern 'p4.*' {modifier invert-match;" + " error-app-tag \"pattern-violation\"; error-message \"invalid pattern of value\";}" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + /* inverted value */ + TEST_SUCCESS_XML("T1", "abcde", STRING, "abcde"); + TEST_SUCCESS_XML("T1", "abcde<", STRING, "abcde<"); + TEST_ERROR_XML("T1", "p4abc"); + CHECK_LOG_CTX_APPTAG("invalid pattern of value", "/T1:port", 1, "pattern-violation"); + /* size 20 */ + TEST_SUCCESS_XML("T1", "ahojahojahojahojahoj", STRING, "ahojahojahojahojahoj"); + TEST_SUCCESS_XML("T1", "abc-de", STRING, "abc-de"); + /* ERROR LENGTH */ + TEST_ERROR_XML("T1", "p4a<"); + CHECK_LOG_CTX("Unsatisfied length - string \"p4a<\" length is not allowed.", "/T1:port", 1); + + /* TEST DEFAULT VALUE */ + schema = MODULE_CREATE_YANG("T2", + "container cont {\n" + " leaf port {type string {length \"0 .. 50 | 105\";} default \"test\";}" + "}"); + /* using default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "<cont xmlns=\"urn:tests:" "T2" "\">" "</cont>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *) tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 1, 0, 0, 1, 1, + STRING, "test");\ + lyd_free_all(tree); + + /* rewriting default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "<cont xmlns=\"urn:tests:T2\">" "<port> 52 </port>" "</cont>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *) tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 0, 0, 0, 1, 1, + STRING, " 52 "); + lyd_free_all(tree); + + /* WHIT STRING TEST */ + schema = MODULE_CREATE_YANG("T_WHITE", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_XML("T_WHITE", " \n\t", STRING, " \n\t"); + TEST_SUCCESS_XML("T_WHITE", " \n<\t", STRING, " \n<\t"); + + /* UTF-8 length and pattern*/ + schema = MODULE_CREATE_YANG("T_UTF8", "leaf port {type string {" + " length \"5 .. 10\";" + " pattern '[€]{5,7}' ;" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_XML("T_UTF8", "€€€€€", STRING, "€€€€€"); + TEST_ERROR_XML("T_UTF8", "€€€"); + CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.", "/T_UTF8:port", 1); + TEST_ERROR_XML("T_UTF8", "€€€€€€€€"); + CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".", "/T_UTF8:port", 1); + + /* ANCHOR TEST ^$ is implicit */ + schema = MODULE_CREATE_YANG("T_ANCHOR", "leaf port {type string {" + " pattern 'a.*b' ;" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_ERROR_XML("T_ANCHOR", "abc"); + CHECK_LOG_CTX("Unsatisfied pattern - \"abc\" does not conform to \"a.*b\".", "/T_ANCHOR:port", 1); + TEST_ERROR_XML("T_ANCHOR", "cab"); + CHECK_LOG_CTX("Unsatisfied pattern - \"cab\" does not conform to \"a.*b\".", "/T_ANCHOR:port", 1); +} + +static void +test_data_json(void **state) +{ + const char *schema; + struct lyd_node *tree; + const char *data; + struct lysc_node_container *lysc_root; + struct lyd_node_inner *lyd_root; + + /* NO RESTRICTION TESTS */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_JSON("T0", "this is text", STRING, "this is text"); + /* space on start and new line */ + TEST_SUCCESS_JSON("T0", " 50 \\n\\t 64", STRING, " 50 \n\t 64"); + /* nuber as string */ + TEST_SUCCESS_JSON("T0", "50", STRING, "50"); + TEST_SUCCESS_JSON("T0", "+250", STRING, "+250"); + /* references */ + TEST_SUCCESS_JSON("T0", "\\\"", STRING, "\""); + TEST_SUCCESS_JSON("T0", "|&|", STRING, "|&|"); + TEST_SUCCESS_JSON("T0", "<", STRING, "<"); + TEST_SUCCESS_JSON("T0", ">", STRING, ">"); + TEST_SUCCESS_JSON("T0", "\\u042F", STRING, "Я"); + TEST_SUCCESS_JSON("T0", "\\u042FFF", STRING, "ЯFF"); + /* special characters */ + TEST_SUCCESS_JSON("T0", "'", STRING, "'"); + TEST_SUCCESS_JSON("T0", ">", STRING, ">"); + TEST_SUCCESS_JSON("T0", "", STRING, ""); + TEST_SUCCESS_JSON("T0", "\\\" \\\\ \\r \\/ \\n \\t \\u20ac", STRING, "\" \\ \r / \n \t €"); + + /* ERROR invalid json string */ + TEST_ERROR_JSON("T0", "\n"); + CHECK_LOG_CTX("Invalid character in JSON string \"\n\" (0x0000000a).", NULL, 1); + /* backspace and form feed are valid JSON escape sequences, but the control characters they represents are not allowed values for YANG string type */ + TEST_ERROR_JSON("T0", "\\b"); + CHECK_LOG_CTX("Invalid character reference \"\\b\" (0x00000008).", NULL, 1); + + TEST_ERROR_JSON("T0", "\\f"); + CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", NULL, 1); + + TEST_ERROR_JSON("T0", "\""); + CHECK_LOG_CTX("Invalid character sequence \"\"}\", expected a JSON object-end or next item.", NULL, 1); + + TEST_ERROR_JSON("T0", "aabb \\x"); + CHECK_LOG_CTX("Invalid character escape sequence \\x.", NULL, 1); + + /* TEST INVERTED PATTERN ADN LENGTH */ + schema = MODULE_CREATE_YANG("T1", "leaf port {type string {" + " length \"5 .. 10 | 20\";" + " pattern '[a-zA-Z_][a-zA-Z0-9\\-_.<]*\\n[a-zA-Z0-9\\-_.<]*' ;" + " pattern 'p4.*\\n' {modifier invert-match;}" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + /* inverted value */ + TEST_SUCCESS_JSON("T1", "a\\nbcde", STRING, "a\nbcde"); + TEST_ERROR_JSON("T1", "p4abc\\n"); + CHECK_LOG_CTX("Unsatisfied pattern - \"p4abc\n\" does not conform to inverted \"p4.*\\n\".", "/T1:port", 1); + /* size 20 */ + TEST_SUCCESS_JSON("T1", "ahojahojaho\\njahojaho", STRING, "ahojahojaho\njahojaho"); + TEST_SUCCESS_JSON("T1", "abc\\n-de", STRING, "abc\n-de"); + /* ERROR LENGTH */ + TEST_ERROR_JSON("T1", "p4a\u042F"); + CHECK_LOG_CTX("Unsatisfied length - string \"p4aЯ\" length is not allowed.", "/T1:port", 1); + + /* TEST DEFAULT VALUE */ + schema = MODULE_CREATE_YANG("T_DEFAULT2", + "container cont {\n" + " leaf port {type string {length \"0 .. 50 | 105\";} default \"test\";}" + "}"); + /* using default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "{\"T_DEFAULT2:cont\":{}}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *) tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 1, 0, 0, 1, 1, + STRING, "test");\ + lyd_free_all(tree); + + /* rewriting default value */ + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = "{\"T_DEFAULT2:cont\":{\":port\": \" 52 \"}}";\ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lysc_root = (void *)tree->schema; + CHECK_LYSC_NODE(lysc_root->child, NULL, 0, 0x205, 1, "port", 0, LYS_LEAF, 1, 0, 0, 0); + lyd_root = ((struct lyd_node_inner *) tree); + CHECK_LYD_NODE_TERM((struct lyd_node_term *) lyd_root->child, 0, 0, 0, 1, 1, + STRING, " 52 "); + lyd_free_all(tree); + + /* UTF-8 length and pattern*/ + schema = MODULE_CREATE_YANG("T_UTF8", "leaf port {type string {" + " length \"5 .. 10\";" + " pattern '[€]{5,7}' ;" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_JSON("T_UTF8", "€€€€€", STRING, "€€€€€"); + TEST_ERROR_JSON("T_UTF8", "€€€"); + CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.", "/T_UTF8:port", 1); + TEST_ERROR_JSON("T_UTF8", "€€€€€€€€"); + CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".", "/T_UTF8:port", 1); + + /* ANCHOR TEST ^$ is implicit */ + schema = MODULE_CREATE_YANG("T_ANCHOR", "leaf port {type string {" + " pattern 'a.*b' ;" + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_ERROR_JSON("T_ANCHOR", "abc"); + CHECK_LOG_CTX("Unsatisfied pattern - \"abc\" does not conform to \"a.*b\".", "/T_ANCHOR:port", 1); + TEST_ERROR_JSON("T_ANCHOR", "cb"); + CHECK_LOG_CTX("Unsatisfied pattern - \"cb\" does not conform to \"a.*b\".", "/T_ANCHOR:port", 1); +} + +static void +test_data_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "port", ""); + TEST_SUCCESS_LYB("lyb", "port", "a"); + TEST_SUCCESS_LYB("lyb", "port", "abcdefghijklmnopqrstuvwxyz"); +} + +static void +test_diff(void **state) +{ + (void) state; + const char *schema; + + schema = MODULE_CREATE_YANG("T_DIFF", "leaf port {type string {length \"6 .. 50 | 120\";}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + struct lyd_node *model_1, *model_2; + struct lyd_node *diff; + const char *data_1 = "<port xmlns=\"urn:tests:T_DIFF\"> text abc < </port>"; + const char *data_2 = "<port xmlns=\"urn:tests:T_DIFF\"> text abc > </port>"; + const char *diff_expected = "<port xmlns=\"urn:tests:T_DIFF\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"replace\" yang:orig-default=\"false\"" + " yang:orig-value=\" text abc < \"> text abc > </port>"; + + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); + + /* create data from diff */ + diff_expected = "<port xmlns=\"urn:tests:T_DIFF\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"replace\" yang:orig-default=\"false\"" + " yang:orig-value=\" 10^20 \">jjjjjjj</port>"; + + CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, diff); + data_1 = "<port xmlns=\"urn:tests:T_DIFF\"> 10^20 </port>"; + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + const char *expected = "<port xmlns=\"urn:tests:T_DIFF\">jjjjjjj</port>"; + + CHECK_LYD_STRING_PARAM(model_1, expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + lyd_free_all(model_1); + lyd_free_all(diff); + + /* check creating data breaking restrictions */ + diff_expected = "<port xmlns=\"urn:tests:T_DIFF\" " + "xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" " + "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\" 555 555\">" + "121</port>"; + CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1); + CHECK_LOG_CTX("Unsatisfied length - string \"121\" length is not allowed.", "/T_DIFF:port", 1); + + /* diff from default value */ + data_1 = "<cont xmlns=\"urn:tests:T_DIFF1\"></cont>"; + data_2 = "<cont xmlns=\"urn:tests:T_DIFF1\"> <port> 6 </port> </cont>"; + diff_expected = "<cont xmlns=\"urn:tests:T_DIFF1\"" + " xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\"" + " yang:operation=\"create\"><port> 6 </port></cont>"; + + schema = MODULE_CREATE_YANG("T_DIFF1", + "container cont {\n" + " leaf port {type string; default \" 20\n30 \";}" + "}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + CHECK_PARSE_LYD_PARAM(data_2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(model_1, model_2, 0, &diff)); + assert_non_null(diff); + CHECK_LYD_STRING_PARAM(diff, diff_expected, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + assert_int_equal(LY_SUCCESS, lyd_diff_apply_all(&model_1, diff)); + CHECK_LYD(model_1, model_2); + lyd_free_all(diff); + + lyd_free_all(model_1); + lyd_free_all(model_2); +} + +static void +test_print(void **state) +{ + (void) state; + const char *schema; + + schema = MODULE_CREATE_YANG("T_PRINT", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + struct lyd_node *model_1; + const char *data_1 = "<port xmlns=\"urn:tests:T_PRINT\"> < hello > </port>"; + + CHECK_PARSE_LYD_PARAM(data_1, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_1); + + /* XML */ + const char *expected_xml = "<port xmlns=\"urn:tests:T_PRINT\"> < hello > </port>"; + + CHECK_LYD_STRING_PARAM(model_1, expected_xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + /* JSON */ + const char *expected_json = "{\"T_PRINT:port\":\" < hello > \"}"; + + CHECK_LYD_STRING_PARAM(model_1, expected_json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK); + + lyd_free_all(model_1); +} + +static void +test_plugin_store(void **state) +{ + (void) state; + + const char *val_text = NULL; + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value value = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + struct lysc_type *lysc_type; + char *alloc_text; + unsigned int alloc_text_size; + LY_ERR ly_ret; + const char *schema; + + schema = MODULE_CREATE_YANG("T0", "leaf port {type string {length \"0 .. 10\";" + "pattern '[0-9\\n<>\\\"\\|]*' ;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type; + + /* check proper type */ + assert_string_equal("libyang 2 - string, version 1", type->id); + + /* check store */ + val_text = "20"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "20"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "150\n"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "150\n"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "<\"150>\n"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "<\"150>\n"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = "<\"150>\n|hi how are you"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, 8, + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "<\"150>\n|"); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + val_text = ""; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, ""); + assert_ptr_equal(value.realtype, lysc_type); + type->free(UTEST_LYCTX, &value); + + /* + * minor tests + * dynamic alocated input text + */ + val_text = "<250>"; + alloc_text_size = strlen(val_text); + alloc_text = (char *) malloc(alloc_text_size + 1); + memcpy(alloc_text, val_text, alloc_text_size + 1); + + ly_ret = type->store(UTEST_LYCTX, lysc_type, alloc_text, alloc_text_size, + LYPLG_TYPE_STORE_DYNAMIC, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + alloc_text = NULL; + assert_int_equal(LY_SUCCESS, ly_ret); + CHECK_LYD_VALUE(value, STRING, "<250>"); + type->free(UTEST_LYCTX, &value); + + /* wrong lysc_type of value */ + struct lysc_type lysc_type_test = *lysc_type; + + lysc_type_test.basetype = LY_TYPE_UINT8; + val_text = "20"; + ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EINVAL, ly_ret); + ly_err_free(err); + + /* TEST pattern backslash */ + + schema = MODULE_CREATE_YANG("TPATTERN_BC_1", "leaf port {type string {" + "pattern '\\\\[a]b';" /* pattern '\\[a]b'; */ + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + val_text = "\\ab"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "\\ab"); + type->free(UTEST_LYCTX, &value); + + schema = MODULE_CREATE_YANG("TPATTERN_BC_2", "leaf port {type string {" + "pattern \"\\\\\\\\[a]b\";" /* pattern "\\\\[a]b"; */ + "}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + val_text = "\\ab"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err)); + CHECK_LYD_VALUE(value, STRING, "\\ab"); + type->free(UTEST_LYCTX, &value); + + /* ERROR TESTS */ + + val_text = "10 \"| bcdei"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = "012345678901"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + val_text = "10"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err); + assert_int_equal(LY_EVALID, ly_ret); + ly_err_free(err); + + /* LYPLG_TYPE_STORE_ONLY test */ + val_text = "10 \"| bcdei"; + err = NULL; + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_text, strlen(val_text), + LYPLG_TYPE_STORE_ONLY, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, + &value, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + type->free(UTEST_LYCTX, &value); + ly_err_free(err); +} + +static void +test_plugin_compare(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + const char *schema; + + /* different type */ + const char *diff_type_text = "20"; + struct lyd_value diff_type_val; + struct lysc_type *diff_type; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "typedef my_int_type {type string; }" + "leaf p1 {type my_int_type;}" + "leaf p2 {type my_int_type;}" + "leaf p3 {type string;}" + "leaf p4 {type uint8;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type; + + /* CREATE VALUES */ + const char *val_init[] = {"hi", "hello", "hi", "hello", "hell", "hh"}; + + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* BASIC TEST; */ + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[0]), &(values[0]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[0]), &(values[2]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[0]), &(values[1]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &(values[1]), &(values[2]))); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &(values[1]), &(values[3]))); + + /* SAME TYPE but different node */ + diff_type_text = "hi"; + diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next)->type; + ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + assert_int_equal(LY_SUCCESS, type->compare(UTEST_LYCTX, &diff_type_val, &(values[0]))); + assert_int_equal(LY_ENOT, type->compare(UTEST_LYCTX, &diff_type_val, &(values[1]))); + type->free(UTEST_LYCTX, &(diff_type_val)); + + /* delete values */ + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_print(void **state) +{ + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + struct lysc_type *lysc_type; + LY_ERR ly_ret; + + /* create schema. Prepare common used variables */ + const char *schema = MODULE_CREATE_YANG("defs", "leaf port {type string;}"); + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaf *) mod->compiled->data)->type; + + /* CREATE VALUES */ + const char *val_init[] = {"20", "0x4A", "<|>", "\""}; + + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type, val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print value */ + ly_bool dynamic = 0; + + assert_string_equal("20", type->print(UTEST_LYCTX, &(values[0]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("0x4A", type->print(UTEST_LYCTX, &(values[1]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("<|>", type->print(UTEST_LYCTX, &(values[2]), LY_VALUE_XML, NULL, &dynamic, NULL)); + assert_string_equal("\"", type->print(UTEST_LYCTX, &(values[3]), LY_VALUE_XML, NULL, &dynamic, NULL)); + + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +static void +test_plugin_dup(void **state) +{ + + struct ly_err_item *err = NULL; + struct lys_module *mod; + struct lyd_value values[10]; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_STRING]); + struct lysc_type *lysc_type[2]; + const char *schema; + LY_ERR ly_ret; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("T0", "leaf port {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type[0] = ((struct lysc_node_leaf *) mod->compiled->data)->type; + + schema = MODULE_CREATE_YANG("T1", + "typedef my_int_type {" + " type string {length \"1 .. 100\";} default 20;" + "}" + "leaf port {type my_int_type; }"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type[1] = ((struct lysc_node_leaf *) mod->compiled->data)->type; + + /* CREATE VALUES */ + const char *val_init[] = {"20", "0x4A", "<\">", "0x4A"}; + + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + ly_ret = type->store(UTEST_LYCTX, lysc_type[it % 2], val_init[it], strlen(val_init[it]), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &(values[it]), NULL, &err); + assert_int_equal(LY_SUCCESS, ly_ret); + } + + /* print duplicate value */ + struct lyd_value dup_value; + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[0]), &dup_value)); + CHECK_LYD_VALUE(dup_value, STRING, "20"); + assert_ptr_equal(dup_value.realtype, values[0].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[1]), &dup_value)); + CHECK_LYD_VALUE(dup_value, STRING, "0x4A"); + assert_ptr_equal(dup_value.realtype, values[1].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[2]), &dup_value)); + CHECK_LYD_VALUE(dup_value, STRING, "<\">"); + assert_ptr_equal(dup_value.realtype, values[2].realtype); + type->free(UTEST_LYCTX, &dup_value); + + assert_int_equal(LY_SUCCESS, type->duplicate(UTEST_LYCTX, &(values[3]), &dup_value)); + CHECK_LYD_VALUE(dup_value, STRING, "0x4A"); + assert_ptr_equal(dup_value.realtype, values[3].realtype); + type->free(UTEST_LYCTX, &dup_value); + + /* error tests */ + assert_int_equal(LY_EINVAL, type->duplicate(NULL, &(values[0]), &dup_value)); + + for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) { + type->free(UTEST_LYCTX, &(values[it])); + } +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema_yang), + UTEST(test_schema_yin), + UTEST(test_schema_print), + UTEST(test_data_xml), + UTEST(test_data_json), + UTEST(test_data_lyb), + UTEST(test_diff), + UTEST(test_print), + + UTEST(test_plugin_store), + UTEST(test_plugin_compare), + UTEST(test_plugin_print), + UTEST(test_plugin_dup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/uint16.c b/tests/utests/types/uint16.c new file mode 100644 index 0000000..2ded9eb --- /dev/null +++ b/tests/utests/types/uint16.c @@ -0,0 +1,74 @@ +/** + * @file uint16.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for uint16 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type uint16 {range 150..200;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", "\n 1500 \t\n "); + CHECK_LOG_CTX("Unsatisfied range - value \"1500\" is out of the allowed range.", "/defs:port", 3); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/uint32.c b/tests/utests/types/uint32.c new file mode 100644 index 0000000..89221c4 --- /dev/null +++ b/tests/utests/types/uint32.c @@ -0,0 +1,74 @@ +/** + * @file uint32.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for uint32 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type uint32;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", "-10"); + CHECK_LOG_CTX("Value \"-10\" is out of type uint32 min/max bounds.", "/defs:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/uint64.c b/tests/utests/types/uint64.c new file mode 100644 index 0000000..71e024e --- /dev/null +++ b/tests/utests/types/uint64.c @@ -0,0 +1,80 @@ +/** + * @file uint64.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for uint64 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, ## __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type uint64;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_ERROR_XML("defs", ""); + CHECK_LOG_CTX("Invalid type uint64 empty value.", "/defs:port", 1); + + TEST_ERROR_XML("defs", " "); + CHECK_LOG_CTX("Invalid type uint64 empty value.", "/defs:port", 1); + + TEST_ERROR_XML("defs", "10 xxx"); + CHECK_LOG_CTX("Invalid type uint64 value \"10 xxx\".", "/defs:port", 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/uint8.c b/tests/utests/types/uint8.c new file mode 100644 index 0000000..618e422 --- /dev/null +++ b/tests/utests/types/uint8.c @@ -0,0 +1,87 @@ +/** + * @file uint8.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief test for uint8 values + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* GLOBAL INCLUDE HEADERS */ +#include <ctype.h> + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" +#include "plugins_internal.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYSC_NODE(tree->schema, NULL, 0, 0x5, 1, "port", 0, LYS_LEAF, 0, 0, 0, 0); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_PARSE_STORE_ONLY_XML(MOD_NAME, DATA) \ + {\ + struct lyd_node *tree; \ + const char *data = "<port xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</port>"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STORE_ONLY, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + lyd_free_all(tree); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf port {type uint8 {range 150..200;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML("defs", "\n 150 \t\n ", UINT8, "150", 150); + + TEST_ERROR_XML("defs", "\n 15 \t\n "); + CHECK_LOG_CTX("Unsatisfied range - value \"15\" is out of the allowed range.", "/defs:port", 3); + + /* LYPLG_TYPE_STORE_ONLY test */ + TEST_SUCCESS_PARSE_STORE_ONLY_XML("defs", "\n 15 \t\n "); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c new file mode 100644 index 0000000..c2541aa --- /dev/null +++ b/tests/utests/types/union.c @@ -0,0 +1,315 @@ +/** + * @file union.c + * @author Adam Piecek <piecek@cesnet.cz> + * @brief test for built-in enumeration type + * + * Copyright (c) 2021 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +/* LOCAL INCLUDE HEADERS */ +#include "libyang.h" +#include "path.h" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 1, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML2(XML1, MOD_NAME, NAMESPACES, NODE_NAME, DATA, RET) \ + {\ + struct lyd_node *tree; \ + const char *data = XML1 "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, RET, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + const enum ly_path_pred_type val1[] = {LY_PATH_PREDTYPE_LEAFLIST}; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "identity ident1; identity ident2 {base ident1;}" + "leaf un1 {type union {" + " type leafref {path /int8; require-instance true;}" + " type leafref {path /int64; require-instance true;}" + " type union { type identityref {base ident1;} type instance-identifier {require-instance true;} }" + " type string {length 1..20;}}}" + "leaf int8 {type int8 {range 10..20;}}" + "leaf int64 {type int64;}" + "leaf-list llist {type string;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">12</int8>", + "defs", "", "un1", "12", UNION, "12", INT8, "12", 12); + + TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">12</int8>", + "defs", "", "un1", "2", UNION, "2", STRING, "2"); + + TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>", + "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident2", UNION, "defs:ident2", IDENT, "defs:ident2", "ident2"); + + TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>", + "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident55", UNION, "x:ident55", STRING, "x:ident55"); + + TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>" + "<llist xmlns=\"urn:tests:defs\">y</llist>", + "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "/x:llist[.='y']", UNION, "/defs:llist[.='y']", + INST, "/defs:llist[.='y']", val1); + + TEST_SUCCESS_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>" + "<llist xmlns=\"urn:tests:defs\">y</llist>", + "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "/x:llist[3]", UNION, "/x:llist[3]", + STRING, "/x:llist[3]"); + + /* invalid value */ + TEST_ERROR_XML2("", + "defs", "", "un1", "123456789012345678901", LY_EVALID); + CHECK_LOG_CTX("Invalid union value \"123456789012345678901\" - no matching subtype found:\n" + " libyang 2 - leafref, version 1: Invalid type int8 value \"123456789012345678901\".\n" + " libyang 2 - leafref, version 1: Invalid type int64 value \"123456789012345678901\".\n" + " libyang 2 - identityref, version 1: Invalid identityref \"123456789012345678901\" value - identity not found in module \"defs\".\n" + " libyang 2 - instance-identifier, version 1: Invalid instance-identifier \"123456789012345678901\" value - syntax error.\n" + " libyang 2 - string, version 1: Unsatisfied length - string \"123456789012345678901\" length is not allowed.\n", + "/defs:un1", 1); +} + +static void +test_data_json(void **state) +{ + const char *schema, *data; + struct lyd_node *tree; + + /* xml test */ + schema = MODULE_CREATE_YANG("defs", "leaf un21 {type union {type uint8; type string;}}" + "leaf un22 {type union {type uint16; type string;}}" + "leaf un2 {type union {type leafref {path /un21; require-instance false;} type leafref {path /un22; require-instance false;}}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "{\"defs:un2\":\"str\"}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_STRING_PARAM(tree, data, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); +} + +static void +test_plugin_lyb(void **state) +{ + const char *schema; + + schema = MODULE_CREATE_YANG("lyb", + "leaf int8 {type int8 {range 10..20;}}" + "leaf un1 {type union {" + " type leafref {path /int8; require-instance true;}" + " type string;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + TEST_SUCCESS_LYB("lyb", "un1", "12"); + TEST_SUCCESS_LYB("lyb", "un1", "some_string"); + TEST_SUCCESS_LYB("lyb", "un1", ""); +} + +static void +test_plugin_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[LY_TYPE_UNION]); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + schema = MODULE_CREATE_YANG("sort", "leaf-list ll {type union {type uint16; type int16;}}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data)->type; + + v1 = "1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val1, NULL, &err)); + v2 = "-1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + v1 = "-1"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val1, NULL, &err)); + v2 = "-2"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_JSON, NULL, LYD_VALHINT_DECNUM, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +static void +test_validation(void **state) +{ + const char *schema, *data; + struct lyd_node *tree; + char *out; + uint32_t uint_val; + + schema = MODULE_CREATE_YANG("val", + "leaf l1 {\n" + " type union {\n" + " type uint32 {\n" + " range \"0..1048575\";\n" + " }\n" + " type enumeration {\n" + " enum auto;\n" + " }\n" + " }\n" + "}\n" + "leaf int8 {type int8 {range 10..20;}}\n" + "leaf l2 {\n" + " type union {\n" + " type leafref {path /int8; require-instance true;}\n" + " type string;\n" + " }\n" + "}\n"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* parse from LYB */ + data = "<l1 xmlns=\"urn:tests:val\">auto</l1><int8 xmlns=\"urn:tests:val\">15</int8><l2 xmlns=\"urn:tests:val\">15</l2>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)); + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + free(out); + + /* validate */ + assert_int_equal(LY_SUCCESS, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL)); + + /* print and compare */ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)); + assert_string_equal(out, data); + + free(out); + lyd_free_all(tree); + + schema = MODULE_CREATE_YANG("lref", + "container test {\n" + " list a {\n" + " key \"name\";\n" + " leaf name {\n" + " type enumeration {\n" + " enum zero;\n" + " enum one;\n" + " enum two;\n" + " }\n" + " }\n" + " }\n" + "\n" + " list b {\n" + " key \"name\";\n" + " leaf name {\n" + " type uint32;\n" + " }\n" + " }\n" + "\n" + " list community {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf view {\n" + " type union {\n" + " type leafref {\n" + " path \"../../a/name\";\n" + " }\n" + " type leafref {\n" + " path \"../../b/name\";\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* parse from LYB #1 */ + data = "<test xmlns=\"urn:tests:lref\"><b><name>2</name></b><community><name>test</name><view>2</view></community></test>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)); + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + free(out); + lyd_free_all(tree); + + /* parse from LYB #2 */ + data = "<test xmlns=\"urn:tests:lref\"><a><name>one</name></a><community><name>test</name><view>one</view></community></test>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)); + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + free(out); + + /* remove the target and create another, which is represented the same way in LYB */ + lyd_free_tree(lyd_child(tree)); + uint_val = 1; + assert_int_equal(LY_SUCCESS, lyd_new_list(tree, NULL, "b", LYD_NEW_VAL_BIN, NULL, &uint_val, sizeof uint_val)); + assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL)); + CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n" + " libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n" + " libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_data_json), + UTEST(test_plugin_lyb), + UTEST(test_plugin_sort), + UTEST(test_validation), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c new file mode 100644 index 0000000..f79238e --- /dev/null +++ b/tests/utests/types/yang_types.c @@ -0,0 +1,323 @@ +/** + * @file yang_types.c + * @author Michal VaÅ¡ko <mvasko@cesnet.cz> + * @brief test for ietf-yang-types values + * + * Copyright (c) 2021 - 2023 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 + */ + +/* INCLUDE UTEST HEADER */ +#define _UTEST_MAIN_ +#include "../utests.h" + +#include <stdlib.h> + +#include "compat.h" +#include "libyang.h" + +#define MODULE_CREATE_YIN(MOD_NAME, NODES) \ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ + "<module name=\"" MOD_NAME "\"\n" \ + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" \ + " xmlns:pref=\"urn:tests:" MOD_NAME "\">\n" \ + " <yang-version value=\"1.1\"/>\n" \ + " <namespace uri=\"urn:tests:" MOD_NAME "\"/>\n" \ + " <prefix value=\"pref\"/>\n" \ + NODES \ + "</module>\n" + +#define MODULE_CREATE_YANG(MOD_NAME, NODES) \ + "module " MOD_NAME " {\n" \ + " yang-version 1.1;\n" \ + " namespace \"urn:tests:" MOD_NAME "\";\n" \ + " prefix pref;\n" \ + " import ietf-yang-types {\n" \ + " prefix yang;\n" \ + " }\n" \ + NODES \ + "}\n" + +#define TEST_SUCCESS_XML(MOD_NAME, NODE_NAME, DATA, TYPE, ...) \ + { \ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \ + CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \ + lyd_free_all(tree); \ + } + +#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA, RET) \ + {\ + struct lyd_node *tree; \ + const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \ + assert_null(tree); \ + } + +#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \ + { \ + struct lyd_node *tree_1; \ + struct lyd_node *tree_2; \ + char *xml_out, *data; \ + data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \ + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \ + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \ + assert_non_null(tree_2); \ + CHECK_LYD(tree_1, tree_2); \ + free(xml_out); \ + lyd_free_all(tree_1); \ + lyd_free_all(tree_2); \ + } + +static void +test_data_xml(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("a", + "leaf l {type yang:date-and-time;}" + "leaf l21 {type yang:hex-string;}" + "leaf l22 {type yang:uuid;}" + "leaf l3 {type yang:xpath1.0;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + schema = MODULE_CREATE_YANG("b", + ""); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* date-and-time */ + TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00"); + TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00"); + TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00"); + + /* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */ + TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T00:59:59-02:00"); + TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1969-12-31T23:59:59-02:00"); + + /* canonize */ + TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-01T23:15:15-02:00"); + + /* fractional hours */ + TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T16:45:15.88888-02:00"); + + /* unknown timezone -- timezone conversion MUST NOT happen */ + TEST_SUCCESS_XML("a", "l", "2017-02-01T00:00:00-00:00", STRING, "2017-02-01T00:00:00-00:00"); + TEST_SUCCESS_XML("a", "l", "2021-02-29T00:00:00-00:00", STRING, "2021-03-01T00:00:00-00:00"); + + TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00", LY_EVALID); + CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to " + "\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".", + "/a:l", 1); + + TEST_ERROR_XML("a", "l", "2023-16-15T20:13:01+01:00", LY_EINVAL); + CHECK_LOG_CTX("Invalid date-and-time month \"15\".", "/a:l", 1); + + TEST_ERROR_XML("a", "l", "2023-10-15T20:13:01+95:00", LY_EINVAL); + CHECK_LOG_CTX("Invalid date-and-time timezone hour \"95\".", "/a:l", 1); + + /* hex-string */ + TEST_SUCCESS_XML("a", "l21", "DB:BA:12:54:fa", STRING, "db:ba:12:54:fa"); + TEST_SUCCESS_XML("a", "l22", "f81D4fAE-7dec-11d0-A765-00a0c91E6BF6", STRING, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"); + + /* xpath1.0 */ + TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l3", "/aa:l3[. = '4']", STRING, "/a:l3[.='4']"); + TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l3", + "/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING, + "/ietf-yang-library:yang-library/datastore/name='ietf-datastores:running'"); + TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l3", + "/a1:node1/a2:node2[a1:node3/bb:node4]/bb:node5 | bb:node6 and (bb:node7)", STRING, + "/a:node1/node2[node3/b:node4]/b:node5 | b:node6 and (b:node7)"); + TEST_SUCCESS_XML("a", "l3", "/l3[. = '4']", STRING, "/l3[.='4']"); + + TEST_ERROR_XML("a", "l3", "/a:l3[. = '4']", LY_EVALID); + CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "/a:l3", 1); + TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l3", + "/yl:yang-library/yl:datastore/yl::name", LY_EVALID); + CHECK_LOG_CTX("Storing value failed.", "/a:l3", 1); + CHECK_LOG_CTX("Invalid character 'y'[31] of expression '/yl:yang-library/yl:datastore/yl::name'.", "/a:l3", 1); +} + +static void +test_print(void **state) +{ + const char *schema = MODULE_CREATE_YANG("a", "leaf l {type yang:xpath1.0;}"); + const char *data, *expected; + struct lyd_node *tree; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* XML data */ + data = "<l xmlns=\"urn:tests:a\" xmlns:aa=\"urn:tests:a\">/aa:l[. = '/aa:l']</l>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + + /* XML print */ + expected = "<l xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l[.='/pref:l']</l>"; + CHECK_LYD_STRING_PARAM(tree, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + /* JSON print */ + expected = "{\"a:l\":\"/a:l[.='/a:l']\"}"; + CHECK_LYD_STRING_PARAM(tree, expected, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + lyd_free_tree(tree); + + /* JSON data */ + data = "{\"a:l\":\"/a:l/k/m[. = '/a:l']\"}"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + + /* XML print */ + expected = "<l xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l/pref:k/pref:m[.='/pref:l']</l>"; + CHECK_LYD_STRING_PARAM(tree, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + /* JSON print */ + expected = "{\"a:l\":\"/a:l/k/m[. = '/a:l']\"}"; + CHECK_LYD_STRING_PARAM(tree, expected, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + lyd_free_tree(tree); +} + +static void +test_duplicate(void **state) +{ + const char *schema = MODULE_CREATE_YANG("a", "leaf l1 {type yang:date-and-time;} leaf l2 {type yang:xpath1.0;}"); + const char *data, *expected; + struct lyd_node *tree, *dup; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "<l1 xmlns=\"urn:tests:a\">2005-05-25T23:15:15.88888+04:30</l1>" + "<l2 xmlns=\"urn:tests:a\" xmlns:aa=\"urn:tests:a\">/aa:l2[. = '/aa:l2']</l2>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + + /* duplicate */ + assert_int_equal(LY_SUCCESS, lyd_dup_siblings(tree, NULL, 0, &dup)); + + /* print */ + expected = "<l1 xmlns=\"urn:tests:a\">2005-05-25T16:45:15.88888-02:00</l1>" + "<l2 xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l2[.='/pref:l2']</l2>"; + CHECK_LYD_STRING_PARAM(dup, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + lyd_free_siblings(tree); + lyd_free_siblings(dup); +} + +static void +test_lyb(void **state) +{ + const char *schema; + + /* xml test */ + schema = MODULE_CREATE_YANG("a", + "leaf l {type yang:date-and-time;}" + "leaf l2 {type yang:xpath1.0;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* date-and-time */ + TEST_SUCCESS_LYB("a", "l", "2005-05-25T23:15:15.88888Z"); + TEST_SUCCESS_LYB("a", "l", "2005-05-31T23:15:15-08:59"); + TEST_SUCCESS_LYB("a", "l", "2005-05-01T20:15:15-00:00"); + + /* xpath1.0 */ + TEST_SUCCESS_LYB("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']"); +} + +static void +test_sort(void **state) +{ + const char *v1, *v2; + const char *schema; + struct lys_module *mod; + struct lyd_value val1 = {0}, val2 = {0}; + struct lyplg_type *type = lyplg_type_plugin_find(NULL, "ietf-yang-types", "2013-07-15", "date-and-time"); + struct lysc_type *lysc_type; + struct ly_err_item *err = NULL; + + /* create schema. Prepare common used variables */ + schema = MODULE_CREATE_YANG("a", + "leaf-list ll {type yang:date-and-time;}"); + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + lysc_type = ((struct lysc_node_leaflist *)mod->compiled->data)->type; + + /* v1 > v2, v2 < v1, v1 == v1 */ + v1 = "2005-05-25T23:15:15Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2005-05-25T23:15:14Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 < type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 > type->sort(UTEST_LYCTX, &val2, &val1)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* unknown timezone */ + v1 = "2005-05-25T23:15:15Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2005-05-25T23:15:15-00:00"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val2)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* fractions of a second */ + v1 = "2005-05-25T23:15:15.88888Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2005-05-25T23:15:15.88889Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* zero fractions of a second */ + v1 = "2005-05-25T23:15:15.00Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2005-05-25T23:15:15Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val2)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val2, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); + + /* zero fractions of a second and non-zero */ + v1 = "2005-05-25T23:15:15.00Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v1, strlen(v1), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val1, NULL, &err)); + v2 = "2005-05-25T23:15:15.0001Z"; + assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, v2, strlen(v2), + 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &val2, NULL, &err)); + assert_true(0 > type->sort(UTEST_LYCTX, &val1, &val2)); + assert_true(0 < type->sort(UTEST_LYCTX, &val2, &val1)); + assert_int_equal(0, type->sort(UTEST_LYCTX, &val1, &val1)); + type->free(UTEST_LYCTX, &val1); + type->free(UTEST_LYCTX, &val2); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_data_xml), + UTEST(test_print), + UTEST(test_duplicate), + UTEST(test_lyb), + UTEST(test_sort), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/utests.h b/tests/utests/utests.h new file mode 100644 index 0000000..0835f6f --- /dev/null +++ b/tests/utests/utests.h @@ -0,0 +1,1414 @@ +/** + * @file utests.h + * @author Radek IÅ¡a <isa@cesnet.cz> + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief this file contains macros for simplification test writing + * + * Copyright (c) 2021 - 2022 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 + */ + +#ifndef _UTESTS_H_ +#define _UTESTS_H_ + +#define _POSIX_C_SOURCE 200809L /* strdup */ + +#include <setjmp.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +#include <cmocka.h> + +#include <string.h> + +#include "libyang.h" +#include "plugins_exts/metadata.h" +#include "plugins_internal.h" +#include "plugins_types.h" +#include "tests_config.h" +#include "tree_schema_internal.h" + +/** + * TESTS OVERVIEW + * + * To include utest's environment, just include "utests.h" in the test's source + * code. In case it is the main source code for a cmocka test group (there is a + * main() function), define _UTEST_MAIN_ before including this header. + * + * TESTS VARIABLES + * + * Checking macros use internal storage to store various variables necessary + * during the checking. It is possible to access these variables using the + * following macros: + * + * UTEST_LYCTX - libyang context + * UTEST_IN - input handler + * UTEST_OUT - output handler + * + * All these variables are cleaned with test's teardown. + * + * TESTS SETUP + * + * CMocka's CMUnitTest list definition macros (cmoka_unit_test*()) are replaced + * by UTEST macro with possibility to specify own setup and teardown functions: + * + * UTEST(test_func) - only implicit setup and teardown functions are used + * UTEST(test_func, setup) - implicit teardown but own setup + * UTEST(test_func, setup, teardown) - both setup and teardown are test-specific + * + * Note that the tests environment always provide (and need) internal setup and + * teardown functions. In case the test-specific setup or teardown are used, they + * are supposed to include UTEST_SETUP at the setup beginning and UTEST_TEARDOWN + * at the teardown end. + * + * Libyang context is part of the prepared environment. To add a schema into the + * context (despite it is in the test-specific setup or in test function itself), + * use UTEST_ADD_MODULE macro. + * + * LOGGING + * + * There are 2 macros to check content of the log from the previously called + * libyang function. CHECK_LOG macro test only the last error message and path + * stored directly via logging callback. CHECK_LOG_CTX gets error message and + * path from the libyang context (in case the function does not store the error + * information into the libyang context, the message cannot be checked this way). + * libyang is set to store multiple error information, so multiple couples of + * error message and path can be provided to be checked (the first couple + * corresponds to the latest error). The macro also cleanups the errors list, so + * it is fine to check that there is no error after succeeding successful + * function call. + */ + +/** + * @brief Test's context to provide common storage for various variables. + */ +struct utest_context { + struct ly_ctx *ctx; /**< libyang context */ + + struct ly_in *in; /**< Input handler */ + struct ly_out *out; /**< Outpu handler */ + + char *orig_tz; /**< Original "TZ" environment variable value */ +}; + +/** + * @brief Shortcut to access utest_context. + */ +#define _UC ((struct utest_context *)*state) + +/** + * @brief libyang context provider. + */ +#define UTEST_LYCTX (_UC->ctx) + +/** + * @brief Context's input handler provider + */ +#define UTEST_IN (_UC->in) + +/** + * @brief Context's input handler provider + */ +#define UTEST_OUT (_UC->out) + +/** + * @brief Parse (and validate) data from the input handler as a YANG data tree. + * + * @param[in] INPUT The input data in the specified @p format to parse (and validate) + * @param[in] INPUT_FORMAT Format of the input data to be parsed. Can be 0 to try to detect format from the input handler. + * @param[in] PARSE_OPTIONS Options for parser, see @ref dataparseroptions. + * @param[in] VALIDATE_OPTIONS Options for the validation phase, see @ref datavalidationoptions. + * @param[in] RET expected return status + * @param[out] OUT_NODE Resulting data tree built from the input data. Note that NULL can be a valid result as a + * representation of an empty YANG data tree. + */ +#define CHECK_PARSE_LYD_PARAM(INPUT, INPUT_FORMAT, PARSE_OPTIONS, VALIDATE_OPTIONS, RET, OUT_NODE) \ + { \ + LY_ERR _r = lyd_parse_data_mem(_UC->ctx, INPUT, INPUT_FORMAT, PARSE_OPTIONS, VALIDATE_OPTIONS, &OUT_NODE); \ + if (_r != RET) { \ + if (_r) { \ + fail_msg("%s != 0x%d; MSG: %s", #RET, _r, ly_err_last(_UC->ctx)->msg); \ + } else { \ + fail_msg("%s != 0x%d", #RET, _r); \ + } \ + } \ + } + +/** + * @brief Check if lyd_node and his subnodes have correct values. Print lyd_node and his subnodes into a string in json or xml format. + * @param[in] NODE pointer to lyd_node + * @param[in] TEXT expected output string in json or xml format. + * @param[in] FORMAT format of input text. LYD_JSON, LYD_XML + * @param[in] PARAM options [Data printer flags](@ref dataprinterflags). + */ +#define CHECK_LYD_STRING_PARAM(NODE, TEXT, FORMAT, PARAM) \ + { \ + char *str; \ + LY_ERR _r = lyd_print_mem(&str, NODE, FORMAT, PARAM); \ + if (_r) { \ + fail_msg("Print err 0x%d; MSG: %s", _r, ly_err_last(_UC->ctx)->msg); \ + } \ + assert_string_equal(str, TEXT); \ + free(str); \ + } + +/** + * @brief Compare two lyd_node structure. Macro print lyd_node structure into string and then compare string. Print function use these two parameters. LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK; + * @param[in] NODE_1 pointer to lyd_node + * @param[in] NODE_2 pointer to lyd_node + */ +#define CHECK_LYD(NODE_1, NODE_2) \ + { \ + char *str1; \ + char *str2; \ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str1, NODE_1, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); \ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str2, NODE_2, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); \ + assert_non_null(str1); \ + assert_non_null(str2); \ + assert_string_equal(str1, str2); \ + free(str1); \ + free(str2); \ + } + +/* + * SUPPORT MACROS + */ + +/** + * @brief Internal macro witch assert that two given string are equal or are both null. + * + * @param[in] STRING string to check + * @param[in] TEXT string to compare + */ +#define CHECK_STRING(STRING, TEXT)\ + if (TEXT == NULL) { \ + assert_null(STRING); \ + } else { \ + assert_non_null(STRING); \ + assert_string_equal(STRING, TEXT); \ + } + +/** + * @brief Internal macro witch assert that pointer is null when flag is 0. + * + * @param[in] POINTER pointer to check + * @param[in] FLAG 0 -> pointer is NULL, 1 -> pointer is not null + */ +#define CHECK_POINTER(POINTER, FLAG) \ + assert_true(FLAG == 0 ? POINTER == NULL : POINTER != NULL) + +/** + * @brief Internal macro check size of [sized array](@ref sizedarrays)'s + * + * @param[in] ARRAY pointer to [sized array](@ref sizedarrays) + * @param[in] SIZE expected [sized array](@ref sizedarrays) size of array + */ +#define CHECK_ARRAY(ARRAY, SIZE) \ + assert_true((SIZE == 0) ? \ + (ARRAY == NULL) : \ + (ARRAY != NULL && SIZE == LY_ARRAY_COUNT(ARRAY))); + +/* + * LIBYANG NODE CHECKING + */ + +/** + * @brief check compileted type + * + * @param[in] NODE pointer to lysc_type value + * @param[in] TYPE expected type [LY_DATA_TYPE](@ref LY_DATA_TYPE) + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + */ +#define CHECK_LYSC_TYPE(NODE, TYPE, EXTS) \ + assert_non_null(NODE); \ + assert_int_equal((NODE)->basetype, TYPE); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_ptr_equal((NODE)->plugin, lyplg_type_plugin_find(NULL, "", NULL, ly_data_type2str[TYPE])) + +/** + * @brief check compileted numeric type + * + * @param[in] NODE pointer to lysc_type_num value + * @param[in] TYPE expected type [LY_DATA_TYPE](@ref LY_DATA_TYPE) + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @warning only integer types INT, UINT, NUM + */ +#define CHECK_LYSC_TYPE_NUM(NODE, TYPE, EXTS, RANGE) \ + CHECK_LYSC_TYPE(NODE, TYPE, EXTS);\ + CHECK_POINTER((NODE)->range, RANGE) + +/** + * @brief check compiled string type + * + * @param[in] NODE pointer to lysc_type_num value + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @param[in] LENGTH 0 -> node dosnt have length limitation, 1 -> node have length limitation + * @param[in] PATTERNS expected number of patterns [sized array](@ref sizedarrays) + * @warning only integer types INT, UINT, NUM + */ +#define CHECK_LYSC_TYPE_STR(NODE, EXTS, LENGTH, PATTERNS) \ + CHECK_LYSC_TYPE(NODE, LY_TYPE_STRING, EXTS); \ + CHECK_POINTER((NODE)->length, LENGTH); \ + CHECK_ARRAY((NODE)->patterns, PATTERNS) + +/** + * @brief check compiled bits type + * + * @param[in] NODE pointer to lysc_type_num value + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @param[in] BITS expected number of bits + * @warning only integer types INT, UINT, NUM + */ +#define CHECK_LYSC_TYPE_BITS(NODE, EXTS, BITS) \ + CHECK_LYSC_TYPE(NODE, LY_TYPE_BITS, EXTS); \ + CHECK_ARRAY((NODE)->bits, BITS) + +#define CHECK_LYSC_TYPE_BITENUM_ITEM(NODE, POSITION, DSC, EXTS, FLAGS, NAME, REF)\ + assert_non_null(NODE); \ + assert_int_equal((NODE)->position, POSITION); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_STRING((NODE)->name, NAME); \ + CHECK_STRING((NODE)->ref, REF) \ + +/** + * @brief check range + * + * @param[in] NODE pointer to lysc_range value + * @param[in] DSC expected descriptin (string) + * @param[in] EAPPTAG expected string reprezenting error-app-tag value + * @param[in] EMSG expected string reprezenting error message + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @param[in] PARTS expected [sized array](@ref sizedarrays) number of rang limitations + * @param[in] REF expected reference + */ +#define CHECK_LYSC_RANGE(NODE, DSC, EAPPTAG, EMSG, EXTS, PARTS, REF) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_STRING((NODE)->eapptag, EAPPTAG); \ + CHECK_STRING((NODE)->emsg, EMSG); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + CHECK_ARRAY((NODE)->parts, PARTS); \ + CHECK_STRING((NODE)->ref, REF) + +/** + * @brief check pattern + * + * @param[in] NODE pointer to lysc_pattern value + * @param[in] DSC expected descriptin (string) + * @param[in] EAPPTAG expected string reprezenting error-app-tag value + * @param[in] EMSG expected string reprezenting error message + * @param[in] EEXPR expected string reprezenting original, not compiled, regular expression + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @param[in] INVERTED if regular expression is inverted. + * @param[in] REF expected reference + */ +#define CHECK_LYSC_PATTERN(NODE, DSC, EAPPTAG, EMSG, EXPR, EXTS, INVERTED, REF) \ + assert_non_null(NODE); \ + assert_non_null((NODE)->code); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_STRING((NODE)->eapptag, EAPPTAG); \ + CHECK_STRING((NODE)->emsg, EMSG); \ + CHECK_STRING((NODE)->expr, EXPR); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->inverted, INVERTED); \ + CHECK_STRING((NODE)->ref, REF) + +/** + * @brief assert that lysp_action_inout structure members are correct + * + * @param[in] NODE pointer to lysp_action_inout variable + * @param[in] DATA 0 -> check if pointer to data is NULL, 1 -> check if pointer to data is not null + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extens list + * @param[in] GROUPINGS expected [sized array](@ref sizedarrays) size of grouping list + * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of must restriction list + * @param[in] NODETYPE node type. LYS_INPUT or LYS_OUTPUT + * @param[in] PARENT 0 -> check if node is root, 1 -> check if node is not root + * @param[in] TYPEDEFS expected [sized array](@ref sizedarrays) size of typedefs list + */ +#define CHECK_LYSP_ACTION_INOUT(NODE, DATA, EXTS, GROUPINGS, MUSTS, NODETYPE, PARENT, TYPEDEFS) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->child, DATA); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + CHECK_POINTER((NODE)->groupings, GROUPINGS); \ + CHECK_ARRAY((NODE)->musts, MUSTS); \ + assert_int_equal((NODE)->nodetype, NODETYPE); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_ARRAY((NODE)->typedefs, TYPEDEFS); + +/** + * @brief assert that lysp_action structure members are correct + * + * @param[in] NODE pointer to lysp_action variable + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of extension list + * @param[in] FLAGS expected [schema node flags](@ref snodeflags) + * @param[in] GROUPINGS expected [sized array](@ref sizedarrays) size of grouping list + * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of if-feature expressions list + * @param[in] INPUT_* ::LYSP_ACTION_INOUT_CHECK + * @param[in] NAME expected name + * @param[in] NODETYPE node type. LYS_RPC or LYS_ACTION + * @param[in] OUTPUT_* ::LYSP_ACTION_INOUT_CHECK + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] REF expected reference + * @param[in] TYPEDEFS expected [sized array](@ref sizedarrays) size of list of typedefs + */ +#define CHECK_LYSP_ACTION(NODE, DSC, EXTS, FLAGS, GROUPINGS, IFFEATURES, \ + INPUT_DATA, INPUT_EXTS, INPUT_GROUPINGS, INPUT_MUSTS, \ + INPUT_PARENT, INPUT_TYPEDEFS, \ + NAME, NODETYPE, \ + OUTPUT_DATA, OUTPUT_EXTS, OUTPUT_GROUPINGS, OUTPUT_MUSTS, \ + OUTPUT_PARENT, OUTPUT_TYPEDEFS, \ + PARENT, REF, TYPEDEFS) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->groupings, GROUPINGS); \ + CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \ + CHECK_LYSP_ACTION_INOUT(&((NODE)->input), INPUT_DATA, INPUT_EXTS, INPUT_GROUPINGS, \ + INPUT_MUSTS, LYS_INPUT, INPUT_PARENT, INPUT_TYPEDEFS); \ + assert_string_equal((NODE)->name, NAME); \ + assert_int_equal((NODE)->nodetype, NODETYPE); \ + CHECK_LYSP_ACTION_INOUT(&((NODE)->output), OUTPUT_DATA, OUTPUT_EXTS, OUTPUT_GROUPINGS, \ + OUTPUT_MUSTS, LYS_OUTPUT, OUTPUT_PARENT, OUTPUT_TYPEDEFS); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_ARRAY((NODE)->typedefs, TYPEDEFS) \ + +/** + * @brief assert that lysp_when structure members are correct + * + * @param[in] NODE pointer to lysp_when variable + * @param[in] COND expected string specifid condition + * @param[in] DSC expected string description statement + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension array + * @param[in] REF expected string reference + */ +#define CHECK_LYSP_WHEN(NODE, COND, DSC, EXTS, REF) \ + assert_non_null(NODE); \ + assert_string_equal((NODE)->cond, COND); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + if (REF == NULL) { \ + assert_null((NODE)->ref); \ + } else { \ + assert_non_null((NODE)->ref); \ + assert_string_equal((NODE)->ref, REF); \ + } + +/** + * @brief assert that lysp_restr structure members are correct + * + * @param[in] NODE pointer to lysp_restr variable + * @param[in] ARG_STR expected string. The restriction expression/value + * @param[in] DSC expected descrition + * @param[in] EAPPTAG expected string reprezenting error-app-tag value + * @param[in] EMSG expected string reprezenting error message + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension array + * @param[in] REF expected reference + */ + +#define CHECK_LYSP_RESTR(NODE, ARG_STR, DSC, EAPPTAG, EMSG, EXTS, REF) \ + assert_non_null(NODE); \ + assert_non_null((NODE)->arg.mod); \ + assert_string_equal((NODE)->arg.str, ARG_STR); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_STRING((NODE)->eapptag, EAPPTAG); \ + CHECK_STRING((NODE)->emsg, EMSG); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + CHECK_STRING((NODE)->ref, REF); + +/** + * @brief assert that lysp_import structure members are correct + * + * @param[in] NODE pointer to lysp_import variable + * @param[in] DSC expected description or NULL + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extensions + * @param[in] NAME expected name of imported module + * @param[in] PREFIX expected prefix for the data from the imported schema + * @param[in] REF expected reference + * @param[in] REV expected reprezenting date in format "11-10-2020" + */ +#define CHECK_LYSP_IMPORT(NODE, DSC, EXTS, NAME, PREFIX, REF, REV) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_string_equal((NODE)->name, NAME); \ + assert_string_equal((NODE)->prefix, PREFIX); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_STRING((NODE)->rev, REV); \ + +/** + * @brief assert that lysp_ext structure members are correct + * + * @param[in] NODE pointer to lysp_ext_instance variable + * @param[in] ARGNAME expected argument name + * @param[in] COMPILED 0 -> compiled data dosnt exists, 1 -> compiled data exists + * @param[in] DSC expected string reprezent description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension instances + * @param[in] FLAGS expected LYS_STATUS_* and LYS_YINELEM_* values (@ref snodeflags) + * @param[in] NAME expected name + * @param[in] REF expected ref + */ +#define CHECK_LYSP_EXT(NODE, ARGNAME, COMPILED, DSC, EXTS, FLAGS, NAME, REF) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->argname, ARGNAME); \ + CHECK_POINTER((NODE)->compiled, COMPILED); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + assert_string_equal((NODE)->name, NAME); \ + CHECK_STRING((NODE)->ref, REF); + +/** + * @brief assert that lysp_ext_instance structure members are correct + * + * @param[in] NODE pointer to lysp_ext_instance variable + * @param[in] ARGUMENT expected optional value of the extension's argument + * @param[in] CHILD 0 -> node doesnt have child, 1 -> node have children + * @param[in] PARENT_STMT expected value identifying placement of the extension instance + * @param[in] PARENT_STMT_INDEX expected indentifi index + * @param[in] FORMAT expected format + */ +#define CHECK_LYSP_EXT_INSTANCE(NODE, ARGUMENT, CHILD, PARENT_STMT, PARENT_STMT_INDEX, NAME, FORMAT) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->argument, ARGUMENT); \ + CHECK_POINTER((NODE)->child, CHILD); \ + assert_int_equal((NODE)->parent_stmt, PARENT_STMT); \ + assert_int_equal((NODE)->parent_stmt_index, PARENT_STMT_INDEX); \ + assert_string_equal((NODE)->name, NAME); \ + assert_int_equal((NODE)->format, FORMAT); + +/** + * @brief assert that lysp_stmt structure members are correct + * + * @param[in] NODE pointer to lysp_stmt variable + * @param[in] ARG expected statemet argumet + * @param[in] CHILD 0 -> node doesnt have child, 1 -> node have children + * @param[in] FLAGS expected statement flags, can be set to LYS_YIN_ATTR + * @param[in] KW expected numeric respresentation of the stmt value + * @param[in] NEXT 0 -> pointer is NULL, 1 -> pointer is not null + * @param[in] STMS expected identifier of the statement + */ +#define CHECK_LYSP_STMT(NODE, ARG, CHILD, FLAGS, KW, NEXT, STMT) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->arg, ARG); \ + CHECK_POINTER((NODE)->child, CHILD); \ + assert_int_equal((NODE)->flags, FLAGS); \ + assert_int_equal((NODE)->kw, KW); \ + CHECK_POINTER((NODE)->next, NEXT); \ + assert_string_equal((NODE)->stmt, STMT); \ + +/** + * @brief assert that lysp_type_enum structure members are correct + * + * @param[in] NODE pointer to lysp_type_enum variable + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS only LYS_STATUS_ and LYS_SET_VALUE values are allowed + * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] NAME expected name + * @param[in] REF expected reference statement + * @param[in] VALUE expected enum's value or bit's position + */ +#define CHECK_LYSP_TYPE_ENUM(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, REF, VALUE) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \ + CHECK_STRING((NODE)->name, NAME); \ + CHECK_STRING((NODE)->ref, REF); \ + assert_int_equal(VALUE, (NODE)->value); + +/** + * @brief assert that lysp_type_enum structure members are correct + * + * @param[in] NODE pointer to lysp_type variable + * @param[in] BASES expected [sized array](@ref sizedarrays) size of list of indentifiers + * @param[in] BITS expected [sized array](@ref sizedarrays) size of list of bits + * @param[in] COMPILED 0 -> pointer to compiled type is null, 1 -> pointer to compilet type is valid + * @param[in] ENUMS expected [sized array](@ref sizedarrays) size of list of enums-stmts + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of extension instances + * @param[in] FLAGS expected flags + * @param[in] FRACTION_DIGITS expected number of fraction digits decimal64 + * @param[in] LENGTH expected 0 -> there isnt any restriction on length, 1 -> type is restricted on length (string, binary) + * @param[in] NAME expected name of type + * @param[in] PATH 0 -> no pointer to parsed path, 1 -> pointer to parsed path is valid + * @param[in] PATTERNS expected [sized array](@ref sizedarrays) size of list of patterns for string + * @param[in] PMOD expected submodule where type is defined 0 -> pointer is null, 1 -> pointer is not null + * @param[in] RANGE expected [sized array](@ref sizedarrays) size of list of range restriction + * @param[in] REQUIRE_INSTANCE expected require instance flag + * @param[in] TYPES expected [sized array](@ref sizedarrays) size of list of sub-types + */ +#define CHECK_LYSP_TYPE(NODE, BASES, BITS, COMPILED, ENUMS, EXTS, FLAGS, FRACTIONS_DIGITS, \ + LENGTH, NAME, PATH, PATTERNS, PMOD, RANGE, REQUIRE_INSTANCE, TYPES) \ + assert_non_null(NODE);\ + CHECK_ARRAY((NODE)->bases, BASES); \ + CHECK_ARRAY((NODE)->bits, BITS); \ + CHECK_POINTER((NODE)->compiled, COMPILED); \ + CHECK_ARRAY((NODE)->enums, ENUMS); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + assert_int_equal((NODE)->fraction_digits, FRACTIONS_DIGITS); \ + CHECK_POINTER((NODE)->length, LENGTH); \ + CHECK_STRING((NODE)->name, NAME); \ + CHECK_POINTER((NODE)->path, PATH); \ + CHECK_ARRAY((NODE)->patterns, PATTERNS); \ + CHECK_POINTER((NODE)->pmod, PMOD); \ + CHECK_POINTER((NODE)->range, RANGE); \ + assert_int_equal((NODE)->require_instance, REQUIRE_INSTANCE); \ + CHECK_ARRAY((NODE)->types , TYPES) + +/** + * @brief assert that lysp_node structure members are correct + * + * @param[in] NODE pointer to lysp_node variable + * @param[in] DSC expected description statement + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] NODETYPE node type LYS_UNKNOWN, LYS_CONTAINER, LYS_CHOICE, LYS_LEAF, LYS_LEAFLIST, + * LYS_LIST, LYS_ANYXML, LYS_ANYDATA, LYS_CASE, LYS_RPC, LYS_ACTION, LYS_NOTIF, + * LYS_USES, LYS_INPUT, LYS_OUTPUT, LYS_GROUPING, LYS_AUGMENT + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] REF expected reference statement + * @param[in] WHEN 0-> pointer is null, 1 -> pointer is not null + */ +#define CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, NODETYPE, PARENT, REF, WHEN) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_ARRAY((NODE)->iffeatures, IFFEATURES); \ + CHECK_STRING((NODE)->name, NAME); \ + CHECK_POINTER((NODE)->next, NEXT); \ + assert_int_equal((NODE)->nodetype, NODETYPE); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_POINTER(lysp_node_when((struct lysp_node *)NODE), WHEN); + +/** + * @brief assert that lysp_node structure members are correct + * + * @param[in] NODE pointer to lysp_node variable + * @param[in] DSC expected description statement + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] IFFEATURES expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] REF expected reference statement + * @param[in] WHEN 0-> pointer is null, 1 -> pointer is not null + * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction + * @param[in] UNITS expected string reprezenting units + * @param[in] DFLT 0-> node dosn't have default value. 1 -> node have default value + */ +#define CHECK_LYSP_NODE_LEAF(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, \ + PARENT, REF, WHEN, MUSTS, UNITS, DFLT) \ + CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, LYS_LEAF, PARENT, REF, WHEN); \ + CHECK_ARRAY((NODE)->musts, MUSTS); \ + CHECK_STRING((NODE)->units, UNITS); \ + CHECK_STRING((NODE)->dflt.str, DFLT); + +/** + * @brief assert that lysc_notif structure members are correct + * + * @param[in] NODE pointer to lysp_notif variable + * @param[in] DATA 0 pointer is null, 1 pointer is not null + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] MODULE 0 pointer is null, 1 pointer is not null + * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction + * @param[in] NAME expected name + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null + * @param[in] REF expected reference + * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements + */ +#define CHECK_LYSC_NOTIF(NODE, DATA, DSC, EXTS, FLAGS, MODULE, MUSTS, NAME, PARENT, PRIV, REF, WHEN) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->child, DATA); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->module, MODULE); \ + CHECK_ARRAY((NODE)->musts, MUSTS); \ + assert_string_equal((NODE)->name, NAME); \ + assert_int_equal((NODE)->nodetype, LYS_NOTIF); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_POINTER((NODE)->priv, PRIV); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN); + +/** + * @brief assert that lysc_action_inout structure members are correct + * + * @param[in] NODE pointer to lysp_notif variable + * @param[in] DATA 0 pointer is null, 1 pointer is not null + * @param[in] MUST expected [sized array](@ref sizedarrays) size of list of must restrictions + * @param[in] NODETYPE LYS_INPUT or LYS_OUTPUT + */ +#define CHECK_LYSC_ACTION_INOUT(NODE, DATA, MUST, NODETYPE) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->child, DATA); \ + CHECK_ARRAY((NODE)->musts, MUST); \ + assert_int_equal((NODE)->nodetype, NODETYPE); + +/** + * @brief assert that lysc_action structure members are correct + * + * @param[in] NODE pointer to lysp_action variable + * @param[in] DSC string description statement + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] INPUT_DATA 0 pointer is null, 1 pointer is not null + * @param[in] INPUT_MUST expected [sized array](@ref sizedarrays) size of input list of must restrictions + * @param[in] INPUT_EXTS expected [sized array](@ref sizedarrays) size of the input extension instances of input + * @param[in] MODULE 0 pointer is null, 1 pointer is not null + * @param[in] NAME expected name + * @param[in] NODETYPE LYS_RPC, LYS_ACTION + * @param[in] OUTPUT_DATA 0 pointer is null, 1 pointer is not null + * @param[in] OUTPUT_MUST expected [sized array](@ref sizedarrays) size of output list of must restrictions + * @param[in] OUTPUT_EXTS expected [sized array](@ref sizedarrays) size of the output extension instances of input + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null + * @param[in] REF expected reference + * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements + */ +#define CHECK_LYSC_ACTION(NODE, DSC, EXTS, FLAGS, INPUT_DATA, INPUT_MUST, INPUT_EXTS, MODULE, NAME, NODETYPE, \ + OUTPUT_DATA, OUTPUT_MUST, OUTPUT_EXTS, PARENT, PRIV, REF, WHEN) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_LYSC_ACTION_INOUT(&(NODE)->input, INPUT_DATA, INPUT_MUST, LYS_INPUT); \ + CHECK_ARRAY((NODE)->input.exts, INPUT_EXTS); \ + CHECK_POINTER((NODE)->module, MODULE); \ + assert_string_equal((NODE)->name, NAME); \ + assert_int_equal((NODE)->nodetype, NODETYPE); \ + CHECK_LYSC_ACTION_INOUT(&(NODE)->output, OUTPUT_DATA, OUTPUT_MUST, LYS_OUTPUT); \ + CHECK_ARRAY((NODE)->output.exts, OUTPUT_EXTS); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_POINTER((NODE)->priv, PRIV); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN); + +/** + * @brief assert that lysc_node structure members are correct + * + * @param[in] NODE pointer to lysc_node variable + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] MODULE 0 pointer is null, 1 pointer is not null + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] NODETYPE type of the node LYS_UNKNOWN, LYS_CONTAINER, LYS_CHOICE, LYS_LEAF, + * LYS_LEAFLIST, LYS_LIST, LYS_ANYXML, LYS_ANYDATA, LYS_CASE, LYS_RPC, + * LYS_ACTION, LYS_NOTIF, LYS_USES, LYS_INPUT, LYS_OUTPUT, LYS_GROUPING, + * LYS_AUGMENT + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null + * @param[in] REF expected reference + * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements + */ +#define CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, NODETYPE, PARENT, PRIV, REF, WHEN) \ + assert_non_null(NODE); \ + CHECK_STRING((NODE)->dsc, DSC); \ + CHECK_ARRAY((NODE)->exts, EXTS); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->module, MODULE); \ + assert_string_equal((NODE)->name, NAME); \ + CHECK_POINTER((NODE)->next, NEXT); \ + assert_int_equal((NODE)->nodetype, NODETYPE); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + assert_non_null((NODE)->prev); \ + CHECK_POINTER((NODE)->priv, PRIV); \ + CHECK_STRING((NODE)->ref, REF); \ + CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN); + +/** + * @brief assert that lysc_node_leaf structure members are correct + * + * @param[in] NODE pointer to lysc_node variable + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] MODULE 0 pointer is null, 1 pointer is not null + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null + * @param[in] REF expected reference + * @param[in] ACTIONS 1 if is set pointer to structure lysc_node_action other 0 + * @param[in] CHILD 1 if is set pointer to child other 0 + * @param[in] MAX possible maximum elements in list + * @param[in] MIN possible minimum elements in list + * @param[in] MUSTS [sized array](@ref sizedarrays) number of must node elements in array + * @param[in] NOTIFS 1 if is set pointer to any notifs node + * @param[in] UNIQUES [sized array](@ref sizedarrays) number of unique nodes element in array + * @param[in] WHEN [sized array](@ref sizedarrays) size of when node array + */ +#define CHECK_LYSC_NODE_LIST(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, \ + PARENT, PRIV, REF, ACTIONS, CHILD, MAX, MIN, MUSTS, NOTIFS, UNIQUES, WHEN) \ + CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, LYS_LIST, PARENT, PRIV, REF, WHEN); \ + CHECK_POINTER((NODE)->actions, ACTIONS); \ + CHECK_POINTER((NODE)->child, CHILD); \ + assert_int_equal((NODE)->max, MAX); \ + assert_int_equal((NODE)->min, MIN); \ + CHECK_ARRAY((NODE)->musts, MUSTS); \ + CHECK_POINTER((NODE)->notifs, NOTIFS); \ + CHECK_ARRAY((NODE)->uniques, UNIQUES); \ + CHECK_ARRAY(lysc_node_when((const struct lysc_node *)NODE), WHEN) + +/** + * @brief assert that lysc_node_leaf structure members are correct + * + * @param[in] NODE pointer to lysc_node variable + * @param[in] DSC expected description + * @param[in] EXTS expected [sized array](@ref sizedarrays) size of list of the extension instances + * @param[in] FLAGS [schema node flags](@ref snodeflags) + * @param[in] MODULE 0 pointer is null, 1 pointer is not null + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] PARENT 0-> check if node is root, 1-> check if node is not root + * @param[in] PRIV 0-> pointer is null, 1-> pointer is not null + * @param[in] REF expected reference + * @param[in] WHEN expected [sized array](@ref sizedarrays) size of list of pointers to when statements + * @param[in] MUSTS expected [sized array](@ref sizedarrays) size of list of must restriction + * @param[in] UNITS expected string reprezenting units + * @param[in] DFLT 0-> node dosn't have default value. 1 -> node have default value + */ +#define CHECK_LYSC_NODE_LEAF(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, \ + PARENT, PRIV, REF, WHEN, MUSTS, UNITS, DFLT) \ + CHECK_LYSC_NODE(NODE, DSC, EXTS, FLAGS, MODULE, NAME, NEXT, LYS_LEAF, PARENT, PRIV, REF, WHEN); \ + CHECK_ARRAY((NODE)->musts, MUSTS); \ + assert_non_null((NODE)->type); \ + CHECK_STRING((NODE)->units, UNITS); \ + CHECK_POINTER((NODE)->dflt, DFLT); + +/** + * @brief assert that lyd_meta structure members are correct + * + * @param[in] NODE pointer to lyd_meta variable + * @param[in] ANNOTATION 0 pointer is null, 1 pointer is not null + * @param[in] NAME expected name + * @param[in] NEXT 0 pointer is null, 1 pointer is not null + * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT + * part of text reprezenting LY_DATA_TYPE. + * @param[in] ... ::CHECK_LYD_VALUE + */ +#define CHECK_LYD_META(NODE, ANNOTATION, NAME, NEXT, PARENT, TYPE_VAL, ...) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->annotation, ANNOTATION); \ + assert_string_equal((NODE)->name, NAME); \ + CHECK_POINTER((NODE)->next, NEXT); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + CHECK_LYD_VALUE((NODE)->value, TYPE_VAL, __VA_ARGS__); + +/** + * @brief assert that lyd_node_term structure members are correct + * + * @param[in] NODE pointer to lyd_node_term variable + * @param[in] FLAGS expected [data node flags](@ref dnodeflags) + * @param[in] META 0 -> meta is not prezent, 1 -> meta is prezent + * @param[in] NEXT 0 -> next node is not prezent, 1 -> next node is prezent + * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT + * part of text reprezenting LY_DATA_TYPE. + * @param[in] ... ::CHECK_LYD_VALUE + */ +#define CHECK_LYD_NODE_TERM(NODE, FLAGS, META, NEXT, PARENT, SCHEMA, VALUE_TYPE, ...) \ + assert_non_null(NODE); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->meta, META); \ + CHECK_POINTER((NODE)->next, NEXT); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + assert_non_null((NODE)->prev); \ + CHECK_POINTER((NODE)->schema, SCHEMA); \ + CHECK_LYD_VALUE((NODE)->value, VALUE_TYPE, __VA_ARGS__); + +/** + * @brief assert that lyd_node_any structure members are correct + * + * @param[in] NODE pointer to lyd_node_term variable + * @param[in] FLAGS expected [data node flags](@ref dnodeflags) + * @param[in] META 0 meta isnt present , 1 meta is present + * @param[in] PARENT 0 it is root node , 1 node have parent + * @param[in] VALUE_TYPE value type ::lyd_node_any.value + */ +#define CHECK_LYD_NODE_ANY(NODE, FLAGS, META, PARENT, VALUE_TYPE) \ + assert_non_null(NODE); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->meta, META); \ + CHECK_POINTER((NODE)->meta, PARENT); \ + assert_non_null((NODE)->prev); \ + assert_non_null((NODE)->schema); \ + assert_int_equal((NODE)->value_type, VALUE_TYPE); + +/** + * @brief assert that lyd_node_opaq structure members are correct + * + * @param[in] NODE pointer to lyd_node_opaq variable + * @param[in] ATTR 0 if pointer is null ,1 if pointer is not null + * @param[in] CHILD 0 if pointer is null ,1 if pointer is not null + * @param[in] FORMAT LY_PREF_XML or LY_PREF_JSON + * @param[in] VAL_PREFS 0 if pointer is null ,1 if pointer is not null + * @param[in] NAME expected name + * @param[in] value expected orignal value + */ +#define CHECK_LYD_NODE_OPAQ(NODE, ATTR, CHILD, FORMAT, NAME, NEXT, PARENT, PREFIX, VAL_PREFS, VALUE) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->attr, ATTR); \ + CHECK_POINTER((NODE)->child, CHILD); \ + assert_ptr_equal((NODE)->ctx, UTEST_LYCTX); \ + assert_int_equal((NODE)->flags, 0); \ + assert_true((NODE)->format == FORMAT); \ + assert_int_equal((NODE)->hash, 0); \ + assert_string_equal((NODE)->name.name, NAME); \ + assert_non_null((NODE)->prev); \ + assert_null((NODE)->schema); \ + CHECK_POINTER((NODE)->val_prefix_data, VAL_PREFS); \ + assert_string_equal((NODE)->value, VALUE); + +/** + * @brief assert that lyd_node_opaq structure members are correct + * + * @param[in] NODE pointer to lyd_node_opaq variable + * @param[in] CHILD 1 if node has children other 0 + * @param[in] HILD_HT 1 if node has children hash table other 0 + * @param[in] META 1 if node has metadata other 0 + * @param[in] FLAGS expected flag + * @param[in] NEXT 1 if next node is present other 0 + * @param[in] PARENT 1 if node has parent other 0 + * @param[in] PRIV 1 if node has private data other 0 + * @param[in] SCHEMA 1 if node has schema other 0 +*/ +#define CHECK_LYD_NODE_INNER(NODE, CHILD, CHILD_HT, META, FLAGS, NEXT, PARENT, PRIV, SCHEMA) \ + assert_non_null(NODE); \ + CHECK_POINTER((NODE)->child, CHILD); \ + CHECK_POINTER((NODE)->children_ht, CHILD_HT); \ + CHECK_POINTER((NODE)->meta, META); \ + assert_int_equal((NODE)->flags, FLAGS); \ + CHECK_POINTER((NODE)->parent, PARENT); \ + assert_non_null((NODE)->prev); \ + CHECK_POINTER((NODE)->next, NEXT); \ + CHECK_POINTER((NODE)->priv, PRIV); \ + CHECK_POINTER((NODE)->schema, SCHEMA) + +/** + * @brief assert that lyd_value structure members are correct + * + * @param[in] NODE lyd_value + * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT + * part of text reprezenting LY_DATA_TYPE. + * @param[in] ... Unspecified parameters. Type and numbers of parameters are specified + * by type of value. These parameters are passed to macro + * CHECK_LYD_VALUE_ ## TYPE_VAL. + */ +#define CHECK_LYD_VALUE(NODE, TYPE_VAL, ...) \ + CHECK_LYD_VALUE_ ## TYPE_VAL (NODE, __VA_ARGS__); + +/* + * LYD VALUES CHECKING SPECIALIZATION + */ + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type EMPTY + * Example CHECK_LYD_VALUE(node->value, EMPTY, ""); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + */ +#define CHECK_LYD_VALUE_EMPTY(NODE, CANNONICAL_VAL) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal((NODE).realtype->basetype, LY_TYPE_EMPTY); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UNION + * Example CHECK_LYD_VALUE(node->value, UNION, "12", INT8, "12", 12); + * @warning type of subvalue cannot be UNION. Example of calling + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] TYPE_VAL value type. EMPTY, UNION, BITS, INST, ENUM, INT8, INT16, UINT8, STRING, LEAFREF, DEC64, BINARY, BOOL, IDENT + * @param[in] ... Unspecified parameters. Type and numbers of parameters are specified + * by type of value. These parameters are passed to macro + * CHECK_LYD_VALUE_ ## TYPE_VAL. + */ +#define CHECK_LYD_VALUE_UNION(NODE, CANNONICAL_VAL, TYPE_VAL, ...) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_UNION, (NODE).realtype->basetype); \ + assert_non_null((NODE).subvalue); \ + assert_non_null((NODE).subvalue->prefix_data); \ + CHECK_LYD_VALUE_ ## TYPE_VAL ((NODE).subvalue->value, __VA_ARGS__) + +/** + * @brief Internal macro. Get 1st variadic argument. + */ +#define _GETARG1(ARG1, ...) ARG1 + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BITS + * Example arr[] = {"a", "b"}; CHECK_LYD_VALUE(node->value, BITS, "a b", arr); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected array of bits names + */ +#define CHECK_LYD_VALUE_BITS(NODE, ...) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, _GETARG1(__VA_ARGS__, DUMMY)); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_BITS, (NODE).realtype->basetype); \ + { \ + const char *arr[] = { __VA_ARGS__ }; \ + LY_ARRAY_COUNT_TYPE arr_size = (sizeof(arr) / sizeof(arr[0])) - 1; \ + struct lyd_value_bits *_val; \ + LYD_VALUE_GET(&(NODE), _val); \ + assert_int_equal(arr_size, LY_ARRAY_COUNT(_val->items)); \ + for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \ + assert_string_equal(arr[it + 1], _val->items[it]->name); \ + } \ + } + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INST + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected array of enum ly_path_pred_type + * @brief Example enum arr[] = {0x0, 0x1}; CHECK_LYD_VALUE(node->value, INST, "test/d", arr); + */ +#define CHECK_LYD_VALUE_INST(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_INST, (NODE).realtype->basetype); \ + { \ + LY_ARRAY_COUNT_TYPE arr_size = sizeof(VALUE) / sizeof(VALUE[0]); \ + assert_int_equal(arr_size, LY_ARRAY_COUNT((NODE).target)); \ + for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \ + if ((NODE).target[it].predicates) { \ + assert_int_equal(VALUE[it], (NODE).target[it].predicates[0].type); \ + } \ + } \ + } + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type ENUM. + * Example CHECK_LYD_VALUE(node->value, ENUM, "item_name", "item_name"); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected enum item name + */ +#define CHECK_LYD_VALUE_ENUM(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_ENUM, (NODE).realtype->basetype); \ + assert_string_equal(VALUE, (NODE).enum_item->name); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INT8 + * Example CHECK_LYD_VALUE(node->value, INT8, "12", 12); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected inteager value (-128 to 127). + */ +#define CHECK_LYD_VALUE_INT8(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_INT8, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).int8); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type INT16 + * Example CHECK_LYD_VALUE(node->value, INT8, "12", 12); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected inteager value. + */ +#define CHECK_LYD_VALUE_INT16(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_INT16, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).int16); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UINT8. + * Example CHECK_LYD_VALUE(node->value, UINT8, "12", 12); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected inteager (0 to 255). + */ +#define CHECK_LYD_VALUE_UINT8(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_UINT8, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).uint8); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type UINT32. + * Example CHECK_LYD_VALUE(node->value, UINT32, "12", 12); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected inteager (0 to MAX_UINT32). + */ +#define CHECK_LYD_VALUE_UINT32(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_UINT32, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).uint32); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type STRING. + * Example CHECK_LYD_VALUE(node->value, STRING, "text"); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + */ +#define CHECK_LYD_VALUE_STRING(NODE, CANNONICAL_VAL) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_STRING, (NODE).realtype->basetype); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type LEAFREF + * Example CHECK_LYD_VALUE(node->value, LEAFREF, ""); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + */ +#define CHECK_LYD_VALUE_LEAFREF(NODE, CANNONICAL_VAL) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_LEAFREF, (NODE).realtype->basetype); \ + assert_non_null((NODE).ptr) + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type DEC64 + * Example CHECK_LYD_VALUE(node->value, DEC64, "125", 125); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected value 64bit inteager +*/ +#define CHECK_LYD_VALUE_DEC64(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_DEC64, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).dec64); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BINARY. + * Example CHECK_LYD_VALUE(node->value, BINARY, "aGVs\nbG8="); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected value data + * @param[in] SIZE expected value data size +*/ +#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL, VALUE, SIZE) \ + { \ + struct lyd_value_binary *_val; \ + LYD_VALUE_GET(&(NODE), _val); \ + assert_int_equal(_val->size, SIZE); \ + assert_int_equal(0, memcmp(_val->data, VALUE, SIZE)); \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_BINARY, (NODE).realtype->basetype); \ + } + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BOOL. + * Example CHECK_LYD_VALUE(node->value, BOOL, "true", 1); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected boolean value 0,1 +*/ +#define CHECK_LYD_VALUE_BOOL(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_BOOL, (NODE).realtype->basetype); \ + assert_int_equal(VALUE, (NODE).boolean); + +/** + * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type IDENT. + * Example CHECK_LYD_VALUE(node->value, IDENT, "types:gigabit-ethernet", "gigabit-ethernet"); + * + * @param[in] NODE lyd_value variable + * @param[in] CANNONICAL_VAL expected cannonical value + * @param[in] VALUE expected ident name +*/ +#define CHECK_LYD_VALUE_IDENT(NODE, CANNONICAL_VAL, VALUE) \ + assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \ + assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \ + assert_non_null((NODE).realtype); \ + assert_int_equal(LY_TYPE_IDENT, (NODE).realtype->basetype); \ + assert_string_equal(VALUE, (NODE).ident->name); + +/** + * @brief Macro testing parser when parsing incorrect module; + * + * @param[in] DATA String storing the schema module representation. + * @param[in] FORMAT Schema format of the @p DATA + * @param[in] FEATURES Array of module's features to enable + * @param[in] RET_VAL ly_in_new_memory return error value + */ +#define UTEST_INVALID_MODULE(DATA, FORMAT, FEATURES, RET_VAL) \ + { \ + struct lys_module *mod; \ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(DATA, &_UC->in)); \ + assert_int_equal(RET_VAL, lys_parse(_UC->ctx, _UC->in, FORMAT, FEATURES, &mod)); \ + assert_null(mod); \ + } \ + ly_in_free(_UC->in, 0); \ + _UC->in = NULL; \ + +/** + * @brief Add module (from a string) into the used libyang context. + * + * @param[in] DATA String storing the schema module representation. + * @param[in] FORMAT Schema format of the @p DATA + * @param[in] FEATURES Array of module's features to enable + * @param[out] MOD Optional parameter as a pointer to variable to store the resulting module. + */ +#define UTEST_ADD_MODULE(DATA, FORMAT, FEATURES, MOD) \ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(DATA, &_UC->in)); \ + { \ + LY_ERR __r = lys_parse(_UC->ctx, _UC->in, FORMAT, FEATURES, MOD); \ + if (__r != LY_SUCCESS) { \ + print_message("[ MSG ] Module parsing failed:\n"); \ + for (const struct ly_err_item *e = ly_err_first(_UC->ctx); e; e = e->next) { \ + print_message("[ MSG ] \t%s Schema path %s\n", e->msg, e->schema_path); \ + } \ + fail(); \ + } \ + } \ + ly_in_free(_UC->in, 0); \ + _UC->in = NULL + +/** + * @brief Check expected last error message. + * + * @param[in] MSG Expected error message. + */ +#define CHECK_LOG_LASTMSG(MSG) \ + CHECK_STRING(ly_last_logmsg(), MSG) + +/** + * @brief Check expected last error in libyang context, which is then cleared. Can be called repeatedly to check + * several errors. If NULL is provided as MSG, no error info record (NULL) is expected. + * + * @param[in] MSG Expected error message. + * @param[in] PATH Expected error data/schema path. + * @param[in] LINE Expected error line. + */ +#define CHECK_LOG_CTX(MSG, PATH, LINE) \ + { \ + struct ly_err_item *_e = (struct ly_err_item *)ly_err_last(_UC->ctx); \ + if (!MSG) { \ + assert_null(_e); \ + } else { \ + assert_non_null(_e); \ + CHECK_STRING(_e->msg, MSG); \ + if (_e->data_path) { \ + CHECK_STRING(_e->data_path, PATH); \ + } else { \ + CHECK_STRING(_e->schema_path, PATH); \ + } \ + assert_int_equal(_e->line, LINE); \ + } \ + ly_err_clean(_UC->ctx, _e); \ + } + +/** + * @brief Check expected error in libyang context including error-app-tag. + * + * @param[in] MSG Expected error message. + * @param[in] PATH Expected error data/schema path. + * @param[in] LINE Expected error line. + * @param[in] APPTAG Expected error-app-tag. + */ +#define CHECK_LOG_CTX_APPTAG(MSG, PATH, LINE, APPTAG) \ + { \ + struct ly_err_item *_e = (struct ly_err_item *)ly_err_last(_UC->ctx); \ + if (!MSG) { \ + assert_null(_e); \ + } else { \ + assert_non_null(_e); \ + CHECK_STRING(_e->msg, MSG); \ + if (_e->data_path) { \ + CHECK_STRING(_e->data_path, PATH); \ + } else { \ + CHECK_STRING(_e->schema_path, PATH); \ + } \ + assert_int_equal(_e->line, LINE); \ + CHECK_STRING(_e->apptag, APPTAG); \ + } \ + ly_err_clean(_UC->ctx, _e); \ + } + +/** + * @brief Clear all errors stored in the libyang context. + */ +#define UTEST_LOG_CTX_CLEAN \ + ly_err_clean(_UC->ctx, NULL) + +#ifdef _UTEST_MAIN_ +/* + * Functions inlined into each C source file including this header with _UTEST_MAIN_ defined + */ + +/** + * @brief Global variable holding the tests context to simplify access to it. + */ +struct utest_context *current_utest_context; + +/** + * @brief Generic utest's setup + */ +static int +utest_setup(void **state) +{ + char *cur_tz; + + /* setup the logger */ + ly_log_options(/*LY_LOLOG |*/ LY_LOSTORE); + + current_utest_context = calloc(1, sizeof *current_utest_context); + assert_non_null(current_utest_context); + *state = current_utest_context; + + /* libyang context */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, ¤t_utest_context->ctx)); + + /* backup timezone, if any */ + cur_tz = getenv("TZ"); + if (cur_tz) { + current_utest_context->orig_tz = strdup(cur_tz); + } + + /* set CET */ + setenv("TZ", "CET+02:00", 1); + + return 0; +} + +/** + * @brief macro to include generic utest's setup into the test-specific setup. + * + * Place at the beginning of the test-specific setup + */ +#define UTEST_SETUP \ + assert_int_equal(0, utest_setup(state)) + +/** + * @brief Generic utest's teardown + */ +static int +utest_teardown(void **state) +{ + *state = NULL; + + /* libyang context, no leftover messages */ + assert_null(ly_err_last(current_utest_context->ctx)); + ly_ctx_destroy(current_utest_context->ctx); + + if (current_utest_context->orig_tz) { + /* restore TZ */ + setenv("TZ", current_utest_context->orig_tz, 1); + } + + /* utest context */ + ly_in_free(current_utest_context->in, 0); + free(current_utest_context->orig_tz); + free(current_utest_context); + current_utest_context = NULL; + + return 0; +} + +/** + * @brief macro to include generic utest's teardown into the test-specific teardown. + * + * Place at the end of the test-specific teardown + */ +#define UTEST_TEARDOWN \ + assert_int_equal(0, utest_teardown(state)) + +/** + * @brief Internal macro for utest setup with test-specific setup and teardown + */ +#define _UTEST_SETUP_TEARDOWN(FUNC, SETUP, TEARDOWN) \ + cmocka_unit_test_setup_teardown(FUNC, SETUP, TEARDOWN) + +/** + * @brief Internal macro for utest setup with test-specific setup and generic teardown + */ +#define _UTEST_SETUP(FUNC, SETUP) \ + cmocka_unit_test_setup_teardown(FUNC, SETUP, utest_teardown) + +/** + * @brief Internal macro for utest setup with generic setup and teardown + */ +#define _UTEST(FUNC) \ + cmocka_unit_test_setup_teardown(FUNC, utest_setup, utest_teardown) + +/** + * @brief Internal helper macro to select _UTEST* macro according to the provided parameters. + */ +#define _GET_UTEST_MACRO(_1, _2, _3, NAME, ...) NAME + +/** + * @brief Macro to specify test function using utest environment. Macro has variadic parameters + * to provide test-specific setup/teardown functions: + * + * UTEST(test_func) - only implicit setup and teardown functions are used + * UTEST(test_func, setup) - implicit teardown but own setup + * UTEST(test_func, setup, teardown) - both setup and teardown are test-specific + */ +#define UTEST(...) \ + _GET_UTEST_MACRO(__VA_ARGS__, _UTEST_SETUP_TEARDOWN, _UTEST_SETUP, _UTEST, DUMMY)(__VA_ARGS__) + +#else /* _UTEST_MAIN_ */ + +extern struct utest_context *current_utest_context; + +#endif /* _UTEST_MAIN_ */ + +#endif /* _UTESTS_H_ */ diff --git a/tests/yanglint/CMakeLists.txt b/tests/yanglint/CMakeLists.txt new file mode 100644 index 0000000..c1e081a --- /dev/null +++ b/tests/yanglint/CMakeLists.txt @@ -0,0 +1,36 @@ +if(WIN32) + set(YANGLINT_INTERACTIVE OFF) +else() + set(YANGLINT_INTERACTIVE ON) +endif() + +function(add_yanglint_test) + cmake_parse_arguments(ADDTEST "" "NAME;VIA;SCRIPT" "" ${ARGN}) + set(TEST_NAME yanglint_${ADDTEST_NAME}) + + if(${ADDTEST_VIA} STREQUAL "tclsh") + set(WRAPPER ${PATH_TCLSH}) + else() + message(FATAL_ERROR "build: unexpected wrapper '${ADDTEST_VIA}'") + endif() + + add_test(NAME ${TEST_NAME} COMMAND ${WRAPPER} ${CMAKE_CURRENT_SOURCE_DIR}/${ADDTEST_SCRIPT}) + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "TESTS_DIR=${CMAKE_CURRENT_SOURCE_DIR}") + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANG_MODULES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/modules") + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANGLINT=${PROJECT_BINARY_DIR}") +endfunction(add_yanglint_test) + +if(ENABLE_TESTS) + # tests of interactive mode using tclsh + find_program(PATH_TCLSH NAMES tclsh) + if(NOT PATH_TCLSH) + message(WARNING "'tclsh' not found! The yanglint(1) interactive tests will not be available.") + else() + if(YANGLINT_INTERACTIVE) + add_yanglint_test(NAME interactive VIA tclsh SCRIPT interactive/all.tcl) + add_yanglint_test(NAME non-interactive VIA tclsh SCRIPT non-interactive/all.tcl) + else() + add_yanglint_test(NAME non-interactive VIA tclsh SCRIPT non-interactive/all.tcl) + endif() + endif() +endif() diff --git a/tests/yanglint/README.md b/tests/yanglint/README.md new file mode 100644 index 0000000..6c51d89 --- /dev/null +++ b/tests/yanglint/README.md @@ -0,0 +1,107 @@ +# yanglint testing + +Testing yanglint is divided into two ways. +It is either tested in interactive mode using the tcl command 'expect' or non-interactively, classically from the command line. +For both modes, unit testing was used using the tcl package tcltest. + +## How to + +The sample commands in this chapter using `tclsh` are called in the `interactive` or `non-interactive` directories. + +### How to run all yanglint tests? + +In the build directory designated for cmake, enter: + +``` +ctest -R yanglint +``` + +### How to run all yanglint tests that are in interactive mode? + +In the interactive directory, run: + +``` +tclsh all.tcl +``` + +### How to run all yanglint tests that are in non-interactive mode? + +In the non-interactive directory, run: + +``` +tclsh all.tcl +``` + +### How to run all unit-tests from .test file? + +``` +tclsh clear.test +``` + +or alternatively: + +``` +tclsh all.tcl -file clear.test +``` + +### How to run one unit-test? + +``` +tclsh clear.test -match clear_ietf_yang_library +``` + +or alternatively: + +``` +tclsh all.tcl -file clear.test -match clear_ietf_yang_library +``` + +### How to run unit-tests for a certain yanglint command? + +Test names are assumed to consist of the command name: + +``` +tclsh all.tcl -match clear* +``` + +### How do I get more detailed information about 'expect' for a certain test? + +In the interactive directory, run: + +``` +tclsh clear.test -match clear_ietf_yang_library -load "exp_internal 1" +``` + +### How do I get more detailed dialog between 'expect' and yanglint for a certain test? + +In the interactive directory, run: + +``` +tclsh clear.test -match clear_ietf_yang_library -load "log_user 1" +``` + +### How do I suppress error message from tcltest? + +Probably only possible to do via `-verbose ""` + +### How can I also debug? + +You can write commands `interact` and `interpreter` from 'Expect' package into some test. +However, the most useful are the `exp_internal` and `log_user`, which can also be written directly into the test. +See also the rlwrap tool. +You can also use other debugging methods used in tcl programming. + +### Are the tests between interactive mode and non-interactive mode similar? + +Sort of... +- regex \n must be changed to \r\n in the tests for interactive yanglint + +### I would like to add a new "ly_" function. + +Add it to the ly.tcl file. +If you need to call other subfunctions in it, add them to namespace ly::private. + +### I would like to use function other than those prefixed with "ly_". + +Look in the common.tcl file in the "uti" namespace, +which contains general tcl functions that can be used in both interactive and non-interactive tests. diff --git a/tests/yanglint/common.tcl b/tests/yanglint/common.tcl new file mode 100644 index 0000000..d186282 --- /dev/null +++ b/tests/yanglint/common.tcl @@ -0,0 +1,114 @@ +# @brief Common functions and variables for yanglint-interactive and yanglint-non-interactive. +# +# The script requires variables: +# ::env(TESTS_DIR) - Main test directory. Must be set if the script is run via ctest. +# +# The script sets the variables: +# ::env(TESTS_DIR) - Main test directory. It is set by default if not defined. +# ::env(YANG_MODULES_DIR) - Directory of YANG modules. +# TUT_PATH - Assumed absolute path to the directory in which the TUT is located. +# TUT_NAME - TUT name (without path). +# ::tcltest::testConstraint ctest - A tcltest variable that is set to true if the script is run via ctest. Causes tests +# to be a skipped. + +package require tcltest +namespace import ::tcltest::test ::tcltest::cleanupTests + +# Set directory paths for testing yanglint. +if { ![info exists ::env(TESTS_DIR)] } { + # the script is not run via 'ctest' so paths must be set + set ::env(TESTS_DIR) "../" + set ::env(YANG_MODULES_DIR) "../modules" + set TUT_PATH "../../../build" + ::tcltest::testConstraint ctest false +} else { + # cmake (ctest) already sets ::env variables + set TUT_PATH $::env(YANGLINT) + ::tcltest::testConstraint ctest true +} + +set TUT_NAME "yanglint" + +# The script continues by defining functions specific to the yanglint tool. + +namespace eval uti { + namespace export * +} + +# Iterate through the items in the list 'lst' and return a new list where +# the items will have the form: <prefix><item><suffix>. +# Parameter 'index' determines at which index it will start wrapping. +# Parameter 'step' specifies how far the iterator must move to wrap the next item. +proc uti::wrap_list_items {lst {prefix ""} {suffix ""} {index 0} {step 1}} { + # counter to track when to insert wrapper + set cnt $step + set len [llength $lst] + + if {$index > 0} { + # copy list from interval <0;$index) + set ret [lrange $lst 0 [expr {$index - 1}]] + } else { + set ret {} + } + + for {set i $index} {$i < $len} {incr i} { + incr cnt + set item [lindex $lst $i] + if {$cnt >= $step} { + # insert wrapper for item + set cnt 0 + lappend ret [string cat $prefix $item $suffix] + } else { + # just copy item + lappend ret $item + } + } + + return $ret +} + +# Wrap list items with xml tags. +# The element format is: <tag>value</tag> +# Parameter 'values' is list of values. +# Parameter 'tag' is the name of the searched tag. +proc uti::wrap_to_xml {values tag {index 0} {step 1}} { + return [wrap_list_items $values "<$tag>" "</$tag>" $index $step] +} + +# Wrap list items with json attributes. +# The pair format is: "attribute": "value" +# Parameter 'values' is list of values. +# Parameter 'attribute' is the name of the searched attribute. +proc uti::wrap_to_json {values attribute {index 0} {step 1}} { + return [wrap_list_items $values "\"$attribute\": \"" "\"" $index $step] +} + +# Convert list to a regex (which is just a string) so that 'delim' is between items, +# 'begin' is at the beginning of the expression and 'end' is at the end. +proc uti::list_to_regex {lst {delim ".*"} {begin ".*"} {end ".*"}} { + return [string cat $begin [join $lst $delim] $end] +} + +# Merge two lists into one such that the nth items are merged into one separated by a delimiter. +# Returns a list that is the same length as 'lst1' and 'lst2' +proc uti::blend_lists {lst1 lst2 {delim ".*"}} { + return [lmap a $lst1 b $lst2 {string cat $a $delim $b}] +} + +# Create regex to find xml elements. +# The element format is: <tag>value</tag> +# Parameter 'values' is list of values. +# Parameter 'tag' is the name of the searched tag. +# The resulting expression looks like: ".*<tag>value1</tag>.*<tag>value2</tag>.*..." +proc uti::regex_xml_elements {values tag} { + return [list_to_regex [wrap_to_xml $values $tag]] +} + +# Create regex to find json pairs. +# The pair format is: "attribute": "value" +# Parameter 'values' is list of values. +# Parameter 'attribute' is the name of the searched attribute. +# The resulting expression looks like: ".*\"attribute\": \"value1\".*\"attribute\": \"value2\".*..." +proc uti::regex_json_pairs {values attribute} { + return [list_to_regex [wrap_to_json $values $attribute]] +} diff --git a/tests/yanglint/data/modaction.xml b/tests/yanglint/data/modaction.xml new file mode 100644 index 0000000..37faa2d --- /dev/null +++ b/tests/yanglint/data/modaction.xml @@ -0,0 +1,8 @@ +<con xmlns="urn:yanglint:modaction"> + <ls> + <lfkey>kv</lfkey> + <act> + <lfi>some_input</lfi> + </act> + </ls> +</con> diff --git a/tests/yanglint/data/modaction_ds.xml b/tests/yanglint/data/modaction_ds.xml new file mode 100644 index 0000000..a5a1727 --- /dev/null +++ b/tests/yanglint/data/modaction_ds.xml @@ -0,0 +1,5 @@ +<con xmlns="urn:yanglint:modaction"> + <ls> + <lfkey>kv</lfkey> + </ls> +</con> diff --git a/tests/yanglint/data/modaction_nc.xml b/tests/yanglint/data/modaction_nc.xml new file mode 100644 index 0000000..a74b6bf --- /dev/null +++ b/tests/yanglint/data/modaction_nc.xml @@ -0,0 +1,13 @@ +<rpc message-id="101" + xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <action xmlns="urn:ietf:params:xml:ns:yang:1"> + <con xmlns="urn:yanglint:modaction"> + <ls> + <lfkey>kv</lfkey> + <act> + <lfi>some_input</lfi> + </act> + </ls> + </con> + </action> +</rpc> diff --git a/tests/yanglint/data/modaction_reply.xml b/tests/yanglint/data/modaction_reply.xml new file mode 100644 index 0000000..7d6532d --- /dev/null +++ b/tests/yanglint/data/modaction_reply.xml @@ -0,0 +1,8 @@ +<con xmlns="urn:yanglint:modaction"> + <ls> + <lfkey>kv</lfkey> + <act> + <lfo>-56</lfo> + </act> + </ls> +</con> diff --git a/tests/yanglint/data/modaction_reply_nc.xml b/tests/yanglint/data/modaction_reply_nc.xml new file mode 100644 index 0000000..f7c3b8f --- /dev/null +++ b/tests/yanglint/data/modaction_reply_nc.xml @@ -0,0 +1,4 @@ +<rpc-reply message-id="101" + xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <lfo xmlns="urn:yanglint:modaction">-56</lfo> +</rpc-reply> diff --git a/tests/yanglint/data/modconfig.xml b/tests/yanglint/data/modconfig.xml new file mode 100644 index 0000000..f8a03a9 --- /dev/null +++ b/tests/yanglint/data/modconfig.xml @@ -0,0 +1,4 @@ +<mcc xmlns="urn:yanglint:modconfig"> + <lft>rw</lft> + <lff>ro</lff> +</mcc> diff --git a/tests/yanglint/data/modconfig2.xml b/tests/yanglint/data/modconfig2.xml new file mode 100644 index 0000000..c96e344 --- /dev/null +++ b/tests/yanglint/data/modconfig2.xml @@ -0,0 +1,3 @@ +<mcc xmlns="urn:yanglint:modconfig"> + <lft>rw</lft> +</mcc> diff --git a/tests/yanglint/data/modconfig_ctx.xml b/tests/yanglint/data/modconfig_ctx.xml new file mode 100644 index 0000000..124989c --- /dev/null +++ b/tests/yanglint/data/modconfig_ctx.xml @@ -0,0 +1,13 @@ +<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set> + <name>main-set</name> + <module> + <name>modconfig</name> + <namespace>urn:yanglint:modconfig</namespace> + </module> + </module-set> + <content-id>1</content-id> +</yang-library> +<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set-id>1</module-set-id> +</modules-state> diff --git a/tests/yanglint/data/moddatanodes.xml b/tests/yanglint/data/moddatanodes.xml new file mode 100644 index 0000000..8ae6e97 --- /dev/null +++ b/tests/yanglint/data/moddatanodes.xml @@ -0,0 +1,17 @@ +<dnc xmlns="urn:yanglint:moddatanodes"> + <lf>x</lf> + <lfl>1</lfl> + <lfl>2</lfl> + <con> + <lt> + <kalf>ka1</kalf> + <kblf>kb1</kblf> + <vlf>v1</vlf> + </lt> + <lt> + <kalf>ka2</kalf> + <kblf>kb2</kblf> + <vlf>v2</vlf> + </lt> + </con> +</dnc> diff --git a/tests/yanglint/data/moddefault.xml b/tests/yanglint/data/moddefault.xml new file mode 100644 index 0000000..00f3a9d --- /dev/null +++ b/tests/yanglint/data/moddefault.xml @@ -0,0 +1,4 @@ +<mdc xmlns="urn:yanglint:moddefault"> + <lf>0</lf> + <di>5</di> +</mdc> diff --git a/tests/yanglint/data/modimp_type_ctx.xml b/tests/yanglint/data/modimp_type_ctx.xml new file mode 100644 index 0000000..e6d158a --- /dev/null +++ b/tests/yanglint/data/modimp_type_ctx.xml @@ -0,0 +1,13 @@ +<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set> + <name>main-set</name> + <module> + <name>modimp-type</name> + <namespace>urn:yanglint:modimp-type</namespace> + </module> + </module-set> + <content-id>1</content-id> +</yang-library> +<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set-id>1</module-set-id> +</modules-state> diff --git a/tests/yanglint/data/modleaf.djson b/tests/yanglint/data/modleaf.djson new file mode 100644 index 0000000..25af218 --- /dev/null +++ b/tests/yanglint/data/modleaf.djson @@ -0,0 +1,3 @@ +{ + "modleaf:lfl": 7 +} diff --git a/tests/yanglint/data/modleaf.dxml b/tests/yanglint/data/modleaf.dxml new file mode 100644 index 0000000..408936a --- /dev/null +++ b/tests/yanglint/data/modleaf.dxml @@ -0,0 +1 @@ +<lfl xmlns="urn:yanglint:modleaf">7</lfl> diff --git a/tests/yanglint/data/modleaf.xml b/tests/yanglint/data/modleaf.xml new file mode 100644 index 0000000..408936a --- /dev/null +++ b/tests/yanglint/data/modleaf.xml @@ -0,0 +1 @@ +<lfl xmlns="urn:yanglint:modleaf">7</lfl> diff --git a/tests/yanglint/data/modleafref.xml b/tests/yanglint/data/modleafref.xml new file mode 100644 index 0000000..c9fb147 --- /dev/null +++ b/tests/yanglint/data/modleafref.xml @@ -0,0 +1,2 @@ +<lfl xmlns="urn:yanglint:modleaf">7</lfl> +<lfr xmlns="urn:yanglint:modleafref">7</lfr> diff --git a/tests/yanglint/data/modleafref2.xml b/tests/yanglint/data/modleafref2.xml new file mode 100644 index 0000000..3946daf --- /dev/null +++ b/tests/yanglint/data/modleafref2.xml @@ -0,0 +1,2 @@ +<lfl xmlns="urn:yanglint:modleaf">7</lfl> +<lfr xmlns="urn:yanglint:modleafref">10</lfr> diff --git a/tests/yanglint/data/modmandatory.xml b/tests/yanglint/data/modmandatory.xml new file mode 100644 index 0000000..108cb2a --- /dev/null +++ b/tests/yanglint/data/modmandatory.xml @@ -0,0 +1,3 @@ +<mmc xmlns="urn:yanglint:modmandatory"> + <lft>9</lft> +</mmc> diff --git a/tests/yanglint/data/modmandatory_invalid.xml b/tests/yanglint/data/modmandatory_invalid.xml new file mode 100644 index 0000000..de71895 --- /dev/null +++ b/tests/yanglint/data/modmandatory_invalid.xml @@ -0,0 +1,3 @@ +<mmc xmlns="urn:yanglint:modmandatory"> + <lff>9</lff> +</mmc> diff --git a/tests/yanglint/data/modmerge.xml b/tests/yanglint/data/modmerge.xml new file mode 100644 index 0000000..b52eff5 --- /dev/null +++ b/tests/yanglint/data/modmerge.xml @@ -0,0 +1,4 @@ +<mmc xmlns="urn:yanglint:modmerge"> + <en>one</en> + <lm>4</lm> +</mmc> diff --git a/tests/yanglint/data/modmerge2.xml b/tests/yanglint/data/modmerge2.xml new file mode 100644 index 0000000..e7f17c4 --- /dev/null +++ b/tests/yanglint/data/modmerge2.xml @@ -0,0 +1,3 @@ +<mmc xmlns="urn:yanglint:modmerge"> + <en>zero</en> +</mmc> diff --git a/tests/yanglint/data/modmerge3.xml b/tests/yanglint/data/modmerge3.xml new file mode 100644 index 0000000..6ef857e --- /dev/null +++ b/tests/yanglint/data/modmerge3.xml @@ -0,0 +1,3 @@ +<mmc xmlns="urn:yanglint:modmerge"> + <lf>str</lf> +</mmc> diff --git a/tests/yanglint/data/modnotif.xml b/tests/yanglint/data/modnotif.xml new file mode 100644 index 0000000..81cab21 --- /dev/null +++ b/tests/yanglint/data/modnotif.xml @@ -0,0 +1,5 @@ +<con xmlns="urn:yanglint:modnotif"> + <nfn> + <lf>nested</lf> + </nfn> +</con> diff --git a/tests/yanglint/data/modnotif2.xml b/tests/yanglint/data/modnotif2.xml new file mode 100644 index 0000000..fc75b57 --- /dev/null +++ b/tests/yanglint/data/modnotif2.xml @@ -0,0 +1,3 @@ +<nfg xmlns="urn:yanglint:modnotif"> + <lf>top</lf> +</nfg> diff --git a/tests/yanglint/data/modnotif2_nc.xml b/tests/yanglint/data/modnotif2_nc.xml new file mode 100644 index 0000000..c87cfa0 --- /dev/null +++ b/tests/yanglint/data/modnotif2_nc.xml @@ -0,0 +1,6 @@ +<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"> + <eventTime>2010-12-06T08:00:01Z</eventTime> + <nfg xmlns="urn:yanglint:modnotif"> + <lf>top</lf> + </nfg> +</notification> diff --git a/tests/yanglint/data/modnotif_ds.xml b/tests/yanglint/data/modnotif_ds.xml new file mode 100644 index 0000000..efd835b --- /dev/null +++ b/tests/yanglint/data/modnotif_ds.xml @@ -0,0 +1 @@ +<con xmlns="urn:yanglint:modnotif"></con> diff --git a/tests/yanglint/data/modnotif_nc.xml b/tests/yanglint/data/modnotif_nc.xml new file mode 100644 index 0000000..39a3440 --- /dev/null +++ b/tests/yanglint/data/modnotif_nc.xml @@ -0,0 +1,8 @@ +<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"> + <eventTime>2010-12-06T08:00:01Z</eventTime> + <con xmlns="urn:yanglint:modnotif"> + <nfn> + <lf>nested</lf> + </nfn> + </con> +</notification> diff --git a/tests/yanglint/data/modoper_leafref_action.xml b/tests/yanglint/data/modoper_leafref_action.xml new file mode 100644 index 0000000..7ccf29f --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_action.xml @@ -0,0 +1,8 @@ +<cond xmlns="urn:yanglint:modoper-leafref"> + <list> + <klf>key_val</klf> + <act> + <lfi>rw</lfi> + </act> + </list> +</cond> diff --git a/tests/yanglint/data/modoper_leafref_action_reply.xml b/tests/yanglint/data/modoper_leafref_action_reply.xml new file mode 100644 index 0000000..39ec672 --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_action_reply.xml @@ -0,0 +1,8 @@ +<cond xmlns="urn:yanglint:modoper-leafref"> + <list> + <klf>key_val</klf> + <act> + <lfo>rw</lfo> + </act> + </list> +</cond> diff --git a/tests/yanglint/data/modoper_leafref_ds.xml b/tests/yanglint/data/modoper_leafref_ds.xml new file mode 100644 index 0000000..f934b9b --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_ds.xml @@ -0,0 +1,9 @@ +<mcc xmlns="urn:yanglint:modconfig"> + <lft>rw</lft> + <lff>ro</lff> +</mcc> +<cond xmlns="urn:yanglint:modoper-leafref"> + <list> + <klf>key_val</klf> + </list> +</cond> diff --git a/tests/yanglint/data/modoper_leafref_notif.xml b/tests/yanglint/data/modoper_leafref_notif.xml new file mode 100644 index 0000000..2c56b67 --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_notif.xml @@ -0,0 +1,3 @@ +<notifg xmlns="urn:yanglint:modoper-leafref"> + <lfr>rw</lfr> +</notifg> diff --git a/tests/yanglint/data/modoper_leafref_notif2.xml b/tests/yanglint/data/modoper_leafref_notif2.xml new file mode 100644 index 0000000..466697c --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_notif2.xml @@ -0,0 +1,8 @@ +<cond xmlns="urn:yanglint:modoper-leafref"> + <list> + <klf>key_val</klf> + <notif> + <lfn>rw</lfn> + </notif> + </list> +</cond> diff --git a/tests/yanglint/data/modoper_leafref_notif_err.xml b/tests/yanglint/data/modoper_leafref_notif_err.xml new file mode 100644 index 0000000..1622ded --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_notif_err.xml @@ -0,0 +1,7 @@ +<mcc xmlns="urn:yanglint:modconfig"> + <lft>rw</lft> + <lff>ro</lff> +</mcc> +<notifg xmlns="urn:yanglint:modoper-leafref"> + <lf>rw</lf> +</notifg> diff --git a/tests/yanglint/data/modoper_leafref_rpc.xml b/tests/yanglint/data/modoper_leafref_rpc.xml new file mode 100644 index 0000000..b294544 --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_rpc.xml @@ -0,0 +1,3 @@ +<rpcg xmlns="urn:yanglint:modoper-leafref"> + <lfi>rw</lfi> +</rpcg> diff --git a/tests/yanglint/data/modoper_leafref_rpc_reply.xml b/tests/yanglint/data/modoper_leafref_rpc_reply.xml new file mode 100644 index 0000000..e8f7af3 --- /dev/null +++ b/tests/yanglint/data/modoper_leafref_rpc_reply.xml @@ -0,0 +1,5 @@ +<rpcg xmlns="urn:yanglint:modoper-leafref"> + <cono> + <lfo>rw</lfo> + </cono> +</rpcg> diff --git a/tests/yanglint/data/modrpc.xml b/tests/yanglint/data/modrpc.xml new file mode 100644 index 0000000..a4f924d --- /dev/null +++ b/tests/yanglint/data/modrpc.xml @@ -0,0 +1,3 @@ +<rpc xmlns="urn:yanglint:modrpc"> + <lfi>some_input</lfi> +</rpc> diff --git a/tests/yanglint/data/modrpc_nc.xml b/tests/yanglint/data/modrpc_nc.xml new file mode 100644 index 0000000..78d3149 --- /dev/null +++ b/tests/yanglint/data/modrpc_nc.xml @@ -0,0 +1,6 @@ +<rpc message-id="101" + xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <rpc xmlns="urn:yanglint:modrpc"> + <lfi>some_input</lfi> + </rpc> +</rpc> diff --git a/tests/yanglint/data/modrpc_reply.xml b/tests/yanglint/data/modrpc_reply.xml new file mode 100644 index 0000000..632971c --- /dev/null +++ b/tests/yanglint/data/modrpc_reply.xml @@ -0,0 +1,5 @@ +<rpc xmlns="urn:yanglint:modrpc"> + <con> + <lfo>-56</lfo> + </con> +</rpc> diff --git a/tests/yanglint/data/modrpc_reply_nc.xml b/tests/yanglint/data/modrpc_reply_nc.xml new file mode 100644 index 0000000..da2a01c --- /dev/null +++ b/tests/yanglint/data/modrpc_reply_nc.xml @@ -0,0 +1,6 @@ +<rpc-reply message-id="101" + xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <con xmlns="urn:yanglint:modrpc"> + <lfo>-56</lfo> + </con> +</rpc-reply> diff --git a/tests/yanglint/data/modsm.xml b/tests/yanglint/data/modsm.xml new file mode 100644 index 0000000..bb0793c --- /dev/null +++ b/tests/yanglint/data/modsm.xml @@ -0,0 +1,3 @@ +<root xmlns="urn:yanglint:modsm"> + <lfl xmlns="urn:yanglint:modleaf">7</lfl> +</root> diff --git a/tests/yanglint/data/modsm2.xml b/tests/yanglint/data/modsm2.xml new file mode 100644 index 0000000..ff6f103 --- /dev/null +++ b/tests/yanglint/data/modsm2.xml @@ -0,0 +1,4 @@ +<root xmlns="urn:yanglint:modsm"> + <lfl xmlns="urn:yanglint:modleaf">7</lfl> + <alf xmlns="urn:yanglint:modsm-augment">str</alf> +</root> diff --git a/tests/yanglint/data/modsm_ctx_ext.xml b/tests/yanglint/data/modsm_ctx_ext.xml new file mode 100644 index 0000000..e80141a --- /dev/null +++ b/tests/yanglint/data/modsm_ctx_ext.xml @@ -0,0 +1,20 @@ +<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set> + <name>test-set</name> + <module> + <name>modleaf</name> + <namespace>urn:yanglint:modleaf</namespace> + </module> + </module-set> + <content-id>1</content-id> +</yang-library> +<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set-id>1</module-set-id> +</modules-state> +<schema-mounts xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount"> + <mount-point> + <module>modsm</module> + <label>root</label> + <inline></inline> + </mount-point> +</schema-mounts> diff --git a/tests/yanglint/data/modsm_ctx_main.xml b/tests/yanglint/data/modsm_ctx_main.xml new file mode 100644 index 0000000..5405d4d --- /dev/null +++ b/tests/yanglint/data/modsm_ctx_main.xml @@ -0,0 +1,17 @@ +<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set> + <name>main-set</name> + <module> + <name>modsm</name> + <namespace>urn:yanglint:modsm</namespace> + </module> + <module> + <name>modsm-augment</name> + <namespace>urn:yanglint:modsm-augment</namespace> + </module> + </module-set> + <content-id>1</content-id> +</yang-library> +<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"> + <module-set-id>1</module-set-id> +</modules-state> diff --git a/tests/yanglint/interactive/add.test b/tests/yanglint/interactive/add.test new file mode 100644 index 0000000..d1cacc1 --- /dev/null +++ b/tests/yanglint/interactive/add.test @@ -0,0 +1,59 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) + +test add_basic {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "add $mdir/modleafref.yang" + ly_cmd "list" "I modleafref\r.*I modleaf" +}} + +test add_disable_searchdir_once {add --disable-searchdir} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {!ctest} -body { + ly_cmd "add $mdir/modimp-cwd.yang" + ly_cmd "clear" + ly_cmd_err "add -D $mdir/modimp-cwd.yang" "not found in local searchdirs" +}} + +test add_disable_searchdir_twice {add -D -D} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {!ctest} -body { + ly_cmd "add $mdir/ietf-ip.yang" + ly_cmd "clear" + ly_cmd_err "add -D -D $mdir/ietf-ip.yang" "Loading \"ietf-interfaces\" module failed." +}} + +test add_with_feature {Add module with feature} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "add --feature modfeature:ftr2 $mdir/modfeature.yang" + ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(off\\)\r\n\tftr2 \\(on\\)" +}} + +test add_make_implemented_once {add --make-implemented} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_ignore "add $mdir/modmust.yang" + ly_cmd "list" "I modmust\r.*i modleaf" + ly_cmd "clear" + ly_ignore "add -i $mdir/modmust.yang" + ly_cmd "list" "I modmust\r.*I modleaf" +}} + +test add_make_implemented_twice {add -i -i} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "add $mdir/modimp-type.yang" + ly_cmd "list" "I modimp-type\r.*i modtypedef" + ly_cmd "clear" + ly_cmd "add -i -i $mdir/modimp-type.yang" + ly_cmd "list" "I modimp-type\r.*I modtypedef" +}} + +test add_extended_leafref_enabled {Valid module with --extended-leafref option} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "add -X $mdir/modextleafref.yang" +}} + +test add_extended_leafref_disabled {Expected error if --extended-leafref is not set} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd_err "add $mdir/modextleafref.yang" "Unexpected XPath token \"FunctionName\"" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/all.tcl b/tests/yanglint/interactive/all.tcl new file mode 100644 index 0000000..b22a5ab --- /dev/null +++ b/tests/yanglint/interactive/all.tcl @@ -0,0 +1,15 @@ +package require tcltest + +# Hook to determine if any of the tests failed. +# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0. +proc tcltest::cleanupTestsHook {} { + variable numTests + set ::exitCode [expr {$numTests(Failed) > 0}] +} + +if {[info exists ::env(TESTS_DIR)]} { + tcltest::configure -testdir "$env(TESTS_DIR)/interactive" +} + +tcltest::runAllTests +exit $exitCode diff --git a/tests/yanglint/interactive/clear.test b/tests/yanglint/interactive/clear.test new file mode 100644 index 0000000..cac0810 --- /dev/null +++ b/tests/yanglint/interactive/clear.test @@ -0,0 +1,53 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data + +test clear_searchpath {searchpath is also deleted} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "searchpath ./" + ly_cmd "clear" + ly_cmd "searchpath" "List of the searchpaths:" -ex +}} + +test clear_make_implemented_once {clear --make-implemented} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -i" + ly_cmd "add $mdir/modmust.yang" + ly_cmd "list" "I modmust\r.*I modleaf" +}} + +test clear_make_implemented_twice {clear -i -i} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -i -i" + ly_cmd "add $mdir/modmust.yang" + ly_cmd "list" "I modmust\r.*I modleaf" +}} + +test clear_ietf_yang_library {clear --yang-library} { +-setup $ly_setup -cleanup $ly_cleanup -body { + # add models + ly_cmd "clear -y" + ly_cmd "list" "I ietf-yang-library" +}} + +test clear_ylf_list {apply --yang-library-file and check result by --list} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -Y $ddir/modimp_type_ctx.xml" + ly_cmd "list" "I modimp-type.*i modtypedef" +}} + +test clear_ylf_make_implemented {apply --yang-library-file and --make-implemented} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -Y $ddir/modimp_type_ctx.xml -i -i" + ly_cmd "list" "I modimp-type.*I modtypedef" +}} + +test clear_ylf_augment_ctx {Setup context by yang-library-file and augment module} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -Y $ddir/modconfig_ctx.xml" + ly_cmd "add $mdir/modconfig-augment.yang" + ly_cmd "print -f tree modconfig" "mca:alf" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/completion.test b/tests/yanglint/interactive/completion.test new file mode 100644 index 0000000..86ded1f --- /dev/null +++ b/tests/yanglint/interactive/completion.test @@ -0,0 +1,69 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" + +variable ly_cleanup { + ly_ignore + ly_exit +} + +test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "add $mdir/ietf-ip.yang" + + # completion and hint + ly_completion "print -f info -P " "print -f info -P /ietf-" + + set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"} + ly_hint "" "print -f info -P /ietf-" $hints + + # double completion + ly_completion "i" "print -f info -P /ietf-interfaces:interfaces" + ly_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface" + + # a lot of hints + set hints {"/ietf-interfaces:interfaces/interface" + "/ietf-interfaces:interfaces/interface/name" "/ietf-interfaces:interfaces/interface/description" + "/ietf-interfaces:interfaces/interface/type" "/ietf-interfaces:interfaces/interface/enabled" + "/ietf-interfaces:interfaces/interface/link-up-down-trap-enable" + "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6" + } + ly_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints + + # double tab + ly_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" + ly_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4" + set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled" + "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu" + "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor" + } + ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints + + # no more completion + ly_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled " +}} + +# Note that somehow a command is automatically sent again (\t\t replaced by \r) after the hints. +# But that doesn't affect the test because the tests only focus on the word in the hint. + +test hint_data_file {Show file hints for command data} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_hint "data $mdir\t\t" "data $mdir" "modleaf.yang.*" +}} + +test hint_data_format {Show print hints for command data --format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_hint "data -f \t\t" "data -f " "xml.*" +}} + +test hint_data_file_after_opt {Show file hints after option with argument} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_hint "data -f xml $mdir\t\t" "data -f xml $mdir" "modleaf.yang.*" +}} + +test hint_data_file_after_opt2 {Show file hints after option without argument} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_hint "data -m $mdir\t\t" "data -m $mdir" "modleaf.yang.*" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_default.test b/tests/yanglint/interactive/data_default.test new file mode 100644 index 0000000..1953acc --- /dev/null +++ b/tests/yanglint/interactive/data_default.test @@ -0,0 +1,41 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mods "ietf-netconf-with-defaults moddefault" +set data "$::env(TESTS_DIR)/data/moddefault.xml" + +test data_default_not_set {Print data without --default parameter} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load $mods" + ly_cmd "data -f xml $data" "</lf>.*</di>\r\n</mdc>" + ly_cmd "data -f json $data" "lf\".*di\"\[^\"]*" +}} + +test data_default_all {data --default all} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load $mods" + ly_cmd "data -d all -f xml $data" "</lf>.*</di>.*</ds>\r\n</mdc>" + ly_cmd "data -d all -f json $data" "lf\".*di\".*ds\"\[^\"]*" +}} + +test data_default_all_tagged {data --default all-tagged} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load $mods" + ly_cmd "data -d all-tagged -f xml $data" "</lf>.*<di.*default.*</di>.*<ds.*default.*</ds>\r\n</mdc>" + ly_cmd "data -d all-tagged -f json $data" "lf\".*di\".*ds\".*@ds\".*default\"\[^\"]*" +}} + +test data_default_trim {data --default trim} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load $mods" + ly_cmd "data -d trim -f xml $data" "</lf>\r\n</mdc>" + ly_cmd "data -d trim -f json $data" "lf\"\[^\"]*" +}} + +test data_default_implicit_tagged {data --default implicit-tagged} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load $mods" + ly_cmd "data -d implicit-tagged -f xml $data" "</lf>.*<di>5</di>.*<ds.*default.*</ds>\r\n</mdc>" + ly_cmd "data -d implicit-tagged -f json $data" "lf\".*di\"\[^@]*ds\".*default\"\[^\"]*" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_format.test b/tests/yanglint/interactive/data_format.test new file mode 100644 index 0000000..dc4b7e0 --- /dev/null +++ b/tests/yanglint/interactive/data_format.test @@ -0,0 +1,23 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" + +test data_format_xml {Print data in xml format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "data -f xml $ddir/modleaf.xml" "<lfl xmlns=\"urn:yanglint:modleaf\">7</lfl>" +}} + +test data_format_json {Print data in json format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "data -f json $ddir/modleaf.xml" "{\r\n \"modleaf:lfl\": 7\r\n}" +}} + +test data_format_lyb_err {Print data in lyb format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd_err "data -f lyb $ddir/modleaf.xml" "The LYB format requires the -o" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_in_format.test b/tests/yanglint/interactive/data_in_format.test new file mode 100644 index 0000000..cc5f37e --- /dev/null +++ b/tests/yanglint/interactive/data_in_format.test @@ -0,0 +1,21 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" + +test data_in_format_xml {--in-format xml} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "data -F xml $ddir/modleaf.dxml" + ly_cmd_err "data -F json $ddir/modleaf.dxml" "Failed to parse" + ly_cmd_err "data -F lyb $ddir/modleaf.dxml" "Failed to parse" +}} + +test data_in_format_json {--in-format json} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "data -F json $ddir/modleaf.djson" + ly_cmd_err "data -F xml $ddir/modleaf.djson" "Failed to parse" + ly_cmd_err "data -F lyb $ddir/modleaf.djson" "Failed to parse" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_merge.test b/tests/yanglint/interactive/data_merge.test new file mode 100644 index 0000000..38754c7 --- /dev/null +++ b/tests/yanglint/interactive/data_merge.test @@ -0,0 +1,33 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" + +test data_merge_basic {Data is merged and the node is added} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modmerge" + ly_cmd "data -m -f xml $ddir/modmerge.xml $ddir/modmerge3.xml" "<en>.*<lm>.*<lf>" +}} + +test data_merge_validation_failed {Data is merged but validation failed.} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modmerge" + ly_cmd "data $ddir/modmerge.xml" + ly_cmd "data $ddir/modmerge2.xml" + ly_cmd "data -m $ddir/modmerge2.xml $ddir/modmerge.xml" + ly_cmd_err "data -m $ddir/modmerge.xml $ddir/modmerge2.xml" "Merged data are not valid" +}} + +test data_merge_dataconfig {The merge option has effect only for 'data' and 'config' TYPEs} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modnotif modconfig modleaf" + set wrn1 "option has effect only for" + ly_cmd_wrn "data -m -t rpc $ddir/modrpc.xml $ddir/modrpc.xml" $wrn1 + ly_cmd_wrn "data -m -t notif $ddir/modnotif2.xml $ddir/modnotif2.xml" $wrn1 + ly_cmd_wrn "data -m -t get $ddir/modleaf.xml $ddir/modconfig.xml" $wrn1 + ly_cmd_wrn "data -m -t getconfig $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1 + ly_cmd_wrn "data -m -t edit $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1 + ly_cmd "data -m -t config $ddir/modleaf.xml $ddir/modconfig2.xml" + ly_cmd "data -m -t data $ddir/modleaf.xml $ddir/modconfig.xml" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_not_strict.test b/tests/yanglint/interactive/data_not_strict.test new file mode 100644 index 0000000..201a5a9 --- /dev/null +++ b/tests/yanglint/interactive/data_not_strict.test @@ -0,0 +1,25 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir $::env(TESTS_DIR)/data + +test data_no_strict_basic {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd_err "data $ddir/modmandatory.xml" "No module with namespace \"urn:yanglint:modmandatory\" in the context." + ly_cmd "data -n $ddir/modmandatory.xml" +}} + +test data_no_strict_invalid_data {validation with --no-strict but data are invalid} { +-setup $ly_setup -cleanup $ly_cleanup -body { + set errmsg "Mandatory node \"lft\" instance does not exist." + ly_cmd "load modmandatory" + ly_cmd_err "data -n $ddir/modmandatory_invalid.xml" $errmsg +}} + +test data_no_strict_ignore_invalid_data {--no-strict ignore invalid data if no schema is provided} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "data -f xml -n $ddir/modmandatory_invalid.xml $ddir/modleaf.xml" "modleaf.*</lfl>$" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_operational.test b/tests/yanglint/interactive/data_operational.test new file mode 100644 index 0000000..c0c7b1c --- /dev/null +++ b/tests/yanglint/interactive/data_operational.test @@ -0,0 +1,86 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" +set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types" + +test data_operational_twice {it is not allowed to specify more than one --operational parameter} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t notif -O $ddir/modconfig.xml -O $ddir/modleaf.xml" "cannot be set multiple times" +}} + +test data_operational_no_type {--operational should be with parameter --type} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd_wrn "data -O $ddir/modconfig.xml $ddir/modoper_leafref_notif.xml" $err1 +}} + +test data_operational_missing {--operational is omitted and the datastore contents is in the data file} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd_err "data $ddir/modoper_leafref_notif_err.xml" "Failed to parse input data file" +}} + +test data_operational_wrong_type {data are not defined as an operation} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd_wrn "data -t data -O $ddir/modconfig.xml $ddir/modleaf.xml" $err1 +}} + +test data_operational_datastore_with_unknown_data {unknown data are ignored} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc" + ly_cmd "data -t rpc -O $ddir/modmandatory_invalid.xml $ddir/modrpc.xml" +}} + +test data_operational_empty_datastore {datastore is considered empty because it contains unknown data} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modnotif" + ly_cmd "data -t rpc -O $ddir/modmandatory_invalid.xml $ddir/modrpc.xml" + set msg "parent \"/modnotif:con\" not found in the operational data" + ly_cmd_err "data -t notif -O $ddir/modmandatory_invalid.xml $ddir/modnotif.xml" $msg +}} + +test data_operational_notif_leafref {--operational data is referenced from notification-leafref} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t notif -O $ddir/modconfig.xml $ddir/modoper_leafref_notif.xml" +}} + +test data_operational_nested_notif_leafref {--operational data is referenced from nested-notification-leafref} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t notif -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_notif2.xml" +}} + +test data_operational_nested_notif_parent_missing {--operational data are invalid due to missing parent node} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + set msg "klf='key_val']\" not found in the operational data" + ly_cmd_err "data -t notif -O $ddir/modconfig.xml $ddir/modoper_leafref_notif2.xml" $msg +}} + +test data_operational_action_leafref {--operational data is referenced from action-leafref} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t rpc -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_action.xml" +}} + +test data_operational_action_reply_leafref {--operational data is referenced from action-leafref output} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t reply -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_action_reply.xml" +}} + +test data_operational_rpc_leafref {--operational data is referenced from rpc-leafref} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t rpc -O $ddir/modconfig.xml $ddir/modoper_leafref_rpc.xml" +}} + +test data_operational_rpc_reply_leafref {--operational data is referenced from rpc-leafref output} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modoper-leafref" + ly_cmd "data -t reply -O $ddir/modconfig.xml $ddir/modoper_leafref_rpc_reply.xml" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_present.test b/tests/yanglint/interactive/data_present.test new file mode 100644 index 0000000..4bba596 --- /dev/null +++ b/tests/yanglint/interactive/data_present.test @@ -0,0 +1,25 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" + +test data_present_via_mandatory {validation of mandatory-stmt will pass only with the --present} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf modmandatory" + ly_cmd_err "data $ddir/modleaf.xml" "Mandatory node \"lft\" instance does not exist." + ly_cmd "data -e $ddir/modleaf.xml" +}} + +test data_present_merge {validation with --present and --merge} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf modmandatory moddefault" + ly_cmd_err "data -m $ddir/modleaf.xml $ddir/moddefault.xml" "Mandatory node \"lft\" instance does not exist." + ly_cmd "data -e -m $ddir/modleaf.xml $ddir/moddefault.xml" +}} + +test data_present_merge_invalid {using --present and --merge but data are invalid} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf modmandatory" + ly_cmd_err "data -e -m $ddir/modleaf.xml $ddir/modmandatory_invalid.xml" "Mandatory node \"lft\" instance does not exist." +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_type.test b/tests/yanglint/interactive/data_type.test new file mode 100644 index 0000000..a442813 --- /dev/null +++ b/tests/yanglint/interactive/data_type.test @@ -0,0 +1,140 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ddir "$::env(TESTS_DIR)/data" + +test data_type_data {data --type data} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modconfig" + ly_cmd "data -t data $ddir/modconfig.xml" +}} + +test data_type_config {data --type config} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modconfig" + ly_cmd_err "data -t config $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "data -t config $ddir/modconfig2.xml" +}} + +test data_type_get {data --type get} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleafref" + ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "data -t get $ddir/modleafref2.xml" +}} + +test data_type_getconfig_no_state {No state node for data --type getconfig} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modconfig" + ly_cmd_err "data -t getconfig $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "data -t getconfig $ddir/modconfig2.xml" +}} + +test data_type_getconfig_parse_only {No validation performed for data --type getconfig} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleafref" + ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "data -t getconfig $ddir/modleafref2.xml" +}} + +test data_type_edit_no_state {No state node for data --type edit} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modconfig" + ly_cmd_err "data -t edit $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "data -t edit $ddir/modconfig2.xml" +}} + +test data_type_edit_parse_only {No validation performed for data --type edit} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleafref" + ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "data -t edit $ddir/modleafref2.xml" +}} + +test data_type_rpc {Validation of rpc-statement by data --type rpc} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modleaf" + ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "data -t rpc $ddir/modrpc.xml" +}} + +test data_type_rpc_nc {Validation of rpc-statement by data --type nc-rpc} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modleaf ietf-netconf" + ly_cmd_err "data -t nc-rpc $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope" + ly_cmd "data -t nc-rpc $ddir/modrpc_nc.xml" +}} + +test data_type_rpc_reply {Validation of rpc-reply by data --type reply} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modleaf" + ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node." + ly_cmd_wrn "data -t reply -R $ddir/modrpc.xml $ddir/modrpc_reply.xml" "needed only for NETCONF" + ly_cmd "data -t reply $ddir/modrpc_reply.xml" +}} + +test data_type_rpc_reply_nc {Validation of rpc-reply by data --type nc-reply} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modrpc modleaf" + ly_cmd_err "data -t nc-reply -R $ddir/modrpc_nc.xml $ddir/modleaf.xml" "Missing NETCONF <rpc-reply> envelope" + ly_cmd_err "data -t nc-reply $ddir/modrpc_reply_nc.xml" "Missing source RPC" + ly_cmd "data -t nc-reply -R $ddir/modrpc_nc.xml $ddir/modrpc_reply_nc.xml" +}} + +test data_type_rpc_action {Validation of action-statement by data --type rpc} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modaction modleaf" + ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "data -t rpc -O $ddir/modaction_ds.xml $ddir/modaction.xml" +}} + +test data_type_rpc_action_nc {Validation of action-statement by data --type nc-rpc} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modaction modleaf" + ly_cmd_err "data -t nc-rpc $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope" + ly_cmd "data -t nc-rpc -O $ddir/modaction_ds.xml $ddir/modaction_nc.xml" +}} + +test data_type_rpc_action_reply {Validation of action-reply by data --type reply} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modaction modleaf" + ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "data -t reply -O $ddir/modaction_ds.xml $ddir/modaction_reply.xml" +}} + +test data_type_rpc_action_reply_nc {Validation of action-reply by data --type nc-reply} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modaction modleaf" + ly_cmd_err "data -t nc-reply -R $ddir/modaction_nc.xml $ddir/modleaf.xml" "Missing NETCONF <rpc-reply> envelope" + ly_cmd_err "data -t nc-reply $ddir/modaction_reply_nc.xml" "Missing source RPC" + ly_cmd_err "data -t nc-reply -R $ddir/modaction_nc.xml $ddir/modaction_reply_nc.xml" "operational parameter needed" + ly_cmd "data -t nc-reply -O $ddir/modaction_ds.xml -R $ddir/modaction_nc.xml $ddir/modaction_reply_nc.xml" +}} + +test data_type_notif {Validation of notification-statement by data --type notif} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modnotif modleaf" + ly_cmd_err "data -t notif $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "data -t notif $ddir/modnotif2.xml" +}} + +test data_type_notif_nc {Validation of notification-statement by data --type nc-notif} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modnotif modleaf ietf-netconf" + ly_cmd_err "data -t nc-notif $ddir/modleaf.xml" "Missing NETCONF <notification> envelope" + ly_cmd "data -t nc-notif $ddir/modnotif2_nc.xml" +}} + +test data_type_notif_nested {Validation of nested-notification-statement by data --type notif} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modnotif modleaf" + ly_cmd "data -t notif -O $ddir/modnotif_ds.xml $ddir/modnotif.xml" +}} + +test data_type_notif_nested_nc {Validation of nested-notification-statement by data --type nc-notif} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modnotif modleaf ietf-netconf" + ly_cmd_err "data -t nc-notif $ddir/modleaf.xml" "Missing NETCONF <notification> envelope" + ly_cmd "data -t nc-notif -O $ddir/modnotif_ds.xml $ddir/modnotif_nc.xml" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/data_xpath.test b/tests/yanglint/interactive/data_xpath.test new file mode 100644 index 0000000..398cb9f --- /dev/null +++ b/tests/yanglint/interactive/data_xpath.test @@ -0,0 +1,57 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set data "$::env(TESTS_DIR)/data/moddatanodes.xml" + +test data_xpath_empty {--xpath to missing node} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + ly_cmd "data -x /moddatanodes:dnc/mis $data" "Empty" +}} + +test data_xpath_leaf {--xpath to leaf node} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + ly_cmd "data -x /moddatanodes:dnc/lf $data" "leaf \"lf\" \\(value: \"x\"\\)" +}} + +test data_xpath_leaflist {--xpath to leaf-list node} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + set r1 "leaf-list \"lfl\" \\(value: \"1\"\\)" + set r2 "leaf-list \"lfl\" \\(value: \"2\"\\)" + ly_cmd "data -x /moddatanodes:dnc/lfl $data" "$r1\r\n $r2" +}} + +test data_xpath_list {--xpath to list} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + set r1 "list \"lt\" \\(\"kalf\": \"ka1\"; \"kblf\": \"kb1\";\\)" + set r2 "list \"lt\" \\(\"kalf\": \"ka2\"; \"kblf\": \"kb2\";\\)" + ly_cmd "data -x /moddatanodes:dnc/con/lt $data" "$r1\r\n $r2" +}} + +test data_xpath_container {--xpath to container} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + ly_cmd "data -x /moddatanodes:dnc/con $data" "container \"con\"" +}} + +test data_xpath_wrong_path {--xpath to a non-existent node} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + ly_cmd_err "data -x /moddatanodes:dnc/wrng $data" "xpath failed" +}} + +test data_xpath_err_format {--xpath cannot be combined with --format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes" + ly_cmd_err "data -f xml -x /moddatanodes:dnc/lf $data" "option cannot be combined" +}} + +test data_xpath_err_default {--xpath cannot be combined with --default} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load moddatanodes ietf-netconf-with-defaults" + ly_cmd_err "data -d all -x /moddatanodes:dnc/lf $data" "option cannot be combined" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/debug.test b/tests/yanglint/interactive/debug.test new file mode 100644 index 0000000..8a64c92 --- /dev/null +++ b/tests/yanglint/interactive/debug.test @@ -0,0 +1,33 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) + +test debug_dict {Check debug message DICT} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body { + ly_cmd "verb debug" + ly_cmd "debug dict" + ly_cmd "load modleaf" "DICT" +}} + +test debug_xpath {Check debug message XPATH} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body { + ly_cmd "verb debug" + ly_cmd "debug xpath" + ly_cmd "load modmust" "XPATH" +}} + +test debug_dep_sets {Check debug message DEPSETS} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body { + ly_cmd "verb debug" + ly_cmd "debug dep-sets" + ly_cmd "load modleaf" "DEPSETS" +}} + +test debug_depsets_xpath {Check debug message DEPSETS and XPATH} { +-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body { + ly_cmd "verb debug" + ly_cmd "debug dep-sets xpath" + ly_cmd "load modmust" "DEPSETS.*XPATH" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/extdata.test b/tests/yanglint/interactive/extdata.test new file mode 100644 index 0000000..e253d1a --- /dev/null +++ b/tests/yanglint/interactive/extdata.test @@ -0,0 +1,63 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" +set ddir "$::env(TESTS_DIR)/data" + +test extdata_set_clear {Set and clear extdata file} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "extdata" "No file set" + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "extdata" "$ddir/modsm_ctx_ext.xml" + ly_cmd "extdata -c" + ly_cmd "extdata" "No file set" +}} + +test extdata_clear_cmd {Clear extdata file by 'clear' command} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "clear" + ly_cmd "extdata" "No file set" +}} + +test extdata_one_only {Only one file for extdata} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd_err "extdata $ddir/modsm_ctx_ext.xml $ddir/modsm_ctx_ext.xml" "Only one file must be entered" +}} + +test extdata_schema_mount_tree {Print tree output of a model with Schema Mount} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -y" + ly_cmd "searchpath $mdir" + ly_cmd "load modsm" + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "print -f tree modsm" "--mp root.*--rw lfl/" +}} + +test ext_data_schema_mount_tree_yanglibfile {Print tree output of a model with Schema Mount and --yang-library-file} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -Y $ddir/modsm_ctx_main.xml" + ly_cmd "searchpath $mdir" + ly_cmd "load modsm" + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "print -f tree modsm" "--mp root.*--rw lfl/.*--rw msa:alf?" +}} + +test ext_data_schema_mount_xml {Validating and printing mounted data} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -y" + ly_cmd "searchpath $mdir" + ly_cmd "load modsm" + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "data -f xml -t config $ddir/modsm.xml" "</lfl>" +}} + +test ext_data_schema_mount_xml_yanglibfile {Validating and printing mounted data with --yang-library-file} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -Y $ddir/modsm_ctx_main.xml" + ly_cmd "searchpath $mdir" + ly_cmd "load modsm" + ly_cmd "extdata $ddir/modsm_ctx_ext.xml" + ly_cmd "data -f xml -t config $ddir/modsm2.xml" "</lfl>.*</alf>" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/feature.test b/tests/yanglint/interactive/feature.test new file mode 100644 index 0000000..84bfa8e --- /dev/null +++ b/tests/yanglint/interactive/feature.test @@ -0,0 +1,37 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +test feature_all_default {Default output of feature --all} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "feature -a" "yang:\r\n\t(none)\r\n\r\nietf-yang-schema-mount:\r\n\t(none)\r\n" -ex +}} + +test feature_all_add_module {Add module with only one feature and call feature --all} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load --feature modfeature:ftr1 modfeature" + ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(on\\)\r\n\tftr2 \\(off\\)" +}} + +test feature_all_on {Add module with all enabled features and call feature --all} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load --feature modfeature:* modfeature" + ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(on\\)\r\n\tftr2 \\(on\\)" +}} + +test feature_one_module {Show features for one module} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "feature -f ietf-ip" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" -ex +}} + +test feature_more_modules {Show a mix of modules with and without features} { +-setup $ly_setup -cleanup $ly_cleanup -body { + + set features " -F modfeature:ftr1,ftr2\ +-F modleaf:\ +-F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" + + ly_cmd "load ietf-ip modleaf modfeature" + ly_cmd "feature -f modfeature modleaf ietf-ip" $features -ex +}} + +cleanupTests diff --git a/tests/yanglint/interactive/list.test b/tests/yanglint/interactive/list.test new file mode 100644 index 0000000..ab59a32 --- /dev/null +++ b/tests/yanglint/interactive/list.test @@ -0,0 +1,34 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] +namespace import uti::regex_xml_elements uti::regex_json_pairs + +set modules {ietf-yang-library ietf-inet-types} + +test list_basic {basic test} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "list" "ietf-yang-types" +}} + +test list_format_xml {list --format xml} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -y" + ly_cmd "list -f xml" [regex_xml_elements $modules "name"] +}} + +test list_format_json {list --format json} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -y" + ly_cmd "list -f json" [regex_json_pairs $modules "name"] +}} + +test list_ietf_yang_library {Error due to missing ietf-yang-library} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd_err "list -f xml" "Module \"ietf-yang-library\" is not implemented." +}} + +test list_bad_format {Error due to bad format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "clear -y" + ly_cmd_err "list -f csv" "Unknown output format csv" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/load.test b/tests/yanglint/interactive/load.test new file mode 100644 index 0000000..a95d044 --- /dev/null +++ b/tests/yanglint/interactive/load.test @@ -0,0 +1,45 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +test load_basic {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleafref" + ly_cmd "list" "I modleafref\r.*I modleaf" +}} + +test load_with_feature {Load module with feature} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load --feature modfeature:ftr2 modfeature" + ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(off\\)\r\n\tftr2 \\(on\\)" +}} + +test load_make_implemented_once {load --make-implemented} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_ignore "load modmust" + ly_cmd "list" "I modmust\r.*i modleaf" + ly_cmd "clear" + ly_cmd "searchpath $::env(YANG_MODULES_DIR)" + ly_cmd "load -i modmust" + ly_cmd "list" "I modmust\r.*I modleaf" +}} + +test load_make_implemented_twice {load -i -i} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modimp-type" + ly_cmd "list" "I modimp-type\r.*i modtypedef" + ly_cmd "clear" + ly_cmd "searchpath $::env(YANG_MODULES_DIR)" + ly_cmd "load -i -i modimp-type" + ly_cmd "list" "I modimp-type\r.*I modtypedef" +}} + +test load_extended_leafref_enabled {Valid module with --extended-leafref option} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load -X modextleafref" +}} + +test load_extended_leafref_disabled {Expected error if --extended-leafref is not set} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd_err "load modextleafref" "Unexpected XPath token \"FunctionName\"" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/ly.tcl b/tests/yanglint/interactive/ly.tcl new file mode 100644 index 0000000..4c56be4 --- /dev/null +++ b/tests/yanglint/interactive/ly.tcl @@ -0,0 +1,81 @@ +# @brief The main source of functions and variables for testing yanglint in the interactive mode. + +# For testing yanglint. +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/common.tcl" : "../common.tcl"}] +# For testing any interactive tool. +source "$::env(TESTS_DIR)/../tool_i.tcl" + +# The script continues by defining variables and functions specific to the interactive yanglint tool. + +# set the timeout to 5 seconds +set timeout 5 +# prompt of yanglint +set prompt "> " +# turn off dialog between expect and yanglint +log_user 0 +# setting some large terminal width +stty columns 720 + +# default setup for every unit test +variable ly_setup { + spawn $TUT + ly_skip_warnings + # Searchpath is set, so modules can be loaded via the 'load' command. + ly_cmd "searchpath $::env(YANG_MODULES_DIR)" +} + +# default cleanup for every unit test +variable ly_cleanup { + ly_exit +} + +# Skip no dir and/or no history warnings and prompt. +proc ly_skip_warnings {} { + global prompt + expect -re "(YANGLINT.*)*$prompt" {} +} + +# Send command 'cmd' to the process, expect error header and then check output string by 'pattern'. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex. It must not contain a prompt. +proc ly_cmd_err {cmd pattern} { + global prompt + + send -- "${cmd}\r" + expect -- "${cmd}\r\n" + + expect { + -re "YANGLINT\\\[E\\\]: .*${pattern}.*\r\n${prompt}$" {} + -re "libyang\\\[\[0-9]+\\\]: .*${pattern}.*\r\n${prompt}$" {} + -re "\r\n${prompt}$" { + error "unexpected output:\n$expect_out(buffer)" + } + } +} + +# Send command 'cmd' to the process, expect warning header and then check output string by 'pattern'. +# Parameter cmd is a string of arguments. +# Parameter pattern is a regex. It must not contain a prompt. +proc ly_cmd_wrn {cmd pattern} { + ly_cmd_header $cmd "YANGLINT\\\[W\\\]:" $pattern +} + +# Send 'exit' and wait for eof. +proc ly_exit {} { + send "exit\r" + expect eof +} + +# Check if yanglint is configured as DEBUG. +# Return 1 on success. +proc yanglint_debug {} { + global TUT + # Call non-interactive yanglint with --help. + set output [exec -- $TUT "-h"] + # Find option --debug. + if { [regexp -- "--debug=GROUPS" $output] } { + return 1 + } else { + return 0 + } +} diff --git a/tests/yanglint/interactive/modcwd.yang b/tests/yanglint/interactive/modcwd.yang new file mode 100644 index 0000000..db33e73 --- /dev/null +++ b/tests/yanglint/interactive/modcwd.yang @@ -0,0 +1,4 @@ +module modcwd { + namespace "urn:yanglint:modcwd"; + prefix mc; +} diff --git a/tests/yanglint/interactive/print.test b/tests/yanglint/interactive/print.test new file mode 100644 index 0000000..8b9d740 --- /dev/null +++ b/tests/yanglint/interactive/print.test @@ -0,0 +1,77 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set ipv6_path "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address" + +test print_yang {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "print -f yang modleaf" "leaf lfl" +}} + +test print_yang_submodule {Print submodule in yang format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modinclude" + ly_cmd "print -f yang modsub" "submodule modsub" +}} + +test print_yin {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "print -f yin modleaf" "<leaf name=\"lfl\">" +}} + +test print_yin_submodule {Print submodule in yin format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modinclude" + ly_cmd "print -f yin modsub" "<submodule name=\"modsub\"" +}} + +test print_info {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "print -f info modleaf" "status current" +}} + +test print_info_path {Print subtree in info format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "print -f info -P $ipv6_path" "^list address .* leaf prefix-length" +}} + +test print_info_path_single_node {Print node in info format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "print -f info -q -P $ipv6_path" "^list address .* IPv6 addresses on the interface.\";\r\n\}$" +}} + +test print_tree {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modleaf" + ly_cmd "print -f tree modleaf" "\\+--rw lfl" +}} + +test print_tree_submodule {Print submodule in tree format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load modinclude" + ly_cmd "print -f tree modsub" "submodule: modsub" +}} + +test print_tree_path {Print subtree in tree format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "print -f tree -P $ipv6_path" "\\+--rw address.*\\+--rw prefix-length" +}} + +test print_tree_path_single_node {Print node in tree format} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "print -f tree -q -P $ipv6_path" "\\+--rw address\\* \\\[ip\\\]$" +}} + +test print_tree_path_single_node_line_length {Print node in the tree format and limit row size} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "load ietf-ip" + ly_cmd "print -f tree -L 20 -q -P $ipv6_path" "\\+--rw address\\*\r\n *\\\[ip\\\]$" +}} + +cleanupTests diff --git a/tests/yanglint/interactive/searchpath.test b/tests/yanglint/interactive/searchpath.test new file mode 100644 index 0000000..3bd6796 --- /dev/null +++ b/tests/yanglint/interactive/searchpath.test @@ -0,0 +1,24 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) + +variable ly_setup { + spawn $TUT + ly_skip_warnings +} + +test searchpath_basic {} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "searchpath $mdir" + ly_cmd "searchpath" "$mdir" + ly_cmd "load modleaf" +}} + +test searchpath_clear {searchpath --clear} { +-setup $ly_setup -cleanup $ly_cleanup -body { + ly_cmd "searchpath $mdir" + ly_cmd "searchpath --clear" + ly_cmd_err "load modleaf" "Data model \"modleaf\" not found in local searchdirs" +}} + +cleanupTests diff --git a/tests/yanglint/modules/ietf-interfaces.yang b/tests/yanglint/modules/ietf-interfaces.yang new file mode 100644 index 0000000..ad64425 --- /dev/null +++ b/tests/yanglint/modules/ietf-interfaces.yang @@ -0,0 +1,725 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: Thomas Nadeau + <mailto:tnadeau@lucidvision.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 7223; see + the RFC itself for full legal notices."; + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Configuration data nodes + */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request if the implementation does not support + pre-provisioning of interfaces or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation to + + decide whether to modify this single leaf in 'startup' or + perform an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + /* + * Operational state data nodes + */ + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + + + + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + + + + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + + + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + + + + + + + + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + + + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + + + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + + + + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + + + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} diff --git a/tests/yanglint/modules/ietf-ip.yang b/tests/yanglint/modules/ietf-ip.yang new file mode 100644 index 0000000..1499120 --- /dev/null +++ b/tests/yanglint/modules/ietf-ip.yang @@ -0,0 +1,758 @@ +module ietf-ip { + + namespace "urn:ietf:params:xml:ns:yang:ietf-ip"; + prefix ip; + + import ietf-interfaces { + prefix if; + } + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: Thomas Nadeau + <mailto:tnadeau@lucidvision.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + + + + + + + + + + description + "This module contains a collection of YANG definitions for + configuring IP implementations. + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 7277; see + the RFC itself for full legal notices."; + + revision 2014-06-16 { + description + "Initial revision."; + reference + "RFC 7277: A YANG Data Model for IP Management"; + } + + /* + + * Features + */ + + feature ipv4-non-contiguous-netmasks { + description + "Indicates support for configuring non-contiguous + subnet masks."; + } + + feature ipv6-privacy-autoconf { + description + "Indicates support for Privacy Extensions for Stateless Address + Autoconfiguration in IPv6."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6"; + } + + + + + + /* + * Typedefs + */ + + typedef ip-address-origin { + type enumeration { + enum other { + description + "None of the following."; + } + enum static { + description + "Indicates that the address has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum dhcp { + description + "Indicates an address that has been assigned to this + system by a DHCP server."; + } + enum link-layer { + description + "Indicates an address created by IPv6 stateless + autoconfiguration that embeds a link-layer address in its + interface identifier."; + } + enum random { + description + "Indicates an address chosen by the system at + + random, e.g., an IPv4 address within 169.254/16, an + RFC 4941 temporary address, or an RFC 7217 semantically + opaque address."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + RFC 7217: A Method for Generating Semantically Opaque + Interface Identifiers with IPv6 Stateless + Address Autoconfiguration (SLAAC)"; + } + } + description + "The origin of an address."; + } + + + + typedef neighbor-origin { + type enumeration { + enum other { + description + "None of the following."; + } + enum static { + description + "Indicates that the mapping has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum dynamic { + description + "Indicates that the mapping has been dynamically resolved + using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery + protocol."; + } + } + description + "The origin of a neighbor entry."; + } + + /* + * Configuration data nodes + */ + + augment "/if:interfaces/if:interface" { + description + "Parameters for configuring IP on interfaces. + + If an interface is not capable of running IP, the server + must not allow the client to configure these parameters."; + + container ipv4 { + presence + "Enables IPv4 unless the 'enabled' leaf + (which defaults to 'true') is set to 'false'"; + description + "Parameters for the IPv4 address family."; + + + + + + + + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv4 is enabled or disabled on this + interface. When IPv4 is enabled, this interface is + connected to an IPv4 stack, and the interface can send + and receive IPv4 packets."; + } + leaf forwarding { + type boolean; + default false; + description + "Controls IPv4 packet forwarding of datagrams received by, + but not addressed to, this interface. IPv4 routers + forward datagrams. IPv4 hosts do not (except those + source-routed via the host)."; + } + leaf mtu { + type uint16 { + range "68..max"; + } + units octets; + description + "The size, in octets, of the largest IPv4 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 791: Internet Protocol"; + } + list address { + key "ip"; + description + "The list of configured IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address on the interface."; + } + + + + choice subnet { + mandatory true; + description + "The subnet can be specified as a prefix-length, or, + if the server supports non-contiguous netmasks, as + a netmask."; + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the subnet prefix."; + } + leaf netmask { + if-feature ipv4-non-contiguous-netmasks; + type yang:dotted-quad; + description + "The subnet specified as a netmask."; + } + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv4 addresses to + link-layer addresses. + + Entries in this list are used as static entries in the + ARP Cache."; + reference + "RFC 826: An Ethernet Address Resolution Protocol"; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + } + + + container ipv6 { + presence + "Enables IPv6 unless the 'enabled' leaf + (which defaults to 'true') is set to 'false'"; + description + "Parameters for the IPv6 address family."; + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv6 is enabled or disabled on this + interface. When IPv6 is enabled, this interface is + connected to an IPv6 stack, and the interface can send + and receive IPv6 packets."; + } + leaf forwarding { + type boolean; + default false; + description + "Controls IPv6 packet forwarding of datagrams received by, + but not addressed to, this interface. IPv6 routers + forward datagrams. IPv6 hosts do not (except those + source-routed via the host)."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 6.2.1, IsRouter"; + } + leaf mtu { + type uint32 { + range "1280..max"; + } + units octets; + description + "The size, in octets, of the largest IPv6 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + Section 5"; + } + + + list address { + key "ip"; + description + "The list of configured IPv6 addresses on the interface."; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address on the interface."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + mandatory true; + description + "The length of the subnet prefix."; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv6 addresses to + link-layer addresses. + + Entries in this list are used as static entries in the + Neighbor Cache."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + + + + + + leaf dup-addr-detect-transmits { + type uint32; + default 1; + description + "The number of consecutive Neighbor Solicitation messages + sent while performing Duplicate Address Detection on a + tentative address. A value of zero indicates that + Duplicate Address Detection is not performed on + tentative addresses. A value of one indicates a single + transmission with no follow-up retransmissions."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + container autoconf { + description + "Parameters to control the autoconfiguration of IPv6 + addresses, as described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + + leaf create-global-addresses { + type boolean; + default true; + description + "If enabled, the host creates global addresses as + described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration + Section 5.5"; + } + leaf create-temporary-addresses { + if-feature ipv6-privacy-autoconf; + type boolean; + default false; + description + "If enabled, the host creates temporary addresses as + described in RFC 4941."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6"; + } + + + + + + + + leaf temporary-valid-lifetime { + if-feature ipv6-privacy-autoconf; + type uint32; + units "seconds"; + default 604800; + description + "The time period during which the temporary address + is valid."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_VALID_LIFETIME"; + } + leaf temporary-preferred-lifetime { + if-feature ipv6-privacy-autoconf; + type uint32; + units "seconds"; + default 86400; + description + "The time period during which the temporary address is + preferred."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_PREFERRED_LIFETIME"; + } + } + } + } + + /* + * Operational state data nodes + */ + + augment "/if:interfaces-state/if:interface" { + description + "Data nodes for the operational state of IP on interfaces."; + + container ipv4 { + presence "Present if IPv4 is enabled on this interface"; + config false; + description + "Interface-specific parameters for the IPv4 address family."; + + + + + + leaf forwarding { + type boolean; + description + "Indicates whether IPv4 packet forwarding is enabled or + disabled on this interface."; + } + leaf mtu { + type uint16 { + range "68..max"; + } + units octets; + description + "The size, in octets, of the largest IPv4 packet that the + interface will send and receive."; + reference + "RFC 791: Internet Protocol"; + } + list address { + key "ip"; + description + "The list of IPv4 addresses on the interface."; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address on the interface."; + } + choice subnet { + description + "The subnet can be specified as a prefix-length, or, + if the server supports non-contiguous netmasks, as + a netmask."; + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the subnet prefix."; + } + leaf netmask { + if-feature ipv4-non-contiguous-netmasks; + type yang:dotted-quad; + description + "The subnet specified as a netmask."; + } + } + + + leaf origin { + type ip-address-origin; + description + "The origin of this address."; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv4 addresses to + link-layer addresses. + + This list represents the ARP Cache."; + reference + "RFC 826: An Ethernet Address Resolution Protocol"; + + leaf ip { + type inet:ipv4-address-no-zone; + description + "The IPv4 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + description + "The link-layer address of the neighbor node."; + } + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry."; + } + } + + } + + container ipv6 { + presence "Present if IPv6 is enabled on this interface"; + config false; + description + "Parameters for the IPv6 address family."; + + + + + + + + + leaf forwarding { + type boolean; + default false; + description + "Indicates whether IPv6 packet forwarding is enabled or + disabled on this interface."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 6.2.1, IsRouter"; + } + leaf mtu { + type uint32 { + range "1280..max"; + } + units octets; + description + "The size, in octets, of the largest IPv6 packet that the + interface will send and receive."; + reference + "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + Section 5"; + } + list address { + key "ip"; + description + "The list of IPv6 addresses on the interface."; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address on the interface."; + } + leaf prefix-length { + type uint8 { + range "0..128"; + } + mandatory true; + description + "The length of the subnet prefix."; + } + leaf origin { + type ip-address-origin; + description + "The origin of this address."; + } + + + + leaf status { + type enumeration { + enum preferred { + description + "This is a valid address that can appear as the + destination or source address of a packet."; + } + enum deprecated { + description + "This is a valid but deprecated address that should + no longer be used as a source address in new + communications, but packets addressed to such an + address are processed as expected."; + } + enum invalid { + description + "This isn't a valid address, and it shouldn't appear + as the destination or source address of a packet."; + } + enum inaccessible { + description + "The address is not accessible because the interface + to which this address is assigned is not + operational."; + } + enum unknown { + description + "The status cannot be determined for some reason."; + } + enum tentative { + description + "The uniqueness of the address on the link is being + verified. Addresses in this state should not be + used for general communication and should only be + used to determine the uniqueness of the address."; + } + enum duplicate { + description + "The address has been determined to be non-unique on + the link and so must not be used."; + } + + + + + + + + enum optimistic { + description + "The address is available for use, subject to + restrictions, while its uniqueness on a link is + being verified."; + } + } + description + "The status of an address. Most of the states correspond + to states from the IPv6 Stateless Address + Autoconfiguration protocol."; + reference + "RFC 4293: Management Information Base for the + Internet Protocol (IP) + - IpAddressStatusTC + RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + } + list neighbor { + key "ip"; + description + "A list of mappings from IPv6 addresses to + link-layer addresses. + + This list represents the Neighbor Cache."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)"; + + leaf ip { + type inet:ipv6-address-no-zone; + description + "The IPv6 address of the neighbor node."; + } + leaf link-layer-address { + type yang:phys-address; + description + "The link-layer address of the neighbor node."; + } + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry."; + } + leaf is-router { + type empty; + description + "Indicates that the neighbor node acts as a router."; + } + leaf state { + type enumeration { + enum incomplete { + description + "Address resolution is in progress, and the link-layer + address of the neighbor has not yet been + determined."; + } + enum reachable { + description + "Roughly speaking, the neighbor is known to have been + reachable recently (within tens of seconds ago)."; + } + enum stale { + description + "The neighbor is no longer known to be reachable, but + until traffic is sent to the neighbor no attempt + should be made to verify its reachability."; + } + enum delay { + description + "The neighbor is no longer known to be reachable, and + traffic has recently been sent to the neighbor. + Rather than probe the neighbor immediately, however, + delay sending probes for a short while in order to + give upper-layer protocols a chance to provide + reachability confirmation."; + } + enum probe { + description + "The neighbor is no longer known to be reachable, and + unicast Neighbor Solicitation probes are being sent + to verify reachability."; + } + } + description + "The Neighbor Unreachability Detection state of this + entry."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 7.3.2"; + } + } + } + } +} diff --git a/tests/yanglint/modules/ietf-netconf-acm.yang b/tests/yanglint/modules/ietf-netconf-acm.yang new file mode 100644 index 0000000..d372fa0 --- /dev/null +++ b/tests/yanglint/modules/ietf-netconf-acm.yang @@ -0,0 +1,411 @@ +module ietf-netconf-acm { + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; + prefix nacm; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <http://tools.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + + WG Chair: Bert Wijnen + <mailto:bertietf@bwijnen.net> + + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + description + "NETCONF Access Control Model. + + Copyright (c) 2012 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD + License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6536; see + the RFC itself for full legal notices."; + + revision 2012-02-22 { + description + "Initial version"; + reference + "RFC 6536: Network Configuration Protocol (NETCONF) + Access Control Model"; + } + + extension default-deny-write { + description + "Used to indicate that the data model node + represents a sensitive security system parameter. + + If present, and the NACM module is enabled (i.e., + /nacm/enable-nacm object equals 'true'), the NETCONF server + will only allow the designated 'recovery session' to have + write access to the node. An explicit access control rule is + required for all other users. + + The 'default-deny-write' extension MAY appear within a data + definition statement. It is ignored otherwise."; + } + + extension default-deny-all { + description + "Used to indicate that the data model node + controls a very sensitive security system parameter. + + If present, and the NACM module is enabled (i.e., + /nacm/enable-nacm object equals 'true'), the NETCONF server + will only allow the designated 'recovery session' to have + read, write, or execute access to the node. An explicit + access control rule is required for all other users. + + The 'default-deny-all' extension MAY appear within a data + definition statement, 'rpc' statement, or 'notification' + statement. It is ignored otherwise."; + } + + typedef user-name-type { + type string { + length "1..max"; + } + description + "General Purpose Username string."; + } + + typedef matchall-string-type { + type string { + pattern "\\*"; + } + description + "The string containing a single asterisk '*' is used + to conceptually represent all possible values + for the particular leaf using this data type."; + } + + typedef access-operations-type { + type bits { + bit create { + description + "Any protocol operation that creates a + new data node."; + } + bit read { + description + "Any protocol operation or notification that + returns the value of a data node."; + } + bit update { + description + "Any protocol operation that alters an existing + data node."; + } + bit delete { + description + "Any protocol operation that removes a data node."; + } + bit exec { + description + "Execution access to the specified protocol operation."; + } + } + description + "NETCONF Access Operation."; + } + + typedef group-name-type { + type string { + length "1..max"; + pattern "[^\\*].*"; + } + description + "Name of administrative group to which + users can be assigned."; + } + + typedef action-type { + type enumeration { + enum "permit" { + description + "Requested action is permitted."; + } + enum "deny" { + description + "Requested action is denied."; + } + } + description + "Action taken by the server when a particular + rule matches."; + } + + typedef node-instance-identifier { + type yang:xpath1.0; + description + "Path expression used to represent a special + data node instance identifier string. + + A node-instance-identifier value is an + unrestricted YANG instance-identifier expression. + All the same rules as an instance-identifier apply + except predicates for keys are optional. If a key + predicate is missing, then the node-instance-identifier + represents all possible server instances for that key. + + This XPath expression is evaluated in the following context: + + o The set of namespace declarations are those in scope on + the leaf element where this type is used. + + o The set of variable bindings contains one variable, + 'USER', which contains the name of the user of the current + session. + + o The function library is the core function library, but + note that due to the syntax restrictions of an + instance-identifier, no functions are allowed. + + o The context node is the root node in the data tree."; + } + + container nacm { + nacm:default-deny-all; + description + "Parameters for NETCONF Access Control Model."; + leaf enable-nacm { + type boolean; + default "true"; + description + "Enables or disables all NETCONF access control + enforcement. If 'true', then enforcement + is enabled. If 'false', then enforcement + is disabled."; + } + leaf read-default { + type action-type; + default "permit"; + description + "Controls whether read access is granted if + no appropriate rule is found for a + particular read request."; + } + leaf write-default { + type action-type; + default "deny"; + description + "Controls whether create, update, or delete access + is granted if no appropriate rule is found for a + particular write request."; + } + leaf exec-default { + type action-type; + default "permit"; + description + "Controls whether exec access is granted if no appropriate + rule is found for a particular protocol operation request."; + } + leaf enable-external-groups { + type boolean; + default "true"; + description + "Controls whether the server uses the groups reported by the + NETCONF transport layer when it assigns the user to a set of + NACM groups. If this leaf has the value 'false', any group + names reported by the transport layer are ignored by the + server."; + } + leaf denied-operations { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request was denied."; + } + leaf denied-data-writes { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that a + protocol operation request to alter + a configuration datastore was denied."; + } + leaf denied-notifications { + type yang:zero-based-counter32; + config false; + mandatory true; + description + "Number of times since the server last restarted that + a notification was dropped for a subscription because + access to the event type was denied."; + } + container groups { + description + "NETCONF Access Control Groups."; + list group { + key "name"; + description + "One NACM Group Entry. This list will only contain + configured entries, not any entries learned from + any transport protocols."; + leaf name { + type group-name-type; + description + "Group name associated with this entry."; + } + leaf-list user-name { + type user-name-type; + description + "Each entry identifies the username of + a member of the group associated with + this entry."; + } + } + } + list rule-list { + key "name"; + ordered-by user; + description + "An ordered collection of access control rules."; + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule-list."; + } + leaf-list group { + type union { + type matchall-string-type; + type group-name-type; + } + description + "List of administrative groups that will be + assigned the associated access rights + defined by the 'rule' list. + + The string '*' indicates that all groups apply to the + entry."; + } + list rule { + key "name"; + ordered-by user; + description + "One access control rule. + + Rules are processed in user-defined order until a match is + found. A rule matches if 'module-name', 'rule-type', and + 'access-operations' match the request. If a rule + matches, the 'action' leaf determines if access is granted + or not."; + leaf name { + type string { + length "1..max"; + } + description + "Arbitrary name assigned to the rule."; + } + leaf module-name { + type union { + type matchall-string-type; + type string; + } + default "*"; + description + "Name of the module associated with this rule. + + This leaf matches if it has the value '*' or if the + object being accessed is defined in the module with the + specified module name."; + } + choice rule-type { + description + "This choice matches if all leafs present in the rule + match the request. If no leafs are present, the + choice matches all requests."; + case protocol-operation { + leaf rpc-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if + its value equals the requested protocol operation + name."; + } + } + case notification { + leaf notification-name { + type union { + type matchall-string-type; + type string; + } + description + "This leaf matches if it has the value '*' or if its + value equals the requested notification name."; + } + } + case data-node { + leaf path { + type node-instance-identifier; + mandatory true; + description + "Data Node Instance Identifier associated with the + data node controlled by this rule. + + Configuration data or state data instance + identifiers start with a top-level data node. A + complete instance identifier is required for this + type of path value. + + The special value '/' refers to all possible + datastore contents."; + } + } + } + leaf access-operations { + type union { + type matchall-string-type; + type access-operations-type; + } + default "*"; + description + "Access operations associated with this rule. + + This leaf matches if it has the value '*' or if the + bit corresponding to the requested operation is set."; + } + leaf action { + type action-type; + mandatory true; + description + "The access control action associated with the + rule. If a rule is determined to match a + particular request, then this object is used + to determine whether to permit or deny the + request."; + } + leaf comment { + type string; + description + "A textual description of the access rule."; + } + } + } + } +} diff --git a/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang b/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang new file mode 100644 index 0000000..e19d2b3 --- /dev/null +++ b/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang @@ -0,0 +1,140 @@ +module ietf-netconf-with-defaults { + + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"; + + prefix ncwd; + + import ietf-netconf { prefix nc; } + + organization + "IETF NETCONF (Network Configuration Protocol) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netconf/> + + WG List: <netconf@ietf.org> + + WG Chair: Bert Wijnen + <bertietf@bwijnen.net> + + WG Chair: Mehmet Ersue + <mehmet.ersue@nsn.com> + + Editor: Andy Bierman + <andy.bierman@brocade.com> + + Editor: Balazs Lengyel + <balazs.lengyel@ericsson.com>"; + + description + "This module defines an extension to the NETCONF protocol + that allows the NETCONF client to control how default + values are handled by the server in particular NETCONF + operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6243; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial version."; + reference + "RFC 6243: With-defaults Capability for NETCONF"; + } + + typedef with-defaults-mode { + description + "Possible modes to report default data."; + reference + "RFC 6243; Section 3."; + type enumeration { + enum report-all { + description + "All default data is reported."; + reference + "RFC 6243; Section 3.1"; + } + enum report-all-tagged { + description + "All default data is reported. + Any nodes considered to be default data + will contain a 'default' XML attribute, + set to 'true' or '1'."; + reference + "RFC 6243; Section 3.4"; + } + enum trim { + description + "Values are not reported if they contain the default."; + reference + "RFC 6243; Section 3.2"; + } + enum explicit { + description + "Report values that contain the definition of + explicitly set data."; + reference + "RFC 6243; Section 3.3"; + } + } + } + + grouping with-defaults-parameters { + description + "Contains the <with-defaults> parameter for control + of defaults in NETCONF retrieval operations."; + + leaf with-defaults { + description + "The explicit defaults processing mode requested."; + reference + "RFC 6243; Section 4.5.1"; + + type with-defaults-mode; + } + } + + // extending the get-config operation + augment /nc:get-config/nc:input { + description + "Adds the <with-defaults> parameter to the + input of the NETCONF <get-config> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the get operation + augment /nc:get/nc:input { + description + "Adds the <with-defaults> parameter to + the input of the NETCONF <get> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + + // extending the copy-config operation + augment /nc:copy-config/nc:input { + description + "Adds the <with-defaults> parameter to + the input of the NETCONF <copy-config> operation."; + reference + "RFC 6243; Section 4.5.1"; + + uses with-defaults-parameters; + } + +} diff --git a/tests/yanglint/modules/ietf-netconf@2011-06-01.yang b/tests/yanglint/modules/ietf-netconf@2011-06-01.yang new file mode 100644 index 0000000..3053db2 --- /dev/null +++ b/tests/yanglint/modules/ietf-netconf@2011-06-01.yang @@ -0,0 +1,934 @@ +module ietf-netconf { + + // the namespace for NETCONF XML definitions is unchanged + // from RFC 4741, which this document replaces + namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; + + prefix nc; + + import ietf-inet-types { + prefix inet; + } + + import ietf-netconf-acm { prefix nacm; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netconf/> + WG List: <netconf@ietf.org> + + WG Chair: Bert Wijnen + <bertietf@bwijnen.net> + + WG Chair: Mehmet Ersue + <mehmet.ersue@nsn.com> + + Editor: Martin Bjorklund + <mbj@tail-f.com> + + Editor: Juergen Schoenwaelder + <j.schoenwaelder@jacobs-university.de> + + Editor: Andy Bierman + <andy.bierman@brocade.com>"; + description + "NETCONF Protocol Data Types and Protocol Operations. + + Copyright (c) 2011 IETF Trust and the persons identified as + the document authors. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6241; see + the RFC itself for full legal notices."; + + revision 2011-06-01 { + description + "Initial revision; + 2013-09-29: Updated to include NACM attributes, + as specified in RFC 6536: sec 3.2.5 and 3.2.8"; + reference + "RFC 6241: Network Configuration Protocol"; + } + + extension get-filter-element-attributes { + description + "If this extension is present within an 'anyxml' + statement named 'filter', which must be conceptually + defined within the RPC input section for the <get> + and <get-config> protocol operations, then the + following unqualified XML attribute is supported + within the <filter> element, within a <get> or + <get-config> protocol operation: + + type : optional attribute with allowed + value strings 'subtree' and 'xpath'. + If missing, the default value is 'subtree'. + + If the 'xpath' feature is supported, then the + following unqualified XML attribute is + also supported: + + select: optional attribute containing a + string representing an XPath expression. + The 'type' attribute must be equal to 'xpath' + if this attribute is present."; + } + + // NETCONF capabilities defined as features + feature writable-running { + description + "NETCONF :writable-running capability; + If the server advertises the :writable-running + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.2"; + } + + feature candidate { + description + "NETCONF :candidate capability; + If the server advertises the :candidate + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.3"; + } + + feature confirmed-commit { + if-feature candidate; + description + "NETCONF :confirmed-commit:1.1 capability; + If the server advertises the :confirmed-commit:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + + reference "RFC 6241, Section 8.4"; + } + + feature rollback-on-error { + description + "NETCONF :rollback-on-error capability; + If the server advertises the :rollback-on-error + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.5"; + } + + feature validate { + description + "NETCONF :validate:1.1 capability; + If the server advertises the :validate:1.1 + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.6"; + } + + feature startup { + description + "NETCONF :startup capability; + If the server advertises the :startup + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.7"; + } + + feature url { + description + "NETCONF :url capability; + If the server advertises the :url + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.8"; + } + + feature xpath { + description + "NETCONF :xpath capability; + If the server advertises the :xpath + capability for a session, then this feature must + also be enabled for that session. Otherwise, + this feature must not be enabled."; + reference "RFC 6241, Section 8.9"; + } + + // NETCONF Simple Types + + typedef session-id-type { + type uint32 { + range "1..max"; + } + description + "NETCONF Session Id"; + } + + typedef session-id-or-zero-type { + type uint32; + description + "NETCONF Session Id or Zero to indicate none"; + } + typedef error-tag-type { + type enumeration { + enum in-use { + description + "The request requires a resource that + already is in use."; + } + enum invalid-value { + description + "The request specifies an unacceptable value for one + or more parameters."; + } + enum too-big { + description + "The request or response (that would be generated) is + too large for the implementation to handle."; + } + enum missing-attribute { + description + "An expected attribute is missing."; + } + enum bad-attribute { + description + "An attribute value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-attribute { + description + "An unexpected attribute is present."; + } + enum missing-element { + description + "An expected element is missing."; + } + enum bad-element { + description + "An element value is not correct; e.g., wrong type, + out of range, pattern mismatch."; + } + enum unknown-element { + description + "An unexpected element is present."; + } + enum unknown-namespace { + description + "An unexpected namespace is present."; + } + enum access-denied { + description + "Access to the requested protocol operation or + data model is denied because authorization failed."; + } + enum lock-denied { + description + "Access to the requested lock is denied because the + lock is currently held by another entity."; + } + enum resource-denied { + description + "Request could not be completed because of + insufficient resources."; + } + enum rollback-failed { + description + "Request to roll back some configuration change (via + rollback-on-error or <discard-changes> operations) + was not completed for some reason."; + + } + enum data-exists { + description + "Request could not be completed because the relevant + data model content already exists. For example, + a 'create' operation was attempted on data that + already exists."; + } + enum data-missing { + description + "Request could not be completed because the relevant + data model content does not exist. For example, + a 'delete' operation was attempted on + data that does not exist."; + } + enum operation-not-supported { + description + "Request could not be completed because the requested + operation is not supported by this implementation."; + } + enum operation-failed { + description + "Request could not be completed because the requested + operation failed for some reason not covered by + any other error condition."; + } + enum partial-operation { + description + "This error-tag is obsolete, and SHOULD NOT be sent + by servers conforming to this document."; + } + enum malformed-message { + description + "A message could not be handled because it failed to + be parsed correctly. For example, the message is not + well-formed XML or it uses an invalid character set."; + } + } + description "NETCONF Error Tag"; + reference "RFC 6241, Appendix A"; + } + + typedef error-severity-type { + type enumeration { + enum error { + description "Error severity"; + } + enum warning { + description "Warning severity"; + } + } + description "NETCONF Error Severity"; + reference "RFC 6241, Section 4.3"; + } + + typedef edit-operation-type { + type enumeration { + enum merge { + description + "The configuration data identified by the + element containing this attribute is merged + with the configuration at the corresponding + level in the configuration datastore identified + by the target parameter."; + } + enum replace { + description + "The configuration data identified by the element + containing this attribute replaces any related + configuration in the configuration datastore + identified by the target parameter. If no such + configuration data exists in the configuration + datastore, it is created. Unlike a + <copy-config> operation, which replaces the + entire target configuration, only the configuration + actually present in the config parameter is affected."; + } + enum create { + description + "The configuration data identified by the element + containing this attribute is added to the + configuration if and only if the configuration + data does not already exist in the configuration + datastore. If the configuration data exists, an + <rpc-error> element is returned with an + <error-tag> value of 'data-exists'."; + } + enum delete { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if and only if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, an <rpc-error> element is returned with + an <error-tag> value of 'data-missing'."; + } + enum remove { + description + "The configuration data identified by the element + containing this attribute is deleted from the + configuration if the configuration + data currently exists in the configuration + datastore. If the configuration data does not + exist, the 'remove' operation is silently ignored + by the server."; + } + } + default "merge"; + description "NETCONF 'operation' attribute values"; + reference "RFC 6241, Section 7.2"; + } + + // NETCONF Standard Protocol Operations + + rpc get-config { + description + "Retrieve all or part of a specified configuration."; + + reference "RFC 6241, Section 7.1"; + + input { + container source { + description + "Particular configuration to retrieve."; + + choice config-source { + mandatory true; + description + "The configuration to retrieve."; + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source. + This is optional-to-implement on the server because + not all servers will support filtering for this + datastore."; + } + } + } + + anyxml filter { + description + "Subtree or XPath filter to use."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the source datastore subset that matched + the filter criteria (if any). An empty data container + indicates that the request did not produce any results."; + } + } + } + + rpc edit-config { + description + "The <edit-config> operation loads all or part of a specified + configuration to the specified target configuration."; + + reference "RFC 6241, Section 7.2"; + + input { + container target { + description + "Particular configuration to edit."; + + choice config-target { + mandatory true; + description + "The configuration target."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config source."; + } + } + } + + leaf default-operation { + type enumeration { + enum merge { + description + "The default operation is merge."; + } + enum replace { + description + "The default operation is replace."; + } + enum none { + description + "There is no default operation."; + } + } + default "merge"; + description + "The default operation to use."; + } + + leaf test-option { + if-feature validate; + type enumeration { + enum test-then-set { + description + "The server will test and then set if no errors."; + } + enum set { + description + "The server will set without a test first."; + } + + enum test-only { + description + "The server will only test and not set, even + if there are no errors."; + } + } + default "test-then-set"; + description + "The test option to use."; + } + + leaf error-option { + type enumeration { + enum stop-on-error { + description + "The server will stop on errors."; + } + enum continue-on-error { + description + "The server may continue on errors."; + } + enum rollback-on-error { + description + "The server will roll back on errors. + This value can only be used if the 'rollback-on-error' + feature is supported."; + } + } + default "stop-on-error"; + description + "The error option to use."; + } + + choice edit-content { + mandatory true; + description + "The content for the edit operation."; + + anyxml config { + description + "Inline Config content."; + } + leaf url { + if-feature url; + type inet:uri; + description + "URL-based config content."; + } + } + } + } + + rpc copy-config { + description + "Create or replace an entire configuration datastore with the + contents of another complete configuration datastore."; + + reference "RFC 6241, Section 7.3"; + + input { + container target { + description + "Particular configuration to copy to."; + + choice config-target { + mandatory true; + description + "The configuration target of the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + if-feature writable-running; + type empty; + description + "The running configuration is the config target. + This is optional-to-implement on the server."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + + container source { + description + "Particular configuration to copy from."; + + choice config-source { + mandatory true; + description + "The configuration source for the copy operation."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: <config> element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } + + rpc delete-config { + nacm:default-deny-all; + description + "Delete a configuration datastore."; + + reference "RFC 6241, Section 7.4"; + + input { + container target { + description + "Particular configuration to delete."; + + choice config-target { + mandatory true; + description + "The configuration target to delete."; + + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config target."; + } + } + } + } + } + + rpc lock { + description + "The lock operation allows the client to lock the configuration + system of a device."; + + reference "RFC 6241, Section 7.5"; + + input { + container target { + description + "Particular configuration to lock."; + + choice config-target { + mandatory true; + description + "The configuration target to lock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc unlock { + description + "The unlock operation is used to release a configuration lock, + previously obtained with the 'lock' operation."; + + reference "RFC 6241, Section 7.6"; + + input { + container target { + description + "Particular configuration to unlock."; + + choice config-target { + mandatory true; + description + "The configuration target to unlock."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config target."; + } + leaf running { + type empty; + description + "The running configuration is the config target."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config target."; + } + } + } + } + } + + rpc get { + description + "Retrieve running configuration and device state information."; + + reference "RFC 6241, Section 7.7"; + + input { + anyxml filter { + description + "This parameter specifies the portion of the system + configuration and state data to retrieve."; + nc:get-filter-element-attributes; + } + } + + output { + anyxml data { + description + "Copy of the running datastore subset and/or state + data that matched the filter criteria (if any). + An empty data container indicates that the request did not + produce any results."; + } + } + } + + rpc close-session { + description + "Request graceful termination of a NETCONF session."; + + reference "RFC 6241, Section 7.8"; + } + + rpc kill-session { + nacm:default-deny-all; + description + "Force the termination of a NETCONF session."; + + reference "RFC 6241, Section 7.9"; + + input { + leaf session-id { + type session-id-type; + mandatory true; + description + "Particular session to kill."; + } + } + } + + rpc commit { + if-feature candidate; + + description + "Commit the candidate configuration as the device's new + current configuration."; + + reference "RFC 6241, Section 8.3.4.1"; + + input { + leaf confirmed { + if-feature confirmed-commit; + type empty; + description + "Requests a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf confirm-timeout { + if-feature confirmed-commit; + type uint32 { + range "1..max"; + } + units "seconds"; + default "600"; // 10 minutes + description + "The timeout interval for a confirmed commit."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist { + if-feature confirmed-commit; + type string; + description + "This parameter is used to make a confirmed commit + persistent. A persistent confirmed commit is not aborted + if the NETCONF session terminates. The only way to abort + a persistent confirmed commit is to let the timer expire, + or to use the <cancel-commit> operation. + + The value of this parameter is a token that must be given + in the 'persist-id' parameter of <commit> or + <cancel-commit> operations in order to confirm or cancel + the persistent confirmed commit. + + The token should be a random string."; + reference "RFC 6241, Section 8.3.4.1"; + } + + leaf persist-id { + if-feature confirmed-commit; + type string; + description + "This parameter is given in order to commit a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the <commit> operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + reference "RFC 6241, Section 8.3.4.1"; + } + + } + } + + rpc discard-changes { + if-feature candidate; + + description + "Revert the candidate configuration to the current + running configuration."; + reference "RFC 6241, Section 8.3.4.2"; + } + + rpc cancel-commit { + if-feature confirmed-commit; + description + "This operation is used to cancel an ongoing confirmed commit. + If the confirmed commit is persistent, the parameter + 'persist-id' must be given, and it must match the value of the + 'persist' parameter."; + reference "RFC 6241, Section 8.4.4.1"; + + input { + leaf persist-id { + type string; + description + "This parameter is given in order to cancel a persistent + confirmed commit. The value must be equal to the value + given in the 'persist' parameter to the <commit> operation. + If it does not match, the operation fails with an + 'invalid-value' error."; + } + } + } + + rpc validate { + if-feature validate; + + description + "Validates the contents of the specified configuration."; + + reference "RFC 6241, Section 8.6.4.1"; + + input { + container source { + description + "Particular configuration to validate."; + + choice config-source { + mandatory true; + description + "The configuration source to validate."; + + leaf candidate { + if-feature candidate; + type empty; + description + "The candidate configuration is the config source."; + } + leaf running { + type empty; + description + "The running configuration is the config source."; + } + leaf startup { + if-feature startup; + type empty; + description + "The startup configuration is the config source."; + } + leaf url { + if-feature url; + type inet:uri; + description + "The URL-based configuration is the config source."; + } + anyxml config { + description + "Inline Config content: <config> element. Represents + an entire configuration datastore, not + a subset of the running datastore."; + } + } + } + } + } + +} diff --git a/tests/yanglint/modules/modaction.yang b/tests/yanglint/modules/modaction.yang new file mode 100644 index 0000000..5a3f92f --- /dev/null +++ b/tests/yanglint/modules/modaction.yang @@ -0,0 +1,26 @@ +module modaction { + yang-version 1.1; + namespace "urn:yanglint:modaction"; + prefix ma; + + container con { + list ls { + key "lfkey"; + leaf lfkey { + type string; + } + action act { + input { + leaf lfi { + type string; + } + } + output { + leaf lfo { + type int16; + } + } + } + } + } +} diff --git a/tests/yanglint/modules/modconfig-augment.yang b/tests/yanglint/modules/modconfig-augment.yang new file mode 100644 index 0000000..d94b366 --- /dev/null +++ b/tests/yanglint/modules/modconfig-augment.yang @@ -0,0 +1,15 @@ +module modconfig-augment { + yang-version 1.1; + namespace "urn:yanglint:modconfig-augment"; + prefix "mca"; + + import modconfig { + prefix mc; + } + + augment "/mc:mcc" { + leaf alf { + type string; + } + } +} diff --git a/tests/yanglint/modules/modconfig.yang b/tests/yanglint/modules/modconfig.yang new file mode 100644 index 0000000..1d12ca6 --- /dev/null +++ b/tests/yanglint/modules/modconfig.yang @@ -0,0 +1,17 @@ +module modconfig { + namespace "urn:yanglint:modconfig"; + prefix mc; + + container mcc { + leaf lft { + type string; + config true; + mandatory true; + } + leaf lff { + type string; + config false; + mandatory true; + } + } +} diff --git a/tests/yanglint/modules/moddatanodes.yang b/tests/yanglint/modules/moddatanodes.yang new file mode 100644 index 0000000..ae4ab20 --- /dev/null +++ b/tests/yanglint/modules/moddatanodes.yang @@ -0,0 +1,31 @@ +module moddatanodes { + yang-version 1.1; + namespace "urn:yanglint:moddatanodes"; + prefix mdn; + + container dnc { + leaf lf { + type string; + } + leaf-list lfl { + type string; + } + leaf mis { + type string; + } + container con { + list lt { + key "kalf kblf"; + leaf kalf { + type string; + } + leaf kblf { + type string; + } + leaf vlf { + type string; + } + } + } + } +} diff --git a/tests/yanglint/modules/moddefault.yang b/tests/yanglint/modules/moddefault.yang new file mode 100644 index 0000000..26570c3 --- /dev/null +++ b/tests/yanglint/modules/moddefault.yang @@ -0,0 +1,19 @@ +module moddefault { + namespace "urn:yanglint:moddefault"; + prefix md; + + container mdc { + leaf lf { + type uint16; + } + leaf di { + type int16; + default "5"; + } + leaf ds { + type string; + default "str"; + } + } + +} diff --git a/tests/yanglint/modules/modextleafref.yang b/tests/yanglint/modules/modextleafref.yang new file mode 100644 index 0000000..d45ec71 --- /dev/null +++ b/tests/yanglint/modules/modextleafref.yang @@ -0,0 +1,24 @@ +module modextleafref { + namespace "urn:yanglint:modextleafref"; + prefix mel; + + list ls { + key k; + leaf k { + type string; + } + leaf lf { + type uint8; + } + } + leaf lfr { + type leafref { + path "../ls/k"; + } + } + leaf lfrderef { + type leafref { + path "deref(../lfr)/../lf"; + } + } +} diff --git a/tests/yanglint/modules/modfeature.yang b/tests/yanglint/modules/modfeature.yang new file mode 100644 index 0000000..f59d4c8 --- /dev/null +++ b/tests/yanglint/modules/modfeature.yang @@ -0,0 +1,7 @@ +module modfeature { + namespace "urn:yanglint:modfeature"; + prefix l; + + feature ftr1; + feature ftr2; +} diff --git a/tests/yanglint/modules/modimp-cwd.yang b/tests/yanglint/modules/modimp-cwd.yang new file mode 100644 index 0000000..3249462 --- /dev/null +++ b/tests/yanglint/modules/modimp-cwd.yang @@ -0,0 +1,8 @@ +module modimp-cwd { + namespace "urn:yanglint:modimp-cwd"; + prefix ic; + + import modcwd { + prefix mc; + } +} diff --git a/tests/yanglint/modules/modimp-path.yang b/tests/yanglint/modules/modimp-path.yang new file mode 100644 index 0000000..d9dbb9b --- /dev/null +++ b/tests/yanglint/modules/modimp-path.yang @@ -0,0 +1,8 @@ +module modimp-path { + namespace "urn:yanglint:modimp-path"; + prefix ip; + + import modpath { + prefix mp; + } +} diff --git a/tests/yanglint/modules/modimp-type.yang b/tests/yanglint/modules/modimp-type.yang new file mode 100644 index 0000000..ec21d31 --- /dev/null +++ b/tests/yanglint/modules/modimp-type.yang @@ -0,0 +1,12 @@ +module modimp-type { + namespace "urn:yanglint:modimp-type"; + prefix mit; + + import modtypedef { + prefix mtd; + } + + leaf lf { + type mtd:mui8; + } +} diff --git a/tests/yanglint/modules/modinclude.yang b/tests/yanglint/modules/modinclude.yang new file mode 100644 index 0000000..849d43f --- /dev/null +++ b/tests/yanglint/modules/modinclude.yang @@ -0,0 +1,9 @@ +module modinclude { + yang-version 1.1; + namespace "urn:yanglint:modinclude"; + prefix mi; + + include "modsub"; + + container mic; +} diff --git a/tests/yanglint/modules/modleaf.yang b/tests/yanglint/modules/modleaf.yang new file mode 100644 index 0000000..48ce786 --- /dev/null +++ b/tests/yanglint/modules/modleaf.yang @@ -0,0 +1,8 @@ +module modleaf { + namespace "urn:yanglint:modleaf"; + prefix l; + + leaf lfl { + type uint16; + } +} diff --git a/tests/yanglint/modules/modleafref.yang b/tests/yanglint/modules/modleafref.yang new file mode 100644 index 0000000..f86fb3f --- /dev/null +++ b/tests/yanglint/modules/modleafref.yang @@ -0,0 +1,14 @@ +module modleafref { + namespace "urn:yanglint:modleafref"; + prefix m; + + import modleaf { + prefix ml; + } + + leaf lfr { + type leafref { + path "/ml:lfl"; + } + } +} diff --git a/tests/yanglint/modules/modmandatory.yang b/tests/yanglint/modules/modmandatory.yang new file mode 100644 index 0000000..4d48540 --- /dev/null +++ b/tests/yanglint/modules/modmandatory.yang @@ -0,0 +1,14 @@ +module modmandatory { + namespace "urn:yanglint:modmandatory"; + prefix mm; + + container mmc { + leaf lft { + type int16; + mandatory true; + } + leaf lff { + type int16; + } + } +} diff --git a/tests/yanglint/modules/modmerge.yang b/tests/yanglint/modules/modmerge.yang new file mode 100644 index 0000000..60fd75c --- /dev/null +++ b/tests/yanglint/modules/modmerge.yang @@ -0,0 +1,21 @@ +module modmerge { + namespace "urn:yanglint:modmerge"; + prefix mm; + + container mmc { + leaf en { + type enumeration { + enum zero; + enum one; + } + } + leaf lm { + type int16; + must "../en != 'zero'"; + } + leaf lf { + type string; + } + } + +} diff --git a/tests/yanglint/modules/modmust.yang b/tests/yanglint/modules/modmust.yang new file mode 100644 index 0000000..99971bd --- /dev/null +++ b/tests/yanglint/modules/modmust.yang @@ -0,0 +1,13 @@ +module modmust { + namespace "urn:yanglint:modmust"; + prefix m; + + import modleaf { + prefix ml; + } + + leaf lfm { + type string; + must "/ml:lfl > 0"; + } +} diff --git a/tests/yanglint/modules/modnotif.yang b/tests/yanglint/modules/modnotif.yang new file mode 100644 index 0000000..a2155a0 --- /dev/null +++ b/tests/yanglint/modules/modnotif.yang @@ -0,0 +1,19 @@ +module modnotif { + yang-version 1.1; + namespace "urn:yanglint:modnotif"; + prefix mn; + + container con { + notification nfn { + leaf lf { + type string; + } + } + } + + notification nfg { + leaf lf { + type string; + } + } +} diff --git a/tests/yanglint/modules/modoper-leafref.yang b/tests/yanglint/modules/modoper-leafref.yang new file mode 100644 index 0000000..36a1124 --- /dev/null +++ b/tests/yanglint/modules/modoper-leafref.yang @@ -0,0 +1,68 @@ +module modoper-leafref { + yang-version 1.1; + namespace "urn:yanglint:modoper-leafref"; + prefix mol; + + import modconfig { + prefix mc; + } + + container cond { + list list { + key "klf"; + leaf klf { + type string; + } + action act { + input { + leaf lfi { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } + output { + leaf lfo { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } + } + notification notif { + leaf lfn { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } + } + } + + rpc rpcg { + input { + leaf lfi { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } + output { + container cono { + leaf lfo { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } + } + } + + notification notifg { + leaf lfr { + type leafref { + path "/mc:mcc/mc:lft"; + } + } + } +} diff --git a/tests/yanglint/modules/modpath.yang b/tests/yanglint/modules/modpath.yang new file mode 100644 index 0000000..da099a2 --- /dev/null +++ b/tests/yanglint/modules/modpath.yang @@ -0,0 +1,4 @@ +module modpath { + namespace "urn:yanglint:modpath"; + prefix mp; +} diff --git a/tests/yanglint/modules/modrpc.yang b/tests/yanglint/modules/modrpc.yang new file mode 100644 index 0000000..dc0cced --- /dev/null +++ b/tests/yanglint/modules/modrpc.yang @@ -0,0 +1,19 @@ +module modrpc { + namespace "urn:yanglint:modrpc"; + prefix mr; + + rpc rpc { + input { + leaf lfi { + type string; + } + } + output { + container con { + leaf lfo { + type int16; + } + } + } + } +} diff --git a/tests/yanglint/modules/modsm-augment.yang b/tests/yanglint/modules/modsm-augment.yang new file mode 100644 index 0000000..5d16fbd --- /dev/null +++ b/tests/yanglint/modules/modsm-augment.yang @@ -0,0 +1,15 @@ +module modsm-augment { + yang-version 1.1; + namespace "urn:yanglint:modsm-augment"; + prefix "msa"; + + import modsm { + prefix msm; + } + + augment "/msm:root" { + leaf alf { + type string; + } + } +} diff --git a/tests/yanglint/modules/modsm.yang b/tests/yanglint/modules/modsm.yang new file mode 100644 index 0000000..dfe8830 --- /dev/null +++ b/tests/yanglint/modules/modsm.yang @@ -0,0 +1,13 @@ +module modsm { + yang-version 1.1; + namespace "urn:yanglint:modsm"; + prefix "msm"; + + import ietf-yang-schema-mount { + prefix sm; + } + + container root { + sm:mount-point "root"; + } +} diff --git a/tests/yanglint/modules/modsub.yang b/tests/yanglint/modules/modsub.yang new file mode 100644 index 0000000..79d9286 --- /dev/null +++ b/tests/yanglint/modules/modsub.yang @@ -0,0 +1,8 @@ +submodule modsub { + yang-version 1.1; + belongs-to modinclude { + prefix mi; + } + + container msc; +} diff --git a/tests/yanglint/modules/modtypedef.yang b/tests/yanglint/modules/modtypedef.yang new file mode 100644 index 0000000..ea09c95 --- /dev/null +++ b/tests/yanglint/modules/modtypedef.yang @@ -0,0 +1,8 @@ +module modtypedef { + namespace "urn:yanglint:typedef"; + prefix mt; + + typedef mui8 { + type uint8; + } +} diff --git a/tests/yanglint/non-interactive/all.tcl b/tests/yanglint/non-interactive/all.tcl new file mode 100644 index 0000000..998c03a --- /dev/null +++ b/tests/yanglint/non-interactive/all.tcl @@ -0,0 +1,15 @@ +package require tcltest + +# Hook to determine if any of the tests failed. +# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0. +proc tcltest::cleanupTestsHook {} { + variable numTests + set ::exitCode [expr {$numTests(Failed) > 0}] +} + +if {[info exists ::env(TESTS_DIR)]} { + tcltest::configure -testdir "$env(TESTS_DIR)/non-interactive" +} + +tcltest::runAllTests +exit $exitCode diff --git a/tests/yanglint/non-interactive/data_default.test b/tests/yanglint/non-interactive/data_default.test new file mode 100644 index 0000000..be19d72 --- /dev/null +++ b/tests/yanglint/non-interactive/data_default.test @@ -0,0 +1,31 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mods "$::env(YANG_MODULES_DIR)/ietf-netconf-with-defaults@2011-06-01.yang $::env(YANG_MODULES_DIR)/moddefault.yang" +set data "$::env(TESTS_DIR)/data/moddefault.xml" + +test data_default_not_set {Print data without --default parameter} { + ly_cmd "-f xml $mods $data" "</lf>.*</di>\n</mdc>" + ly_cmd "-f json $mods $data" "lf\".*di\"\[^\"]*" +} {} + +test data_default_all {data --default all} { + ly_cmd "-d all -f xml $mods $data" "</lf>.*</di>.*</ds>\n</mdc>" + ly_cmd "-d all -f json $mods $data" "lf\".*di\".*ds\"\[^\"]*" +} {} + +test data_default_all_tagged {data --default all-tagged} { + ly_cmd "-d all-tagged -f xml $mods $data" "</lf>.*<di.*default.*</di>.*<ds.*default.*</ds>\n</mdc>" + ly_cmd "-d all-tagged -f json $mods $data" "lf\".*di\".*ds\".*@ds\".*default\"\[^\"]*" +} {} + +test data_default_trim {data --default trim} { + ly_cmd "-d trim -f xml $mods $data" "</lf>\n</mdc>" + ly_cmd "-d trim -f json $mods $data" "lf\"\[^\"]*" +} {} + +test data_default_implicit_tagged {data --default implicit-tagged} { + ly_cmd "-d implicit-tagged -f xml $mods $data" "</lf>.*<di>5</di>.*<ds.*default.*</ds>\n</mdc>" + ly_cmd "-d implicit-tagged -f json $mods $data" "lf\".*di\"\[^@]*ds\".*default\"\[^\"]*" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_in_format.test b/tests/yanglint/non-interactive/data_in_format.test new file mode 100644 index 0000000..f1336dd --- /dev/null +++ b/tests/yanglint/non-interactive/data_in_format.test @@ -0,0 +1,18 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data + +test data_in_format_xml {--in-format xml} { + ly_cmd "-I xml $mdir/modleaf.yang $ddir/modleaf.dxml" + ly_cmd_err "-I json $mdir/modleaf.yang $ddir/modleaf.dxml" "Failed to parse" + ly_cmd_err "-I lyb $mdir/modleaf.yang $ddir/modleaf.dxml" "Failed to parse" +} {} + +test data_in_format_json {--in-format json} { + ly_cmd "-I json $mdir/modleaf.yang $ddir/modleaf.djson" + ly_cmd_err "-I xml $mdir/modleaf.yang $ddir/modleaf.djson" "Failed to parse" + ly_cmd_err "-I lyb $mdir/modleaf.yang $ddir/modleaf.djson" "Failed to parse" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_merge.test b/tests/yanglint/non-interactive/data_merge.test new file mode 100644 index 0000000..4ecfcee --- /dev/null +++ b/tests/yanglint/non-interactive/data_merge.test @@ -0,0 +1,28 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data + +test data_merge_basic {Data is merged and the node is added} { + ly_cmd "-m -f xml $mdir/modmerge.yang $ddir/modmerge.xml $ddir/modmerge3.xml" "<en>.*<lm>.*<lf>" +} {} + +test data_merge_validation_failed {Data is merged but validation failed.} { + ly_cmd "$mdir/modmerge.yang $ddir/modmerge.xml" + ly_cmd "$mdir/modmerge.yang $ddir/modmerge2.xml" + ly_cmd "-m $mdir/modmerge.yang $ddir/modmerge2.xml $ddir/modmerge.xml" + ly_cmd_err "-m $mdir/modmerge.yang $ddir/modmerge.xml $ddir/modmerge2.xml" "Merged data are not valid" +} {} + +test data_merge_dataconfig {The merge option has effect only for 'data' and 'config' TYPEs} { + set wrn1 "option has effect only for" + ly_cmd_wrn "-m -t rpc $mdir/modrpc.yang $ddir/modrpc.xml $ddir/modrpc.xml" $wrn1 + ly_cmd_wrn "-m -t notif $mdir/modnotif.yang $ddir/modnotif2.xml $ddir/modnotif2.xml" $wrn1 + ly_cmd_wrn "-m -t get $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig.xml" $wrn1 + ly_cmd_wrn "-m -t getconfig $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1 + ly_cmd_wrn "-m -t edit $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1 + ly_cmd "-m -t config $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml" + ly_cmd "-m -t data $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig.xml" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_not_strict.test b/tests/yanglint/non-interactive/data_not_strict.test new file mode 100644 index 0000000..b91eed8 --- /dev/null +++ b/tests/yanglint/non-interactive/data_not_strict.test @@ -0,0 +1,20 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data + +test data_no_strict_basic {} { + ly_cmd_err "$ddir/modmandatory.xml $mdir/modleaf.yang" "No module with namespace \"urn:yanglint:modmandatory\" in the context." + ly_cmd "-n $ddir/modmandatory.xml $mdir/modleaf.yang" +} {} + +test data_no_strict_invalid_data {validation with --no-strict but data are invalid} { + set errmsg "Mandatory node \"lft\" instance does not exist." + ly_cmd_err "-n $ddir/modmandatory_invalid.xml $mdir/modmandatory.yang" $errmsg +} {} + +test data_no_strict_ignore_invalid_data {--no-strict ignore invalid data if no schema is provided} { + ly_cmd "-f xml -n $ddir/modmandatory_invalid.xml $ddir/modleaf.xml $mdir/modleaf.yang" "modleaf.*</lfl>$" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_operational.test b/tests/yanglint/non-interactive/data_operational.test new file mode 100644 index 0000000..82e861e --- /dev/null +++ b/tests/yanglint/non-interactive/data_operational.test @@ -0,0 +1,62 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" +set ddir "$::env(TESTS_DIR)/data" +set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types" + +test data_operational_twice {it is not allowed to specify more than one --operational parameter} { + ly_cmd_err "-t notif -O $ddir/modconfig.xml -O $ddir/modleaf.xml" "cannot be set multiple times" +} {} + +test data_operational_no_type {--operational should be with parameter --type} { + ly_cmd_err "-O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif.xml" $err1 +} {} + +test data_operational_missing {--operational is omitted and the datastore contents is in the data file} { + ly_cmd_err "$mdir/modoper-leafref.yang $ddir/modoper_leafref_notif_err.xml" "Failed to parse input data file" +} {} + +test data_operational_wrong_type {data are not defined as an operation} { + ly_cmd_wrn "-t data -O $ddir/modconfig.xml $mdir/modleaf.yang $ddir/modleaf.xml" $err1 +} {} + +test data_operational_datastore_with_unknown_data {unknown data are ignored} { + ly_cmd "-t rpc -O $ddir/modmandatory_invalid.xml $mdir/modrpc.yang $ddir/modrpc.xml" +} {} + +test data_operational_empty_datastore {datastore is considered empty because it contains unknown data} { + ly_cmd "-t rpc -O $ddir/modmandatory_invalid.xml $mdir/modrpc.yang $ddir/modrpc.xml" + set msg "parent \"/modnotif:con\" not found in the operational data" + ly_cmd_err "-t notif -O $ddir/modmandatory_invalid.xml $mdir/modnotif.yang $ddir/modnotif.xml" $msg +} {} + +test data_operational_notif_leafref {--operational data is referenced from notification-leafref} { + ly_cmd "-t notif -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif.xml" +} {} + +test data_operational_nested_notif_leafref {--operational data is referenced from nested-notification-leafref} { + ly_cmd "-t notif -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif2.xml" +} {} + +test data_operational_nested_notif_parent_missing {--operational data are invalid due to missing parent node} { + set msg "klf='key_val']\" not found in the operational data" + ly_cmd_err "-t notif -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif2.xml" $msg +} {} + +test data_operational_action_leafref {--operational data is referenced from action-leafref} { + ly_cmd "-t rpc -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_action.xml" +} {} + +test data_operational_action_reply_leafref {--operational data is referenced from action-leafref output} { + ly_cmd "-t reply -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_action_reply.xml" +} {} + +test data_operational_rpc_leafref {--operational data is referenced from rpc-leafref} { + ly_cmd "-t rpc -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_rpc.xml" +} {} + +test data_operational_rpc_reply_leafref {--operational data is referenced from rpc-leafref output} { + ly_cmd "-t reply -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_rpc_reply.xml" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_present.test b/tests/yanglint/non-interactive/data_present.test new file mode 100644 index 0000000..81aac14 --- /dev/null +++ b/tests/yanglint/non-interactive/data_present.test @@ -0,0 +1,25 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data + +test data_present_via_mandatory {validation of mandatory-stmt will pass only with the --present} { + set mods "$mdir/modleaf.yang $mdir/modmandatory.yang" + ly_cmd_err "$ddir/modleaf.xml $mods" "Mandatory node \"lft\" instance does not exist." + ly_cmd "-e $ddir/modleaf.xml $mods" +} {} + +test data_present_merge {validation with --present and --merge} { + set mods "$mdir/modleaf.yang $mdir/modmandatory.yang $mdir/moddefault.yang" + set data "$ddir/modleaf.xml $ddir/moddefault.xml" + ly_cmd_err "-m $data $mods" "Mandatory node \"lft\" instance does not exist." + ly_cmd "-m -e $data $mods" +} {} + +test data_present_merge_invalid {using --present and --merge but data are invalid} { + set mods "$mdir/modleaf.yang $mdir/modmandatory.yang" + set data "$ddir/modleaf.xml $ddir/modmandatory_invalid.xml" + ly_cmd_err "-e -m $data $mods" "Mandatory node \"lft\" instance does not exist." +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_type.test b/tests/yanglint/non-interactive/data_type.test new file mode 100644 index 0000000..e3691d7 --- /dev/null +++ b/tests/yanglint/non-interactive/data_type.test @@ -0,0 +1,107 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" +set ddir "$::env(TESTS_DIR)/data" +set modnc "$mdir/ietf-netconf@2011-06-01.yang" + +test data_type_data {data --type data} { + ly_cmd "-t data $mdir/modconfig.yang $ddir/modconfig.xml" +} {} + +test data_type_config {data --type config} { + ly_cmd_err "-t config $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "-t config $mdir/modconfig.yang $ddir/modconfig2.xml" +} {} + +test data_type_get {data --type get} { + ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "-t get $mdir/modleafref.yang $ddir/modleafref2.xml" +} {} + +test data_type_getconfig_no_state {No state node for data --type getconfig} { + ly_cmd_err "-t getconfig $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "-t getconfig $mdir/modconfig.yang $ddir/modconfig2.xml" +} {} + +test data_type_getconfig_parse_only {No validation performed for data --type getconfig} { + ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "-t getconfig $mdir/modleafref.yang $ddir/modleafref2.xml" +} {} + +test data_type_edit_no_state {No state node for data --type edit} { + ly_cmd_err "-t edit $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\"" + ly_cmd "-t edit $mdir/modconfig.yang $ddir/modconfig2.xml" +} {} + +test data_type_edit_parse_only {No validation performed for data --type edit} { + ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value" + ly_cmd "-t edit $mdir/modleafref.yang $ddir/modleafref2.xml" +} {} + +test data_type_rpc {Validation of rpc-statement by data --type rpc} { + ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "-t rpc $mdir/modrpc.yang $ddir/modrpc.xml" +} {} + +test data_type_rpc_nc {Validation of rpc-statement by data --type nc-rpc} { + ly_cmd_err "-t nc-rpc $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope" + ly_cmd "-t nc-rpc $modnc $mdir/modrpc.yang $ddir/modrpc_nc.xml" +} {} + +test data_type_rpc_reply {Validation of rpc-reply by data --type reply} { + ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node." + ly_cmd_wrn "-t reply -R $ddir/modrpc.xml $mdir/modrpc.yang $ddir/modrpc_reply.xml" "needed only for NETCONF" + ly_cmd "-t reply $mdir/modrpc.yang $ddir/modrpc_reply.xml" +} {} + +test data_type_rpc_reply_nc {Validation of rpc-reply by data --type nc-reply} { + set err1 "Missing NETCONF <rpc-reply> envelope" + ly_cmd_err "-t nc-reply -R $ddir/modrpc_nc.xml $mdir/modrpc.yang $mdir/modleaf.yang $ddir/modleaf.xml" $err1 + ly_cmd_err "-t nc-reply $mdir/modrpc.yang $ddir/modrpc_reply_nc.xml" "Missing source RPC" + ly_cmd "-t nc-reply -R $ddir/modrpc_nc.xml $mdir/modrpc.yang $ddir/modrpc_reply_nc.xml" +} {} + +test data_type_rpc_action {Validation of action-statement by data --type rpc} { + ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "-t rpc -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction.xml" +} {} + +test data_type_rpc_action_nc {Validation of action-statement by data --type nc-rpc} { + ly_cmd_err "-t nc-rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope" + ly_cmd "-t nc-rpc -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction_nc.xml" +} {} + +test data_type_rpc_action_reply {Validation of action-reply by data --type reply} { + ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "-t reply -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction_reply.xml" +} {} + +test data_type_rpc_action_reply_nc {Validation of action-reply by data --type nc-reply} { + set err1 "Missing NETCONF <rpc-reply> envelope" + set err2 "operational parameter needed" + ly_cmd_err "-t nc-reply -R $ddir/modaction_nc.xml $mdir/modaction.yang $mdir/modleaf.yang $ddir/modleaf.xml" $err1 + ly_cmd_err "-t nc-reply $mdir/modaction.yang $ddir/modaction_reply_nc.xml" "Missing source RPC" + ly_cmd_err "-t nc-reply -R $ddir/modaction_nc.xml $mdir/modaction.yang $ddir/modaction_reply_nc.xml" $err2 + ly_cmd "-t nc-reply -O $ddir/modaction_ds.xml -R $ddir/modaction_nc.xml $mdir/modaction.yang $ddir/modaction_reply_nc.xml" +} {} + +test data_type_notif {Validation of notification-statement by data --type notif} { + ly_cmd_err "-t notif $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node." + ly_cmd "-t notif $mdir/modnotif.yang $ddir/modnotif2.xml" +} {} + +test data_type_notif_nc {Validation of notification-statement by data --type nc-notif} { + ly_cmd_err "-t nc-notif $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <notification> envelope" + ly_cmd "-t nc-notif $modnc $mdir/modnotif.yang $ddir/modnotif2_nc.xml" +} {} + +test data_type_notif_nested {Validation of nested-notification-statement by data --type notif} { + ly_cmd "-t notif -O $ddir/modnotif_ds.xml $mdir/modnotif.yang $ddir/modnotif.xml" +} {} + +test data_type_notif_nested_nc {Validation of nested-notification-statement by data --type nc-notif} { + ly_cmd_err "-t nc-notif $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <notification> envelope" + ly_cmd "-t nc-notif -O $ddir/modnotif_ds.xml $modnc $mdir/modnotif.yang $ddir/modnotif_nc.xml" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/data_xpath.test b/tests/yanglint/non-interactive/data_xpath.test new file mode 100644 index 0000000..1d96106 --- /dev/null +++ b/tests/yanglint/non-interactive/data_xpath.test @@ -0,0 +1,42 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mod "$::env(YANG_MODULES_DIR)/moddatanodes.yang" +set data "$::env(TESTS_DIR)/data/moddatanodes.xml" + +test data_xpath_empty {--data-path to missing node} { + ly_cmd "-E /moddatanodes:dnc/mis $mod $data" "Empty" +} {} + +test data_xpath_leaf {--xpath to leaf node} { + ly_cmd "-E /moddatanodes:dnc/lf $mod $data" "leaf \"lf\" \\(value: \"x\"\\)" +} {} + +test data_xpath_leaflist {--xpath to leaf-list node} { + set r1 "leaf-list \"lfl\" \\(value: \"1\"\\)" + set r2 "leaf-list \"lfl\" \\(value: \"2\"\\)" + ly_cmd "-E /moddatanodes:dnc/lfl $mod $data" "$r1\n $r2" +} {} + +test data_xpath_list {--xpath to list} { + set r1 "list \"lt\" \\(\"kalf\": \"ka1\"; \"kblf\": \"kb1\";\\)" + set r2 "list \"lt\" \\(\"kalf\": \"ka2\"; \"kblf\": \"kb2\";\\)" + ly_cmd "-E /moddatanodes:dnc/con/lt $mod $data" "$r1\n $r2" +} {} + +test data_xpath_container {--xpath to container} { + ly_cmd "-E /moddatanodes:dnc/con $mod $data" "container \"con\"" +} {} + +test data_xpath_wrong_path {--xpath to a non-existent node} { + ly_cmd_err "-E /moddatanodes:dnc/wrng $mod $data" "xpath failed" +} {} + +test data_xpath_err_format {--xpath cannot be combined with --format} { + ly_cmd_err "-f xml -E /moddatanodes:dnc/lf $mod $data" "option cannot be combined" +} {} + +test data_xpath_err_default {--xpath cannot be combined with --default} { + ly_cmd_err "-d all -E /moddatanodes:dnc/lf $mod $data" "option cannot be combined" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/disabled_searchdir.test b/tests/yanglint/non-interactive/disabled_searchdir.test new file mode 100644 index 0000000..49fe13e --- /dev/null +++ b/tests/yanglint/non-interactive/disabled_searchdir.test @@ -0,0 +1,18 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $env(YANG_MODULES_DIR) + +# Test should be skipped if called by ctest. +test disable_searchdir_once {Unsuccessfully imports module due to disabled cwd searching} { +-constraints {!ctest} -body { + ly_cmd "$mdir/modimp-cwd.yang" + ly_cmd_err "-D $mdir/modimp-cwd.yang" "not found in local searchdirs" +}} + +test disable_searchdir_twice {Unsuccessfully imports module due to -D -D} { + ly_cmd "$mdir/ietf-ip.yang" + ly_cmd_err "-D -D $mdir/ietf-ip.yang" "Loading \"ietf-interfaces\" module failed." +} {} + +cleanupTests + diff --git a/tests/yanglint/non-interactive/ext_data.test b/tests/yanglint/non-interactive/ext_data.test new file mode 100644 index 0000000..d4e3c44 --- /dev/null +++ b/tests/yanglint/non-interactive/ext_data.test @@ -0,0 +1,29 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" +set ddir "$::env(TESTS_DIR)/data" + +test ext_data_schema_mount_tree {Print tree output of a model with Schema Mount} { + # mounting node lfl from modleaf.yang into modsm.yang + set out1 "--mp root.*--rw lfl/" + ly_cmd "-f tree -p $mdir -y -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang" $out1 +} {} + +test ext_data_schema_mount_tree_yanglibfile {Print tree output of a model with Schema Mount and --yang-library-file} { + # yang-library-file context contains an augment node 'alf' for modsm + set out1 "--mp root.*--rw lfl/.*--rw msa:alf?" + ly_cmd "-f tree -p $mdir -Y $ddir/modsm_ctx_main.xml -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang" $out1 +} {} + +test ext_data_schema_mount_xml {Validating and printing mounted data} { + ly_cmd "-f xml -t config -p $mdir -y -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang $ddir/modsm.xml" "</lfl>" +} {} + +test ext_data_schema_mount_xml_yanglibfile {Validating and printing mounted data with --yang-library-file} { + set yanglibfile "$ddir/modsm_ctx_main.xml" + set extdata "$ddir/modsm_ctx_ext.xml" + set out1 "</lfl>.*</alf>" + ly_cmd "-f xml -t config -p $mdir -Y $yanglibfile -x $extdata $mdir/modsm.yang $ddir/modsm2.xml" $out1 +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/extended_leafref.test b/tests/yanglint/non-interactive/extended_leafref.test new file mode 100644 index 0000000..5e1a90e --- /dev/null +++ b/tests/yanglint/non-interactive/extended_leafref.test @@ -0,0 +1,13 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) + +test extended_leafref_enabled {Valid module with --extended-leafref option} { + ly_cmd "-X $mdir/modextleafref.yang" +} {} + +test extended_leafref_disabled {Expected error if --extended-leafref is not set} { + ly_cmd_err "$mdir/modextleafref.yang" "Unexpected XPath token \"FunctionName\"" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/format.test b/tests/yanglint/non-interactive/format.test new file mode 100644 index 0000000..8df5544 --- /dev/null +++ b/tests/yanglint/non-interactive/format.test @@ -0,0 +1,72 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) +set ddir $::env(TESTS_DIR)/data +set ipv6_path "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address" + +test format_yang {} { + ly_cmd "-f yang $mdir/modleaf.yang" "leaf lfl" +} {} + +test format_yang_submodule {Print submodule in yang format} { + ly_cmd "-s modsub -f yang $mdir/modinclude.yang" "submodule modsub" +} {} + +test format_yin {} { + ly_cmd "-f yin $mdir/modleaf.yang" "<leaf name=\"lfl\">" +} {} + +test format_yin_submodule {Print submodule in yin format} { + ly_cmd "-s modsub -f yin $mdir/modinclude.yang" "<submodule name=\"modsub\"" +} {} + +test format_info {} { + ly_cmd "-f info $mdir/modleaf.yang" "status current" +} {} + +test format_tree {} { + ly_cmd "-f tree $mdir/modleaf.yang" "\\+--rw lfl" +} {} + +test format_data_xml {Print data in xml format} { + ly_cmd "-f xml $mdir/modleaf.yang $ddir/modleaf.xml" "<lfl xmlns=\"urn:yanglint:modleaf\">7</lfl>" +} {} + +test format_data_json {Print data in json format} { + ly_cmd "-f json $mdir/modleaf.yang $ddir/modleaf.xml" "{\n \"modleaf:lfl\": 7\n}" +} {} + +test format_data_lyb_err {Printing in LYB format: expect error due to missing parameter} { + ly_cmd_err "-f lyb $mdir/modleaf.yang $ddir/modleaf.xml" "The LYB format requires the -o" +} {} + +test format_tree_submodule {Print submodule in tree format} { + ly_cmd "-s modsub -f tree $mdir/modinclude.yang" "submodule: modsub" +} {} + +test format_tree_path {Print subtree in tree format} { + ly_cmd "-f tree -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address.*\\+--rw prefix-length" +} {} + +test format_tree_path_single_node {Print node in tree format} { + ly_cmd "-f tree -q -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address\\* \\\[ip\\\]$" +} {} + +test format_tree_path_single_node_line_length {Print node in the tree format and limit row size} { + ly_cmd "-f tree -L 20 -q -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address\\*\n *\\\[ip\\\]$" +} {} + +test format_feature_param_one_module {Show features for one module} { + ly_cmd "-f feature-param $mdir/ietf-ip.yang" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" -ex +} {} + +test format_feature_param_more_modules {Show a mix of modules with and without features} { + + set features " -F modfeature:ftr1,ftr2\ +-F modleaf:\ +-F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" + + ly_cmd "-f feature-param $mdir/modfeature.yang $mdir/modleaf.yang $mdir/ietf-ip.yang" $features -ex +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/list.test b/tests/yanglint/non-interactive/list.test new file mode 100644 index 0000000..626d9a1 --- /dev/null +++ b/tests/yanglint/non-interactive/list.test @@ -0,0 +1,26 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] +namespace import uti::regex_xml_elements uti::regex_json_pairs + +set modules {ietf-yang-library ietf-inet-types} + +test list_basic {} { + ly_cmd "-l" "ietf-yang-types" +} {} + +test list_format_xml {list --format xml} { + ly_cmd "-y -f xml -l" [regex_xml_elements $modules "name"] +} {} + +test list_format_json {list --format json} { + ly_cmd "-y -f json -l" [regex_json_pairs $modules "name"] +} {} + +test list_ietf_yang_library {Error due to missing ietf-yang-library} { + ly_cmd_err "-f xml -l" "Module \"ietf-yang-library\" is not implemented." +} {} + +test list_bad_format {Error due to bad format} { + ly_cmd_err "-f csv -l" "Unknown output format csv" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/ly.tcl b/tests/yanglint/non-interactive/ly.tcl new file mode 100644 index 0000000..f6bb2c7 --- /dev/null +++ b/tests/yanglint/non-interactive/ly.tcl @@ -0,0 +1,8 @@ +# @brief The main source of functions and variables for testing yanglint in the non-interactive mode. + +# For testing yanglint. +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/common.tcl" : "../common.tcl"}] +# For testing any non-interactive tool. +source "$::env(TESTS_DIR)/../tool_ni.tcl" + +# The script continues by defining variables and functions specific to the non-interactive yanglint tool. diff --git a/tests/yanglint/non-interactive/make_implemented.test b/tests/yanglint/non-interactive/make_implemented.test new file mode 100644 index 0000000..40cead9 --- /dev/null +++ b/tests/yanglint/non-interactive/make_implemented.test @@ -0,0 +1,17 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $::env(YANG_MODULES_DIR) + +test make_impl_no_set {Import while --make-implemented is not set} { + ly_cmd "-l $mdir/modleafref.yang" "I modleafref\n.*I modleaf" +} {} + +test make_impl_set_once {--make-implemented} { + ly_cmd "-l -i $mdir/modmust.yang" "I modmust\n.*I modleaf" +} {} + +test make_impl_set_twice {-i -i} { + ly_cmd "-l -i -i $mdir/modimp-type.yang" "I modimp-type\n.*I modtypedef" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/modcwd.yang b/tests/yanglint/non-interactive/modcwd.yang new file mode 100644 index 0000000..db33e73 --- /dev/null +++ b/tests/yanglint/non-interactive/modcwd.yang @@ -0,0 +1,4 @@ +module modcwd { + namespace "urn:yanglint:modcwd"; + prefix mc; +} diff --git a/tests/yanglint/non-interactive/path.test b/tests/yanglint/non-interactive/path.test new file mode 100644 index 0000000..bf915ff --- /dev/null +++ b/tests/yanglint/non-interactive/path.test @@ -0,0 +1,9 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir $env(YANG_MODULES_DIR) + +test path_basic {} { + ly_cmd "-p $::env(TESTS_DIR)/data $::env(YANG_MODULES_DIR)/modimp-path.yang" +} {} + +cleanupTests diff --git a/tests/yanglint/non-interactive/yang_library_file.test b/tests/yanglint/non-interactive/yang_library_file.test new file mode 100644 index 0000000..bd95978 --- /dev/null +++ b/tests/yanglint/non-interactive/yang_library_file.test @@ -0,0 +1,18 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}] + +set mdir "$::env(YANG_MODULES_DIR)" +set ddir "$::env(TESTS_DIR)/data" + +test ylf_list {apply --yang-library-file and check result by --list} { + ly_cmd "-Y $ddir/modimp_type_ctx.xml -p $mdir -l" "I modimp-type.*i modtypedef" +} {} + +test ylf_make_implemented {apply --yang-library-file and --make-implemented} { + ly_cmd "-Y $ddir/modimp_type_ctx.xml -p $mdir -i -i -l" "I modimp-type.*I modtypedef" +} {} + +test ylf_augment_ctx {Setup context by yang-library-file and augment module} { + ly_cmd "-Y $ddir/modconfig_ctx.xml -p $mdir -f tree $mdir/modconfig.yang $mdir/modconfig-augment.yang" "mca:alf" +} {} + +cleanupTests diff --git a/tests/yangre/CMakeLists.txt b/tests/yangre/CMakeLists.txt new file mode 100644 index 0000000..ce5b39b --- /dev/null +++ b/tests/yangre/CMakeLists.txt @@ -0,0 +1,8 @@ +find_program(PATH_TCLSH NAMES tclsh) +if(NOT PATH_TCLSH) + message(WARNING "'tclsh' not found! The yangre test will not be available.") +else() + add_test(NAME "yangre" COMMAND "tclsh" "${CMAKE_CURRENT_SOURCE_DIR}/all.tcl") + set_property(TEST "yangre" APPEND PROPERTY ENVIRONMENT "TESTS_DIR=${CMAKE_CURRENT_SOURCE_DIR}") + set_property(TEST "yangre" APPEND PROPERTY ENVIRONMENT "YANGRE=${PROJECT_BINARY_DIR}") +endif() diff --git a/tests/yangre/all.tcl b/tests/yangre/all.tcl new file mode 100644 index 0000000..f00563f --- /dev/null +++ b/tests/yangre/all.tcl @@ -0,0 +1,15 @@ +package require tcltest + +# Hook to determine if any of the tests failed. +# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0. +proc tcltest::cleanupTestsHook {} { + variable numTests + set ::exitCode [expr {$numTests(Failed) > 0}] +} + +if {[info exists ::env(TESTS_DIR)]} { + tcltest::configure -testdir "$env(TESTS_DIR)" +} + +tcltest::runAllTests +exit $exitCode diff --git a/tests/yangre/arg.test b/tests/yangre/arg.test new file mode 100644 index 0000000..821aad1 --- /dev/null +++ b/tests/yangre/arg.test @@ -0,0 +1,19 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}] + +test arg_empty {Missing arguments} { + ly_cmd_err "" "missing <string> parameter to process" +} {} + +test arg_wrong {Wrong argument} { + ly_cmd_err "-j" "invalid option" +} {} + +test arg_help {Print help} { + ly_cmd "-h" "Usage:" +} {} + +test arg_version {Print version} { + ly_cmd "-v" "yangre" +} {} + +cleanupTests diff --git a/tests/yangre/file.test b/tests/yangre/file.test new file mode 100644 index 0000000..80ea3f6 --- /dev/null +++ b/tests/yangre/file.test @@ -0,0 +1,37 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}] + +set fdir "$::env(TESTS_DIR)/files" + +test file_empty {file is empty} { + ly_cmd "-f $fdir/empty.txt" +} {} + +test file_empty_str {<string> is empty} { + ly_cmd "-f $fdir/empty_str.txt" +} {} + +test file_empty_str_err {empty <string> is not allowed} { + ly_cmd_err "-f $fdir/empty_str_err.txt" "does not conform" +} {} + +test file_one_pattern {one pattern in the file} { + ly_cmd "-f $fdir/one_pattern.txt" +} {} + +test file_two_patterns {two patterns in the file} { + ly_cmd "-f $fdir/two_patterns.txt" +} {} + +test file_two_patterns_err {two patterns and the <string> is wrong} { + ly_cmd_err "-f $fdir/two_patterns_err.txt" "does not conform" +} {} + +test file_two_patterns_invert_match {one pattern is inverted} { + ly_cmd "-f $fdir/two_patterns_invert_match.txt" +} {} + +test file_two_patterns_invert_match_err {one pattern is inverted and the <string> is wrong} { + ly_cmd_err "-f $fdir/two_patterns_invert_match_err.txt" "does not conform to inverted" +} {} + +cleanupTests diff --git a/tests/yangre/files/empty.txt b/tests/yangre/files/empty.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/yangre/files/empty.txt diff --git a/tests/yangre/files/empty_str.txt b/tests/yangre/files/empty_str.txt new file mode 100644 index 0000000..bf9b1b5 --- /dev/null +++ b/tests/yangre/files/empty_str.txt @@ -0,0 +1,2 @@ +"[0-9a-fA-F]*" + diff --git a/tests/yangre/files/empty_str_err.txt b/tests/yangre/files/empty_str_err.txt new file mode 100644 index 0000000..f48a15d --- /dev/null +++ b/tests/yangre/files/empty_str_err.txt @@ -0,0 +1,2 @@ +"[0-9a-fA-F]+" + diff --git a/tests/yangre/files/one_pattern.txt b/tests/yangre/files/one_pattern.txt new file mode 100644 index 0000000..cf9acc5 --- /dev/null +++ b/tests/yangre/files/one_pattern.txt @@ -0,0 +1,3 @@ +"[0-9a-fA-F]*" + +1F diff --git a/tests/yangre/files/two_patterns.txt b/tests/yangre/files/two_patterns.txt new file mode 100644 index 0000000..7d04b2c --- /dev/null +++ b/tests/yangre/files/two_patterns.txt @@ -0,0 +1,4 @@ +"[0-9a-fA-F]*" +'[a-zA-Z0-9\-_.]*' + +1F diff --git a/tests/yangre/files/two_patterns_err.txt b/tests/yangre/files/two_patterns_err.txt new file mode 100644 index 0000000..78f9878 --- /dev/null +++ b/tests/yangre/files/two_patterns_err.txt @@ -0,0 +1,4 @@ +"[0-9a-fA-F]*" +'[a-zA-Z0-9\-_.]*' + +@!@ diff --git a/tests/yangre/files/two_patterns_invert_match.txt b/tests/yangre/files/two_patterns_invert_match.txt new file mode 100644 index 0000000..ffbd835 --- /dev/null +++ b/tests/yangre/files/two_patterns_invert_match.txt @@ -0,0 +1,4 @@ +"[a-z]*" + '[a-f]*' + +gh diff --git a/tests/yangre/files/two_patterns_invert_match_err.txt b/tests/yangre/files/two_patterns_invert_match_err.txt new file mode 100644 index 0000000..f182aab --- /dev/null +++ b/tests/yangre/files/two_patterns_invert_match_err.txt @@ -0,0 +1,4 @@ +"[a-z]*" + '[a-f]*' + +ab diff --git a/tests/yangre/invert_match.test b/tests/yangre/invert_match.test new file mode 100644 index 0000000..707ca9d --- /dev/null +++ b/tests/yangre/invert_match.test @@ -0,0 +1,28 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}] + +test invert_match_from_help1 {Test the first pattern from help via invert match} { + ly_cmd_err {-p {"[0-9a-fA-F]*"} -i {1F}} "not conform to inverted" + ly_cmd {-p {"[0-9a-fA-F]*"} -i {GUN}} +} {} + +test invert_match_from_help2 {Test the second pattern from help via invert match} { + ly_cmd_err {-p {'[a-zA-Z0-9\-_.]*'} -i {a-b}} "not conform to inverted" + ly_cmd {-p {'[a-zA-Z0-9\-_.]*'} -i {%@}} +} {} + +test invert_match_from_help3 {Test the third pattern from help via invert match} { + ly_cmd_err {-p {[xX][mM][lL].*} -i {xml-encoding}} "not conform to inverted" + ly_cmd {-p {[xX][mM][lL].*} -i {json}} +} {} + +test invert_match_three_at_once {Test three inverted patterns and once} { + ly_cmd_err {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {xml}} "not conform to inverted" + ly_cmd {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {%@}} +} {} + +test invert_match_second_is_not {Test three patterns but the second one is not inverted} { + ly_cmd_err {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {o_O}} "not conform to inverted" + ly_cmd {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -p {[xX][mM][lL].*} -i {o_O}} +} {} + +cleanupTests diff --git a/tests/yangre/ly.tcl b/tests/yangre/ly.tcl new file mode 100644 index 0000000..3bb62b5 --- /dev/null +++ b/tests/yangre/ly.tcl @@ -0,0 +1,17 @@ +# @brief The main source of functions and variables for testing yangre. + +package require tcltest +namespace import ::tcltest::test ::tcltest::cleanupTests + +if { ![info exists ::env(TESTS_DIR)] } { + # the script is not run via 'ctest' so paths must be set + set ::env(TESTS_DIR) "./" + set TUT_PATH "../../build" +} else { + # cmake (ctest) already sets ::env variables + set TUT_PATH $::env(YANGRE) +} +set TUT_NAME "yangre" +source "$::env(TESTS_DIR)/../tool_ni.tcl" + +# The script continues by defining variables and functions specific to the yangre tool. diff --git a/tests/yangre/pattern.test b/tests/yangre/pattern.test new file mode 100644 index 0000000..45b7e3b --- /dev/null +++ b/tests/yangre/pattern.test @@ -0,0 +1,19 @@ +source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}] + +test pattern_from_help1 {Test the first pattern from help} { + ly_cmd {-p {"[0-9a-fA-F]*"} {1F}} +} {} + +test pattern_from_help2 {Test the second pattern from help} { + ly_cmd {-p {'[a-zA-Z0-9\-_.]*'} {a-b}} +} {} + +test pattern_from_help3 {Test the third pattern from help} { + ly_cmd {-p {[xX][mM][lL].*} {xml-encoding}} +} {} + +test pattern_three_at_once {Test three patterns and once} { + ly_cmd {-p {"[0-9a-zA-Z]*"} -p {'[a-zA-Z0-9\-_.]*'} -p {[xX][mM][lL].*} {xml}} +} {} + +cleanupTests |