yanglint TEST moved to the tests directory
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