#!/usr/bin/env bash # Define test functions. # Create working dir as variable "dir" # The functions are somewhat wildgrown, a little too many: # - expectfn # - expecteq # - expecteof # - expecteofeq # - expecteofx # - expecteof_file # - expectwait # - expectmatch #set -e # : ${A=B} vs : ${A:=B} # colon also checks for NULL # Testfile (not including path) : ${testfile:=$(basename $0)} # Add test to this list that you dont want run # Typically add them in your site file : ${SKIPLIST:=""} # Some tests (openconfig/yang_models) just test for the cli to return a version version=4 >&2 echo "Running $testfile" # Site file, an example of this file in README.md if [ -f ./site.sh ]; then . ./site.sh if [ $? -ne 0 ]; then return -1 # skip fi # test skiplist. for f in $SKIPLIST; do if [ "$testfile" = "$f" ]; then echo "...skipped (see site.sh)" return -1 # skip fi done fi # Test number from start : ${testnr:=0} # Test number in this test testi=0 # Single test. Set by "new" testname= # Valgind memory leak check. # The values are: # 0: No valgrind check # 1: Start valgrind at every new testcase. Check result every next new # 2: Start valgrind every new backend start. Check when backend stops # 3: Start valgrind every new restconf start. Check when restconf stops # : ${valgrindtest=0} # Valgrind log file. This should be removed automatically. Note that mktemp # actually creates a file so do not call it by default #: ${valgrindfile=$(mktemp)} # If set to 0, override starting of clixon_backend in test (you bring your own) : ${BE:=1} # If BE is set, some tests have a user timeout to show which params to set # for starting a backend : ${BETIMEOUT:=10} # If set, enable debugging (of backend and restconf daemons) : ${DBG:=0} # If set to 0, override starting of clixon_resrtcong in test (you bring your own) : ${RC:=1} # Where to log restconf. Some systems may not have syslog, # eg logging to a file: RCLOG="-l f/www-data/restconf.log" : ${RCLOG:=} # Wait after daemons (backend/restconf) start. See mem.sh for valgrind if [ "$(uname -m)" = "armv7l" ]; then : ${RCWAIT:=8} else : ${RCWAIT:=2} fi # RESTCONF protocol, eg http or https : ${RCPROTO:=http} # RESTCONF error message (if not up) : ${RCERROR:="HTTP/1.1 502 Bad Gateway"} # www user (on linux typically www-data, freebsd www) # could be taken from configure : ${wwwuser:=www-data} # Parse yangmodels from https://github.com/YangModels/yang # Recommended: checkout yangmodels elsewhere in the tree and set the env # to that #: ${YANGMODELS=$(pwd)/yang} # just skip if not set # Parse yang openconfig models from https://github.com/openconfig/public #: ${OPENCONFIG=$(pwd)/public} # just skip if not set # Standard IETF RFC yang files. : ${IETFRFC=../yang/standard} #: ${IETFRFC=$YANGMODELS/standard/ietf/RFC} # Backend user BUSER=clicon # If set, unknown XML is treated as ANYDATA : ${YANG_UNKNOWN_ANYDATA:=false} # Follow the binary programs that can be parametrized (eg with valgrind) : ${clixon_cli:=clixon_cli} : ${clixon_netconf:=clixon_netconf} : ${clixon_restconf:=/www-data/clixon_restconf} : ${clixon_backend:=clixon_backend} dir=/var/tmp/$0 if [ ! -d $dir ]; then mkdir $dir fi # Some tests may set owner of testdir to something strange and quit, need # to reset to me if [ ! -G $dir ]; then u=$(whoami) sudo chown $u $dir sudo chgrp $u $dir fi # If you bring your own backend BE=0 (it is already started),the backend may # have created some files (eg unix socket) in $dir and therefore cannot # be deleted if [ $BE -ne 0 ]; then rm -rf $dir/* fi # error and exit, # arg1: expected # arg2: errmsg[optional] err(){ echo -e "\e[31m\nError in Test$testnr [$testname]:" if [ $# -gt 0 ]; then echo "Expected: $1" echo fi if [ $# -gt 1 ]; then echo "Received: $2" fi echo -e "\e[0m" echo "$ret"| od -t c > $dir/clixon-ret echo "$expect"| od -t c > $dir/clixon-expect diff $dir/clixon-expect $dir/clixon-ret exit -1 #$testnr } # Test is previous test had valgrind errors if so quit checkvalgrind(){ if [ -f $valgrindfile ]; then res=$(cat $valgrindfile | grep -e "Invalid" |awk '{print $4}' | grep -v '^0$') if [ -n "$res" ]; then >&2 cat $valgrindfile sudo rm -f $valgrindfile exit -1 fi res=$(cat $valgrindfile | grep -e "reachable" -e "lost:"|awk '{print $4}' | grep -v '^0$') if [ -n "$res" ]; then >&2 cat $valgrindfile sudo rm -f $valgrindfile exit -1 fi sudo rm -f $valgrindfile fi } # Start backend with all varargs. # If valgrindtest == 2, start valgrind start_backend(){ if [ $valgrindtest -eq 2 ]; then # Start in background since daemon version creates two traces: parent, # child. If background then only the single relevant. sudo $clixon_backend -F -D $DBG $* & else sudo $clixon_backend -D $DBG $* fi if [ $? -ne 0 ]; then err fi } stop_backend(){ sudo clixon_backend -z $* if [ $? -ne 0 ]; then err "kill backend" fi if [ $valgrindtest -eq 2 ]; then sleep 1 checkvalgrind fi sudo pkill -f clixon_backend # extra ($BUSER?) } # Wait for restconf to stop sending 502 Bad Gateway wait_backend(){ reply=$(echo ']]>]]>' | $clixon_netconf -qef $cfg 2> /dev/null) let i=0; while [[ $reply != "]]>]]>' | clixon_netconf -qef $cfg 2> /dev/null) let i++; # echo "wait_backend $i" if [ $i -ge $RCWAIT ]; then err "backend timeout $RCWAIT seconds" fi done } # Start restconf daemon # @see wait_restconf start_restconf(){ # Start in background # echo "sudo -u $wwwuser -s $clixon_restconf $RCLOG -D $DBG $*" sudo -u $wwwuser -s $clixon_restconf $RCLOG -D $DBG $* & if [ $? -ne 0 ]; then err fi } stop_restconf(){ sudo pkill -u $wwwuser -f clixon_restconf # Dont use $clixon_restoconf doesnt work in valgrind if [ $valgrindtest -eq 3 ]; then sleep 1 checkvalgrind fi } # Wait for restconf to stop sending 502 Bad Gateway # @see start_restconf wait_restconf(){ # echo "curl -kis $RCPROTO://localhost/restconf" hdr=$(curl -kis $RCPROTO://localhost/restconf) # echo "hdr:\"$hdr\"" let i=0; while [[ $hdr != *"200 OK"* ]]; do sleep 1 hdr=$(curl -kis $RCPROTO://localhost/restconf) # echo "hdr:\"$hdr\"" let i++; # echo "wait_restconf $i" if [ $i -ge $RCWAIT ]; then err "restconf timeout $RCWAIT seconds" fi done if [ $valgrindtest -eq 3 ]; then sleep 2 # some problems with valgrind fi } # Increment test number and print a nice string new(){ if [ $valgrindtest -eq 1 ]; then checkvalgrind fi testnr=`expr $testnr + 1` testi=`expr $testi + 1` testname=$1 >&2 echo "Test $testi($testnr) [$1]" } # clixon command tester. # Arguments: # - command, # - expected command return value (0 if OK) # - expected* stdout outcome, (can be many) # Example: expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "line1" "line2" # XXX: for some reason some curl commands dont work here, eg # curl -H 'Accept: application/xrd+xml' # instead use expectpart expectfn(){ cmd=$1 retval=$2 expect="$3" if [ $# = 4 ]; then expect2=$4 else expect2= fi ret=$($cmd) r=$? # echo "cmd:\"$cmd\"" # echo "retval:\"$retval\"" # echo "expect:\"$expect\"" # echo "ret:\"$ret\"" # echo "r:\"$r\"" if [ $r != $retval ]; then echo -e "\e[31m\nError ($r != $retval) in Test$testnr [$testname]:" echo -e "\e[0m:" exit -1 fi # if [ $ret -ne $retval ]; then # echo -e "\e[31m\nError in Test$testnr [$testname]:" # echo -e "\e[0m:" # exit -1 # fi # Match if both are empty string (special case) if [ -z "$ret" -a -z "$expect" ]; then return fi if [ -z "$ret" -a "$expect" = "^$" ]; then return fi # Loop over all variable args expect strings let i=0; for exp in "$@"; do if [ $i -gt 1 ]; then match=`echo $ret | grep --null -Eo "$exp"` if [ -z "$match" ]; then err "$exp" "$ret" fi fi let i++; done } # Evaluate and return # Example: expecteq $(fn arg) 0 "my return" # - evaluated expression # - expected command return value (0 if OK) # - expected stdout outcome expecteq(){ r=$? ret=$1 retval=$2 expect=$3 # echo "r:$r" # echo "ret:\"$ret\"" # echo "retval:$retval" # echo "expect:$expect" if [ $r != $retval ]; then echo -e "\e[31m\nError ($r != $retval) in Test$testnr [$testname]:" echo -e "\e[0m:" exit -1 fi if [ -z "$ret" -a -z "$expect" ]; then return fi if [[ "$ret" != "$expect" ]]; then err "$expect" "$ret" fi } # Evaluate and return # like expecteq but partial match is OK # Example: expectpart $(fn arg) 0 "my return" -- "foo" # - evaluated expression # - expected command return value (0 if OK) # - expected stdout outcome* # - the token "--not--" # - not expected stdout outcome* # Example: # expectpart "$(a-shell-cmd arg)" 0 'expected match 1' 'expected match 2' --not-- 'not expected 1' # @note need to escape \[\] expectpart(){ r=$? ret=$1 retval=$2 expect=$3 # echo "r:$r" # echo "ret:\"$ret\"" # echo "retval:$retval" # echo "expect:\"$expect\"" if [ $r != $retval ]; then echo -e "\e[31m\nError ($r != $retval) in Test$testnr [$testname]:" echo -e "\e[0m:" exit -1 fi if [ -z "$ret" -a -z "$expect" ]; then return fi # Loop over all variable args expect strings (skip first two args) # note that "expect" var is never actually used # Then test positive for strings, if the token --neg-- is detected, then test negative for the rest positive=true; let i=0; for exp in "$@"; do if [ "$exp" == "--not--" ]; then positive=false; elif [ $i -gt 1 ]; then # echo "echo \"$ret\" | grep --null -o \"$exp"\" match=$(echo "$ret" | grep --null -o "$exp") # XXX -EZo: -E cant handle {} r=$? if $positive; then if [ $r != 0 ]; then err "$exp" "$ret" fi else if [ $r == 0 ]; then err "not $exp" "$ret" fi fi fi let i++; done # if [[ "$ret" != "$expect" ]]; then # err "$expect" "$ret" # fi } # Pipe stdin to command # Arguments: # - Command # - expected command return value (0 if OK) # - stdin input # - expected stdout outcome # Use this if you want regex eg ^foo$ expecteof(){ cmd=$1 retval=$2 input=$3 expect=$4 # Do while read stuff ret=$($cmd< /tmp/flag ret="" sleep $wait | cat <(echo $input) -| $cmd | while [ 1 ] ; do read -t 20 r # echo "r:$r" ret="$ret$r" match=$(echo "$ret" | grep -Eo "$expect"); if [ -z "$match" ]; then echo error > /tmp/flag err "$expect" "$ret" else echo ok > /tmp/flag # only this is OK break; fi done # cat /tmp/flag if [ $(cat /tmp/flag) != "ok" ]; then # err "ok" $(cat /tmp/flag) # cat /tmp/flag exit -1 fi } expectmatch(){ ret=$1 r=$2 expret=$3 expect=$4 if [ $r != $expret ]; then echo -e "\e[31m\nError ($r != $retval) in Test$testnr [$testname]:" echo -e "\e[0m:" exit -1 fi if [ -z "$ret" -a -z "$expect" ]; then echo > /dev/null else match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then err "$expect" "$ret" fi if [ -n "$expect2" ]; then match=`echo "$ret" | grep --null -Eo "$expect2"` if [ -z "$match" ]; then err $expect "$ret" fi fi fi }