diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 04:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 04:23:18 +0000 |
commit | b90161ccd3b318f3314a23cb10c387651ad35831 (patch) | |
tree | a47dc087160299ce02d728cbf031d84af6281537 /tests/yanglint | |
parent | Adding upstream version 2.1.30. (diff) | |
download | libyang2-upstream.tar.xz libyang2-upstream.zip |
Adding upstream version 2.1.148.upstream/2.1.148upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
116 files changed, 5512 insertions, 0 deletions
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/debug.test b/tests/yanglint/non-interactive/debug.test new file mode 100644 index 0000000..4543acb --- /dev/null +++ b/tests/yanglint/non-interactive/debug.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) + +test debug_dict {Check debug message DICT} { +-constraints {[ly_opt_exists "-G"]} -body { + ly_cmd_wrn "-V -V -G dict $mdir/modleaf.yang" "DICT" +}} + +test debug_xpath {Check debug message XPATH} { +-constraints {[ly_opt_exists "-G"]} -body { + ly_cmd_wrn "-V -V -G xpath $mdir/modmust.yang" "XPATH" +}} + +test debug_dep_sets {Check debug message DEPSETS} { +-constraints {[ly_opt_exists "-G"]} -body { + ly_cmd_wrn "-V -V -G dep-sets $mdir/modleaf.yang" "DEPSETS" +}} + +test debug_depsets_xpath {Check debug message DEPSETS and XPATH} { +-constraints {[ly_opt_exists "-G"]} -body { + ly_cmd_wrn "-V -V -G dep-sets,xpath $mdir/modmust.yang" "DEPSETS.*XPATH" +}} + +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 |