test_cbd.sh #7

  • //
  • guest/
  • perforce_software/
  • cbd/
  • dev/
  • test/
  • test_cbd.sh
  • View
  • Commits
  • Open Download .zip Download (34 KB)
#!/bin/bash
#------------------------------------------------------------------------------
set -u

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

# Allow override of P4U_HOME, which is set only when testing P4U scripts.
export P4U_HOME=${P4U_HOME:-/p4/common/bin}
export P4U_LIB=${P4U_LIB:-/p4/common/lib}
export P4U_ENV=$P4U_LIB/p4u_env.sh
export P4U_LOG="/tmp/test_cbd.$(date +'%Y%m%d-%H%M%S').log"
export VERBOSITY=${VERBOSITY:-3}

# Environment isolation.  For stability and security reasons, prepend
# PATH to include dirs where known-good scripts exist.
# known/tested PATH and, by implication, executables on the PATH.
export PATH=$P4U_HOME:$PATH:~/bin:.
export P4CONFIG=BogusValueNonExistentFile

[[ -r "$P4U_ENV" ]] || {
   echo -e "\nError: Cannot load environment from: $P4U_ENV.  Aborting.\n\n"
   exit 1
}

declare BASH_LIBS=$P4U_ENV
BASH_LIBS+=" $P4U_LIB/libcore.sh"
BASH_LIBS+=" $P4U_LIB/libp4u.sh"

for bash_lib in $BASH_LIBS; do
   source $bash_lib
done

declare Version=1.4.0
declare -i SilentMode=0
declare -i OverallReturnStatus=0

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

#------------------------------------------------------------------------------
# Override of the default bail() function. This sets the global
# OverallReturnStatus.
function bail () {
   echo -e "\nFATAL: ${1:-Unknown Error}\n"
   OverallReturnStatus=${2:-1}
   exit ${2:-1};
}

#------------------------------------------------------------------------------
# Function: terminate
function terminate
{
   # Disable signal trapping.
   trap - EXIT SIGINT SIGTERM

   # Don't litter.
   cleanTrash

   vvmsg "$THISSCRIPT: EXITCODE: $OverallReturnStatus"

   # Stop logging.
   [[ "${P4U_LOG}" == off ]] || stoplog

   # With the trap removed, exit.
   exit $OverallReturnStatus
}

#------------------------------------------------------------------------------
# Function: get_pids()
#
# Safely get just the appropraite processes.  Requires fully qualified path
# to a copy of p4d/p4broker used for this instance.
#
# Examples:
#   p4dPids=get_pids($p4dExe)
#   p4brokerPids=get_pids($p4brokerExe)
#------------------------------------------------------------------------------
function get_pids()
{
   [[ -z "$1" ]] && bail "get_pids(): No executable specified."
   exe=$1

   if [[ $ThisOS == Darwin ]]; then
      pids=$(/bin/ps -ef | grep "$exe" | grep "$(id -u)" | grep -v grep | awk '{print $2}')
   else
      pids=$(/bin/ps -f -C $(basename $exe) | grep $exe|awk '{print $2}')
   fi

   echo $pids
}

#------------------------------------------------------------------------------
# Function: shutdown_servers()
#------------------------------------------------------------------------------
function shutdown_servers()
{
   vvmsg "CALL shutdown_servers($*)"

   p4dPids=$(get_pids "$SampleDepotHome/p4d")
   if [[ -n "$p4dPids" ]]; then
      runCmd "kill -9 $p4dPids" "Killing P4D pids [$p4dPids]."
   else
      msg "Verified: No P4D processes for the Sample Depot are running."
   fi

   p4brokerPids=$(get_pids "$SampleDepotHome/p4broker")
   if [[ -n "$p4brokerPids" ]]; then
      runCmd "kill -9 $p4brokerPids" "Killing P4Broker pids [$p4brokerPids]."
   else
      msg "Verified: No P4Broker processes for the Sample Depot are running."
   fi

   msg "Waiting a second to ensure pids stop."
   sleep 1
}

#------------------------------------------------------------------------------
# Function: get_sample_depot()
#------------------------------------------------------------------------------
function get_sample_depot()
{
   vvmsg "CALL get_sample_depot($*)"
   msg "Getting Sample Depot."

   if [[ -d $SampleDepotHome ]]; then
      runCmd "/bin/rm -rf $SampleDepotHome" "Blasting existing Sample Depot home dir [$SampleDepotHome]." ||\
         bail "Failed to blast existing Sample Depot home dir [$SampleDepotHome]."
   fi

   runCmd "/bin/mkdir -p $SampleDepotHome" "Creating empty new Sample Depot home dir [$SampleDepotHome]" ||\
      bail "Failed to create new Sample Depot root [$SampleDepotHome]."

   cd $SampleDepotHome || bail "Failed to cd to Sample Depot home dir [$SampleDepotHome]."

   msg "Operating in [$PWD]."

   runCmd "wget -q $SampleDepotURL" \
      "Pulling Sample Depot from [$SampleDepotURL]." ||\
      bail "Failed to pull Sample Depot."

   msg "Sample Depot pulled successfully."

   msg "Now, pulling Perforce executables for $p4Version on $Platform."

   for exeUrl in $p4URL $p4dURL $p4brokerURL; do
      exe=${exeUrl##*/}
      runCmd "wget -q $exeUrl" \
         "Pulling exe from [$exeUrl]." ||\
      bail "Failed to pull exe from [$exeUrl]."
      runCmd "chmod +x $exe" "Adjusting exe perms (chmod +x)."
      runCmd "./$exe -V" "Obtained [$exe] version:" 0 1
   done

   msg "Perforce executables pulled."
}

#------------------------------------------------------------------------------
# Function: init_sample_depot()
#
# This picks up where get_sample_depot leaves off.
# The $SampleDepotHome folder will exist and contain the sample_depot.tar.gz
# file, as downloaded from the Perforce FTP site.
#------------------------------------------------------------------------------
function init_sample_depot()
{
   vvmsg "CALL init_sample_depot($*)"

   if [[ ! -d "$SampleDepotHome" ]]; then
      msg "Info: Sample Depot Home dir [$SampleDepotHome] does not exist.  Operating as if '-G' was specified."
      shutdown_servers
      get_sample_depot
   fi

   cd $SampleDepotHome || bail "Failed to cd to Sample Depot Home dir [$SampleDepotHome]."
   msg "Operating in [$PWD]."

   [[ -r "$SampleFileGZFile" ]] || bail "Cannot access Sample Depot GZip file [$SampleFileGZFile]."

   runCmd "tar -xzpf $SampleFileGZFile" || bail "Failed to expand tar file [$tmpTarFile]."

   cd PerforceSample ||\
      bail "Exploded tarfile is missing expected 'PerforceSample' dir."

   [[ $PWD != $SampleDepotP4ROOT ]] && bail "Unexpected Error: Bad value for \$PWD."

   msg "Operating in [$PWD]."

   echo "P4PORT=$SampleDepotBrokerPort
P4USER=bruno
P4CLIENT=NoWorkspaceDefined
P4TICKETS=$P4TICKETS
P4IGNORE=.p4ignore" > .p4config

   export P4CONFIG=$PWD/.p4config

   msg "Generated this .p4config file:\n$(cat .p4config)\n"

   runCmd "/bin/rm -rf server.locks"
   runCmd "/bin/rm -f db.* rdb.* state log journal audit"
   runCmd "$p4dExe -r $P4ROOT -jr checkpoint" \
      "Loading Sample Depot checkpoint." ||\
      bail "Failed to load Sample Depot checkpoint."
   runCmd "$p4dExe -r $P4ROOT -xu" \
      "Upgrading Sample Depot databases." ||\
      bail "Failed to upgrade Sample Depot checkpoint."

   runCmd "$p4dExe -r $P4ROOT -p $P4PORT -d -J $P4JOURNAL -L $P4LOG -v $P4DEBUG < /dev/null" \
      "Starting Sample Depot P4D on port $P4PORT."

   msg "Writing p4broker config file [$BrokerCfg]."

   echo "target      = localhost:$SampleDepotP4PORT;
listen      = $SampleDepotBrokerPort;
directory   = $SampleDepotP4ROOT;
logfile     = $SampleDepotP4ROOT/p4broker.log;
debug-level = server=1;
admin-name  = \"Tom Tyler\";
admin-phone = 617-513-2414;
admin-email = \"ttyler@perforce.com\";
compress = false;
redirection  = selective;

command: ^(sync|update|flush)\$
{
   action = filter;
   execute = \"$CBD_HOME/scripts/wssync.sh\";
}

command: ^(sbi)\$
{
   action = filter;
   execute = \"$CBD_HOME/scripts/sbi.pl\";
}


" > $BrokerCfg

   runCmd "$p4brokerExe -d -c $BrokerCfg < /dev/null" \
      "Starting Sample Depot P4Broker $SampleDepotBrokerPort."

   p4brokerPids=$(get_pids "$p4brokerExe")

   if [[ -n "$p4brokerPids" ]]; then
      p4brokerPidFile=$PWD/.p4broker.pid
      echo $p4brokerPids > $p4brokerPidFile
      msg "P4Broker process pids: $(cat $p4brokerPidFile)."
   else
      bail "Could not determine pid for p4broker."
   fi 
}

#------------------------------------------------------------------------------
# Function: init_cbd
#
# Intialize the CBD stuff.  This needs to be restartable/idempotent.
#------------------------------------------------------------------------------
function init_cbd()
{
   vvmsg "CALL init_cbd($*)"
   ws=$1
   wsRoot=$2
   tmpFile=/tmp/tmp.test_cbd.$$.$RANDOM

   if [[ ! -f "$BrokerCfg" ]]; then
      msg "Info: Broker Config file [$BrokerCfg] does not exist.  Operating as if '-I' was specified."
      shutdown_servers
      init_sample_depot
   fi

   if [[ -d "$CBD_HOME/logs" ]]; then
      runCmd "/bin/rm -rf $CBD_HOME/logs" "Cleaning up logs from earlier runs."
   fi

   if [[ ! -d "$CBD_HOME/logs" ]]; then
      runCmd "/bin/mkdir -p $CBD_HOME/logs" "Creating empty log dir." ||\
         bail "Failed to create CBD Logs dir [$CBD_HOME/logs]."
   fi

   msg "Loading CBD Stream and Workspace Template Update triggers."

   echo -e "Triggers:\n\tSSTemplateUpdate change-content //...cbdsst \"$CBD_HOME/triggers/SSTemplateUpdate.py -v DEBUG -L $CBD_HOME/logs/SSTemplateUpdate.log %changelist%\"\n\tSSTemplateUpdate change-content //...cbdwst \"$CBD_HOME/triggers/WSTemplateUpdate.py -v DEBUG -L $CBD_HOME/logs/SSTemplateUpdate.log %changelist%\"" > $tmpFile
   $p4 -s triggers -i < $tmpFile || cat $tmpFile

   msg "Triggers table is now:"
   $p4 triggers -o | egrep -v '^#'

   msg "Generating workspace for bruno."

   sstFile=$wsRoot/jam.cbdsst

   runCmd "$p4 client -d $ws"

   msg "Making bruno a super user."

   if [[ "$NO_OP" != 1 ]]; then
      msg "Running: $p4 protect -o \| $p4 -s protect -i"
      $p4 protect -o | $p4 -s protect -i
   else
      msg "NO_OP: Would run: $p4 protect -o \| $p4 -s protect -i"
   fi

   echo -e "Client:\t$ws\n
Owner:\tbruno\n
Description:\n\tJam stream workspace.\n
Root:\t$wsRoot\n
Options:\tnoallwrite noclobber nocompress unlocked modtime rmdir\n
SubmitOptions:\tleaveunchanged\n
LineEnd:\tlocal\n
Stream: //jam/rel2.1\n" > $tmpFile
   $p4 -s client -f -i < $tmpFile || cat $tmpFile

   if [[ -d "$wsRoot" ]]; then
      runCmd "/bin/rm -rf $wsRoot" ||\
         bail "Failed to cleanup test workspace root dir [$wsRoot]."
   fi

   runCmd "/bin/mkdir -p $wsRoot"

   msg "Cleanup from earlier runs."
   runCmd "$p4 -c $ws obliterate -y $sstFile"
   runCmd "/bin/rm -f $sstFile"

   echo "P4PORT=$SampleDepotBrokerPort
P4USER=bruno
P4CLIENT=$ws
P4TICKETS=$P4TICKETS
P4IGNORE=.p4ignore" > $wsRoot/.p4config

   msg "Generated P4CONFIG file $wsRoot/.p4config file:\n$(cat $wsRoot/.p4config)\n"

   echo -e "Client: bruno.LT-8BQ0VM1.Jam\n
Owner: bruno\n
Host: LT-8BQ0VM1\n
Description:\n\tCreated by bruno.\n
Root: C:\\p4\\Jam
Options: noallwrite noclobber nocompress unlocked modtime rmdir\n
SubmitOptions: leaveunchanged\n
LineEnd: local\n
Stream: //jam/rel2.1\n\n" > $tmpFile

   $p4 -s client -f -i < $tmpFile || cat $tmpFile

   msg "Adjusting flow of change, 2.1 -> 2.2 -> 2.3 -> main -> dev*"

   echo -e "Stream: //jam/rel2.1\n
Owner: bruno\n
Name:  rel2.1\n
Parent: //jam/rel2.2\n
Type:  release\n
Description:\n\tJam 2.1 release\n
Options: allsubmit unlocked toparent nofromparent\n
Paths:\n\tshare ...\n" > $tmpFile

   $p4 -s stream -f -i < $tmpFile || cat $tmpFile

   echo -e "Stream: //jam/rel2.2\n
Owner: bruno\n
Name: rel2.2\n
Parent: //jam/rel2.3\n
Type: release\n
Description:\n\tJam 2.2 release\n
Options: allsubmit unlocked toparent nofromparent\n
Paths:\n\tshare ...\n" > $tmpFile

   $p4 -s stream -f -i < $tmpFile || cat $tmpFile

   echo -e "Stream: //jam/rel2.3\n
Owner: bruno\n
Name: rel2.3\n
Parent: //jam/main\n
Type: release\n
Description:\n\tJam 2.3 release stream\n
Options: allsubmit unlocked toparent nofromparent\n
Paths:\n\tshare ...\n" > $tmpFile

   $p4 -s stream -f -i < $tmpFile || cat $tmpFile

   echo -e "Stream: //jam/dev2.3\n
Owner: bruno\n
Name: dev2.3\n
Parent: //jam/main\n
Type: development\n
Description:\n\tJam 2.3 development stream\n
Options: allsubmit unlocked toparent fromparent\n
Paths:\n\tshare ...\n" > $tmpFile

   $p4 -s stream -f -i < $tmpFile || cat $tmpFile

   echo -e "Stream: //jam/task-4491\n
Owner: bruno\n
Name: task-4491\n
Parent: //jam/dev2.3\n
Type: task\n
Description:\n\tTask stream for task 4491.\n
Options: allsubmit unlocked toparent fromparent\n
Paths:\n\tshare ...\n" > $tmpFile
   $p4 -s stream -f -i < $tmpFile || cat $tmpFile

   echo -e "Client: jsmith.TomsLaptop.depot\n
Owner: jsmith\n
Host: LT-8BQ0VM1\n
Description:\n\tCreated by jsmith.\n
Root: C:\\p4\\CBD\n
Options: noallwrite noclobber nocompress unlocked modtime rmdir\n
SubmitOptions:  leaveunchanged\n
LineEnd:        local\n
Stream: //jam/task-4491\n\n" > $tmpFile

   $p4 -s client -f -i < $tmpFile || cat $tmpFile

   msg "Generating sample Stream Spec Template file."

   echo -e "Description:\n\tStream spec for __EDITME_STREAM__.\n\nPaths:\n\tshare ...\n\timport pb/... //pb/1.5.1-p/...@8883\n\timport gwt/... //gwt-streams/release1.5/...@12047\n\n" > $sstFile

   runCmd "$p4 -c $ws add $sstFile" "Adding Stream Spec Template file."
   runCmd "$p4 -c $ws submit -r -d Added. $sstFile"
   runCmd "$p4 -c $ws tag -l Jam-2.0.1.01 //jam/rel2.1/..." "Applying Jam-2.0.1.01 label to //jam/rel2.1/...." 1 0 0

   sleep 1
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="
   runCmd "/bin/rm -f $CBD_HOME/logs/SSTemplateUpdate.log"

   echo -e "Description:\n\tStream spec for __EDITME_STREAM__.\n\nPaths:\n\tshare ...\n\timport pb/... //pb/1.5.1-p/...@8906\n\timport gwt/... //gwt-streams/release1.5/...@12048\n\n" > $sstFile

   runCmd "$p4 -c $ws submit -r -d Updated. $sstFile"
   runCmd "$p4 -c $ws tag -l Jam-2.0.1.02 //jam/rel2.1/..." "Applying Jam-2.0.1.02 label to //jam/rel2.1/...." 1 0 0
   sleep 1
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="
   runCmd "/bin/rm -f $CBD_HOME/logs/SSTemplateUpdate.log"

   echo -e "Description:\n\tStream spec for __EDITME_STREAM__.\n\nPaths:\n\tshare ...\n\timport pb/... //pb/1.5.1-p/...@8906\n\timport gwt/... //gwt-streams/release1.5/...@12050\n\n" > $sstFile

   runCmd "$p4 -c $ws submit -d Re-Updated. $sstFile"
   runCmd "$p4 -c $ws tag -l Jam-2.0.1.03 //jam/rel2.1/..." "Applying Jam-2.0.1.03 label to //jam/rel2.1/...." 1 0 0
   sleep 1
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="
   runCmd "/bin/rm -f $CBD_HOME/logs/SSTemplateUpdate.log"

   runCmd "$p4 -c $ws populate //jam/rel2.1/jam.cbdsst //jam/rel2.2/jam.cbdsst" \
      "Populating CBD stream spec template file in jam-rel2.2."
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="

   runCmd "$p4 -c $ws populate //jam/rel2.2/jam.cbdsst //jam/rel2.3/jam.cbdsst" \
      "Populating CBD stream spec template file in jam-rel2.3."
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="

   runCmd "$p4 -c $ws populate //jam/rel2.3/jam.cbdsst //jam/main/jam.cbdsst" \
      "Populating CBD stream spec template file in main."
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="

   runCmd "$p4 -c $ws populate //jam/main/jam.cbdsst //jam/dev2.3/jam.cbdsst" \
      "Populating CBD stream spec template file in dev2.3."
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="

   runCmd "$p4 -c $ws populate //jam/dev2.3/... //jam/task-4491/..." \
      "Populating task stream jam-task4491."
   runCmd "cat $CBD_HOME/logs/SSTemplateUpdate.log" "== Stream Spec Trigger Log =="

   msg "Setting a password for user earl."
   echo -e "Abcdefg123\nAbcdefg123" > $tmpFile
   $p4 -u bruno passwd earl < $tmpFile

   msg "Creating locaked earl_jams workspace."

   echo -e "Client: earl_jams\n\nOwner:\tearl\n\nDescription:\n\tCreated by earl.\n\nRoot: c:\\p4\\CT\n\nOptions: noallwrite noclobber nocompress locked modtime rmdir\n\nSubmitOptions: leaveunchanged\n\nLineEnd: local\n\nStream: //jam/rel2.1\n\n" > $tmpFile

   $p4 -s client -f -i < $tmpFile || cat $tmpFile

   runCmd "/bin/rm -f $tmpFile" "Removing temp file [$tmpFile]."
}

#------------------------------------------------------------------------------
# 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}

   echo "USAGE for $THISSCRIPT v$Version:

$THISSCRIPT [-G|-I|-C] [-d <cfg_dir>] [-f] [-L <log>] [-si] [-v<n>] [-D]

or

$THISSCRIPT [-h|-man|-V]
"
   if [[ $style == -man ]]; then
      echo -e "
DESCRIPTION:
	This script runs test for the Component Based Develoment (CBD)
	system, supplemental automation that enhances the CBD capabilities
	of a Perforce Helix server.

OPTIONS:
 -d <cfg_dir>
	Specify the configuration directory where configuration and test
	data are expected to exist.  By default, scripts are expected to live
	in a directory named '/shared' if that directory exists, or else in
	the current directory at the time this script was called.

	The use of the /shared folder is consistent with the 'helix-01' test
	virtual machine setup for automated testing.

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

 -G	Starts test preparation from scratch, by:
	* Getting a pristine copy of the Sample Depot tarfile from the
	Perforce FTP server,
	* Downloading Helix executables p4/p4d/p4broker

	Using -G implies -I and -C.

	The existing Sample Depot and related Perforce server instance are
	blasted with -G.

	The '-G' is implied if the configured Sample Depot Home dir does not
	exist.  It can be specified to force a clean start with the latest
	exeutables.

 -I	Continues test preparation almost from scratch, using an existing copy
	of the downloaded Sample Depot.  This picks up where '-G' leaves off.

	With -G or -I, Any exisitng p4d or p4broker processes from earlier test
	runs are killed.
	
	Next, it:
	* Configures the broker for CBD operation.
	* Blasts and resets the P4ROOT for the Sample Depot checkpoint.
	* Starts 'p4d' and 'p4broker' services.

	Using -I implies -C.

 -C	Initialize CBD on the Sample depot.  This picks up where '-I' leaves
	off.

	It:
	* Installs the CBD triggers.
	* Creates several streams for testing.
	* Creates and submits *.cbdsst files, causing the CBD trigger to
	fire and generate 'keys' data.

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

 -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:
	$(dirname ${P4U_LOG}).

	NOTE: This script is self-logging.  That is, output displayed on the screen
	is simultaneously captured in the log file.  Do not run this script with
	redirection operators like '> log' or '2>&1', and do not use '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.

HELP OPTIONS:
 -h	Display short help message
 -man	Display man-style help message
 -V	Dispay version info for this script and its libraries.

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

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

	Each bit.*.txt (broker input test) file define one test simulated with
	a handcrafted broker input file.  The bit.cfg file defines the list of
	bit.*.txt files to be executed.

	The auto_test_cbd.sh wrapper script call this script with options
	optimzed for use in continuous integration build tests.  For example,
	the normally helpful self-logging features of this script are disasbled
	for CI builds, since the build system does it's own snazzy and webby
	log capture.

	ti.sh is a manual test support script that parses and verifies the
	syntax of the the cli_tests.txt test input data file.

EXAMPLES:
	For typical usage, no arguments are needed:

	cd /p4/common/bin/cbd/test
	./test_cbd.sh
"
   fi

   exit 1
}

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

declare -i RequireValidTests=1
declare -i GetSampleDepot=0
declare -i InitSampleDepot=0
declare -i InitCBD=0
declare -i shiftArgs=0
declare CfgDir=Undefined
declare CfgFile=

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-d) CfgDir=$2; shiftArgs=1;;
      (-f) RequireValidTests=0;;
      (-G) GetSampleDepot=1; InitSampleDepot=1; InitCBD=1;;
      (-I) InitSampleDepot=1; InitCBD=1;;
      (-C) InitCBD=1;;
      (-h) usage -h;;
      (-man) usage -man;;
      (-V) show_versions; exit 1;;
      (-v1) export VERBOSITY=1;;
      (-v2) export VERBOSITY=2;;
      (-v3) export VERBOSITY=3;;
      (-v4) export VERBOSITY=4;;
      (-v5) export VERBOSITY=5;;
      (-L) export P4U_LOG=$2; shiftArgs=1;;
      (-si) SilentMode=1;;
      (-n) export NO_OP=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

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

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

trap terminate EXIT SIGINT SIGTERM

if [[ "${P4U_LOG}" != off ]]; then
   LogDir="$(dirname $P4U_LOG)"
   if [[ ! -d $LogDir ]]; then
      /bin/mkdir -p "$LogDir" || bail "Couldn't create log dir [$LogDir]."
   fi

   touch "${P4U_LOG}" || bail "Couldn't touch log file [${P4U_LOG}]."

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

   initlog
fi

if [[ $CfgDir == Undefined ]]; then
   if [[ -d /shared ]]; then
      CfgDir=/shared
   else
      CfgDir=$PWD
   fi
fi

CfgFile=$CfgDir/test_cbd.${HOSTNAME%%\.*}.cfg

if [[ -r "$CfgFile" ]]; then
   source "$CfgFile" || bail "Failed to load config file [$CfgFile]. Aborting."
else
   bail "Missing config file [$CfgFile]. Aborting."
fi

# Sanity check on values loaded from the config file.
[[ -z "$SampleDepotHome" ]] && bail "Environment loaded from  is missing variable definitions. Aborting."
[[ -z "$CBD_HOME" ]] && bail "Environment loaded from  is missing variable definitions. Aborting."

declare p4Exe=$SampleDepotHome/p4
declare p4dExe=$SampleDepotHome/p4d
declare p4brokerExe=$SampleDepotHome/p4broker
declare BrokerCfg=$SampleDepotHome/PerforceSample/p4broker.cbd.cfg
declare p4="$p4Exe -p $SampleDepotBrokerPort -u bruno"
declare p4c="$p4 -c $TestWS"

declare ThisOS=$(uname -s)

export P4ROOT=$SampleDepotP4ROOT
export P4USER=bruno
export P4PORT=$SampleDepotP4PORT
export P4JOURNAL=$SampleDepotP4ROOT/journal
export P4LOG=$SampleDepotP4ROOT/log
export P4DEBUG=server=4
export P4TICKETS=$PWD/.p4tickets
export P4ENVIRO=$PWD/.p4enviro
export P4TRUST=$PWD/.p4trust
unset P4NAME
unset P4AUDIT

if [[ ${HOSTNAME%%\.*} != $RunHost ]]; then
   bail "Run this only on $RunHost."
   exit 1
else
   echo "Verified: Running on $RunHost."
fi

declare P4TestCmd=
declare BITFile=
declare ExpectedExit=
declare -i ExpectedExitCode
declare -i ActualExitCode
declare ExpectedString=
declare -i ExpectedStringInOutput
declare Comments=
declare CLITestDataOK=1
declare CLITestDataFile=$CfgDir/cli_tests.txt
declare BITestCfgFile=$CfgDir/bit.cfg
declare OutputFile=/tmp/cbd.test_output.$$.$RANDOM
declare -i AllTestsPassed
declare -i TestPassed
declare -i Line=0
declare -i TestCount=0
declare -i TestPassedCount=0
declare -i TestFailedCount=0
GARBAGE+=" $OutputFile"

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>|<ExitCode>|<Output>|<Comments>
# The '<P4Cmd>' command *must* start with 'p4 '
while read entry; do
   Line=$((Line+1))
   [[ -z "$(echo $entry)" ]] && continue
   [[ $entry == "#"* ]] && continue
   P4TestCmd=${entry%%|*}
   ExpectedExit=${entry#*|}
   ExpectedExit=${ExpectedExit%%|*}
   ExpectedString=${entry%|*}
   ExpectedString=${ExpectedString##*|}
   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
      echo -e "Warning: 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
         echo -e "Warning: Entry on line $Line of $CLITestDataFile has a bogus exit code.  Must be numeric or 'U', value is $ExpectedExit. Skipping this test."
         DataOK=0
         continue
      fi
   fi

   echo "C:[$P4TestCmd] E:[$ExpectedExit] S:[$ExpectedString] Comments: $Comments"
done < $CLITestDataFile

if [[ $CLITestDataOK -eq 1 ]]; then
   echo "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. Aborting."
   else
      echo -e "\nWarning: Some tests were skipped due to invalid entries in $CLITestDataFile. Continuing due to '-f'.\n"
   fi
fi

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

if [[ $GetSampleDepot -eq 1 || $InitSampleDepot -eq 1 ]]; then
   shutdown_servers
fi

[[ $GetSampleDepot -eq 1 ]] && get_sample_depot
[[ $InitSampleDepot -eq 1 ]] && init_sample_depot
[[ $InitCBD -eq 1 ]] && init_cbd "$TestWS" "$TestWSRoot"

msg "$H\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.

AllTestsPassed=1

while read entry; do
   [[ -z "$(echo $entry)" ]] && continue
   [[ $entry == "#"* ]] && continue

   TestPassed=1
   P4TestCmd=${entry%%|*}
   ExpectedExit=${entry#*|}
   ExpectedExit=${ExpectedExit%%|*}
   ExpectedString=${entry%|*}
   ExpectedString=${ExpectedString##*|}

   # If the ExpectedString from the data file is prefixed with 'LOG:',
   # set ExpectedStringInOutput=0, indicating the cbd.log file shoudl be
   # searched instead of the command outpout.
   if [[ "$ExpectedString" == "LOG:"* ]]; then
      ExpectedString=${ExpectedString#LOG:}
      ExpectedStringInOutput=0
   else
      ExpectedStringInOutput=1
   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
      continue
   fi


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

   TestCount=$((TestCount+1))
   msg "$H\nTest $TestCount\nTesting Command: $P4TestCmd\nExpected Exit: $ExpectedExit"
   if [[ $ExpectedStringInOutput -eq 1 ]]; then
      msg "Expected string in output: $ExpectedString\nComments: $Comments\n"
   else
      msg "Expected string in cbd.log: $ExpectedString\nComments: $Comments\n"
   fi
   $P4TestCmd > $OutputFile 2>&1
   ActualExitCode=$?

   echo -e "\n== Command Output =="
   cat $OutputFile

   echo TEST_EXIT_CODE: Actual $ActualExitCode, Expected $ExpectedExit

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

   if [[ -f "$CBD_HOME/logs/cbd.log" ]]; then
      echo -e "\n== Contents of cbd.log =="
      cat $CBD_HOME/logs/cbd.log
   fi

   if [[ -n "$ExpectedString" ]]; then
      # Check for the expected string in the command output or the cbd.log file.
      if [[ $ExpectedStringInOutput -eq 1 ]]; then
         grep "$ExpectedString" $OutputFile > /dev/null 2>&1
         if [[ $? -eq 0 ]]; then
            echo -e "\nExpected string [$ExpectedString] found in command outpout."
         else
            echo -e "\nExpected string [$ExpectedString] NOT found in command outpout."
            TestPassed=0
         fi
      else
         if [[ -f "$CBD_HOME/logs/cbd.log" ]]; then
            grep "$ExpectedString" "$CBD_HOME/logs/cbd.log" > /dev/null 2>&1
            if [[ $? -eq 0 ]]; then
               echo -e "\nExpected string [$ExpectedString] found in cbd.log."
            else
               echo -e "\nExpected string [$ExpectedString] NOT found in cbd.log."
               TestPassed=0
            fi
         else
            echo "String [$ExpectedString] expected in cbd.log, but cbd.log is not there."
            TestPassed=0
         fi
      fi
   else
      echo "No expected string defined, skipping check for this test."
   fi

   /bin/rm -f "$CBD_HOME/logs/cbd.log" "$OutputFile"

   if [[ $TestPassed -eq 1 ]]; then
      echo -e "\nTEST $TestCount PASSED\n"
      TestPassedCount=$((TestPassedCount+1))
   else
      echo -e "\nTEST $TestCount FAILED\n"
      TestFailedCount=$((TestFailedCount+1))
      AllTestsPassed=0
   fi

done < $CLITestDataFile

msg "$H\nExecuting Broker Input Simulation Tests."

[[ -r "$BITestCfgFile" ]] || bail "Missing broker input test data file [$BITestCfgFile]."

while read entry; do
   [[ -z "$(echo $entry)" ]] && continue
   [[ $entry == "#"* ]] && continue

   TestPassed=1

   BITFile=${entry%%|*}

   ExpectedExit=${entry#*|}
   ExpectedExit=${ExpectedExit%%|*}
   ExpectedString=${entry%|*}
   ExpectedString=${ExpectedString##*|}

   # If the ExpectedString from the data file is prefixed with 'LOG:',
   # set ExpectedStringInOutput=0, indicating the cbd.log file shoudl be
   # searched instead of the command outpout.
   if [[ "$ExpectedString" == "LOG:"* ]]; then
      ExpectedString=${ExpectedString#LOG:}
      ExpectedStringInOutput=0
   else
      ExpectedStringInOutput=1
   fi

   Comments=${entry##*|}

   TestCount=$((TestCount+1))
   msg "$H\nTest $TestCount\nTesting Broker Input File: $BITFile\nExpected Exit: $ExpectedExit"

   if [[ $ExpectedStringInOutput -eq 1 ]]; then
      msg "Expected string in output: $ExpectedString\nComments: $Comments\n"
   else
      msg "Expected string in cbd.log: $ExpectedString\nComments: $Comments\n"
   fi

   if [[ -r "$CfgDir/$BITFile" ]]; then
      msg "Contents of $BITFile:"
      cat $CfgDir/$BITFile

      $CBD_HOME/scripts/wssync.sh < $CfgDir/$BITFile > $OutputFile 2>&1
      ActualExitCode=$?

      echo -e "\n== Command Output =="
      cat $OutputFile

      echo TEST_EXIT_CODE: Actual $ActualExitCode, Expected $ExpectedExit

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

      if [[ -f "$CBD_HOME/logs/cbd.log" ]]; then
         echo -e "\n== Contents of cbd.log =="
         cat $CBD_HOME/logs/cbd.log
      fi

      if [[ -n "$ExpectedString" ]]; then
         # Check for the expected string in the command output or the cbd.log file.
         if [[ $ExpectedStringInOutput -eq 1 ]]; then
            grep "$ExpectedString" $OutputFile > /dev/null 2>&1
            if [[ $? -eq 0 ]]; then
               echo -e "\nExpected string [$ExpectedString] found in command outpout."
            else
               echo -e "\nExpected string [$ExpectedString] NOT found in command outpout."
               TestPassed=0
            fi
         else
            if [[ -f "$CBD_HOME/logs/cbd.log" ]]; then
               grep "$ExpectedString" "$CBD_HOME/logs/cbd.log" > /dev/null 2>&1
               if [[ $? -eq 0 ]]; then
                  echo -e "\nExpected string [$ExpectedString] found in cbd.log."
               else
                  echo -e "\nExpected string [$ExpectedString] NOT found in cbd.log."
                  TestPassed=0
               fi
            else
               echo "String [$ExpectedString] expected in cbd.log, but cbd.log is not there."
               TestPassed=0
            fi
         fi
      else
         echo "No expected string defined, skipping check for this test."
      fi

      /bin/rm -f "$CBD_HOME/logs/cbd.log" "$OutputFile"

   else
      errmsg "Missing broker input test file [$CfgDir/$BITFile]."
      TestPassed=0
   fi

   if [[ $TestPassed -eq 1 ]]; then
      echo -e "\nTEST $TestCount PASSED\n"
      TestPassedCount=$((TestPassedCount+1))
   else
      echo -e "\nTEST $TestCount FAILED\n"
      TestFailedCount=$((TestFailedCount+1))
      AllTestsPassed=0
   fi

done < $BITestCfgFile

[[ $AllTestsPassed -eq 0 ]] && OverallReturnStatus=1

if [[ $OverallReturnStatus -eq 0 ]]; then
   msg "${H}\nAll $TestCount tests PASSED.\n"
else
   msg "${H}\nProcessing completed, but with errors.\nTests Executed (Passed/Failed): $TestCount ($TestPassedCount/$TestFailedCount)\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 $OverallReturnStatus
# Change User Description Committed
#32 25083 C. Thomas Tyler Fixed a few typos on the man page.
#31 22190 C. Thomas Tyler Adapted logic for logging in to adapt to SDP changes.
#30 21805 C. Thomas Tyler Routine merge down to dev from main.
#29 19331 C. Thomas Tyler Changed Options: of workspaces used in test suite, noallwrite->allwrite,
and noclobber->clobber, to enhance test robustness when running
a series of tests repetitively during development.
#28 19330 C. Thomas Tyler Minor audit tweak to capture version of test_cbd.sh in the log.
#27 19262 C. Thomas Tyler Routine Merge Down to dev from main.
#26 19253 C. Thomas Tyler Routine merge-down from main to dev.
#25 16355 C. Thomas Tyler Routine Merge Down to dev from main using:
p4 -s merge -b perforce_software-cbd-dev
#24 15358 C. Thomas Tyler Tweaked test prep to use an uppercase drive letter in creation of
               test workspace earl_jams, to better test for Windows drive letter
       issues (Workshiop job job000330).

       Added new test for job000330, which initially failed (a valid repro).

       With changes in Cbd.py, it now passes.
#23 15280 C. Thomas Tyler Consistency tweak, now CBD log is always referred to as CBDLOG, to
differenciate from other logs.
#22 15278 C. Thomas Tyler Enhanced configurability of broker input tests.

Fixed bug in test suite where Broker Input Tests error code checking
       always failed unless the expected exit code was 0.
#21 15274 C. Thomas Tyler Merge Down of cbd to dev from main using:
p4 merge -b perforce_software-cbd-dev
#20 15178 C. Thomas Tyler Routine Merge Down to dev from main for CBD using:
p4 merge -b perforce_software-cbd-dev
#19 15155 C. Thomas Tyler Removed obsolete cleanup logic.
#18 15046 C. Thomas Tyler Minor code cleanup in test sutie.
#17 14996 C. Thomas Tyler Minor tweaks to SDP/test suite handling.
#16 14994 C. Thomas Tyler Added logic to acquire Helix Installer SDP Reset script if
it isn't in the expected location.

Refined SDP interaction.
#15 14992 C. Thomas Tyler Simplified.
#14 14981 C. Thomas Tyler Enhanced broker input simulation testing to account for testing
with SDP, by parameterizing previously hard-coded test data
fields.
#13 14979 C. Thomas Tyler Fixed reference to CBD log file.
#12 14977 C. Thomas Tyler Re-added -B.
#11 14975 C. Thomas Tyler Handles stream spec trigger log same way as CBD log.

Test suite fails early if submit fails while setting up
the test scenarios (e.g. if blocked by the trigger).

Added '-no_ssl' flags to called reset_sdp.sh script.
#10 14959 C. Thomas Tyler Implemented '-S' and configured to work existing SDP, as set up
by the Helix SDP Installer.

Work in progress.
#9 14911 C. Thomas Tyler Further tweaked test suite to look hard for the config data.
Convenience tweak to manual test environment script.
#8 14908 C. Thomas Tyler Added '-a' tag to indicate that we are running in Continuous
Integration Automation mode.  Presently, that just means to
shutdown the broker and p4d services started for testing after
the tests are done.

Updated automated test suite to call this.
#7 14907 C. Thomas Tyler Moved test data files into 'shared' folder to make them available
on the 'helix-01' text VM, and tweaked test_cbd.sh to reference them
there.
#6 14894 C. Thomas Tyler Optimized tarfile handling.

Continued removing hard-coded paths, relying on PATH more for
better cross-platform operation.
#5 14859 C. Thomas Tyler Replaced /usr/bin/wget with just 'wget', to support Mac w/o
breaking Linux operation.  The PATH dependeny is OK since
it'll be in /usr/bin or /usr/local/bin, both commonly in
default PATHs.
#4 14855 C. Thomas Tyler Removed '-n' (NoOp) mode, as it is no longer viable as a pre-commit
triggers.

Updated test suite to use pre-commit 'submit-content' type triggers.
#3 14844 C. Thomas Tyler Overhauled regression test suite.

Added various command line tests.

Enhanced test suite to allow entries in the test data file to specify
alternate user and workspaces (differing from defaults defined in
the test test configuration files, test_cbd.*.cfg) for certain
tests.

Added new testing method, broker input testing, to augment existing
       command line testing.

       This test method simulates having 'p4d' call the CBD broker sync
       script by feeding handcrafted/simulated broker input files to the CBD
       engine, simulating the way p4d uses stdin to feed the broker
       parameters when a user makes a request.

       This enables more diverse testing and better simulation of Window
       and P4V-side testing.
#2 14199 C. Thomas Tyler Added test suite.
#1 11352 C. Thomas Tyler Added CBD test script.
 This version is dependendnt
on the environment it was born in; it needs to be
modified to work in a CBD test harness environment.