#!/bin/ksh
#
# Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
#
#-----------------------------------------------------------------#
# run_test:  a ksh script for testing the DMAPI.
#
# USAGE: run_test [-p] [-x] [-u user] 
#        [-F fs_type -M fs_mtpt -R "real" directory] 
#        [-f datafile] [-s sleeptime]
#        bindir testdir fsdir
#
# p:  Causes pausing after every test (not just ones with errors).
#
# x:  Prints an execution trace as the script runs.
#
# user:  Most tests don't need root access, so they will be run
#        under this username. 
#
# (NOTE: the following three must be used together)
#
#     fs_type: For NFS tests; the type of filesystem (ie nfs2, nfs3).
#
#     fs_mtpt: For NFS tests, the path to the mount of your 
#              "real" filesystem.
#              (ie, "mount -t nfs2 localhost:/dmitest $fs_mtpt")
#
#     "real" directory:  For NFS tests, the path to the "real" test directory.
#
# datafile:  The name of the file which contains the tests to run.
#   
# sleeptime: time, in seconds, for the daemon to sleep after  
#            responding to an event. (Useful for testing small
#            outstanding-events queues.)
#
# bindir:  The path to the directory that holds dm_test_daemon, send_msg,
#          the datafile, and any other files required by the datafile.
#    
# testdir:  The path to the test directory.  All DMAPI testing
#           occurs here -- this is where the tests will actually
#           be run.  (For NFS tests, this will be an NFS mount 
#           of the "real" directory.)
#          
# fsdir:  The path name of the test filesystem.  The daemon will start
#         using this path, and the mount and unmount of the DMAPI
#         filesystem will be done here.  (Even for NFS tests, this 
#         should still be the same test filesystem.)
#
#-----------------------------------------------------------------#

# For most reads, we'll want spaces to be the field separators.
IFS=" "

typeset -i fail_flag
typeset -i pause_flag
typeset -i sleeptime

# To run tests that don't require root access, we'll change to
# a user specified by lname.
lname=$LOGNAME

# Set the default external files to use.
datafile=main.dat
sleeptime=0

unset fs_type fs_mtpt
real_dir=set_me_later

# Parse the command-line options
while getopts :pxu:F:M:R:f:b:s: option
do  case $option in
    p) pause_flag=1;;
    x) set -x;;
    u) lname=$OPTARG;;
    F) fs_type=$OPTARG;;
    M) fs_mtpt=$OPTARG;;
    R) real_dir=$OPTARG;;
    f) datafile=$OPTARG;;
    b) bindir=$OPTARG;;
    s) sleeptime=$OPTARG;;
    :) print -u2 "${0##*/}: $OPTARG requires a value"
       exit 2;;
   \?) print -nu2 "USAGE: ${0##*/} [-p] [-x] [-u user] "
       print -nu2 "[-F fs type -M mountpoint directory -R \"real\" directory] "
       print -u2 "[-s sleeptime] [-f datafile] bindir testdir fsdir"
       exit 2;;
    esac
done

# Shift out the examined options, then check that we have
# exactly three arguments left: (the paths to the "bindir", 
# the test directory, and the filesystem).
shift OPTIND-1
if [[ $# != 3 ]]
then  print -nu2 "USAGE: ${0##*/} [-p] [-x] [-u user] "
      print -nu2 "[-F fs type -M mountpoint directory -R \"real\" directory] "
      print -u2 "[-s sleeptime] [-f datafile] bindir testdir fsdir"
      exit 2
fi

# For NFS tests, $2 will be an NFS mount of the test directory;
# real_dir should be the test directory's actual path.
# Otherwise, real_dir should just be $2.
if [[ $real_dir = set_me_later ]]
then real_dir=$2
fi

# Check bindir for the existence of our three critical external files.
error_count=0
for i in dm_test_daemon send_msg $datafile 
do
    if [[ ! ( -r "$1/$i" ) ]]
    then if ((error_count==0)) 
	 then print "Aborting: the directory $1/ is missing critical files:"
	 fi
         print "$1/$i"
         (( error_count = error_count + 1 ))
    fi
done
if (( error_count > 0 )) 
then exit 1
fi

# Open the datafile on file descriptor 3
exec 3< $1/$datafile

# Read datafile and determine what files it needs from bindir;
# then, check for the existence of these files.
error_count=0
while read -u3 file_list
do case $file_list in
   ---*) break;;
    //*) continue;;
      *) for i in $file_list
         do if [[ ! ( -r "$1/$i" ) ]]
	    then if ((error_count==0)) 
	         then print "The directory $1/ is missing these files:"
	         fi
                 print "$1/$i"
                 (( error_count = error_count + 1 ))
            fi
         done;;
   esac
done
if (( error_count > 0 )) 
 then exit 1
fi

# Run initialization stuff without daemon.
while read -u3 cmd
do    case $cmd in
       //*) continue;;
      ---*) break;;
         *) eval "$cmd";;
      esac
done

# If we're testing over nfs, remount the filesystem to clear the cache.
case $fs_type in
   nfs2) print "Clearing nfs2 cache by remounting filesystem..."
         eval "umount $fs_mtpt"
         eval "mount -t nfs2 localhost:$3 $fs_mtpt";;
   nfs3) print "Clearing nfs3 cache by remounting filesystem..."
         eval "umount $fs_mtpt"
         eval "mount -t nfs3 localhost:$3 $fs_mtpt";;
      *) if [[ $fs_type != "" ]] 
	 then print "ERROR: $fs_type not a known or testable filesystem type"
	 fi;;
esac
    

# Check with the user before starting up daemon
print "\n** Using testfile ${datafile##*/} **"
print "** Using userid $lname for tests not requiring root access **"
print "Press enter to begin, or enter q or n to abort..."
read go
case "$go" in
    n|N|q|Q) exit 1;;
    *);;
esac

# Now, the user will need ownership of the test directory 
# ($2, not $real_dir, since real_dir is accessed only as root).
eval "chown $lname $2"

# Now it's time to begin running the daemon as a coprocess.
# The daemon will use a : as its internal field separator.
IFS=":"
if (($sleeptime > 0)) then 
    $1/dm_test_daemon -s $sleeptime $3 |&
else
    $1/dm_test_daemon $3 |&
fi

#Keep track of the coprocess id... "$!" may change.
coproc=$!

# Initialize the count of errors
error_count=0;

# dm_test_daemon starts with a spurious line feed.
read -p junk

# Finally, we've reached the actual loop to read in the testfile.
while true
do    
      clear
      while read -u3 cmd
      do
	    case $cmd in
	    run_without_test)
	        while read -u3 cmd
                do  case $cmd in
		    ---*) clear; continue 2;;
		     //*) continue;;
             	     @@*) cmd=${cmd#@@*}
			  print "!!  ${cmd# *}"
			  continue;;
                       *) eval $cmd;;
		    esac
		done;;
	    run_as_root)
		read -u3 cmd
		root_flag=1
                break;;
	    //*)  continue;;
	    @@*) cmd=${cmd#@@*}
                 print "!!  ${cmd# *}"
	         continue;;
              *) root_flag=0
	         break;;
	    esac
      done
      if (( $root_flag == 1 ))
      then print "Command to execute (as root):\n\n   $cmd\n"
           eval "$cmd"
      else print "Command to execute:\n\n   $cmd\n"
           eval "su $lname -c \"$cmd\""
      fi

      # Note failure of the command.  Also, send a message
      # that the command is done.  We will know we're done
      # reading from the daemon when we see this message. 
      fail_flag=$?
      $1/send_msg over
      print

      # Reset variables for reading this command.
      event_count=0
      unset contents event fs_handle handle length offset
      unset media_designator mode mountpoint_handle
      unset mountpoint_path msg_str name new_name new_parent
      unset parent_handle ret_code root_handle sequence token  
      unset tries_left unmount_mode

      # Read events, report them, and store their data.
      while true 
      do 
	 read -p event[event_count]
	 case "${event[event_count]}" in
	 mount) 
	    print "Report: found mount event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk fs_handle[event_count]
	    read -p junk mountpoint_handle[event_count]
	    read -p junk mountpoint_path[event_count]
	    read -p junk media_designator[event_count]
	    read -p junk root_handle[event_count]
	    read -p junk mode[event_count]
	    read -p junk;;
	 preunmount) 
	    print "Report: found preunmount event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk fs_handle[event_count]
	    read -p junk root_handle[event_count]
	    read -p junk unmount_mode[event_count]
	    read -p junk;;
	 unmount) 
	    print "Report: found unmount event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk fs_handle[event_count]
	    read -p junk unmount_mode[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 nospace) 
	    print "Report: found nospace event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk fs_handle[event_count]
	    read -p junk;;
	 create|remove) 
	    print "Report: found ${event[event_count]} event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk name[event_count]
	    read -p junk mode[event_count]
	    read -p junk;;
	 postcreate) 
	    print "Report: found postcreate event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk handle[event_count]
	    read -p junk name[event_count]
	    read -p junk mode[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 postremove) 
	    print "Report: found postremove event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk name[event_count]
	    read -p junk mode[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 rename) 
	    print "Report: found rename event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk new_parent[event_count]
	    read -p junk name[event_count]
	    read -p junk new_name[event_count]
	    read -p junk;;
	 postrename) 
	    print "Report: found postrename event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk new_parent[event_count]
	    read -p junk name[event_count]
	    read -p junk new_name[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 symlink) 
	    print "Report: found symlink event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk name[event_count]
	    read -p junk contents[event_count]
	    read -p junk;;
	 postsymlink) 
	    print "Report: found postsymlink event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk handle[event_count]
	    read -p junk name[event_count]
	    read -p junk contents[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 link) 
	    print "Report: found link event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk handle[event_count]
	    read -p junk name[event_count]
	    read -p junk;;
	 postlink) 
	    print "Report: found postlink event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk parent_handle[event_count]
	    read -p junk handle[event_count]
	    read -p junk name[event_count]
	    read -p junk ret_code[event_count]
	    read -p junk;;
	 read|write|truncate) 
	    print "Report: found ${event[event_count]} event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk handle[event_count]
	    read -p junk offset[event_count]
	    read -p junk length[event_count]
	    read -p junk;;
	 attribute) 
	    print "Report: found attribute event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk handle[event_count]
	    read -p junk;;
	 destroy) 
	    print "Report: found destroy event."
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk handle[event_count]
	    read -p junk name[event_count]
            read -p junk contents[event_count]
	    read -p junk;;
	 user) 
	    read -p junk token[event_count]
	    read -p junk sequence[event_count]
	    read -p junk msg_str[event_count]
	    case "${msg_str[event_count]}" in
		"over") read -p junk 
                        event[event_count]=end_of_tests
                        print "Report: found \"end of test\" user event. "
			break;;
		*) print "Report: found user event. "
	           read -p junk;; 
	    esac;;
	 pending)
	    read -p junk tries_left[event_count]
	    print -n "Report: process pending. "
	    print "Tries left: ${tries_left[event_count]}"
	    read -p junk;;
	 *) 
	    print -n "Report: found ${event[event_count]} event. "
	    print    "(unknown to this version)"
	    while read -p msg_str[event_count]
	    do case "${msg_str[event_count]}" in
		    "end_of_message") break;;
		    *);;
	       esac
	    done;;
	 esac
	 ((event_count=event_count+1))
      done
      ((old_error_count=error_count));

      IFS=" "

      while read -u3 val_one val_two val_tre
      do case $val_one in
	 ---*)  if [[ $fail_flag != -1 ]]
                then if [[ $fail_flag != 0 ]]
                     then print -n "ERROR: command failed; it was "
                          print "expected to succeed."
	                  ((error_count=error_count+1))
	             fi
                fi
                if (( error_count>old_error_count || pause_flag==1 )) 
                then print "\nEnter q to quit, or press enter to continue..."
                     read go
		     case "$go" in
		     q|Q) break;;
		     *);;
		     esac
	        fi
                IFS=":"
                continue 2;;
	 failure)
           if [[ $fail_flag = 0 ]] 
           then print "ERROR: command succeeded; it was expected to fail."
	   ((error_count=error_count+1))
	   else print "Note: command is expected to fail."
	   fi
	   fail_flag=-1;;
         *) case $val_two in
	    matches)  
               if [[ $(eval "print $"{$val_one}) = $(eval "print $"{$val_tre}) ]] 
               then print "Report: $val_one and $val_tre match; both are "
		    if [[ $(eval "print $"{$val_one}) = "" ]]
		    then print "unset"
		    else print "$(eval "print $"{$val_one})"
		    fi
	       else print -n "ERROR: $val_one was "
		    if [[ $(eval "print $"{$val_one}) = "" ]]
		    then print -n "unset "
                    else print -n "equal to $(eval "print $"{$val_one})"
		    fi
		    print -n ", while $val_tre was "
		    if [[ $(eval "print $"{$val_tre}) = "" ]]
		    then print "unset."
		    else print "equal to $(eval "print $"{$val_tre})."
		    fi
		    ((error_count=error_count+1))
	       fi;;
	    store_in) 
	       eval ${val_tre}=$(eval "print $"{$val_one})
	       print -n "Report: value of ${val_one} copied into "
	       print "${val_tre}.";;
	    *) 
               if [[ $(eval "print $"{$val_one}) = $val_two ]] 
	       then :
	       else if [[ "$val_one" = event_count ]]
		    then print -n "ERROR: expected $val_two event(s), "
                         print "but found $event_count."
			 else print -n "ERROR: $val_one was "
			 if [[ $(eval "print $"{$val_one}) = "" ]]
			 then print -n "unset "
			 else print -n "equal to $(eval "print $"{$val_one}) "
			 fi
	                 print "rather than $val_two as expected."
 	            fi 
 		       ((error_count=error_count+1))
	       fi;;
	    esac;;
	 esac
      done
      if [[ $fail_flag != -1 ]] 
      then if [[ $fail_flag != 0 ]]
	   then print -n "ERROR: command failed; it was "
                print "expected to succeed."
                ((error_count=error_count+1))
           fi
      fi
      if (( error_count>old_error_count || pause_flag==1 )) 
      then print "\nTests complete.  Press enter to quit..."
           read go
      fi
      break
done

# Close the datafile
exec 3<&-

# End the daemon
kill $coproc
wait $coproc

clear
if ((error_count==1)) 
then print "Test result: 1 error found."
else print "Test result: $error_count errors found."
fi
