run_cli_tests.sh #2

  • //
  • guest/
  • perforce_software/
  • sdp/
  • dev/
  • test/
  • bsw/
  • run_cli_tests.sh
  • View
  • Commits
  • Open Download .zip Download (15 KB)
#!/bin/bash
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------
set -u

#==============================================================================
# Declarations and Environment

declare ThisScript="${0##*/}"
declare Version=2.0.0
declare ThisUser=
declare ThisHost=${HOSTNAME%%.*}
declare CmdLine="${0} $*"
declare -i Debug=${SDP_DEBUG:-0}
declare -i ErrorCount=0
declare -i SilentMode=0
declare -i TestCount=0
declare -i TestPassCount=0
declare -i TestFailCount=0
declare -i TestSkipCount=0
declare -i NoOp=0
declare H1="=============================================================================="
declare H2="------------------------------------------------------------------------------"
declare H3="||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
declare Log=
declare LogLink="/tmp/${ThisScript%.sh}.log"
declare P4TestCmd=
declare P4C=
declare P4=p4
declare ExpectedExit=
declare TestLog=
declare -i RequireValidTests=1
declare -i ExpectedExitCode
declare -i ActualExitCode
declare ExpectedString=
declare -i ExpectedStringInOutput=0
declare Comments=
declare CLITestDataOK=1
declare CfgDir=/p4/sdp/test/bsw
declare CLITestDataFile="$CfgDir/cli_tests.cfg"
declare CfgFile=

# Values loaded from general config file.
declare RunHost=
declare TestTag=

declare OutputFile=/tmp/sdp.test_output.$$.$RANDOM
declare -i TestPassed
declare -i Line=0
declare -i TestCount=0
declare -i TestPassCount=0
declare -i TestFailCount=0
GARBAGE+=" $OutputFile"

#==============================================================================
# Local Functions

function msg () { echo -e "$*"; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "$ErrorCount"; }

function pass () { TestCount+=1; TestPassCount+=1; msg "PASS Test $TestCount: $*"; }
function fail () { TestCount+=1; TestFailCount+=1; msg "FAIL Test $TestCount: $*"; }

#------------------------------------------------------------------------------
# Function: terminate
function terminate {
   local garbageFile=

   # Disable signal trapping.
   trap - EXIT SIGINT SIGTERM

   dbg "$ThisScript: EXITCODE: $ErrorCount"

   if [[ -n "$GARBAGE" ]]; then
      for garbageFile in $GARBAGE; do
         rm -f "$garbageFile"
      done
   fi
   

   # Stop logging.
   [[ "$Log" == off ]] || msg "\\nLog is: $Log\\n${H1}\\n"

   # With the trap removed, exit.
   exit "$((ErrorCount+TestFailCount+TestSkipCount))"
}

#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style, either -h (for short form) or -man (for man-page like format).
#------------------------------------------------------------------------------
function usage {
   declare style=${1:--h}

   msg "USAGE for $ThisScript v$Version:

$ThisScript [-d <cfg_dir>] [-f] [-L <log>] [-si] [-v<n>] [-D]

or

$ThisScript [-h|-man|-V]
"
   if [[ $style == -man ]]; then
      msg "
DESCRIPTION:
	This script runs tests for the Server Deployment Package (SDP).

OPTIONS:
 -v<n>	Set verbosity 1-5 (-v1 = quiet, -v5 = highest).

 -f	Use '-f' to bypass any invalid test entries in the command line test
	data.

 -L <log>
	Specify the path to a log file, or the special value 'off' to disable
	logging.  By default, all output (stdout and stderr) goes to:
	/tmp/${ThisScript%.sh}.<timestamp>.log

	NOTE: This script is self-logging.  That is, output displayed on the screen
	is simultaneously captured in the log file.  It is not necessary (nor harmful)
	to use redirection operators like '> log' or '2>&1' or 'tee'.

 -si	Operate silently.  All output (stdout and stderr) is redirected to the log
	only; no output appears on the terminal.  This cannot be used with
	'-L off'.
      
 -D     Set extreme debugging verbosity, for debugging the test suite itself.

HELP OPTIONS:
 -h	Display short help message
 -man	Display man-style help message

FILES:
	The test.<hostname>.cfg files are SDP Test Configuration Files.  They
	define a set of host-specific values that define how SDP is tested,
	where test assets are aquired from, which versions of p4/p4d are used,
	etc.

	The cli_tests.cfg file defines the command line tests to be executed,
	including expected exit codes and output for each.

	Note that the /p4/sdp/test directory will not appear on a customer-deployed
	SDP in the real world. The /p4/sdp directory will exist, but the 'test'
	directory is populated only when using the //sdp/dev_insitu stream.

EXAMPLES:
	For typical usage, no arguments are needed. Run this in a Battle School
	Lab Environment as perforce@bos-helix-01, after first having run 'lab qa'
	as student@bsw-lab-ui to prepare the environment.

	\$ cd /p4/sdp/test/bsw
	\$ ./$ThisScript
"
   fi

   exit 1
}

#==============================================================================
# Command Line Processing

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-d) CfgDir=$2; shiftArgs=1;;
      (-f) RequireValidTests=0;;
      (-h) usage -h;;
      (-man) usage -man;;
      (-L) Log="$2"; shiftArgs=1;;
      (-si) SilentMode=1;;
      (-n) NoOp=1;;
      (-D) set -x;; # Debug; use 'set -x' mode.
      (*) usageError "Unknown arg ($1).";;
   esac

   # Shift (modify $#) the appropriate number of times.
   shift; while [[ $shiftArgs -gt 0 ]]; do
      [[ $# -eq 0 ]] && usageError "Bad usage."
      shiftArgs=$shiftArgs-1
      shift
   done
done
set -u

#==============================================================================
# Command Line Verification

[[ -n "$Log" ]] || Log="/tmp/${ThisScript%.sh}.$(date +'%Y-%m-%d-%H%M%S').log"

[[ "$SilentMode" -eq 1 && "$Log" == off ]] && \
   usageError "Cannot use '-si' with '-L off'."

#==============================================================================
# Main Program

trap terminate EXIT SIGINT SIGTERM

if [[ "$Log" != off ]]; then
   touch "$Log" || bail "Couldn't touch log file [$Log]."

   # Redirect stdout and stderr to a log file.
   if [[ $SilentMode -eq 0 ]]; then
      exec > >(tee "$Log")
      exec 2>&1
   else
      exec >"$Log"
      exec 2>&1
   fi

   # Setup /tmp/test_sdp.log symlink so it points to the current log.
   rm -f "$LogLink"
   ln -s "$Log" "$LogLink"

   msg "${H1}\\nLog is: $Log\\n"
fi

ThisUser=$(id -n -u)
msg "Started $ThisScript v$Version as $ThisUser@$ThisHost on $(date) as pid $$:\\nInitial Command Line:\\n$CmdLine\\n"

CfgFile="$CfgDir/test_sdp.$ThisHost.cfg"

if [[ -r "$CfgFile" ]]; then
   # shellcheck disable=SC1090
   source "$CfgFile" || bail "Failed to load config file [$CfgFile]."
else
   bail "Missing config file [$CfgFile]."
fi

# Sanity check on values loaded from the config file.
[[ -z "$TestTag" ]] && bail "Environment loaded from $CfgFile is missing variable definition for TestTag."

if [[ $ThisHost == "$RunHost" ]]; then
   msg "Verified: Running on $RunHost."
else
   bail "Not configured to run on host $ThisHost.  Run only on $RunHost, as configured in $CfgFile."
fi

msg "Loading and verifying command line test data from: $CLITestDataFile"
[[ -r "$CLITestDataFile" ]] || bail "Missing command line test data file [$CLITestDataFile]."

# See the $CLITestDataFile file for details on the expected file format.
# Short version: We're expecting one-line entries like this:
# <P4Cmd>|<ExpectedExitCode>|<TestLog>|<ExpectedStringRegex>|<Comments>
# The '<P4Cmd>' command *must* start with 'p4 '
Line=0; while read -r entry; do
   Line=$((Line+1))
   # shellcheck disable=SC2116
   [[ -z "$(echo "$entry")" ]] && continue
   [[ $entry == "#"* ]] && continue
   # P4TestCmd is the first field, delimited by '|'.
   P4TestCmd=${entry%%|*}
   # ExpectedExit is the second field, delimited by '|'.
   ExpectedExit=${entry#*|}
   ExpectedExit=${ExpectedExit%%|*}
   # TestLog is the third field, delimited by '|'.
   TestLog=${entry#*|}
   TestLog=${TestLog#*|}
   TestLog=${TestLog%%|*}
   # ExpectedString is the second from the last field; strip from the right.
   ExpectedString=${entry%|*}
   ExpectedString=${ExpectedString##*|}
   # Comments is the last field. Strip from the left.
   Comments=${entry##*|}

   P4C="p4 -u bruno -c jam_ws"

   if [[ $P4TestCmd == "p4 "* ]]; then
      P4TestCmd=${P4TestCmd/p4 /$P4C }
   elif [[ $P4TestCmd == "p4:"* ]]; then
      # Parse entries like: "p4:user:client command args ..."
      User=${P4TestCmd#p4:}
      User=${User%%:*}
      Workspace=${P4TestCmd#p4:}
      Workspace=${Workspace#*:}
      Workspace=${Workspace%% *}
      P4TestCmd=${P4TestCmd/p4:$User:$Workspace /$P4 -u $User -c $Workspace }
   else
      errmsg "Entry on line $Line of $CLITestDataFile has a bogus 'p4' command.  Must start with 'p4 '.  Skipping this test."
      CLITestDataOK=0
      continue
   fi

   if [[ $ExpectedExit == U ]]; then
      ExpectedExit=Undefined
   else
      if [[ $ExpectedExit =~ [0-9]+ ]]; then
         ExpectedExitCode="$ExpectedExit"
      else
         errmsg "Entry on line $Line of $CLITestDataFile has a bogus exit code.  Must be numeric or 'U', value is $ExpectedExit. Skipping this test."
         CLITestDataOK=0
         continue
      fi
   fi

   msg "C:[$P4TestCmd] E:[$ExpectedExit] L:[$TestLog] S:[$ExpectedString] Comments: $Comments"
done < "$CLITestDataFile"

if [[ "$CLITestDataOK" -eq 1 ]]; then
   msg "Verified: All test entries are OK."
else
   if [[ "$RequireValidTests" -eq 1 ]]; then
      bail "The command line test data file [$CLITestDataFile] contained invalid entries. Use '-f' to bypassing bad tests and continue."
   else
      msg "\\nSome tests were skipped due to invalid entries in $CLITestDataFile. Continuing due to '-f'.\\n"
   fi
fi

msg "$H1\\nPre-start test preparations."

msg "Loading SDP Environment file."

# shellcheck disable=SC1091
source /p4/common/bin/p4_vars 1 ||\
   bail "Failed to load SDP environment file p4_vars."

msg "$H3\\nExecuting Command Line Tests."

# It might seem inelegant to simply read-the config file in again, as opposed to storing and re-using the results
# from the data verification run above.  But with 'bash', storing results tends to have bad but subtle side effects,
# like having quote characters disappear.

Line=0; while read -r entry; do
   Line=$((Line+1))
   # shellcheck disable=SC2116
   [[ -z "$(echo "$entry")" ]] && continue
   [[ $entry == "#"* ]] && continue

   TestPassed=1

   # P4TestCmd is the first field, delimited by '|'.
   P4TestCmd=${entry%%|*}
   # ExpectedExit is the second field, delimited by '|'.
   ExpectedExit=${entry#*|}
   ExpectedExit=${ExpectedExit%%|*}
   # TestLog is the third field, delimited by '|'.
   TestLog=${entry#*|}
   TestLog=${TestLog#*|}
   TestLog=${TestLog%%|*}
   # ExpectedString is the second from the last field; strip from the right.
   ExpectedString=${entry%|*}
   ExpectedString=${ExpectedString##*|}
   # Comments is the last field. Strip from the left.
   Comments=${entry##*|}

   if [[ "$TestLog" == "output" ]]; then
      ExpectedStringInOutput=1
   else
      ExpectedStringInOutput=0
   fi

   Comments=${entry##*|}

   if [[ $P4TestCmd == "p4 "* ]]; then
      P4TestCmd=${P4TestCmd/p4 /$P4C }
   elif [[ $P4TestCmd == "p4:"* ]]; then
      # Parse entries like: "p4:user:client command args ..."
      User=${P4TestCmd#p4:}
      User=${User%%:*}
      Workspace=${P4TestCmd#p4:}
      Workspace=${Workspace#*:}
      Workspace=${Workspace%% *}
      P4TestCmd=${P4TestCmd/p4:$User:$Workspace /$P4 -u $User -c $Workspace }
   else
      TestSkipCount+=1
      continue
   fi

   if [[ "$ExpectedExit" == U ]]; then
      ExpectedExit=Undefined
   else
      if [[ "$ExpectedExit" =~ [0-9]+ ]]; then
         ExpectedExitCode="$ExpectedExit"
      else
         TestSkipCount+=1
         continue
      fi
   fi

   TestCount=$((TestCount+1))
   msg "$H2\\nTest $TestCount\\nTesting Command: $P4TestCmd\\nExpected Exit: $ExpectedExit\\nTest Log: $TestLog\\n"
   if [[ "$ExpectedStringInOutput" -eq 1 ]]; then
      msg "Expected string in output: $ExpectedString\\nComments: $Comments\\n"
   else
      msg "Expected string in log [$TestLog]: $ExpectedString\\nComments: $Comments\\n"
   fi

   if [[ "$NoOp" -eq 0 ]]; then
      $P4TestCmd > "$OutputFile" 2>&1
      ActualExitCode=$?
   else
      # No_p mode. Try to spoof successful test.
      msg "NO_OP: Would have run: $P4TestCmd > $OutputFile 2>&1"
      ActualExitCode="$ExpectedExitCode"
      echo "$ExpectedString" > "$OutputFile"
   fi

   msg "\\n== Command Output =="
   cat "$OutputFile"

   msg "TEST_EXIT_CODE: Actual $ActualExitCode, Expected $ExpectedExit"

   if [[ "$ExpectedExit" == U ]]; then
      msg "Ignoring TEST_EXIT_CODE due to 'U' value."
   else
      [[ "$ExpectedExitCode" -ne "$ActualExitCode" ]] && TestPassed=0
   fi

   if [[ -n "$ExpectedString" ]]; then
      # Check for the expected string in the command output or the cbd.log file.
      if [[ "$ExpectedStringInOutput" -eq 1 ]]; then
         if grep -E -q "$ExpectedString" "$OutputFile"; then
            msg "\\nExpected string [$ExpectedString] found in command output."
         else
            msg "\\nExpected string [$ExpectedString] NOT found in command output."
            TestPassed=0
         fi
      else
         if [[ -r "$TestLog" ]]; then
            if grep -q -E "$ExpectedString" "$TestLog"; then
               msg "\\nExpected string [$ExpectedString] found in log [$TestLog]."
            else
               msg "\\nExpected string [$ExpectedString] NOT found in log [$TestLog]."
               TestPassed=0
            fi
         else
            msg "String [$ExpectedString] expected in log [$TestLog] but log is missing."
            TestPassed=0
         fi
      fi
   else
      msg "No expected string defined, skipping check for expected string for this test."
   fi

   if [[ "$TestPassed" -eq 1 ]]; then
      msg "\\nPASS TEST $TestCount\\n"
      TestPassCount=$((TestPassCount+1))
   else
      msg "\\nFAIL TEST $TestCount\\n"
      TestFailCount=$((TestFailCount+1))
   fi

done < "$CLITestDataFile"

if [[ "$TestPassCount" -ge 1 && "$TestFailCount" -eq 0 && "$ErrorCount" -eq 0 ]]; then
   msg "${H1}\\nALL $TestCount tests PASSED.\\n"
else
   if [[ "$ErrorCount" -eq 0 ]]; then
      msg "There were no errors in test setup. Test results should be valid."
   else
      errmsg "There were $ErrorCount errors in test setup. Test results may not be valid."
   fi

   msg "${H1}\\nTest Run completed.  Summary:\\nTests Executed (Passed/Failed/Skipped): $TestCount ($TestPassCount/$TestFailCount/$TestSkipCount)\\n\\nScan above output carefully.\\n" 
fi

# Illustrate using $SECONDS to display runtime of a script.
msg "That took about $((SECONDS/3600)) hours $((SECONDS%3600/60)) minutes $((SECONDS%60)) seconds.\n"

# See the terminate() function, which is really where this script exits.
exit "$((ErrorCount+TestFailCount+TestSkipCount))"
# Change User Description Committed
#15 31522 C. Thomas Tyler Added color to BSW test suite scripts.
#14 31514 C. Thomas Tyler Added P4 environment isolation logic.
#13 31213 C. Thomas Tyler Fixed test suite issue with CLI tests formatted as p4@PORT@USER@CLIENT.
#12 31163 C. Thomas Tyler Added support for specifying port/user/client in individual CLI
tests.
#11 30981 C. Thomas Tyler Enhanced to make host-specific configuration files optional, making
the test suite more easily extended to other purposes.
#10 30748 C. Thomas Tyler Added TestGroup to output related to test descriptions.
#9 30747 C. Thomas Tyler Added Test Group concept to run_cli_tests.sh, with same meaning and
usage as in run_scripted_tests.sh. The goal is to allow running
a defined subset of tests.
#8 30742 C. Thomas Tyler Added support for specifying exit code as 'N' for non-zero.
#7 30711 C. Thomas Tyler Added support for 'no_cmd' test.
#6 30710 C. Thomas Tyler Enhanced method of calling tests to use 'eval' with quoted string, making
it work with a wider array of command lines to test with.
#5 30695 C. Thomas Tyler Added '-cd <cfg_dir>' option to jailbreak these tests scripts; they can
now be used for more general testing.

Added '-e' option to run_cli_tests.sh, same as '-e' that already
exists in run_scripted_tests.sh.

Added '-no_env' option to both scripts.

Added '-sp' to skip preflight checks in run_scripted_tests.sh.
#4 30686 C. Thomas Tyler Enhanced CLI tests to be able to call arbitrary command lines
rather than just p4 commands.
#3 30648 C. Thomas Tyler For run_scripts_tests.sh, added '-e' option to abort on first test
failure.

Updated and corrected documented command line options, removing
obsolete options.
#2 30623 C. Thomas Tyler Added feature for scripted test to run addtional checks from an earlier
execution of a script.

Added more tests.
#1 30622 C. Thomas Tyler Added files for initial SDP BSW test suite.