=== modified file 'src/com/goldencode/harness/BarrierElement.java'
--- old/src/com/goldencode/harness/BarrierElement.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/BarrierElement.java	2023-07-21 09:28:30 +0000
@@ -2,10 +2,11 @@
 ** Module   : BarrierElement.java
 ** Abstract : represents a barrier element in a larger queue
 **
-** Copyright (c) 2009, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 Represents a barrier element in a larger queue.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -113,4 +114,10 @@
    {
       return null;
    }
+
+   @Override
+   public boolean isIgnored()
+   {
+      return false;
+   }
 }

=== modified file 'src/com/goldencode/harness/ConcurrentElements.java'
--- old/src/com/goldencode/harness/ConcurrentElements.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/ConcurrentElements.java	2023-07-21 09:28:40 +0000
@@ -2,11 +2,12 @@
 ** Module   : ConcurrentElements.java
 ** Abstract : represents multiple elements which must execute simultaneously
 **
-** Copyright (c) 2009, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 Represents multiple elements which must execute
 **                  simultaneously.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -215,4 +216,10 @@
       
       return element;
    }
+   @Override
+   public boolean isIgnored()
+   {
+      return false;
+   }
+
 }

=== modified file 'src/com/goldencode/harness/Driver.java'
--- old/src/com/goldencode/harness/Driver.java	2022-08-18 15:08:49 +0000
+++ new/src/com/goldencode/harness/Driver.java	2023-07-21 09:29:06 +0000
@@ -2,7 +2,7 @@
 ** Module   : Driver.java
 ** Abstract : thread that reads tests from a job queue and executes them
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090122 Thread that reads tests from a job queue and executes them.
@@ -14,6 +14,8 @@
 ** 005 TJD 20220808 Changed log format, added option to abort TestSet
 ** 006 TJD 20220815 Abort anytime if RC failed
 ** 007 TJD 20220817 Implement test skip list feature, logging tweaks
+** 008 TJD 20220820 Terminate thread when in backout fails to prevent further failures
+** 009 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -55,7 +57,7 @@
    private JobQueue<Test> queue = null;
    
    /** Object to notify when complete. */
-   private CountDownLatch latch = null;
+   private Phaser phaser = null;
 
    /** Mapping of test executors based on their type. */
    private Map<Class<TestExecutor>, TestExecutor> executors;
@@ -75,18 +77,18 @@
     *           The driver's configurations.
     * @param    queue
     *           Job queue from which tests to be executed are obtained.
-    * @param    latch
+    * @param    phaser
     *           Object to notify when complete.
     * @param    rc
     *           Object in which to store the details of non-testing failures.
     */
    public Driver(Map<String, SessionSettings> cfgs, 
                  JobQueue<Test>               queue, 
-                 CountDownLatch               latch, 
+                 Phaser                       phaser, 
                  FailureNotification          rc)
    {
       this.queue = queue;
-      this.latch = latch;
+      this.phaser = phaser;
       this.cfgs = cfgs;
       this.rc = rc;
    }
@@ -109,7 +111,7 @@
    {
       try
       {
-         LogHelper.finer(this.getName() + " Start");
+         LogHelper.finer("DRIVERSTART|" + this.getName());
          executors = buildExecutors(cfgs, rc);
          
          if (!pre())
@@ -127,6 +129,7 @@
             
             if (test == null)
             {
+               LogHelper.finer(this.getName() + " Queue is empty, exiting");
                break;
             }
             
@@ -136,7 +139,7 @@
                break;
             }
             // check it test is to be skipped
-            if (Harness.isTestOnSkipList(test.getName()))
+            if (Harness.isTestOnSkipList(test.getName()) || !Harness.isTestOnWhiteList(test.getName()))
             {
                // Skipped tests are considered as passed
                LogHelper.info("Skipping test: "+ test.getName());
@@ -145,12 +148,13 @@
             }
             
             test.setDriver(this);
-            LogHelper.fine(this.getName() + "[" + test.getName() + "/" + test.getDependency().toString() + "] execute");
+            LogHelper.finer("DRIVER|" + this.getName() + "|" + test.getName() + "/" 
+                           + test.getDependency().toString() + "| execute");
             test.execute();
             
             Status status = test.getStatus();
-            LogHelper.fine(this.getName() + "[" + test.getName() + "] " + status.toString()
-                               + (status.equals(Status.FAILED)?" reason: " + test.getReason():""));
+            LogHelper.finer("RESULT|" + this.getName() + "|" + test.getName() + "|" + status.toString() 
+                           + "|" + (status.equals(Status.FAILED)?" reason: " + test.getReason():""));
             if (status == Status.FAILED)
             {
                FailureMode fail = test.getFailureMode();
@@ -170,6 +174,14 @@
                      {
                         backout.setDriver(this);
                         backout.execute();
+                        Status backoutStatus = backout.getStatus();
+                        if (backoutStatus != Status.PASSED && Harness.isDebugMode())
+                        {
+                           LogHelper.warn("BACKOUTFAIL|" + this.getName() + "|" + test.getName() 
+                                          + "|" + test.getDependency().toString() 
+                                          + "| Backout failed or not run, terminating thread");
+                           break loop;
+                        }
                      }
                      
                      break;
@@ -179,14 +191,14 @@
       }
       catch (Throwable e)
       {
-         LogHelper.warn(this.getName() + " Exception:", e);
+         LogHelper.warn("DRIVERERROR|" + this.getName() + "| Exception:", e);
       }
       finally
       {
-         LogHelper.finer(this.getName() + " End");
+         LogHelper.finer("DRIVEREND|" + this.getName() + "| End");
          post();
          
-         latch.countDown();
+         phaser.arriveAndDeregister();
       }
    }
    

=== modified file 'src/com/goldencode/harness/Harness.java'
--- old/src/com/goldencode/harness/Harness.java	2022-08-23 16:11:29 +0000
+++ new/src/com/goldencode/harness/Harness.java	2023-07-21 09:32:12 +0000
@@ -2,7 +2,7 @@
 ** Module   : Harness.java
 ** Abstract : test execution master driver 
 **
-** Copyright (c) 2008-2022, Golden Code Development Corporation.
+** Copyright (c) 2008-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090126 Test execution master driver.
@@ -22,6 +22,9 @@
 ** 010 TJD 20220817 Implement performance test mode and some logging tweaks
 ** 011 TJD 20220817 Added missing javadoc, include completionMsg
 ** 012 TJD 20220819 Allow configuring max timeout for single test that require idle waiting
+** 013 TJD 20230119 Timeout multiplier and trace mode support
+** 014 TJD 20230119 Allow restarting of Driver threads
+** 015 TJD 20230621 Tests white list support
 */
 
 /*
@@ -81,11 +84,21 @@
    /** Performance mode enabled flag, enabled with "-m" option, default *false* */
    private static boolean benchmarkMode = false;
    
+   /** Global limit on number of threads */
+   private static double timeoutMultiplier = 1.0;
+   
    /** Set of names of test to skip */
    private static HashSet<String> skipFileSet = new HashSet<>();
    
    /** Debug mode enabled flag, enabled with "-d" option, default *false* */
    private static boolean debugMode = false;
+
+   /** Debug mode enabled flag, enabled with "-d -d" option, default *false* */
+   private static boolean traceMode = false;
+   /** requested maximum for number of parallel test */
+   private static int maximumParallelTests = Integer.MAX_VALUE;
+   /** tests white list - tests names allowed to execute */
+   private static Set<String> testsListWhite = null;
    
    static
    {
@@ -103,10 +116,14 @@
          "",
          "-?                    = display this help screen",
          "-d                    = enable verbose output",
+         "-d -d                 = enable even more verbose output",
          "-l <log_level>        = set log level: WARN,INFO,FINE,FINER",
+         "-g <integer  >        = timeout multiplier",
+         "-q <integer  >        = maximum parallel TestSet, set 1 for sequential ",
          "-c                    = enable dumping reports on CTRL-C",
          "-x                    = abort anytime when RC fails",
          "-m [timeout]          = enable performance test mode with optional user I/O wait timeout {60s}",
+         "-f <test_list>        = comma separated names of tests to be included in the whitelist",
          "-b <path_list>        = specify a default set of baseline search",
          "                        paths; <path_list> is a list of one or more",
          "                        paths separated by the platform specific",
@@ -416,11 +433,14 @@
     * -c                    = enable dump reports on CTRL-C
     * -m [timeout]          = enable performance test mode with optional user I/O wait timeout {60s}
     * -x                    = abort anytime when RC fails,
-    * -b &lt;path_list&gt;        = specify a default set of baseline search
+    * -f &lt;test_list&gt;  = comma separated names of tests to be included in the whitelist
+    * -b &lt;path_list&gt;  = specify a default set of baseline search
     *                         paths; &lt;path_list&gt; is a list of one or more
     *                         paths separated by the platform specific
     *                         path separator character (e.g. on UNIX or
     *                         Linux this would be the colon ':')
+    * -g &lt;integer&gt;    = timeout multiplier",
+    * -q &lt;integer&gt;    = maximum parallel TestSet, set 1 for sequential ",
     * -e                    = force separate host/port/userid/password prompts
     *                         for each target system (the default is to only
     *                         prompt once and to use the same responses
@@ -511,10 +531,20 @@
                      
                      break;
                   case "d":
-                     // enable debug output
-                     LogHelper.enableDebug();
-                     debugMode = true;
-                     LogHelper.info("Enable debug mode");
+                     if (debugMode)
+                     {
+                        // enable trace mode if debug mode is already enabled
+                        LogHelper.enableTrace();
+                        traceMode = true;
+                        LogHelper.info("Enable trace mode");
+                     }
+                     else
+                     {
+                        // enable debug output
+                        LogHelper.enableDebug();
+                        debugMode = true;
+                        LogHelper.info("Enable debug mode");
+                     }
                      break;
                   case "s":
                      // skip file
@@ -525,7 +555,7 @@
                      String skipFileName = args[++idx];
                      if ((skipFileName != null) && (skipFileName.length() > 0))
                      {
-                        LogHelper.fine("Reading names of tests to skip from file: "+skipFileName);
+                        LogHelper.fine("Reading names of tests to skip from file: " + skipFileName);
                         readSkipFile(skipFileName);
                      }
                      break;
@@ -534,6 +564,57 @@
                      failOnRCFailure = true;
                      LogHelper.info("Enable immediate fail on any non-testing error mode");
                      break;
+                  case "f":
+                     if (idx == args.length - 1)
+                     {
+                        syntax(ExitCode.MISSING_OPTION_DATA, null);
+                     }
+                     String testWhiteList = args[++idx];
+                     testsListWhite = new HashSet<String>();
+                     testsListWhite.addAll( Arrays.asList(testWhiteList.split(",")));
+                     break;
+                  case "q":
+                     // set maximum number of allowed parallel TestSets
+                     if (idx == args.length - 1)
+                     {
+                        syntax(ExitCode.MISSING_OPTION_DATA, null);
+                     }
+                     String parallelTestSetsParam = args[++idx];
+                     if ((parallelTestSetsParam != null) && (parallelTestSetsParam.length() > 0))
+                     {
+                        try
+                        {
+                           maximumParallelTests = Integer.parseInt(parallelTestSetsParam);
+                           LogHelper.fine("Setting maximum number of allowed parallel TestSets: " + maximumParallelTests);
+                        }
+                        catch(NumberFormatException e)
+                        {
+                           final String spec = "Invalid value for maximum number of allowed parallel TestSets";
+                           syntax(ExitCode.MISSING_OPTION_DATA, String.format(spec, parallelTestSetsParam));
+                        }
+                     }                  
+                     break;
+                     case "g":
+                     // set timeout multiplier
+                     if (idx == args.length - 1)
+                     {
+                        syntax(ExitCode.MISSING_OPTION_DATA, null);
+                     }
+                     String timeoutMultiplierString = args[++idx];
+                     if ((timeoutMultiplierString != null) && (timeoutMultiplierString.length() > 0))
+                     {
+                        try
+                        {
+                           timeoutMultiplier = Double.parseDouble(timeoutMultiplierString);
+                           LogHelper.fine("Setting timeout multiplier: " + timeoutMultiplier);
+                        }
+                        catch(NumberFormatException e)
+                        {
+                           final String spec = "Invalid value for timeout multiplier";
+                           syntax(ExitCode.MISSING_OPTION_DATA, String.format(spec, timeoutMultiplierString));
+                        }
+                     }
+                     break;
                   case "l":
                      // set log level from string
                      if (idx == args.length - 1)
@@ -982,6 +1063,16 @@
    }
    
    /**
+    * Return true if traceMode flag value.
+    * 
+    * @return   debugMode value.
+    */
+   public static boolean isTraceMode()
+   {
+      return traceMode;
+   }
+
+   /**
     * Check if test identified by name is to be skipped
     * @param    name
     *           test name to be checked
@@ -1003,6 +1094,32 @@
    }
    
    /**
+    * Return test timeout multiplier
+    * 
+    * @return   test timeout multiplier
+    */
+   public static int scaleTimeoutMultiplier(int timeout)
+   {
+      boolean timeoutIsZero = timeout == 0;
+      timeout = (int) ((double) timeout * timeoutMultiplier);
+      if (timeout == 0 && !timeoutIsZero)
+      {
+         return 1;
+      }
+      return timeout;
+   }
+   
+   /**
+    * Return requested maximum for number of parallel test
+    * 
+    * @return   requested maximum for number of parallel test
+    */
+   public static int getMaxParallelTests()
+   {
+      return maximumParallelTests;
+   }
+
+   /**
     * Generate a completion message that describes the length of time in
     * human readable terms.
     *
@@ -1072,4 +1189,16 @@
       }
    }
 
+   /**
+    * Check if a test with a given name is on whitelist
+    * @param    testName
+    *           name of test to check
+    * @return   true if test with a given name is on whitelist 
+    *                or whitelist is not configured at all
+    */
+   public static boolean isTestOnWhiteList(String testName)
+   {
+      return (testsListWhite == null || testsListWhite.contains(testName));
+   }
+
 }

=== modified file 'src/com/goldencode/harness/JobQueue.java'
--- old/src/com/goldencode/harness/JobQueue.java	2022-08-18 15:08:49 +0000
+++ new/src/com/goldencode/harness/JobQueue.java	2023-07-21 09:27:54 +0000
@@ -2,13 +2,14 @@
 ** Module   : JobQueue.java
 ** Abstract : queue tests and allow them to be dequeued based on dependencies
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 Queue tests and allow them to be dequeued based on
 **                  dependencies.
 ** 002 TJD 20220809 Support for aborting the queue
 ** 003 TJD 20220817 Implement performance test mode
+** 004 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -52,6 +53,9 @@
    /** Flag indicating queue abort was requested */
    private final AtomicBoolean abort = new AtomicBoolean(false);
    
+   /** Flag preventing starting new threads */
+   volatile private boolean isEmptyForNewThreads = false;
+   
    /**
     * Construct an instance.
     *
@@ -97,7 +101,7 @@
          synchronized (lock)
          {
             ListIterator<SubQueue<E>> list = queue.listIterator();
-            
+            int ignoredSubqueues = 0;
             // walk through all sub-queues
             while (list.hasNext())
             {
@@ -116,7 +120,7 @@
                // do not go past any barrier sub-queues
                if (subQueue.isBarrier())
                {
-                  if (queue.indexOf(subQueue) == 0)
+                  if (queue.indexOf(subQueue) == ignoredSubqueues)
                   {
                      // all previous sub-queues are finished, remove the
                      // barrier and allow processing to continue
@@ -137,13 +141,20 @@
                   
                   // reserve the next element and allow us to safely exit the
                   // synchronized block
-                  ready.reserve();
-                  
-                  break outer;
+                  if (ready.reserve())
+                  {
+                     break outer;
+                  }
+                  // if not ready - repeat the cycle until its ready
+               }
+               // check if subqueue can be ignored for this thread
+               if (subQueue.isIgnored())
+               {
+                  ignoredSubqueues ++;
                }
             }
             
-            sz = queue.size();
+            sz = queue.size() - ignoredSubqueues;
          }
             
          // no sub-queues are ready yet, the current thread must wait for an
@@ -167,6 +178,11 @@
                break;
             }
          }
+         else
+         {
+            // if any of thread has reached here no new threads should be started
+            isEmptyForNewThreads = true;
+         }
       }
       
       // the dequeuing is intentionally done outside of the synchronized block
@@ -187,4 +203,12 @@
    {
       abort.set(true);
    }
+
+   public boolean isEmpty()
+   {
+      synchronized (lock)
+      {
+         return (queue == null || (queue.size() == 0) || isEmptyForNewThreads);
+      }
+   }
 }

=== modified file 'src/com/goldencode/harness/PerThreadElement.java'
--- old/src/com/goldencode/harness/PerThreadElement.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/PerThreadElement.java	2023-01-23 08:40:46 +0000
@@ -8,6 +8,7 @@
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 Represents a set of duplicate elements which are assigned
 **                  to a specific thread.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -46,6 +47,9 @@
    /** Reservation list. */
    private final Map<Thread, E> reserved = new HashMap<Thread, E>();
    
+   /** Tasks pending recycling. */
+   private final Map<Thread, E> pendingRecycling = new HashMap<Thread, E>();
+
    /** Already serviced list (each thread is only ever serviced once). */
    private final Set<Thread> serviced = new HashSet<Thread>();
    
@@ -75,12 +79,16 @@
    public boolean isEmpty()
    {
       boolean empty = false;
-      
-      synchronized (lock)
+      synchronized(lock)
       {
-         empty = (queue.size() == 0);
+         if (pendingRecycling.containsKey(Thread.currentThread()))
+         {
+            // recycle test used by this thread
+            queue.add(pendingRecycling.remove(Thread.currentThread()));
+         }
       }
-      
+      // per thread elements are never empty and thus they are never removed from master queue 
+      // as they can be reused for restarting threads
       return empty;
    }
    
@@ -95,13 +103,14 @@
    public boolean isReady()
    {
       boolean prev = false;
-      
+      boolean isEmpty = false;
       synchronized (lock)
       {
          prev = serviced.contains(Thread.currentThread());
+         isEmpty = queue.isEmpty();
       }
       
-      return !isEmpty() && !prev;
+      return !isEmpty && !prev;
    }
    
    /**
@@ -170,8 +179,20 @@
       synchronized (lock)
       {
          element = reserved.remove(Thread.currentThread());
+         pendingRecycling.put(Thread.currentThread(), element);
       }
       
       return element;
    }
-}
+   
+   /**
+    * Reports if the subqueue can be ignored when checking if queue is empty
+    *
+    * @return   <code>true</code> if the queue can be ignored.
+    */
+   @Override
+   public boolean isIgnored()
+   {
+      // once executed it can be ignored
+      return !isReady();
+   }}

=== modified file 'src/com/goldencode/harness/Report.java'
--- old/src/com/goldencode/harness/Report.java	2017-04-04 12:40:33 +0000
+++ new/src/com/goldencode/harness/Report.java	2023-07-21 09:33:46 +0000
@@ -2,11 +2,12 @@
 ** Module   : Report.java
 ** Abstract : helpful base class for implementing a report
 **
-** Copyright (c) 2009-2017, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090113 Helpful base class for implementing a report.
 ** 002 CA  20170404 Add the failed test name in the result summary.
+** 003 TJD 20230119 Timeout multiplier and trace mode support
 */
 
 /*
@@ -478,4 +479,15 @@
       
       return num;
    }
+
+   /**
+    * Get the maximum number of milliseconds that this testable activity is allowed to run.
+    * Valid for some of Tests only, -1 if not applicable
+    * @return   The maximum number of milliseconds.
+    */
+   @Override
+   public long getMaxElapsed()
+   {
+      return -1;
+   }
 }

=== modified file 'src/com/goldencode/harness/Reportable.java'
--- old/src/com/goldencode/harness/Reportable.java	2021-09-06 08:55:43 +0000
+++ new/src/com/goldencode/harness/Reportable.java	2023-07-21 09:33:57 +0000
@@ -2,11 +2,12 @@
 ** Module   : Reportable.java
 ** Abstract : API for delegating the tracking of a testable activity
 **
-** Copyright (c) 2009-2021, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090113 API for delegating tracking of a testable activity.
 ** 002 CA  20210906 The content for description and failure is wrapped only for web tests. 
+** 003 TJD 20230119 Timeout multiplier and trace mode support
 */
 
 /*
@@ -148,6 +149,13 @@
    public long getElapsed();
    
    /**
+    * Get the maximum number of milliseconds that this testable activity is allowed to run.
+    * Valid for some of Tests only, -1 if not applicable
+    * @return   The maximum number of milliseconds.
+    */
+   public long getMaxElapsed();
+   
+   /**
     * Get the short name for the testable activity.
     *
     * @return   A name of the testable activity.

=== modified file 'src/com/goldencode/harness/SequentialElements.java'
--- old/src/com/goldencode/harness/SequentialElements.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/SequentialElements.java	2023-01-23 08:40:37 +0000
@@ -7,6 +7,7 @@
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090319 Represents elements to be processed in order on a single
 **                  thread.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -215,4 +216,15 @@
          }
       }
    }
+   
+   /**
+    * Reports if the queue can be ignored, usually for given thread.
+    *
+    * @return   <code>true</code> if the queue can be ignored.
+    */
+   @Override
+   public boolean isIgnored()
+   {
+      return false;
+   }
 }

=== modified file 'src/com/goldencode/harness/SingleElement.java'
--- old/src/com/goldencode/harness/SingleElement.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/SingleElement.java	2023-01-23 08:40:28 +0000
@@ -2,10 +2,11 @@
 ** Module   : SingleElement.java
 ** Abstract : represents a single element in a larger queue
 **
-** Copyright (c) 2009, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 Represents a single element in a larger queue.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -157,4 +158,15 @@
    {
       this.element = element;
    }
+   
+   /**
+    * Reports if the queue can be ignored, usually for given thread.
+    *
+    * @return   <code>true</code> if the queue can be ignored.
+    */
+   @Override
+   public boolean isIgnored()
+   {
+      return false;
+   }
 }

=== modified file 'src/com/goldencode/harness/Status.java'
--- old/src/com/goldencode/harness/Status.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/Status.java	2023-06-12 09:32:56 +0000
@@ -2,10 +2,11 @@
 ** Module   : Status.java
 ** Abstract : encodes all possible test and step status codes
 **
-** Copyright (c) 2008, Golden Code Development Corporation.
+** Copyright (c) 2008-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20081218 Encodes all possible test and step status codes.
+** 002 TJD 20230612 Support for IN_PROGRESS test status
 */
 
 /*
@@ -33,5 +34,6 @@
    NOT_RUN,
    PASSED,
    FAILED,
-   FAILED_DEPENDENCY
+   FAILED_DEPENDENCY,
+   IN_PROGRESS
 }

=== modified file 'src/com/goldencode/harness/SubQueue.java'
--- old/src/com/goldencode/harness/SubQueue.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/SubQueue.java	2023-01-23 08:40:15 +0000
@@ -2,11 +2,12 @@
 ** Module   : SubQueue.java
 ** Abstract : API for objects that represent a portion of a larger queue
 **
-** Copyright (c) 2009, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090318 An API for objects that contain one or more elements that
 **                  represent a portion of a larger queue.
+** 002 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -93,4 +94,11 @@
     * @return   The next element in the queue.
     */
    public E dequeue();
+   
+   /**
+    * Reports if the queue can be ignored, usually for given thread.
+    *
+    * @return   <code>true</code> if the queue can be ignored.
+    */
+   public boolean isIgnored();
 }

=== modified file 'src/com/goldencode/harness/TerminalTestExecutor.java'
--- old/src/com/goldencode/harness/TerminalTestExecutor.java	2023-01-11 20:44:24 +0000
+++ new/src/com/goldencode/harness/TerminalTestExecutor.java	2023-01-19 16:09:03 +0000
@@ -7,6 +7,7 @@
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20210827 Created initial version.
 ** 002 RFB 20230111 Added finer logging which helps with debug (plus review).
+** 003 TJD 20230119 Added log level trace
 */
 
 /*
@@ -31,7 +32,7 @@
 import com.goldencode.harness.terminal.*;
 import com.goldencode.harness.test.*;
 import com.goldencode.harness.transport.*;
-import com.goldencode.util.LogHelper;
+import com.goldencode.util.*;
 
 /**
  * The executor for terminal tests.
@@ -76,10 +77,15 @@
          // failure details are already in the rc
          return;
       }
-      
-      LogHelper.finer("outputstream/inputstream before Terminal os=" + os + " is=" + is);
+      if (LogHelper.isFinestLoggable())
+      {
+         LogHelper.finest("outputstream/inputstream before Terminal os=" + os + " is=" + is);
+      }
       term = new Terminal(new DataSource(is, os), cfg);
-      LogHelper.finer("terminal created term=" + term);
+      if (LogHelper.isFinestLoggable())
+      {
+         LogHelper.finest("terminal created term=" + term);
+      }
    }
    
    /**

=== modified file 'src/com/goldencode/harness/TestPlan.java'
--- old/src/com/goldencode/harness/TestPlan.java	2022-08-23 16:11:29 +0000
+++ new/src/com/goldencode/harness/TestPlan.java	2023-06-15 08:39:42 +0000
@@ -2,7 +2,7 @@
 ** Module   : TestPlan.java
 ** Abstract : stores the data needed to execute a list of test sets
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090122 Stores the data needed to execute a list of test sets.
@@ -11,6 +11,7 @@
 ** 004 TJD 20220815 Abort anytime if RC failed
 ** 005 TJD 20220817 Logging tweaks
 ** 006 TJD 20220819 TestSet are executing sequentially in benchmark mode
+** 007 TJD 20230612 Support for IN_PROGRESS test status
 */
 
 /*
@@ -229,7 +230,7 @@
    
    /**
     * Execute each of the test sets that belong to a specific dependency
-    * type. PM version (executes TestPlan-s sequentially)
+    * type.
     *
     * @param    sb
     *           Buffer in which to accumulate error text from failures.
@@ -240,12 +241,12 @@
     *
     * @return   The number of failures.
     */
-   private int processTestSetsPM(StringBuilder       sb,
+   private int processTestSets(StringBuilder       sb,
                                Dependency          type,
                                FailureNotification rc)
    {
       int num = 0;
-      LogHelper.info("Start: " + this.getName() + "/" + type.toString());
+      LogHelper.info("STARTSET|" + this.getName() + "/" + type.toString());
       
       List<TestSet> list = new ArrayList<TestSet>();
       
@@ -263,113 +264,42 @@
       // create a specific latch of the right size
       
       iter = list.iterator();
-      
-      // execute each of the sets in the group
-      while (iter.hasNext())
+      int maxParallelTests = Harness.isBenchmarkMode() ? 1 : Harness.getMaxParallelTests();
+      // execute each of the sets in the group in sequence
+      Semaphore sem = new Semaphore(maxParallelTests);
+      try            
       {
-         if (rc.isFailed() && Harness.isFailOnRCFailure())
-         {
-            LogHelper.warn("RC failed, stopping TestPlan");
-            break;
-         }
-         CountDownLatch sem = new CountDownLatch(1);
-         TestSet set = iter.next();
-         set.execute(sem, rc);
-         try
-         {
+         while(iter.hasNext())
+         {
+            if (rc.isFailed() && Harness.isFailOnRCFailure())
+            {
+               LogHelper.warn("RC failed, stopping TestPlan");
+               break;
+            }
             // wait for test set to finish
-            while(sem.getCount() != 0)
+            while(!sem.tryAcquire(60, TimeUnit.SECONDS))
             {
-               sem.await(60, TimeUnit.SECONDS);
                // dump statistics every 60 seconds
                dumpStatistics(list, type);
             }
-         }
-         
-         catch (InterruptedException ie)
-         {
-            sb.append("test plan thread interrupted before driver ")
-              .append(" threads finished");
-            num = 1;
-         }
-
-      }
-      
-      LogHelper.info("End: " + this.getName() + "/" + type.toString());
-      dumpStatistics(list, type);
-      
-      return checkResults(sets, sb, num);
-   }
-   /**
-    * Execute each of the test sets that belong to a specific dependency
-    * type.
-    *
-    * @param    sb
-    *           Buffer in which to accumulate error text from failures.
-    * @param    type
-    *           Dependency group to execute.
-    * @param    rc
-    *           Object in which to store the details of non-testing failures.
-    *
-    * @return   The number of failures.
-    */
-   private int processTestSets(StringBuilder       sb,
-                               Dependency          type,
-                               FailureNotification rc)
-   {
-      if (Harness.isBenchmarkMode())
-         return processTestSetsPM(sb, type, rc);
-      int num = 0;
-      LogHelper.info("Start: " + this.getName() + "/" + type.toString());
-      
-      List<TestSet> list = new ArrayList<TestSet>();
-      
-      Iterator<TestSet> iter = sets.iterator();
-      
-      // subset the list of test-sets from the specified dependency group
-      while (iter.hasNext())
-      {
-         TestSet set = iter.next();
-         
-         if (set.getDependency() == type)
-            list.add(set);
-      }
-      
-      // create a specific latch of the right size
-      CountDownLatch sem = new CountDownLatch(list.size());
-      
-      iter = list.iterator();
-      
-      // execute each of the sets in the group
-      while (iter.hasNext())
-      {
-         if (rc.isFailed() && Harness.isFailOnRCFailure())
-         {
-            LogHelper.warn("RC failed, stopping TestPlan");
-            break;
-         }
-         TestSet set = iter.next();
-         set.execute(sem, rc);
-      }
-      
-      try            
-      {
-         // wait for test set to finish
-         while(sem.getCount() != 0)
-         {
-            sem.await(60, TimeUnit.SECONDS);
+            TestSet set = iter.next();
+            set.execute(sem, rc);
+         }
+         //wait for all to finish
+         while(!sem.tryAcquire(maxParallelTests, 60, TimeUnit.SECONDS))
+         {
             // dump statistics every 60 seconds
             dumpStatistics(list, type);
          }
       }
-      
       catch (InterruptedException ie)
       {
          sb.append("test plan thread interrupted before driver ")
-           .append(" threads finished");
+         .append(" threads finished");
          num = 1;
       }
-      LogHelper.info("End: " + this.getName() + "/" + type.toString());
+      
+      LogHelper.info("ENDSET|" + this.getName() + "/" + type.toString());
       dumpStatistics(list, type);
       
       return checkResults(sets, sb, num);
@@ -391,7 +321,8 @@
       int testsFailed = 0;
       int testsNotRun = 0;
       int testsTotal = 0;
-      
+      int testsInProgress = 0;
+      Set<String> testsInProgressNames = new HashSet<String>();
       for(TestSet testSet:list)
       {
          List<Test> tests = testSet.getTests();
@@ -407,6 +338,10 @@
                case NOT_RUN:
                   testsNotRun ++;
                   break;
+               case IN_PROGRESS:
+                  testsInProgress ++;
+                  testsInProgressNames.add(t.getName());
+                  break;
                case PASSED:
                   testsPassed ++;
                   break;
@@ -418,16 +353,21 @@
       }
       double successRate = (testsTotal == testsNotRun) 
                               ? 0.0d 
-                              : (double)testsPassed/((double)testsTotal-(double)testsNotRun) * 100.0d;
+                              : (double) testsPassed/ ((double) testsTotal - (double) testsNotRun 
+                                                                       - (double) testsInProgress) * 100.0d;
       double executionRate = (testsTotal == testsNotRun) 
                                 ? 0.0d 
                                 :(double) (testsPassed + testsFailed)/((double)testsTotal) * 100.0d;
-      LogHelper.finer(this.getName()+"/"+type.toString()
-                        + " Total: "     + testsTotal
-                        + " left: "      + testsNotRun
-                        + " failed: "    + testsFailed
-                        + " passed: "    + testsPassed
-                        + " successful: " + String.format("%2.2f%%", successRate)
-                        + " progress: "  + String.format("%2.2f%%", executionRate));
+      LogHelper.finer("TESTPLANREPORT|" + this.getName()+"/"+type.toString()
+                        + " Total: "       + testsTotal
+                        + " left: "        + testsNotRun
+                        + " in progress: " + testsInProgress
+                        + " failed: "      + testsFailed
+                        + " passed: "      + testsPassed
+                        + " successful: "  + String.format("%2.2f%%", successRate)
+                        + " progress: "   + String.format("%2.2f%%", executionRate));
+      LogHelper.finer("TESTPLANREPORT|" + this.getName()+"/"+type.toString()
+                        + " Tests in progress: "       
+                        + Arrays.toString(testsInProgressNames.toArray()));
    }
 }

=== modified file 'src/com/goldencode/harness/TestPlanFactory.java'
--- old/src/com/goldencode/harness/TestPlanFactory.java	2022-01-18 11:43:14 +0000
+++ new/src/com/goldencode/harness/TestPlanFactory.java	2023-06-21 07:16:47 +0000
@@ -2,7 +2,7 @@
 ** Module   : TestPlanFactory.java
 ** Abstract : instantiates a test plan from an XML definition 
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090122 Instantiates a test plan from an XML definition.
@@ -11,6 +11,7 @@
 ** 003 CA  20210906 Allow override via command line arguments, for: web endpoint, variable initial values and 
 **                  oauth1 parameters. 
 ** 004 CA  20220118 Added Basic authorization.
+** 005 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -200,6 +201,12 @@
             
             processOutputDirectory(root, plan);
             
+            // add output dorectory as variable
+            String outputDirectory = plan.getOutputDirectory();
+            if (outputDirectory != null && outputDirectory.trim().length()>0)
+            {
+               pool.createVariable("outputDirectory", Variable.getSupportedType("String"), outputDirectory);
+            }
             // preload default search paths
             if (testPaths != null)
                index.addTestPathList(testPaths);

=== modified file 'src/com/goldencode/harness/TestSet.java'
--- old/src/com/goldencode/harness/TestSet.java	2022-08-19 20:08:24 +0000
+++ new/src/com/goldencode/harness/TestSet.java	2023-07-21 10:24:15 +0000
@@ -3,7 +3,7 @@
 ** Abstract : stores the data needed to execute a set of tests on a given
 **            target host
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090122 Stores the data needed to execute a set of tests on a given target host.
@@ -12,6 +12,8 @@
 ** 003 TJD 20220803 Added logging, updated for Java 11
 ** 004 TJD 20220808 Changed log format, added option to abort TestSet
 ** 004 TJD 20220815 Logging tweaks
+** 005 TJD 20230119 Updated logging data
+** 005 TJD 20230119 Allow restarting of Driver threads
 */
 
 /*
@@ -33,7 +35,6 @@
 
 import java.util.*;
 import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
 
 import com.goldencode.harness.test.*;
 import com.goldencode.util.*;
@@ -61,7 +62,7 @@
    private ResourcePool pool = null;
    
    /** Semaphore to notify when complete. */
-   private CountDownLatch sem = null;
+   private Semaphore sem = null;
    
    /** Object in which to log non-testing failures. */
    private FailureNotification rc = null;
@@ -197,27 +198,27 @@
    /**
     * Start the thread that executes this test set. See {@link #run}.
     *
-    * @param    sem
+    * @param    sem2
     *           Semaphore to notify when the test set completes processing.
     * @param    rc
     *           Object in which to store the details of non-testing failures.
     */
-   public void execute(CountDownLatch sem, FailureNotification rc)
+   public void execute(Semaphore sem2, FailureNotification rc)
    {
-      this.sem = sem;
+      this.sem = sem2;
       this.rc  = rc;
 
       if (cfgs == null || cfgs.isEmpty())
       {
          if (sem != null)
-            sem.countDown();
+            sem.release();;
          
          rc.setResult(ExitCode.MISSING_CONTENT);
          rc.setDetails("Missing session configuration for test set execution.");
          return;
       }
       
-      if (sem == null)
+      if (sem2 == null)
       {
          rc.setResult(ExitCode.MISSING_CONTENT);
          rc.setDetails("Missing semaphore for test set execution.");
@@ -240,26 +241,57 @@
       setThreadName(Thread.currentThread().getName());
       
       StringBuilder  sb     = new StringBuilder();
-      CountDownLatch latch  = new CountDownLatch(threads);
-      
+      Phaser         phaser = new Phaser();
       int num = 0;
+      int threadIndex = 0;
       
       // start test set timer
       setStartTime(System.currentTimeMillis());
-      LogHelper.info("Start: "+ this.getName());
       
       // start each of the driver threads (there will be at least 1 started)
       for (int i = 0; i < threads; i++)
       {
-         Driver driver = new Driver(cfgs, queue, latch, rc);
-         driver.setName(String.format("[%s/%d]", this.getName(), i));
-         driver.start();
+         if (phaser.register() == 0)
+         {
+            Driver driver = new Driver(cfgs, queue, phaser, rc);
+            driver.setName(String.format("%s/%d", this.getName(), threadIndex++));
+            driver.start();
+         }
       }
       
       try
       {
-         // wait for each driver thread to finish
-         latch.await();
+         while (true)
+         {
+            try
+            {
+               // wait for each driver thread to finish
+               phaser.awaitAdvanceInterruptibly(0, 1, TimeUnit.MINUTES);
+               // all threads arrived, we are done
+               break;
+            }
+            catch (TimeoutException e)
+            {
+               // we have timeout so we can check how many threads has already arrived
+               if (!queue.isEmpty() && phaser.getRegisteredParties() < threads)
+               {
+                  for (int i = phaser.getRegisteredParties(); i < threads; i++)
+                  {
+                     if (phaser.register() == 0)
+                     {
+                        LogHelper.finer("DRIVERSTART|" + this.getName() + "| Starting new driver");
+                        Driver driver = new Driver(cfgs, queue, phaser, rc);
+                        driver.setName(String.format("%s/%d", this.getName(), threadIndex++));
+                        driver.start();
+                     }
+                     else
+                     {
+                        LogHelper.finer("DRIVERSTART|" + this.getName() + "| Not starting new driver, phased out");
+                     }
+                  }
+               }
+            }
+         }
       }
       
       catch (InterruptedException ie)
@@ -271,14 +303,13 @@
       
       // end test set timer
       setEndTime(System.currentTimeMillis());
-      LogHelper.info("End: "+ this.getName() + Harness.completionMsg(getElapsed()));
       
       num = checkResults(tests, sb, num);
       
       setStatus((num > 0) ? Status.FAILED : Status.PASSED,
                 (sb.length() > 0) ? sb.toString() : null);
       
-      sem.countDown();
+      sem.release();;
    }
 }
 

=== modified file 'src/com/goldencode/harness/terminal/Terminal.java'
--- old/src/com/goldencode/harness/terminal/Terminal.java	2023-01-11 20:44:24 +0000
+++ new/src/com/goldencode/harness/terminal/Terminal.java	2023-07-21 09:16:59 +0000
@@ -17,6 +17,7 @@
 ** 007 TJD 20220918 SendText drain mode optimization - shared variable needs to be volatile
 ** 008 RFB 20230111 Added finer logging which helps with debug. Also removed a return value in a comment
 **                  to fix javadoc (plus review).
+** 009 TJD 20230119 Implemented trace log level
 */
 
 /*
@@ -160,7 +161,7 @@
       this.session = session;
       
       setTerminalID(cfg.termType);
-      LogHelper.finer("setTerminalID termType=" + cfg.termType);
+      LogHelper.finer("SETTERMINALTYPE|" + cfg.termType);
       
       // insert a display processor that copies the current screen to a
       // secondary buffer during redrawing
@@ -171,7 +172,7 @@
          
          public void redraw()
          {
-            LogHelper.finer("merging new drawing into buffer");
+            LogHelper.finest("merging new drawing into buffer");
             // merge the newly drawn updates into our buffer
             for (int n = 0; n < height; n++)
             {
@@ -203,7 +204,7 @@
             // lines have a flag at line number + 1 in the array
             update[0] = false;
             
-            LogHelper.finer("new screen, height=" + height + ", width=" + width);
+            LogHelper.finest("new screen, height=" + height + ", width=" + width);
             synchronized (lock)
             {
                // create a new, empty snapshot
@@ -234,13 +235,15 @@
             
             try
             {
-               LogHelper.finer("polling for screen data");
+               LogHelper.finest("polling for screen data");
                int x=0;
                while (true)
                {
                   int i = Terminal.this.session.read(buffer);
-                  LogHelper.finer("read results in i=" + i);
-                  
+                  if (LogHelper.isFinestLoggable())
+                  {
+                     LogHelper.finest("read results in i=" + i);
+                  }
                   // watch for EOF
                   if (i < 0)
                      break;
@@ -268,14 +271,14 @@
          }
       };
       
-      LogHelper.finer("starting readerThread");
+      LogHelper.finest("starting readerThread");
       Thread readerThread = new Thread(reader);
       readerThread.setDaemon(true);
       readerThread.setName("Terminal Reader");
       readerThread.start();
       
       // wait up to 30 seconds for the 1st screen to come through
-      LogHelper.finer("waiting for up to 30 seconds for the first screen...");
+      LogHelper.finest("waiting for up to 30 seconds for the first screen...");
       waitForScreen(30000);
    }
    
@@ -562,8 +565,10 @@
    {
       if (text == null || text.length() == 0)
          return;
-      
-      LogHelper.finer("sendText: text=" + text);
+      if (LogHelper.isFinestLoggable())
+      {
+         LogHelper.finest("SENDTEXT|" + text +"|");
+      }
       delay = Math.max(delay, 50);
       if (Harness.isBenchmarkMode() && delay > 50)
       {
@@ -792,7 +797,10 @@
       
       if (timeout == 0 || waitForScreen(timeout))
       {
-         LogHelper.finer("getScreen: timeout=" + timeout + ", clear=" + clear + ", (screen == null)=" + (screen == null));
+         if (LogHelper.isFinestLoggable())
+         {
+            LogHelper.finest("getScreen: timeout=" + timeout + ", clear=" + clear + ", (screen == null)=" + (screen == null));
+         }
          synchronized (lock)
          {
             current = screen;
@@ -802,7 +810,6 @@
          }
       }
       
-      LogHelper.finer((current != null) ? "getScreen: returning screen data" : "getScreen: no screen data to return");
       return current;
    }
    

=== modified file 'src/com/goldencode/harness/test/LogScreenBuffer.java'
--- old/src/com/goldencode/harness/test/LogScreenBuffer.java	2021-08-30 11:50:34 +0000
+++ new/src/com/goldencode/harness/test/LogScreenBuffer.java	2023-06-15 19:18:46 +0000
@@ -2,12 +2,13 @@
 ** Module   : LogScreenBuffer.java
 ** Abstract : directive to query the current screen and store it in the report
 **
-** Copyright (c) 2009-2021, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090126 Directive to query the current screen and store it in the report.
 ** 002 CA  20210827 Refactored to allow other test types, beside terminal (which can be executed from the same 
 **                  test suite).  Added SOAP and REST web service tests.
+** 003 TJD 20230119 Test timeout multiplier support
 */
 
 /*
@@ -73,7 +74,7 @@
    @Override
    public void execute(Terminal term)
    {
-      setDescription(term.getScreen(clear, millis));
+      setDescription(term.getScreen(clear, Harness.scaleTimeoutMultiplier(millis)));
       setStatus(Status.PASSED, "");
    }
    

=== modified file 'src/com/goldencode/harness/test/Pause.java'
--- old/src/com/goldencode/harness/test/Pause.java	2022-08-18 15:08:49 +0000
+++ new/src/com/goldencode/harness/test/Pause.java	2023-07-21 09:18:15 +0000
@@ -2,13 +2,14 @@
 ** Module   : Pause.java
 ** Abstract : directive to pause for a fixed time interval in milliseconds
 **
-** Copyright (c) 2009-2021, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090114 Directive to pause for a fixed time interval in milliseconds.
 ** 002 CA  20210827 Refactored to allow other test types, beside terminal (which can be executed from the same 
 **                  test suite).  Added SOAP and REST web service tests.
 ** 003 TJD 20220817 Implement performance test mode
+** 004 TJD 20230119 Test timeout multiplier support
 */
 
 /*
@@ -112,4 +113,19 @@
       
       setStatus(status, reason);
    }
+   
+   /**
+    * Get the maximum number of milliseconds that this testable activity is allowed to run.
+    * Valid for some of Tests only, -1 if not applicable
+    * @return   The maximum number of milliseconds.
+    */
+   @Override
+   public long getMaxElapsed()
+   {
+      if (millis > 0)
+      {
+         return Harness.scaleTimeoutMultiplier(millis);
+      }
+      return millis;
+   }
 }

=== modified file 'src/com/goldencode/harness/test/ScreenPoller.java'
--- old/src/com/goldencode/harness/test/ScreenPoller.java	2022-08-23 16:11:29 +0000
+++ new/src/com/goldencode/harness/test/ScreenPoller.java	2023-07-19 07:40:17 +0000
@@ -2,7 +2,7 @@
 ** Module   : ScreenPoller.java
 ** Abstract : base class to handle common polling logic when examining screens
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090210 Base class to handle common polling logic when examining screens.
@@ -13,6 +13,8 @@
 ** 005 TJD 20220817 implemented performance test mode with experimental remove of waits from screen polling,
 ** 006 TJD 20220818 Fix for performance mode bug
 ** 007 TJD 20220819 Allow configuring max timeout for single test that require idle waiting
+** 008 TJD 20230119 Test timeout multiplier support
+** 009 TJD 20230719 Reduce screen poll delay, increase screen poll frequency
 **/
 
 /*
@@ -43,7 +45,7 @@
 implements TerminalTestable
 {
    /** Milliseconds to wait in polling loop. */
-   private static final int INTERVAL = 500;
+   private static final int INTERVAL = 100;
    
    /** Maximum number of milliseconds to wait. */
    private final int millis;
@@ -231,9 +233,9 @@
       String   reason = null;
       
       int      amt        = 0;
-      int      timeout    = millis;
       boolean  oneTime    = (millis == 0);
       boolean  indefinite = (millis == -1);
+      int      timeout    = Harness.scaleTimeoutMultiplier(millis);
       boolean  failure    = false;
       char[][] screen     = null;
       
@@ -393,4 +395,15 @@
       }
       setDescription(sb.toString());
    }
+   
+   /**
+    * Get the maximum number of milliseconds that this testable activity is allowed to run.
+    * Valid for some of Tests only, -1 if not applicable
+    * @return   The maximum number of milliseconds.
+    */
+   @Override
+   public long getMaxElapsed()
+   {
+      return millis;
+   }
 }

=== modified file 'src/com/goldencode/harness/test/SendText.java'
--- old/src/com/goldencode/harness/test/SendText.java	2012-08-27 15:24:49 +0000
+++ new/src/com/goldencode/harness/test/SendText.java	2023-07-21 09:19:08 +0000
@@ -2,10 +2,11 @@
 ** Module   : SendText.java
 ** Abstract : implement the send-text step in a test
 **
-** Copyright (c) 2009, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090113 Implement the send-text step in a test.
+** 002 TJD 20230615 Trace mode additions
 */
 
 /*
@@ -27,6 +28,7 @@
 
 import com.goldencode.harness.*;
 import com.goldencode.harness.terminal.*;
+import com.goldencode.util.*;
 
 /**
  * Implement the send-text step in a test. This directive has the
@@ -241,6 +243,10 @@
     */
    protected void send(Terminal term, String value, String modifiers)
    {
+      if (LogHelper.isFinestLoggable())
+      {
+         LogHelper.finest("sendText:" + value + " modifiers:" + modifiers);
+      }
       if (value != null && value.length() > 0)
       {
          term.sendText(value, modifiers, delay);

=== modified file 'src/com/goldencode/harness/test/Test.java'
--- old/src/com/goldencode/harness/test/Test.java	2022-08-09 14:25:23 +0000
+++ new/src/com/goldencode/harness/test/Test.java	2023-06-12 13:58:49 +0000
@@ -2,7 +2,7 @@
 ** Module   : Test.java
 ** Abstract : defines and manages the execution of a single test 
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090113 Stores the steps for a test and manages the execution of those steps.
@@ -10,6 +10,7 @@
 **                  test suite).  Added SOAP and REST web service tests.
 ** 003 TJD 20220803 Added logging, updated for Java 11
 ** 004 TJD 20220803 Changed log format
+** 005 TJD 20230612 Support for IN_PROGRESS test status
 */
 
 /*
@@ -114,7 +115,7 @@
       super.setStatus(status);
       
       // notify all dependent objects of the final status
-      if (dependents != null)
+      if (dependents != null && !status.equals(Status.IN_PROGRESS))
       {
          String reason = getReason();
          String name   = getName();
@@ -271,7 +272,8 @@
       // start test level timer
       long start = System.currentTimeMillis();
       setStartTime(start);
-      LogHelper.fine(driver.getName() + "[" + this.getName() + "] Start");
+      LogHelper.finer("START|"+driver.getName() + "|" + this.getName());
+      setStatus(Status.IN_PROGRESS);
       
       try
       {
@@ -291,9 +293,16 @@
             // run the step
             try
             {
-               LogHelper.finer(driver.getName() + "[" + this.getName() + "]" + " Step " + ( i + 1) 
-                                  + ": " + next.getName() + ":" + getDescription());
+               LogHelper.finer("STEPSTART|" + driver.getName() + "|" + this.getName() +  "|" + (i + 1) 
+                        +"|Step " + ( i + 1) + ": " + next.getName() + ":" + getDescription());
+               long testStartTime = System.currentTimeMillis();
                driver.execute(next);
+               long testEndTime = System.currentTimeMillis();
+               LogHelper.finer("TIME|" + driver.getName() + "|" + this.getName() + "|" + (i + 1) 
+                        +"|" + (testEndTime - testStartTime) + "|" + next.getMaxElapsed()  
+                        + "|Step " + ( i + 1) + ": " + next.getName() 
+                        + " step time -> " + (testEndTime - testStartTime) 
+                        + " ms, max time:" + next.getMaxElapsed() + " ms");
             }
             
             catch (Throwable thr)
@@ -371,7 +380,7 @@
       // end the test timer
       long end = System.currentTimeMillis();
       setEndTime(end);
-      LogHelper.fine(driver.getName() + "[" + this.getName() + "] End");
+      LogHelper.finer("END|"+driver.getName() + "|" + this.getName() + "| Ended");
       
       long elapsed = end - start;
       

=== modified file 'src/com/goldencode/harness/test/WaitEventSem.java'
--- old/src/com/goldencode/harness/test/WaitEventSem.java	2021-08-30 11:50:34 +0000
+++ new/src/com/goldencode/harness/test/WaitEventSem.java	2023-07-21 09:19:23 +0000
@@ -2,12 +2,13 @@
 ** Module   : WaitEventSem.java
 ** Abstract : directive to wait for an event semaphore to be posted
 **
-** Copyright (c) 2009-2021, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 GES 20090224 Directive to wait for an event semaphore to be posted.
 ** 002 CA  20210827 Refactored to allow other test types, beside terminal (which can be executed from the same 
 **                  test suite).  Added SOAP and REST web service tests.
+** 003 TJD 20230119 Test timeout multiplier support
 */
 
 /*
@@ -75,7 +76,7 @@
       
       try
       {
-         if (!event.waitFor(timeout))
+         if (!event.waitFor(Harness.scaleTimeoutMultiplier(timeout)))
          {
             reason = "Timeout while waiting for event semaphore to be posted."; 
          }
@@ -88,4 +89,19 @@
       
       setStatus((reason == null) ? Status.PASSED : Status.FAILED, reason);
    }
+   
+   /**
+    * Get the maximum number of milliseconds that this testable activity is allowed to run.
+    * Valid for some of Tests only, -1 if not applicable
+    * @return   The maximum number of milliseconds.
+    */
+   @Override
+   public long getMaxElapsed()
+   {
+      if (timeout > 0)
+      {
+         return Harness.scaleTimeoutMultiplier(timeout);
+      }
+      return timeout;
+   }
 }

=== modified file 'src/com/goldencode/harness/transport/SSH2Transport.java'
--- old/src/com/goldencode/harness/transport/SSH2Transport.java	2023-01-11 20:44:24 +0000
+++ new/src/com/goldencode/harness/transport/SSH2Transport.java	2023-06-08 11:14:02 +0000
@@ -11,6 +11,7 @@
 ** 003 TJD 20220808 Logging of ssh errors
 ** 004 TJD 20230110 To prevent data loss Input/Output streams must be created before connecting a channel
 ** 005 RFB 20230111 Added finer logging which helps with debug (plus review).
+** 006 TJD 20230119 Implemented trace log level
 */
 
 /*
@@ -76,10 +77,16 @@
             session = jsch.getSession(cfg.userid, cfg.host, cfg.port);
             session.setPassword(cfg.password);
             session.setConfig("StrictHostKeyChecking", "no");
-            LogHelper.finer("connect id=" + cfg.userid + " host=" + cfg.host + " port=" + cfg.port);
+            if (LogHelper.isFinestLoggable())
+            {
+               LogHelper.finest("Connect id=" + cfg.userid + " host=" + cfg.host + " port=" + cfg.port);
+            }
             session.connect(30000);
-            LogHelper.finer("connect attempt completed. session=" + session.toString());
             
+            if (LogHelper.isFinestLoggable())
+            {
+               LogHelper.finest("connect attempt completed. session=" + session.toString());
+            }
             channel = (ChannelShell) session.openChannel("shell");
 
             if (channel == null)
@@ -100,7 +107,10 @@
             os = channel.getOutputStream();
             is = channel.getInputStream();
             channel.connect(3*1000);
-            LogHelper.finer("channel completed isConnected=" + session.isConnected());
+            if (LogHelper.isFinestLoggable())
+            {
+               LogHelper.finest("channel completed isConnected=" + session.isConnected());
+            }
          }
          catch (IOException e)
          {

=== modified file 'src/com/goldencode/util/LogHelper.java'
--- old/src/com/goldencode/util/LogHelper.java	2022-08-19 20:08:24 +0000
+++ new/src/com/goldencode/util/LogHelper.java	2023-06-12 09:33:06 +0000
@@ -2,7 +2,7 @@
 ** Module   : LogHelper.java
 ** Abstract : static utility methods to assist logging 
 **
-** Copyright (c) 2009-2022, Golden Code Development Corporation.
+** Copyright (c) 2009-2023, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -----------------------Description------------------------
 ** 001 GES 20090212 Static utility methods to assist logging.
@@ -11,6 +11,8 @@
 ** 004 TJD 20220809 Added log level FINER
 ** 005 TJD 20220817 Allow to check detailed log levels
 ** 006 TJD 20220819 Move completionMsg to Harness.java
+** 007 TJD 20230119 Implemented trace log level
+** 008 TJD 20230612 Support for IN_PROGRESS test status
 */
 
 /*
@@ -58,7 +60,7 @@
          {
             return String.format(logFormat, logRecord.getMillis(), logRecord.getLevel(), 
                                    logRecord.getSourceClassName(), logRecord.getMessage(),
-                                   logRecord.getThrown()!=null?dumpThrowable(logRecord.getThrown()):"");
+                                   logRecord.getThrown() != null ? dumpThrowable(logRecord.getThrown()):"");
          }
       };
 //      StreamHandler handler = new StreamHandler(System.out, logFormatter);
@@ -196,6 +198,19 @@
    }
    
    /**
+    * Log trace message string
+    * @param    message
+    *           message to log
+    */
+   public static void finest(String message)
+   {
+      if (!LOG.isLoggable(Level.FINEST))
+         return;
+      String className = getCallingClassName(3);
+      LOG.logp(Level.FINEST, className, null, message);
+   }
+   
+   /**
     * Log debug message string with Throwable
     * @param    message
     *           message to log
@@ -219,6 +234,14 @@
    }
    
    /**
+    * Enable trace output
+    */
+   public static void enableTrace()
+   {
+      LOG.setLevel(Level.FINEST);
+   }
+   
+   /**
     * Enable specified log level
     */
    public static void setLogLevel(String logLevel)
@@ -265,4 +288,14 @@
    {
       return LOG.isLoggable(Level.FINER);
    }
+   
+   /**
+    * Check if level finest/trace is loggable
+    * 
+    * @return   true if level finer is loggable
+    */
+   public static boolean isFinestLoggable()
+   {
+      return LOG.isLoggable(Level.FINEST);
+   }
 }

