mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Add a test harness for interactive behavior
Add a test harness that uses `expect` to drive Fish to test interactive behavior. Include some tests for `read`.
This commit is contained in:
parent
efb1467e4e
commit
190712d4b0
11 changed files with 432 additions and 0 deletions
43
tests/interactive.config
Normal file
43
tests/interactive.config
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# vim: set filetype=fish sw=4 ts=4 et:
|
||||||
|
|
||||||
|
set -g prompt_counter 1
|
||||||
|
set -g prompt_counter_incr 0
|
||||||
|
function fish_prompt
|
||||||
|
echo "prompt $prompt_counter>"
|
||||||
|
if test $prompt_counter_incr -eq 1
|
||||||
|
set -g prompt_counter (expr $prompt_counter + 1)
|
||||||
|
set -g prompt_counter_incr 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function fish_prompt_event --on-event fish_prompt
|
||||||
|
set -g prompt_counter_incr 1
|
||||||
|
end
|
||||||
|
|
||||||
|
set -g fish_greeting ''
|
||||||
|
|
||||||
|
function _quote
|
||||||
|
echo \'(echo $argv[1] | sed -e 's/\'/\\\\\'/g')\'
|
||||||
|
end
|
||||||
|
|
||||||
|
function _echo_var --no-scope-shadowing -d '_echo_var varname [guardval]'
|
||||||
|
if set -q argv[2]; echo "@GUARD:$argv[2]@"; end
|
||||||
|
set -l var $argv[1]
|
||||||
|
switch (count $$var)
|
||||||
|
case 0
|
||||||
|
echo "\$$var has no value"
|
||||||
|
case 1
|
||||||
|
set -l IFS ''
|
||||||
|
echo "\$$var:" (_quote $$var)
|
||||||
|
case \*
|
||||||
|
echo "\$$var:"
|
||||||
|
for i in (seq (count $$var))
|
||||||
|
set -l IFS ''
|
||||||
|
echo "$i:" (_quote $$var[1][$i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if set -q argv[2]; echo "@/GUARD:$argv[2]@"; end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _marker -d '_marker string - prints @MARKER:$string@'
|
||||||
|
echo "@MARKER:$argv[1]@"
|
||||||
|
end
|
0
tests/interactive.err
Normal file
0
tests/interactive.err
Normal file
228
tests/interactive.expect.rc
Normal file
228
tests/interactive.expect.rc
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
# vim: set filetype=expect sw=4 ts=4 et:
|
||||||
|
|
||||||
|
log_user 0
|
||||||
|
log_file -noappend interactive.tmp.log
|
||||||
|
|
||||||
|
set fish ../fish
|
||||||
|
|
||||||
|
set timeout 2
|
||||||
|
|
||||||
|
set send_human {.05 .1 5 .02 .2}
|
||||||
|
|
||||||
|
proc abort {{msg "aborting"}} {
|
||||||
|
error $msg
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# # Debug logging
|
||||||
|
|
||||||
|
set loglevel debug ;# none, info, debug
|
||||||
|
|
||||||
|
proc log_info string {
|
||||||
|
global loglevel
|
||||||
|
switch $loglevel {
|
||||||
|
info -
|
||||||
|
debug {
|
||||||
|
send_log "\[INFO] $string\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc log_debug string {
|
||||||
|
global loglevel
|
||||||
|
switch $loglevel {
|
||||||
|
debug {
|
||||||
|
send_log "\[DEBUG] $string\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
|
||||||
|
set prompt_counter 1
|
||||||
|
# expect_prompt takes an argument list like `expect` does.
|
||||||
|
# It supports a special pattern "unmatched" that is run if no
|
||||||
|
# other provided patterns match.
|
||||||
|
proc expect_prompt {args} {
|
||||||
|
global prompt_counter
|
||||||
|
upvar expect_out expect_out
|
||||||
|
set prompt_pat [list -re "(?:\\r\\n?|^)prompt $prompt_counter>(?:$|\r)"]
|
||||||
|
if {[llength $args] == 1 && [string match "\n*" $args]} {
|
||||||
|
set args [join $args]
|
||||||
|
}
|
||||||
|
set prompt_action ""
|
||||||
|
set expargs {}
|
||||||
|
upvar expect_out up_expect_out
|
||||||
|
set state "firstarg"
|
||||||
|
foreach arg $args {
|
||||||
|
switch $state {
|
||||||
|
"pat" {
|
||||||
|
lappend expargs $arg
|
||||||
|
set state "action"
|
||||||
|
}
|
||||||
|
"action" {
|
||||||
|
lappend expargs [subst -nocommands {
|
||||||
|
log_debug "matched extra pattern to expect_prompt: [quote \$expect_out(0,string)]"
|
||||||
|
if {\$matched} {
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
set matched yes
|
||||||
|
uplevel 1 {$arg}
|
||||||
|
exp_continue
|
||||||
|
}]
|
||||||
|
set state "firstarg"
|
||||||
|
}
|
||||||
|
"firstarg" -
|
||||||
|
"arg" {
|
||||||
|
if {$arg eq "unmatched" && $state eq "firstarg"} {
|
||||||
|
set state "unmatched"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lappend expargs $arg
|
||||||
|
switch $arg {
|
||||||
|
-gl -
|
||||||
|
-re -
|
||||||
|
-ex {
|
||||||
|
set state "pat"
|
||||||
|
}
|
||||||
|
-i -
|
||||||
|
-timeout {
|
||||||
|
set state "flagarg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"flagarg" {
|
||||||
|
lappend expargs $arg
|
||||||
|
set state "arg"
|
||||||
|
}
|
||||||
|
"unmatched" {
|
||||||
|
if {$prompt_action ne ""} continue
|
||||||
|
set prompt_action [subst -nocommands {
|
||||||
|
if {!\$matched} {
|
||||||
|
uplevel 1 {$arg}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
error "BUG: non-exhaustive switch in expect_prompt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if {[llength $expargs] > 0} {
|
||||||
|
log_info "expecting prompt $prompt_counter + patterns"
|
||||||
|
} else {
|
||||||
|
log_info "expecting prompt $prompt_counter"
|
||||||
|
}
|
||||||
|
set expargs [concat $prompt_pat [list $prompt_action] $expargs]
|
||||||
|
set matched no
|
||||||
|
expect {*}$expargs
|
||||||
|
incr prompt_counter
|
||||||
|
}
|
||||||
|
|
||||||
|
trace add execution expect {enter leave} trace_expect
|
||||||
|
proc trace_expect {cmd args} {
|
||||||
|
if {[lindex $cmd 1] eq "*" && [llength $cmd] == 3} {
|
||||||
|
# it's an `expect "*" {..}` command, don't log it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch [lindex $args end] {
|
||||||
|
enter {
|
||||||
|
log_debug "entering expect"
|
||||||
|
uplevel {set expect_out(buffer) {}}
|
||||||
|
}
|
||||||
|
leave {
|
||||||
|
set code [lindex $args 0]
|
||||||
|
if {$code == 0} {
|
||||||
|
log_debug "expect finished: [quote [uplevel set expect_out(buffer)]]"
|
||||||
|
} else {
|
||||||
|
log_debug "expect returned code $code"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trace add execution exp_continue enter trace_exp_continue
|
||||||
|
proc trace_exp_continue {cmd op} {
|
||||||
|
log_debug "exp_continue after consuming: [quote [uplevel set expect_out(buffer)]]"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
trace add execution send enter trace_send
|
||||||
|
proc trace_send {cmd op} {
|
||||||
|
log_info "[quote $cmd]"
|
||||||
|
}
|
||||||
|
|
||||||
|
trace add execution spawn {enter leave} trace_spawn
|
||||||
|
proc trace_spawn {cmd args} {
|
||||||
|
switch [lindex $args end] {
|
||||||
|
enter {
|
||||||
|
log_info "[quote $cmd]"
|
||||||
|
}
|
||||||
|
leave {
|
||||||
|
log_debug "[quote $cmd]: code [lindex $args 0], result [lindex $args 1]"
|
||||||
|
expect_before {
|
||||||
|
timeout {
|
||||||
|
expect "*" {
|
||||||
|
log_debug "timeout; buffer=[quote $expect_out(buffer)]"
|
||||||
|
}
|
||||||
|
abort "timeout"
|
||||||
|
}
|
||||||
|
eof {
|
||||||
|
log_debug "eof; buffer=[quote $expect_out(buffer)]"
|
||||||
|
abort "eof"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc quote string {
|
||||||
|
set map {
|
||||||
|
\\ \\\\
|
||||||
|
\r \\r
|
||||||
|
\n \\n
|
||||||
|
\t \\t
|
||||||
|
\a \\a
|
||||||
|
\v \\v
|
||||||
|
\x1b \\e
|
||||||
|
\x7f \\x7f
|
||||||
|
}
|
||||||
|
for {set x 0} {$x<32} {incr x} {
|
||||||
|
lappend map [format %c $x] [format \\x%02x $x]
|
||||||
|
}
|
||||||
|
string map $map $string
|
||||||
|
}
|
||||||
|
|
||||||
|
proc send_line args {
|
||||||
|
if {[llength $args] > 0} {
|
||||||
|
lset args end [lindex $args end]\r
|
||||||
|
}
|
||||||
|
send {*}$args
|
||||||
|
}
|
||||||
|
|
||||||
|
proc rand_int {low hi} {
|
||||||
|
expr {entier(rand() * ($hi-$low))+$low}
|
||||||
|
}
|
||||||
|
|
||||||
|
# prints the output of `_echo_var $name` (defined in interactive.config)
|
||||||
|
proc print_var_contents name {
|
||||||
|
# generate a random "guard" so we know where to stop matching
|
||||||
|
# the randomness is to defend against the variable value containing the guard
|
||||||
|
set guard [rand_int 1000000000 9999999999]
|
||||||
|
|
||||||
|
# print the variable
|
||||||
|
log_info "get_var_contents: $$name"
|
||||||
|
send_line "_echo_var $name $guard"
|
||||||
|
|
||||||
|
# match on the results
|
||||||
|
set pat {\r\n@GUARD:$guard@\r\n(.*)\r\n@/GUARD:$guard@\r\n}
|
||||||
|
set matched false
|
||||||
|
expect_prompt -re [subst -nocommands -nobackslashes $pat] {
|
||||||
|
log_info "get_var_contents: result: [quote $expect_out(1,string)]"
|
||||||
|
puts $expect_out(1,string)
|
||||||
|
exp_continue
|
||||||
|
} unmatched {
|
||||||
|
log_debug "unmatched: [quote $expect_out(buffer)]"
|
||||||
|
abort "Didn't match output for variable $$name"
|
||||||
|
}
|
||||||
|
}
|
44
tests/interactive.fish
Normal file
44
tests/interactive.fish
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/local/bin/fish
|
||||||
|
#
|
||||||
|
# Interactive tests using `expect`
|
||||||
|
|
||||||
|
function die
|
||||||
|
echo $argv[1] >&2
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in *.expect
|
||||||
|
rm -Rf tmp.interactive.config; or die "Couldn't remove tmp.interactive.config"
|
||||||
|
mkdir -p tmp.interactive.config/fish; or die "Couldn't create tmp.interactive.config/fish"
|
||||||
|
cp interactive.config tmp.interactive.config/fish/config.fish; or die "Couldn't create tmp.interactive.config/fish/config.fish"
|
||||||
|
|
||||||
|
begin
|
||||||
|
set -lx XDG_CONFIG_HOME $PWD/tmp.interactive.config
|
||||||
|
set -lx TERM dumb
|
||||||
|
expect -n -c 'source interactive.expect.rc' -f $i >tmp.out ^tmp.err
|
||||||
|
end
|
||||||
|
set -l tmp_status $status
|
||||||
|
set res ok
|
||||||
|
if not diff tmp.out $i.out >/dev/null
|
||||||
|
set res fail
|
||||||
|
echo "Output differs for file $i. Diff follows:"
|
||||||
|
diff -u tmp.out $i.out
|
||||||
|
end
|
||||||
|
|
||||||
|
if not diff tmp.err $i.err >/dev/null
|
||||||
|
set res fail
|
||||||
|
echo "Error output differs for file $i. Diff follows:"
|
||||||
|
diff -u tmp.err $i.err
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $tmp_status != (cat $i.status)
|
||||||
|
set res fail
|
||||||
|
echo "Exit status differs for file $i."
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $res = ok
|
||||||
|
echo "File $i tested ok"
|
||||||
|
else
|
||||||
|
echo "File $i failed tests"
|
||||||
|
end
|
||||||
|
end
|
1
tests/interactive.out
Normal file
1
tests/interactive.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
File read.expect tested ok
|
1
tests/interactive.status
Normal file
1
tests/interactive.status
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
77
tests/read.expect
Normal file
77
tests/read.expect
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# vim: set filetype=expect:
|
||||||
|
|
||||||
|
proc expect_read_prompt {} {
|
||||||
|
expect -re "\\r\\n?read> $"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc expect_marker {text} {
|
||||||
|
expect_prompt -re "\\r\\n@MARKER:$text@\\r\\n" {} unmatched {
|
||||||
|
abort "Couldn't find marker line '$text'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn $fish
|
||||||
|
|
||||||
|
expect_prompt
|
||||||
|
|
||||||
|
# read
|
||||||
|
|
||||||
|
send_line "read foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line "text"
|
||||||
|
expect_prompt
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
send_line "read foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line "again\r_marker 1"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 1
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
send_line "read foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line -h "bar\r_marker 2"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 2
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
# read -n
|
||||||
|
|
||||||
|
send_line "read -n 3 foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line -h "123_marker 3"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 3
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
send_line "read -n 3 foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line "456_marker 4"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 4
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
send_line "read -n 12 foo bar"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line "hello world!_marker 5"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 5
|
||||||
|
print_var_contents foo
|
||||||
|
print_var_contents bar
|
||||||
|
|
||||||
|
send_line "bind ` 'commandline -i test'`"
|
||||||
|
expect_prompt
|
||||||
|
send_line "read -n 4 foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line "te`_marker 6"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 6
|
||||||
|
print_var_contents foo
|
||||||
|
|
||||||
|
send_line "read -n 4 foo"
|
||||||
|
expect_read_prompt
|
||||||
|
send_line -h "12`_marker 7"
|
||||||
|
expect_prompt
|
||||||
|
expect_marker 7
|
||||||
|
print_var_contents foo
|
0
tests/read.expect.err
Normal file
0
tests/read.expect.err
Normal file
9
tests/read.expect.out
Normal file
9
tests/read.expect.out
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
$foo: 'text'
|
||||||
|
$foo: 'again'
|
||||||
|
$foo: 'bar'
|
||||||
|
$foo: '123'
|
||||||
|
$foo: '456'
|
||||||
|
$foo: 'hello'
|
||||||
|
$bar: 'world!'
|
||||||
|
$foo: 'tete'
|
||||||
|
$foo: '12te'
|
1
tests/read.expect.status
Normal file
1
tests/read.expect.status
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
|
@ -37,6 +37,34 @@ if [ "$argv" != '-n' ]
|
||||||
echo "Profiling failed"
|
echo "Profiling failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
echo "Testing interactive functionality"
|
||||||
|
# bug: `fish -n` throws errors on fishscript functions that don't shadow real commands,
|
||||||
|
# so we can't use `type -q expect` here.
|
||||||
|
if command -s expect >/dev/null
|
||||||
|
# we have expect, we can run the interactive tests
|
||||||
|
begin
|
||||||
|
../fish -n ./interactive.fish ^interactive.tmp.err
|
||||||
|
../fish ./interactive.fish ^^interactive.tmp.err
|
||||||
|
end | tee interactive.tmp.out
|
||||||
|
set -l tmp_status $status
|
||||||
|
if not diff interactive.tmp.out interactive.out >/dev/null
|
||||||
|
set res fail
|
||||||
|
echo "Output differs for file interactive.fish"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not diff interactive.tmp.err interactive.err >/dev/null
|
||||||
|
set res fail
|
||||||
|
echo "Error output differs for file interactive.fish"
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $tmp_status -ne (cat interactive.status)
|
||||||
|
set res fail
|
||||||
|
echo "Exit status differs for file interactive.fish"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo "Tests disabled: `expect` not found"
|
||||||
|
end
|
||||||
|
|
||||||
if test $res = ok
|
if test $res = ok
|
||||||
echo "File test.fish tested ok"
|
echo "File test.fish tested ok"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
Loading…
Reference in a new issue