Added rantest and cqtool to repo
[clearscm.git] / rantest / rantest
diff --git a/rantest/rantest b/rantest/rantest
new file mode 100644 (file)
index 0000000..bb3f5ca
--- /dev/null
@@ -0,0 +1,1375 @@
+#!/usr/bin/perl\r
+##############################################################################\r
+#\r
+# Name:                rantest\r
+#\r
+# Description: This script is a test driver script capable of running tests\r
+#              individually or from a file. There are many facilities for\r
+#              specifying input and options to this program - see the usage\r
+#              and help subroutines for clues. Basically you can run rantest\r
+#              by itself and it will interactively prompt you for what to do\r
+#              and what information or options it needs. Additionally you can\r
+#              set options in the environment such as RANTEST_VIEW or\r
+#              RANTEST_UNIT to serve as defaults. Or you can use -view or\r
+#              -type, for example, at the command line to supply such parms.\r
+#              If rantest still doesn't have all it needs it will prompts.\r
+#\r
+#              Note that options and/or test cases can be specified in config\r
+#              files specified by RANTEST_FILE or -file. Embedded in the\r
+#              config file can be additional options in the form of:\r
+#\r
+#              b2_l3_rnc_irt_001.test:\r
+#              ----------------------\r
+#              view:   p6258c_SIMCQ00000100_intview\r
+#              type:   rnc\r
+#              unit:   4\r
+#              exec: tc CN_SIM/TC_CN_Simulation_RANAP_Setup.profile -timeout 60\r
+#              exec: tc CN_SIM/TC_CN_Simulation.profile -timeout -1 -pause 5\r
+#              exec: tc RBS_SIM/TC_RBS_Sim_Cell_Setup.profile -timeout 180\r
+#              exec: tc l3_rnc/irt/TC_b2_l3_rnc_irt_001.profile -timeout 180\r
+#\r
+#              Above we've set view, type and unit for the test run\r
+#              and defined test steps of tc\r
+#              CN_SIM/TC_CN_Simulation_RANAP_Setup.profile,\r
+#              CN_SIM/TC_CN_Simulation.profile,\r
+#              RBS_SIM/TC_RBS_Sim_Cell_Setup.profile and\r
+#              l3_rnc/irt/TC_b2_l3_rnc_irt_001.profile\r
+#\r
+#              Note that you can specify optional additional\r
+#              parameters after the test name like -timeout and a\r
+#              bunch of other parameters as described in the "Command\r
+#              Line in East" document.\r
+#\r
+#              This would be run as so:\r
+#\r
+#              $ rantest -file b2_l3_rnc_irt_001.test\r
+#\r
+#              Suite files, those ending in .suite, are different\r
+#              from .test files in that they merely contain a list of\r
+#              .test files (relative to <view>/vobs/simdev/test) to\r
+#              execute for this suite. Note that parameters can be\r
+#              added after the suite file name:\r
+#\r
+#              nightly.suite:\r
+#              --------------\r
+#              # RNC IRT tests\r
+#              b2_l3_rnc_irt_007.test -unit 4\r
+#              b2_l3_rnc_irt_014.test\r
+#\r
+#              # RNC SCH tests\r
+#              b2_l3_rnc_sch_001.test -view official_view\r
+#              b2_l3_rnc_sch_003a.test\r
+#\r
+# Author:      Andrew@ClearSCM.com\r
+#\r
+# Copyright (c) 2008, 2009 General Dynamics\r
+#\r
+# All rights reserved except as subject to DFARS 252.227-7014 of contract\r
+# number CP02H8901N issued under prime contract N00039-04-C-2009.\r
+#\r
+# Warning: This document contains technical data whose export is restricted\r
+# by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the\r
+# Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401\r
+# et seq. Violations of these export laws are subject to severe criminal\r
+# penalties. Disseminate in accordance with provisions of DoD Directive\r
+# 5230.25.\r
+#\r
+##############################################################################\r
+use strict;\r
+use warnings;\r
+\r
+use File::Basename;\r
+use File::Glob ':glob';\r
+use File::Path;\r
+use FindBin;\r
+use Getopt::Long;\r
+use Net::Domain qw (hostname);\r
+use Term::ANSIColor qw (:constants);\r
+use Term::ReadLine;\r
+use Term::ReadLine::Gnu;\r
+\r
+# Use the SDE Tools libraries. Sorry for this long path. I didn't come\r
+# up with it!\r
+#use lib "/cleartrig/ent/SNSD/muos/ccadm_tools/vobs/ranccadm/scripts/lib";\r
+use lib "$FindBin::Bin/../lib";\r
+\r
+use DateUtils;\r
+use Display;\r
+use Utils;\r
+use GetConfig;\r
+use Logger;\r
+\r
+error "$FindBin::Script only runs on seast1", 1\r
+  unless hostname eq "seast1";\r
+\r
+use lib "$FindBin::Bin/../lib";\r
+\r
+use GD::RantestDB;\r
+use GD::Nethawk::East;\r
+\r
+use constant VERSION_NBR=> "1.2.5";\r
+use constant VERSION   => BOLD GREEN   VERSION_NBR;\r
+use constant PROMPT    => BOLD YELLOW  "$FindBin::Script>";\r
+use constant DESC      => BOLD RED     "$FindBin::Script",\r
+                          RESET        "Version", VERSION\r
+                         . RESET       ": "\r
+                         . BOLD CYAN   "RAN T"\r
+                         . RESET       "ool for "\r
+                         . BOLD CYAN   "E"\r
+                        . RESET        "xecution of "\r
+                        . BOLD CYAN    "S"\r
+                        . RESET        "ystem "\r
+                        . BOLD CYAN    "T"\r
+                        . RESET        "ests";\r
+\r
+use constant HISTORY_FILE => "$ENV{HOME}/.rantest_hist";\r
+use constant LOGBASE     => "$ENV{MNT_DIR}/testlogs";\r
+\r
+############################################################################\r
+# Globals\r
+############################################################################\r
+my $_east;\r
+my $_connected         = 0;\r
+my $_debugging;\r
+my $_log;\r
+my $_term;\r
+my $_rantestdb;\r
+my $_testNbr           = 0;\r
+my $_validationNbr     = 0;\r
+\r
+my %_stats;\r
+\r
+my %_executionResults;\r
+\r
+my (%_opts, %_cmdline_opts);\r
+\r
+# Seed opts from the environment.\r
+$_opts{eastview}       = $ENV{RANTEST_EASTVIEW}        if $ENV{RANTEST_EASTVIEW};\r
+$_opts{eastview}       = $ENV{RANTEST_VIEW}            if $ENV{RANTEST_VIEW} && !$_opts{eastview};\r
+$_opts{tm500view}      = $ENV{RANTEST_TM500VIEW}       if $ENV{RANTEST_TM500VIEW};\r
+$_opts{nmsview}                = $ENV{RANTEST_NMSVIEW}         if $ENV{RANTEST_NMSVIEW};\r
+$_opts{type}           = $ENV{RANTEST_TYPE}            if $ENV{RANTEST_TYPE};\r
+$_opts{class}          = $ENV{RANTEST_CLASS}           if $ENV{RANTEST_CLASS};\r
+$_opts{unit}           = $ENV{RANTEST_UNIT}            if $ENV{RANTEST_UNIT};\r
+$_opts{test}           = $ENV{RANTEST_TEST}            if $ENV{RANTEST_TEST};\r
+$_opts{file}           = $ENV{RANTEST_FILE}            if $ENV{RANTEST_FILE};\r
+$_opts{rfr}            = $ENV{RANTEST_RFR}             if $ENV{RANTEST_RFR};\r
+$_opts{checkin_on_error}= $ENV{CHECKIN_ON_ERROR}        if $ENV{CHECKIN_ON_ERROR};\r
+$_opts{feature}                = $ENV{RANTEST_FEATURE}         if $ENV{RANTEST_FEATURE};\r
+$_opts{regression}     = $ENV{RANTEST_REGRESSION}      if $ENV{RANTEST_REGRESSION};\r
+$_opts{secure}         = $ENV{RANTEST_SECURE}          if $ENV{RANTEST_SECURE};\r
+\r
+# Explicitly default secure to either $ENV{RANTEST_SECURE}, if defined, otherwise 1\r
+$_opts{secure} = $ENV{RANTEST_SECURE} ? $ENV{RANTEST_SECURE} : 1;\r
+\r
+sub usage (;$) {\r
+  my ($msg) = @_;\r
+\r
+  my $usage = "ERROR: $msg\n\n" if $msg;\r
+\r
+  $usage .= <<END;\r
+Usage: $FindBin::Script\t[-us|age] [-v|erbose] [-d|ebug]\r
+  [-view|-eastview <view>] [-tm500view <view>] [-nmsview <view>]\r
+  [-type <type>] [-class <class>] [-rfr <testcase ID>] [-checkin_on_error]\r
+  [-unit <unit #>] [-test <test>] [-file <file>] [-feature <feature>]\r
+  [-[no]s|ecure]\r
+\r
+Where:\r
+\r
+  -us|age:          Display usage\r
+  -ve|rbose:        Turn on verbose mode\r
+  -d|ebug:          Turn on debug mode\r
+  -[ea|st]view <tag> View tag to run test(s) under\r
+  -tm|500view <tag>  TM 500 view to set into the environment for\r
+                    test\r
+  -nm|sview <tag>    NMS view to set into the environment for\r
+                    test\r
+  -ty|pe <type>      Type of unit to test (i.e. rbs | rnc)\r
+  -c|lass <class>    Class of test (i.e. one of the following):\r
+\r
+      Load          LoadTCRunner\r
+      Pool          RegressionLoadRunner\r
+      TC            RegressionRunner\r
+      TS            RegressionTSRunner\r
+      Manual        Manual test\r
+\r
+  -un|it <unit #>    Unit number to test on\r
+  -te|st <test>      Name of test (Optional: If not specific you will\r
+                     be prompted for test case names)\r
+  -f|ile <file>      File containing a list of tests to execute (Optional:\r
+                     Contains a file of test classes and names to test)\r
+  -rfr <testcase ID> Run for record: ID is the test case ID to store\r
+                    results under\r
+  -checkin_on_error  Checks in rfr log files even if a test(s) fails\r
+  -regression       Run regression tests. These tests will log there\r
+                    results.\r
+  -feature <feature> If specified then FEATURE will be set into the\r
+                    environment on the blade before calling\r
+                    start_east_auto.\r
+  -[no]s|ecure      Indicates whether or not to secure the node before\r
+                    attempting to connect to it. (Default: secure).\r
+\r
+NOTE: Set ANSI_COLORS_DISABLED to turn off colors\r
+\r
+If you specify -file you cannot specify -test nor -class. -class'es are\r
+read from -file.\r
+\r
+Additionally, options above can be specified in the environment by\r
+preceeding the environment variable with \"RANTEST_\". For example,\r
+\r
+  \$ export RANTEST_TYPE=rbs\r
+  \$ export RANTEST_UNIT=2\r
+  \$ rantest\r
+\r
+Also such options can be specified in the -file:\r
+\r
+  unit: 5\r
+  executionlevel: 2\r
+  ts ts1.profile -timeout -1 -pause 5\r
+  ts ts2.profile -displaylevel 3\r
+  ts ts3.profile -activecalls 10\r
+\r
+Options after test profile name are passed directory to EAST's command\r
+line. The exceptions are -timeout and -pause:\r
+\r
+  -timeout <n>         Specifies the timeout for this test's execution.\r
+                       If negative the test will be placed in the\r
+                       background. No result is recovered from\r
+                       background tests nor are any logfiles analysed\r
+                       or stored. If positive then this sets the\r
+                       timeout period for this test in seconds.\r
+\r
+  -pause <n>           Used in conjunction with -timeout. If test is\r
+                       backgrounded then $FindBin::Script will wait\r
+                       pause seconds before returning control from\r
+                       this test. This allows the backgrounded test\r
+                       time to start.\r
+\r
+  -name <name>         Names a test. Used in conditional execution.\r
+\r
+  -if (<name> <status>)        Run this test if the named test returned <status>\r
+                       where <status> is one of\r
+\r
+                         . Success\r
+                         . Failure\r
+                         . In Progress\r
+                         . Timed out\r
+                         . Failed to execute\r
+                         . Rendezvous\r
+                         . Failed to rendezvous\r
+\r
+Note: Required options not supplied will be prompted for.\r
+END\r
+\r
+  my $pager = $ENV{PAGER} ? $ENV{PAGER} : "more";\r
+\r
+  system "echo \"$usage\" | $pager";\r
+\r
+  exit 1 if $msg;\r
+} # usage\r
+\r
+sub help () {\r
+  display DESC . RESET;\r
+  display <<END;\r
+\r
+Valid commands are:\r
+\r
+help:                  This display\r
+usage:                 Displays command line usage\r
+version:               Displays version of $FindBin::Script\r
+exit|quit:             Exits $FindBin::Script\r
+source <file>          Execute the contents of <file>\r
+set <option>=<value>   Set <option> to <value>\r
+get <option>           Displays <option> (if set)\r
+elock <pattern>                Display elock status (default all units)\r
+\r
+Running tests:\r
+\r
+load <test profile> <options> Run a test case by profile (LoadTCRunner)\r
+pool <test profile> <options> Run a regression load (RegressionLoadRunner)\r
+tc   <test profile> <options> Run a regression (RegressionRunner)\r
+ts   <test profile> <options> Run a regression test suite (RegressionTSRunner)\r
+manual\r
+\r
+Note: ReadLine is supported thus you can edit previous commands.\r
+Try the up arrow!\r
+END\r
+} # Help\r
+\r
+sub getParm ($) {\r
+  my ($prompt) = @_;\r
+\r
+  my $value;\r
+\r
+  while (!$value or $value eq "") {\r
+    display_nolf BOLD YELLOW . "$FindBin::Script needs the following parameter - $prompt" . RESET;\r
+\r
+    $value = <STDIN>;\r
+\r
+    chomp $value;\r
+  } # while\r
+\r
+  return $value;\r
+} # getParm\r
+\r
+sub eLock (;$) {\r
+  my ($unit) = @_;\r
+\r
+  my ($status, @locks) = Execute "ls $ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/*/desktop.lock 2> /dev/null";\r
+\r
+  $status >>= 8;\r
+\r
+  foreach (@locks) {\r
+    my $unit_found;\r
+\r
+    if (/.*DUT\/(\w+)\/desktop/) {\r
+      $unit_found = $1;\r
+\r
+      next if $unit && $unit_found !~ /$unit/i;\r
+    } # if\r
+\r
+    my @fields = split /\//, $_;\r
+    my $uid    = (stat $_)[4];\r
+    my $mtime  = (stat $_)[9];\r
+    my $userid = (getpwuid ($uid))[0];\r
+    my $name   = (getpwuid ($uid))[6];\r
+\r
+    display BOLD CYAN  "$fields[5]\t"\r
+          . RESET      "locked since "\r
+          . BOLD YELLOW        localtime ((stat $_)[9])\r
+          . RESET      " by "\r
+          . MAGENTA    $name\r
+          . RESET      " ("\r
+          . GREEN      $userid\r
+         . RESET       ")";\r
+  } # foreach\r
+} # eLock\r
+\r
+sub displaySummary () {\r
+  my $msg = "Summary:";\r
+\r
+  foreach (sort keys %_stats) {\r
+    $msg .= " $_stats{$_} $_";\r
+  } # foreach\r
+\r
+  $_log->msg ($msg) if $_log;\r
+} # displaySummary\r
+\r
+sub announceTestrun ($) {\r
+  my ($testname) = @_;\r
+\r
+  my $user = $ENV{USER}                ? $ENV{USER}\r
+           : $ENV{LOGNAME}     ? $ENV{LOGNAME}\r
+          : "Unknown";\r
+  my $timestamp = YMDHMS;\r
+\r
+  $_testNbr++;\r
+\r
+  verbose BOLD YELLOW  "Test #" . $_testNbr . "\t"\r
+        . RESET CYAN   $testname\r
+        . RESET                " run on $timestamp by "\r
+        . YELLOW       $user\r
+        . RESET                " on "\r
+        . MAGENTA      $_opts{type}\r
+        . RESET                " unit "\r
+        . CYAN         $_opts{unit}\r
+        . RESET;\r
+\r
+  $_log->log ("Test #$_testNbr $testname run on: $timestamp by $user on $_opts{type} unit $_opts{unit}");\r
+} # announceTestrun\r
+\r
+sub saveHistory {\r
+  $_term->WriteHistory (HISTORY_FILE) if $_term;\r
+} # saveHistory\r
+\r
+sub executeTestStep () {\r
+  if (!$_connected) {\r
+    # Get required parameters if not specified in the command line or environment\r
+    $_opts{eastview}   = getParm "View:"  unless $_opts{eastview};\r
+    $_opts{type}       = getParm "Type:"  unless $_opts{type};\r
+    $_opts{class}      = getParm "Class:" unless $_opts{class};\r
+    $_opts{unit}       = getParm "Unit:"  unless $_opts{unit};\r
+\r
+    # Connect to it\r
+    my $msg = $_east->connect (\r
+      $_opts{eastview},\r
+      $_opts{type},\r
+      $_opts{unit},\r
+      $_opts{tm500view},\r
+      $_opts{nmsview},\r
+      $_opts{feature},\r
+      $_opts{secure}\r
+    );\r
+\r
+    if ($msg) {\r
+      $_log->err ("Unable to connect to EAST\n$msg");\r
+      verbose RED "Skipping renaming test steps" . RESET;\r
+      $_stats{Failed}++;\r
+      return -1;\r
+    } else {\r
+      $_connected = 1;\r
+    } # if\r
+  } # if\r
+\r
+  my ($status, $msg) = Nethawk::East::validTestType ($_opts{class});\r
+\r
+  if ($status != 0) {\r
+    $_log->err ($msg);\r
+    return $status;\r
+  } # if\r
+\r
+  if (!$_opts{test}) {\r
+    # Manual tests only have classes\r
+    unless ($_opts{class} eq 'manual') {\r
+      $_log->err ("No test specified");\r
+      return 1;\r
+    } # unless\r
+  } # if\r
+\r
+  $_east->setCollectLogFiles (1);\r
+\r
+  verbose_nolf $_stats{Run} . ": " . CYAN "$_opts{class}\t" . BOLD YELLOW $_opts{test} . RESET;\r
+\r
+  my $testStepResult;\r
+\r
+  my $startTime        = time;\r
+  my $stepName = "Unknown";\r
+\r
+  if ($_opts{test} =~ /(.*)\.profile/) {\r
+    $stepName = $1;\r
+  } elsif ($_opts{class} eq "manual") {\r
+    $stepName = "Manual";\r
+  } elsif ($_opts{class} eq "shell") {\r
+    $stepName = $_opts{test};\r
+  } elsif ($_opts{test} =~ /^rantvl/) {\r
+    $stepName = $_opts{test};\r
+\r
+    $_east->setRantvlStartTime (time);\r
+  } # if\r
+\r
+  my ($stepID, $errMsg) = $_rantestdb->startSteprun ($stepName);\r
+\r
+  ($status, $testStepResult) = $_east->exec (\%_opts, \%_executionResults);\r
+\r
+  # Collect any extended logs\r
+  if ((!defined $_opts{timeout} or $_opts{timeout} > 0) and\r
+      ($_east->{class} eq "load" or\r
+       $_east->{class} eq "tc"   or\r
+       $_east->{class} eq "ts"   or\r
+       $_east->{class} eq "pool")) {\r
+    $_east->collectExtendedLogFiles;\r
+  } # if\r
+\r
+  my $endTime = time;\r
+\r
+  if ($status == 0) {\r
+    if ($testStepResult eq "Success") {\r
+      verbose GREEN " $testStepResult" . RESET;\r
+      $_stats{Passed}++;\r
+    } elsif ($testStepResult eq "In progress" or\r
+            $testStepResult eq "Logging started") {\r
+      verbose MAGENTA " $testStepResult" . RESET;\r
+      $_stats{Concurrent}++;\r
+    } elsif ($testStepResult eq "Rendezvous") {\r
+      verbose BOLD . " $testStepResult" . RESET;\r
+      $_stats{Rendezvous}++;\r
+    } else {\r
+      verbose RED " $testStepResult" . RESET;\r
+      $status = 1;\r
+      $_stats{Failed}++;\r
+    } # if\r
+  } else {\r
+    if ($testStepResult eq "Skipped") {\r
+      verbose BOLD . " $testStepResult" . RESET;\r
+      $_stats{Skipped}++;\r
+    } elsif ($testStepResult eq "Timed out") {\r
+      verbose CYAN " $testStepResult" . RESET;\r
+      $status = 1;\r
+      $_stats{Timedout}++;\r
+    } else {\r
+      verbose RED " $testStepResult" . RESET;\r
+      $status = 1;\r
+      $_stats{Failed}++;\r
+    } # if\r
+  } # if\r
+\r
+  # Log test step result\r
+  $_log->log ("$_stats{Run}: $_opts{class}\t$_opts{test} $testStepResult");\r
+\r
+  my ($dbErrNbr, $dbErrMsg) = $_rantestdb->endSteprun (\r
+    runID      => $_east->{runID},\r
+    stepID     => $stepID,\r
+    start      => UnixDatetime2SQLDatetime (scalar (localtime ($startTime))),\r
+    end                => UnixDatetime2SQLDatetime (scalar (localtime ($endTime))),\r
+    result     => $testStepResult,\r
+  );\r
+\r
+  error $dbErrMsg if $dbErrNbr != 0;\r
+\r
+  return $status;\r
+} # executeTestStep\r
+\r
+sub testTimeout {\r
+  error "Test timed out ($_opts{testtimeout}) seconds passed)";\r
+\r
+  $_east->disconnect;\r
+\r
+  # Collect logfiles\r
+  $_east->collectLogFiles;\r
+} # testTimeout\r
+\r
+sub interrupted {\r
+  use Term::ReadKey;\r
+\r
+  display BLUE "\nInterrupted" . RESET;\r
+\r
+  displaySummary;\r
+\r
+  display_nolf\r
+    CYAN       . BOLD "C" . RESET CYAN         "ontinue"       . RESET . " or " .\r
+    MAGENTA    . BOLD "A" . RESET MAGENTA      "bort run"      . RESET . " (" .\r
+    CYAN       . BOLD "C" . RESET "/" .\r
+    MAGENTA    . BOLD "a" . RESET ")?";\r
+\r
+  ReadMode ("cbreak");\r
+  my $answer = ReadKey (0);\r
+  ReadMode ("normal");\r
+\r
+  if ($answer eq "\n") {\r
+    display "c";\r
+  } else {\r
+    display $answer;\r
+  } # if\r
+\r
+  $answer = lc $answer;\r
+\r
+  if ($answer eq "c") {\r
+    display "Continuing...";\r
+  } elsif ($answer eq "a") {\r
+    display RED "Aborting run" . RESET;\r
+    $_east->setCollectLogFiles (0);\r
+    saveHistory;\r
+    exit;\r
+  } # if\r
+} # interrupted\r
+\r
+sub interpolate ($) {\r
+  my ($str) = @_;\r
+\r
+  # Perform psuedo variable interpolation. The following psuedo\r
+  # variables are supported:\r
+  #\r
+  # view:        Absolute path to your view\r
+  my $view     = "$ENV{MNT_DIR}/snapshot_views/$_east->{userdir}/$_east->{view}";\r
+  my $simdev   = "$view/vobs/simdev";\r
+\r
+  # msgdefs:     Absolute path to msgdefs\r
+  my $msgdefs  = "$simdev/msgdefs";\r
+\r
+  # validation:  Absolute path to validation\r
+  my $validation       = "$simdev/validation";\r
+\r
+  # logpath:     Absolute path into the "testlogs" area where\r
+  #              logfiles are written\r
+  my $logpath  = LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+  while ($str =~ /\$/) {\r
+    my ($var, $slice);\r
+\r
+    if ($str =~ /\$(\w+)/) {   \r
+      # Regular $var\r
+      $var     = $1;\r
+    } elsif ($str =~ /\$(\[.+?\])\[(.+?)\]/) {\r
+      # A $[fileset][slice] reference\r
+      $var     = $1;\r
+      $slice   = $2;\r
+    } elsif ($str =~ /\$(\[.+?\])/) {  \r
+      # A $[fileset] reference\r
+      $var     = $1;\r
+    } # if\r
+\r
+    if ($var eq "logpath") {\r
+      $str =~ s/\$$var/$logpath/;\r
+    } elsif ($var eq "msgdefs") {\r
+      $str =~ s/\$$var/$msgdefs/;\r
+    } elsif ($var eq "validation") {\r
+      $str =~ s/\$$var/$validation/;\r
+    } elsif ($var eq "view") {\r
+      $str =~ s/\$$var/$view/;\r
+    } elsif ($var =~ /\[(.+)\]/) {\r
+      my $fileset = $1;\r
+\r
+      my @fileset = glob $fileset;\r
+      my $list;\r
+\r
+      if (defined $slice) {\r
+       $fileset = quotemeta $fileset;\r
+\r
+       # Here we handle a slice, but if the slice is of the form x..y\r
+       # then we need to handled it differently\r
+       if ($slice =~ /(\d+)\.\.(\d+)/) {\r
+         # Need to turn off warnings for this next construct of\r
+         # @array[$1..$2]. Otherwise it complains. If we use\r
+         # $array[$1..$2] then it doesn't work! Also take on the\r
+         # base fileset defined above.\r
+         #\r
+         # Adjust bounds\r
+         $2 = $#fileset if $2 > $#fileset;\r
+\r
+         no warnings;\r
+         $list = join ",", @fileset[$1..$2];\r
+         use warnings;\r
+       } else {\r
+         # Not a slice really but an array reference\r
+         $list = "$fileset[$slice]";\r
+       } # if\r
+\r
+       $str =~ s/\$\[$fileset\]\[$slice\]/$list/;\r
+      } else {\r
+       $list = join ",", @fileset;\r
+       $str =~ s/\$\[$fileset\]/$list/;\r
+      } # if\r
+\r
+      if (defined $slice) {\r
+       $str =~ s/\$\[$fileset\]\[$slice\]//;\r
+      } else {\r
+       $str =~ s/\$\[$fileset\]//;\r
+      } # if\r
+    } else {\r
+      error "Unknown variable ($var) encountered in val line:\n$str", 1;\r
+    } # if\r
+  } # while\r
+\r
+  return $str;\r
+} # interpolate\r
+\r
+sub runValidation ($$) {\r
+  my ($cmd, $logfile) = @_;\r
+\r
+  my $origCmd = $cmd;\r
+\r
+  my ($stepID, $errMsg);\r
+\r
+  ($stepID, $errMsg) = $_rantestdb->startSteprun ($origCmd);\r
+\r
+  if ($stepID == 0) {\r
+    error "Unable to startSteprun\n$errMsg";\r
+    return 1;\r
+  } # if\r
+\r
+  my $startTime = time;\r
+\r
+  $cmd = interpolate ($cmd);\r
+\r
+  my ($status, @lines) = Execute ("$cmd >> $logfile 2>&1");\r
+\r
+  $status >>= 8;\r
+\r
+  my $endTime = time;\r
+\r
+  my ($dbErrNbr, $dbErrMsg) = $_rantestdb->endSteprun (\r
+    runID      => $_east->{runID},\r
+    stepID     => $stepID,\r
+    start      => UnixDatetime2SQLDatetime (scalar (localtime ($startTime))),\r
+    end                => UnixDatetime2SQLDatetime (scalar (localtime ($endTime))),\r
+    result     => $status ? "Failure" : "Success",\r
+  );\r
+\r
+  error $dbErrMsg if $dbErrNbr != 0;\r
+\r
+  # Output lines to stdout\r
+  if (-e $logfile) {\r
+    verbose "$_\n" foreach (ReadFile $logfile);\r
+  } else {\r
+    verbose "Unable to read $logfile";\r
+    $status++;\r
+  } # if\r
+\r
+  $_validationNbr++;\r
+\r
+  verbose BOLD YELLOW . "Test #$_testNbr validation #$_validationNbr \t" . RESET CYAN $origCmd\r
+    . (($status == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+  $_log->log ("Test #$_testNbr validation #$_validationNbr\t$origCmd " . (($status == 0) ? "Success" : "Failure"));\r
+\r
+  return $status;\r
+} # runValidation\r
+\r
+sub runValidations (@) {\r
+  my @validations = @_;\r
+\r
+  my $validationErrs = 0;\r
+\r
+  # Make Validation log directory\r
+  my $validationDir = LOGBASE . "/" . $_east->getSaveTo . "/Validations";\r
+\r
+  eval { mkpath $validationDir };\r
+\r
+  error "Unable to create Validation directory - $validationDir\n$@", 1 if $@;\r
+\r
+  chmod 0775, $validationDir;\r
+\r
+  my $viewPath = "$ENV{MNT_DIR}/snapshot_views/$_east->{userdir}/$_east->{view}";\r
+  my $vobPath  = "vobs/simdev";\r
+\r
+  foreach (@validations) {\r
+    my @tokens = split;\r
+    my $outfile        = $tokens[0] . ".log";\r
+\r
+    $validationErrs += runValidation $_, "$validationDir/$outfile";\r
+  } # foreach\r
+\r
+  $_stats{Failed} += $validationErrs;\r
+\r
+  return $validationErrs;\r
+} # runValidations\r
+\r
+sub runTestFile ($) {\r
+  my ($file) = @_;\r
+\r
+  my $testName = fileparse $file, ".test";\r
+\r
+  my $testID = $_rantestdb->startTest ($testName);\r
+\r
+  my %fileopts = GetConfig ($file);\r
+\r
+  # GetConfig leaves keys %fileopts as case sensitive but we want them\r
+  # case insentive so fix that here.\r
+  foreach (keys (%fileopts)) {\r
+    my $key = lc $_;\r
+\r
+    # Make "view" an alias for "eastview" but only if there is no\r
+    # eastview already defined\r
+    if ($key eq "view") {\r
+      if (!$fileopts{eastview}) {\r
+       $fileopts{eastview} = delete $fileopts{view};\r
+       $_ = $key = "eastview";\r
+      } # if\r
+    } # if\r
+\r
+    # Set into %_opts only if that key doesn't exist already. This\r
+    # allows command line options to override options specified in the\r
+    # file. The exception to this is the exec array. This gets\r
+    # replaced in suite runs.\r
+    if ($key eq "exec" || !$_opts{$key}) {\r
+      $_opts{$key} = $fileopts{$_};\r
+    } # if\r
+  } # foreach\r
+\r
+  my $testStartTime = time;\r
+\r
+  $_east->setSaveTo ("$testName/$_opts{type}$_opts{unit}/" . YMDHMS ($testStartTime));\r
+\r
+  eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+  error "Unable to create log directory\n$@", 1 if $@;\r
+\r
+  chmod 0775, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+  unless ($_log) {\r
+    $_log = new Logger (\r
+      name     => $testName,\r
+      path     => LOGBASE . "/" . $_east->getSaveTo,\r
+      append   => "yes",\r
+    );\r
+\r
+    $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+  } # unless\r
+\r
+  verbose BOLD CYAN "Using view: " . RESET $_opts{eastview};\r
+\r
+  announceTestrun $testName;\r
+\r
+  my @tokens;\r
+\r
+  my $testFailures     = 0;\r
+  my $result           = 0;\r
+  my $errMsg;\r
+\r
+  # Set testTimer if specified\r
+  if ($_opts{testtimeout}) {\r
+    $SIG{ALRM} = \&testTimeout;\r
+    alarm $_opts{testtimeout};\r
+  } # if\r
+\r
+  ($_east->{runID}, $errMsg) = $_rantestdb->startTestrun (UnixDatetime2SQLDatetime localtime $testStartTime);\r
+\r
+  return ($_east->{runID}, $errMsg) if $_east->{runID} == 0;\r
+\r
+  $_validationNbr = 0;\r
+\r
+  if (ref $_opts{exec} eq "ARRAY") {\r
+    foreach (@{$_opts{exec}}) {\r
+      @tokens = split;\r
+\r
+      $_opts{class} = shift @tokens;\r
+      $_opts{test}  = join " ", @tokens;\r
+\r
+      $_stats{Run}++;\r
+\r
+      $result = executeTestStep;\r
+\r
+      if ($result == -1) {\r
+       $testFailures++;\r
+       last;\r
+      } else {\r
+       $testFailures += $result;\r
+      } # if\r
+    } # foreach\r
+  } else {\r
+    if ($_opts{exec}) {\r
+      @tokens = split /\s+/, $_opts{exec};\r
+\r
+      $_opts{class} = shift @tokens;\r
+      $_opts{test}  = join " ", @tokens;\r
+\r
+      $_stats{Run}++;\r
+\r
+      $result = executeTestStep;\r
+\r
+      if ($result == -1) {\r
+       $testFailures++;\r
+      } else {\r
+       $testFailures += $result;\r
+      } # if\r
+    } # if\r
+  } # if\r
+\r
+  my $execType = $_opts{rfr}           ? "Run for Record"\r
+               : $_opts{regression}    ? "Regression" : "Normal";\r
+\r
+  return 1 if $result == -1;\r
+\r
+  # Disconnect from EAST\r
+  $_east->disconnect;\r
+\r
+  # Assign 'Failed' and 'Timedout' 0 if they are not initialized\r
+  $_stats{Failed}   ||= 0;\r
+  $_stats{Timedout} ||= 0;\r
+\r
+  my $testErrors = $_stats{Failed} + $_stats{Timedout};\r
+\r
+  # Collect log files and check them in based on checkin_on_error option\r
+  $_east->collectLogFiles($testErrors, $_opts{checkin_on_error});\r
+\r
+  if ($testFailures == 0 and $_opts{val}) {\r
+    my @validations = ref $_opts{val} eq "ARRAY"\r
+                    ? @{$_opts{val}}\r
+                   : ($_opts{val});\r
+\r
+    $testFailures += runValidations @validations;\r
+  } # if\r
+\r
+  # Log test results\r
+  verbose BOLD YELLOW . "Test #$_testNbr\t" . RESET CYAN $testName\r
+    . (($testFailures == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+  $_log->log ("Test #$_testNbr\t$testName " . (($testFailures == 0) ? "Success" : "Failure"));\r
+\r
+  my ($_runID, $dbErrMsg) = $_rantestdb->endTestrun (\r
+    runID              => $_east->{runID},\r
+    suiteID            => $_east->{suiteID} ? $_east->{suiteID} : 0,\r
+    name               => fileparse ($file, ".test"),\r
+    execType           => $execType,\r
+    start              => UnixDatetime2SQLDatetime (scalar (localtime ($testStartTime))),\r
+    result             => $testFailures == 0 ? "Success" : "Failure",\r
+    unit               => "$_east->{unitType}$_east->{unitNbr}",\r
+    rantest_version    => VERSION_NBR,\r
+    east_version       => $ENV{EAST_REL},\r
+    ran_version                => $_east->{ran_version},\r
+    tm500_version      => $_east->{tm500_version},\r
+    nms_version                => $_east->{nms_version},\r
+    eastlogs           => LOGBASE . "/" . $_east->getSaveTo,\r
+  );\r
+\r
+  error $dbErrMsg if $_runID == 0;\r
+\r
+  return $testFailures;\r
+} # runTestFile\r
+\r
+sub setPath ($) {\r
+  my ($view) = @_;\r
+\r
+  return if $ENV{PATH} =~ /$view/;\r
+\r
+  my $userdir;\r
+\r
+  if ($view =~ /(\S+)_SIM/) {\r
+    $userdir = $1;\r
+  } else {\r
+    error "Unable to find userdir", 1;\r
+  } # if\r
+\r
+  my @paths = (\r
+    "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/simdev/sbin",\r
+    "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/simdev/bin",\r
+    "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/gdtools/rantest_auto/bin",\r
+  );\r
+\r
+  $ENV{PATH} = join (":", @paths) . ":" . $ENV{PATH};\r
+} # setPath\r
+\r
+sub runSuiteFile ($) {\r
+  my ($file) = @_;\r
+\r
+  error "View must be specified when running in suite mode", 1 unless $_opts{eastview};\r
+\r
+  setPath $_opts{eastview};\r
+\r
+  my $userdir;\r
+\r
+  if ($_opts{eastview} =~ /(\S+)_SIM/) {\r
+    $userdir = $1;\r
+  } else {\r
+    error "Unable to find userdir", 1;\r
+  } # if\r
+\r
+  unless (open FILE, $file) {\r
+    error "Unable to open file $file - $!";\r
+    return 1\r
+  } # unless\r
+\r
+  my @lines = <FILE>;\r
+\r
+  chomp @lines;\r
+\r
+  close FILE;\r
+\r
+  my $i                        = 0;\r
+  my $suiteStartTime   = time;\r
+  my $suiteFailures    = 0;\r
+  my $suiteName                = fileparse ($file, ".suite");\r
+\r
+  $_log = new Logger (\r
+    name       => $suiteName,\r
+    path       => LOGBASE,\r
+    append     => "yes",\r
+  );\r
+\r
+  $_log->log ("$FindBin::Script Version " . VERSION_NBR);\r
+\r
+  ($_east->{suiteID}) = $_rantestdb->startSuiterun ($suiteName);\r
+\r
+  verbose BOLD MAGENTA "Suite\t" . RESET GREEN $suiteName . RESET;\r
+\r
+  $_log->log ("Suite\t$suiteName");\r
+\r
+  foreach (@lines) {\r
+    $i++;\r
+    next if /(^#|^$)/;\r
+\r
+    my @components = split;\r
+\r
+\r
+    my $viewPath = "$ENV{MNT_DIR}/snapshot_views/$userdir/$_opts{eastview}/vobs/simdev/test/";\r
+    my $testFile = "$viewPath/" . shift @components;\r
+\r
+    unless (-e $testFile) {\r
+      error "Unable to find test file $testFile (Line: $i)";\r
+      next;\r
+    } # unless\r
+\r
+    unless (/\.test/) {\r
+      error "Not a .test file: $testFile (Line: $i)";\r
+      next;\r
+    } # unless\r
+\r
+    # Get test options. It seems GetOptions doesn't support taking\r
+    # input from anything but @ARGV so we'll have to save a copy and\r
+    # restore it.\r
+    my @savedOptions = @ARGV;\r
+\r
+    @ARGV = split;\r
+\r
+    my %suiteOptions;\r
+\r
+    my $status = GetOptions (\r
+      \%suiteOptions,\r
+      "eastview=s",\r
+      "tm500view=s",\r
+      "nmsview=s",\r
+      "type=s",\r
+      "class=s",\r
+      "unit=s",\r
+      "test=s",\r
+      "file=s",\r
+      "rfr=s",\r
+      "regression",\r
+    ) || usage "Invalid parameter";\r
+\r
+    # Restore @ARGV\r
+    @ARGV = @savedOptions;\r
+\r
+    # Restore the original command line options:\r
+    %_opts = %_cmdline_opts;\r
+\r
+    # Merge in %suiteOptions: Set into %_opts only if that key doesn't\r
+    # exist already. This allows command line options to override\r
+    # options specified on the .test line in the .suite file\r
+    foreach (keys %suiteOptions) {\r
+      $_opts{$_} = $suiteOptions{$_} unless $_opts{$_};\r
+    } # foreach\r
+\r
+    $suiteFailures += runTestFile $testFile;\r
+\r
+    # Need to disconnect $_east to shut down the previous run\r
+    my $savedSuiteID = $_east->{suiteID};\r
+\r
+    $_east->disconnect;\r
+\r
+    # Collect logfiles\r
+    $_east->collectLogFiles;\r
+\r
+    if ($suiteFailures == 0 and $_opts{val}) {\r
+      my @validations = ref $_opts{val} eq "ARRAY"\r
+                     ? @{$_opts{val}}\r
+                     : ($_opts{val});\r
+\r
+      $suiteFailures += runValidations @validations;\r
+    } # if\r
+\r
+    $_east = new Nethawk::East;\r
+\r
+    $_east->{suiteID} = $savedSuiteID;\r
+\r
+    $_connected = 0;\r
+  } # foreach\r
+\r
+  # Log suite results\r
+  verbose BOLD MAGENTA "Suite\t" . RESET GREEN $suiteName\r
+    . (($suiteFailures == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+  $_log->log ("Suite\t$suiteName" . ($suiteFailures == 0) ? "Success" : "Failure");\r
+\r
+  my $errMsg;\r
+\r
+  ($_east->{suiteID}, $errMsg) = $_rantestdb->endSuiterun (\r
+    name       => fileparse ($file, ".suite"),\r
+    start      => UnixDatetime2SQLDatetime (scalar (localtime ($suiteStartTime))),\r
+    result     => $suiteFailures ? "Failure" : "Success",\r
+  );\r
+\r
+  error $errMsg if $_east->{suiteID} != 0;\r
+\r
+  return $suiteFailures;\r
+} # runSuiteFile\r
+\r
+sub runFile ($) {\r
+  my ($file) = @_;\r
+\r
+  unless (-e $file) {\r
+    error "File $file does not exist";\r
+    return;\r
+  } # if\r
+\r
+  $_term->AddHistory ("source $file")\r
+    unless $_debugging or !-t STDIN;\r
+\r
+  $SIG{INT} = \&interrupted;\r
+\r
+  # Determine file type\r
+  if ($file =~ /\.test$/) {\r
+    runTestFile $file;\r
+  } elsif ($file =~ /\.suite$/) {\r
+    return runSuiteFile $file\r
+  } else {\r
+    error "File $file is not a .suite or .test file", 1;\r
+  } # if\r
+} # runFile\r
+\r
+$SIG{TERM}     =\r
+$SIG{QUIT}     = \&saveHistory;\r
+\r
+# Set a more friendly umask\r
+umask 002;\r
+\r
+GetOptions (\r
+  \%_opts,\r
+  verbose      => sub { set_verbose },\r
+  debug                => sub { set_debug },\r
+  usage                => sub { usage; exit 0 },\r
+  "eastview:s",\r
+  "view:s",\r
+  "tm500view:s",\r
+  "nmsview:s",\r
+  "type:s",\r
+  "class:s",\r
+  "unit:s",\r
+  "test:s",\r
+  "file:s",\r
+  "rfr:s",\r
+  "checkin_on_error",\r
+  "feature:s",\r
+  "secure!",\r
+  "regression",\r
+) || usage "Invalid parameter";\r
+\r
+# Special case elock command\r
+if (scalar @ARGV > 0 and $ARGV[0] =~ /elock/i) {\r
+  eLock ($ARGV[1]);\r
+  exit;\r
+} # if\r
+\r
+usage "Extraneous parameters: " . join " ", @ARGV if scalar @ARGV > 0;\r
+\r
+# Check for mutually exclusive options\r
+if ($_opts{file}) {\r
+  my $suffix = $ENV{RANTEST_FILE} ? "\nNote: The environment variable RANTEST_FILE is set" : "";\r
+\r
+  if ($_opts{test}) {\r
+    $suffix .= $ENV{RANTEST_TEST} ? "\nNote: The environment variable RANTEST_TEST is set" : "";\r
+  } elsif ($_opts{class}) {\r
+    $suffix .= $ENV{RANTEST_CLASS} ? "\nNote: The environment variable RANTEST_CLASS is set" : "";\r
+  } # if\r
+\r
+  usage "<test> and <file> are mutually exclusive$suffix"  if $_opts{test};\r
+  usage "<class> and <file> are mutually exclusive$suffix" if $_opts{class};\r
+} # if\r
+\r
+if ($_opts{eastview}) {\r
+  my $suffix = $ENV{RANTEST_VIEW} ? "\nNote: The environment variable RANTEST_VIEW is set" : "";\r
+  $suffix .= $ENV{RANTEST_EASTVIEW} ? "\nNote: The environment variable RANTEST_EASTVIEW is set" : "";\r
+\r
+  usage "<eastview> and <view> are mutually exclusive$suffix" if $_opts{view};\r
+} # if\r
+\r
+usage "-rfr and -regression are mutually exclusive" if $_opts{rfr} && $_opts{regression};\r
+\r
+# Make "view" an alias for "eastview" but only if there is no eastview\r
+# already defined\r
+$_opts{eastview} = delete $_opts{view} if $_opts{view} && !$_opts{eastview};\r
+\r
+# Check for required parameters\r
+usage "-view or -eastview specified but no view given"\r
+  if defined $_opts{view} and $_opts{view} eq "";\r
+usage "-tm500view specified but no view given"\r
+  if defined $_opts{tm500view} and $_opts{tm500view} eq "";\r
+usage "-nmsview specified but no view given"\r
+  if defined $_opts{nmsview} and $_opts{nmsview} eq "";\r
+usage "-type specified but no type given"\r
+  if defined $_opts{type} and $_opts{type} eq "";\r
+usage "-class specified but no class given"\r
+  if defined $_opts{class} and $_opts{class} eq "";\r
+usage "-unit specified but no unit # given"\r
+  if defined $_opts{unit} and $_opts{unit} eq "";\r
+usage "-test specified but no test given"\r
+  if defined $_opts{test} and $_opts{test} eq "";\r
+usage "-file specified but no file given"\r
+  if defined $_opts{file} and $_opts{file} eq "";\r
+usage "-rfr specified but no testcase ID given"\r
+  if defined $_opts{rfr} and $_opts{rfr} eq "";\r
+\r
+# Save these original command line options. If we are in suite mode\r
+# then we must allow the individual .test options override these\r
+# original command line options.  If -rfr is on then we are by default\r
+# verbose\r
+%_cmdline_opts = %_opts;\r
+\r
+set_verbose if $_opts{rfr};\r
+\r
+# Instantiate a new East object\r
+$_east = new Nethawk::East;\r
+\r
+# Set testcase ID into East object\r
+$_east->setTestCaseID ($_opts{rfr});\r
+\r
+$_debugging = get_debug;\r
+\r
+# If we are debugging (and thus STDIN gets confused between the debugger's\r
+# STDIN and rantest's STDIN) or if we don't have a tty (-t - we would not \r
+# have a tty if run from say cron(1m), then do not perform these actions\r
+# on $_term.\r
+unless ($_debugging or !-t STDIN) {\r
+  $_term = new Term::ReadLine $FindBin::Script;\r
+\r
+  $_term->{AUTORESET} = 1;\r
+\r
+  # Restore previous history, if any\r
+  $_term->ReadHistory (HISTORY_FILE);\r
+} # unless\r
+\r
+# Announce ourselves\r
+verbose DESC . RESET;\r
+\r
+# Open Rantest Database\r
+$_rantestdb = new RantestDB ("pswit", "!qaz2w3e");\r
+\r
+if ($_opts{test}) {\r
+  $SIG{INT} = \&interrupted;\r
+\r
+  $_stats{Run}++;\r
+\r
+  # Separate off options\r
+  my $testName = $_opts{test};\r
+\r
+  if ($_opts{test} =~ /(\S+)\s+\-.*$/) {\r
+    $testName = $1;\r
+ } # if\r
+\r
+  $testName = fileparse ($testName, "\.profile");\r
+\r
+  $_east->setSaveTo ("$testName/$_opts{type}$_opts{unit}/" . YMDHMS);\r
+\r
+  eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+  return (1, $@) if $@;\r
+\r
+  chmod 0775, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+  $_log = new Logger (\r
+    name       => $testName,\r
+    path       => LOGBASE . "/" . $_east->getSaveTo,\r
+    append     => "yes",\r
+  );\r
+\r
+  $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+\r
+  executeTestStep;\r
+\r
+  $_term->AddHistory ("$_opts{class} $_opts{test}") unless $_debugging && -t STDIN;\r
+\r
+  # Disconnect from EAST\r
+  $_east->disconnect;\r
+\r
+  # Collect logfiles\r
+  $_east->collectLogFiles;\r
+} elsif ($_opts{file}) {\r
+  runFile $_opts{file};\r
+} else {\r
+  $_east->setSaveTo ("rantest/" . YMDHMS);\r
+\r
+  eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+  return (1, $@) if $@;\r
+\r
+  chmod 0777, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+  $_log = new Logger (\r
+    path       => LOGBASE . "/" . $_east->getSaveTo,\r
+    append     => "yes"\r
+  );\r
+\r
+  display DESC if !get_verbose;\r
+\r
+  if ($_opts{eastview}) {\r
+    $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+  } else {\r
+    $_log->log ("$FindBin::Script Version " . VERSION_NBR);\r
+  } # if\r
+\r
+  set_verbose;\r
+\r
+  while () {\r
+    my $cmd;\r
+\r
+    unless ($_debugging) {\r
+      $cmd = $_term->readline (PROMPT . RESET);\r
+    } else {\r
+      display_nolf PROMPT . RESET;\r
+\r
+      $cmd = <STDIN>;\r
+    } # if\r
+\r
+    # Handle Control-d\r
+    unless (defined $cmd) {\r
+      display "";\r
+      saveHistory;\r
+      exit 0;\r
+    } # if\r
+\r
+    chomp $cmd;\r
+\r
+    next if $cmd eq "";\r
+\r
+    if ($cmd =~ /exit|quit/i) {\r
+      $_term->remove_history ($_term->where_history);\r
+      saveHistory;\r
+      exit 0;\r
+    } # if\r
+\r
+    if ($cmd =~ /^elock/i) {\r
+      if ($cmd =~ /^elock\s+(\w+)/i) {\r
+       eLock $1;\r
+      } else {\r
+       eLock;\r
+      } # if\r
+\r
+      next;\r
+    } # if\r
+\r
+    my @tokens = split /\s+/, $cmd;\r
+\r
+    $_opts{class}      = shift @tokens;\r
+    $_opts{test}       = join " ", @tokens;\r
+\r
+    $cmd = lc $_opts{class};\r
+\r
+    if ($cmd eq "help") {\r
+      help;\r
+      $_term->remove_history ($_term->where_history) unless $_debugging;\r
+      next;\r
+    } elsif ($cmd eq "usage") {\r
+      usage;\r
+      $_term->remove_history ($_term->where_history) unless $_debugging;\r
+      next;\r
+    } elsif ($cmd eq "version") {\r
+      display DESC;\r
+      $_term->remove_history ($_term->where_history) unless $_debugging;\r
+      next;\r
+    } elsif ($cmd eq "source") {\r
+      runFile $tokens[0];\r
+    } elsif ($cmd eq "set") {\r
+      if ($_opts{test} =~ /\s*(\w+)\s*=\s*(.+)/) {\r
+       my $optionName  = $1;\r
+       my $value       = $2;\r
+\r
+       # Remove quotes, if any. Note no check for balancing.\r
+       $value =~ s/[\"\']//g;\r
+\r
+       # Set option\r
+       $_opts{$optionName} = $value;\r
+      } # if\r
+    } elsif ($cmd eq "get") {\r
+      if ($_opts{$tokens[0]}) {\r
+       display "$tokens[0] = $_opts{$tokens[0]}";\r
+      } else {\r
+       display "$tokens[0] is not set";\r
+      } # if\r
+    } else {\r
+      $_stats{Run}++;\r
+      $_opts{class} = lc $_opts{class};\r
+\r
+      if ( $_opts{class} eq "manual" ) {\r
+        $_opts{test} = " ";\r
+      } # if\r
+\r
+      executeTestStep;\r
+    } # if\r
+  } # while\r
+\r
+  # Disconnect from EAST\r
+  $_east->disconnect;\r
+\r
+  # Assign 'Failed' and 'Timedout' 0 if they are not initialized\r
+  $_stats{Failed}   ||= 0;\r
+  $_stats{Timedout} ||= 0;\r
+\r
+  my $testErrors = $_stats{Failed} + $_stats{Timedout};\r
+\r
+  # Collect log files and check them in based on checkin_on_error option\r
+  $_east->collectLogFiles($testErrors, $_opts{checkin_on_error});\r
+\r
+} # if\r
+\r
+saveHistory;\r
+displaySummary;\r
+\r
+# The combination of Failed and Timedout represents our exit\r
+# status. If either or both of them is defined then they will be\r
+# non-zero and thus we exit with a non-zero status. Only if both are\r
+# undefined, and thus set to 0 by the code below, will we exit 0.\r
+$_stats{Failed}                = 0 unless $_stats{Failed};\r
+$_stats{Timedout}      = 0 unless $_stats{Timedout};\r
+\r
+# Now exit with the correct status\r
+exit ($_stats{Failed} + $_stats{Timedout});\r