//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
 * timersCmdLine.cpp
 */

/* 
 * functions for processing the timer tests command lines
 */

#include "timersCmdLine.h"

#include "..\..\..\common\commonUtils.h"


/* 
 * Prints out the usage message for the timer tests. It tells the user 
 * what the tests do and also specifies the input that the user needs to 
 * provide to run the tests. 
 */

VOID
printTimerUsageMessage ()
{
  Info (_T("These tests aim to test the timers.  There are three different timers,"));
  Info (_T("the real-time clock or RTC, the getTickCount clock or GTC and the"));
  Info (_T("HiPerformance clock. The tests take different command line arguments."));
  Info (_T("Please see the documentation for more information."));
  Info (_T(""));
  Info (_T("Options are case insensitive. All <time> values are given in seconds,"));
  Info (_T("unless the following modifiers are used:"));
  Info (_T("  s => seconds  (12s = 12 seconds"));
  Info (_T("  m => minutes  (12m = 12 minutes)"));
  Info (_T("  h => hours    (12h = 12 hours)"));
  Info (_T("  d => days     (12d = 12 days)"));
  Info (_T(""));
  Info (_T(""));
  Info (_T("TESTS FOR ALL THREE CLOCKS:"));
  Info (_T(""));
  Info (_T("Resolution tests:"));
  Info (_T(""));
  Info (_T("  %s <seconds>          Maximum resolution allowed for the GTC."),
	ARG_STR_GTC_MAX_RES);
  Info (_T("                        Decimal values accepted as well."));
  Info (_T(""));
  Info (_T("Backwards checks:"));
  Info (_T(""));
  Info (_T("  %s <time>      Run time for test."), 
  	                                          ARG_STR_BACKWARDS_CHECK_RUN_TIME);
  Info (_T(""));
  Info (_T("Drift comparison tests:"));
  Info (_T(""));
  Info (_T("  Global args (can't be special cased per test):"));
  Info (_T("    [ %s <time>] [%s <time>]"),
	ARG_STR_DRIFT_RUN_TIME, ARG_STR_DRIFT_SAMPLE_SIZE );
  Info (_T("    (These are not for the %s or %s test.)"), 
	ARG_STR_TRACK_IDLE_PERIODIC_NAME, ARG_STR_TRACK_IDLE_RANDOM_NAME);
  Info (_T(""));
  Info (_T("  Per test args:"));
  Info (_T("    [<testname> [<bound> <num> <num>]]"));
  Info (_T("    <testName> is one of %s, %s, %s, %s"),
	ARG_STR_BUSY_SLEEP_NAME, ARG_STR_OS_SLEEP_NAME, ARG_STR_TRACK_IDLE_PERIODIC_NAME,
	ARG_STR_TRACK_IDLE_RANDOM_NAME);
  Info (_T("    <bound> is one of %s, %s, %s"),
	         ARG_STR_DRIFT_GTC_RTC, ARG_STR_DRIFT_GTC_HI, ARG_STR_DRIFT_HI_RTC);
  Info (_T("    <num> gives the bounds for the given comparison."));
  Info (_T("    Note the lack of dashes preceeding these arguments."));
  Info (_T(""));
  Info (_T("Wall clock tests:"));
  Info (_T(""));
  Info (_T("  %s <time>              Run time for wall clock test."), 
	                                              ARG_STR_WALL_CLOCK_RUN_TIME);
  Info (_T(""));
  Info (_T("GetIdleTime tests:"));
  Info (_T(""));
  Info (_T("  %s <time>            Run time for idle time tests."),
	ARG_STRING_IDLE_RUN_TIME);
  Info (_T(""));
  Info (_T(""));
  Info (_T("TESTS FOR REAL-TIME CLOCK:"));
  Info (_T(""));        
  Info (_T("OEMGetRealTime and OEMSetRealTime tests:"));
  Info (_T(""));
  Info (_T("  %s <mm/dd/yyyy hh:mm:ss> DateAndTime value to test the RTC functions."),
  	                                                     ARG_STR_DATE_AND_TIME);
  Info (_T(""));    
  Info (_T("  The time is in 24 hour format"));
  Info (_T("  Example: %s 06/25/2006 23:47:35"),ARG_STR_DATE_AND_TIME);
  Info (_T(""));    
  Info (_T("Reentrance of OEMGetRealTime and OEMSetRealTime tests:"));   
  Info (_T(""));
  Info (_T("  %s <number>      Number of threads for the reentrance test"),
  	                                                    ARG_STR_NUM_OF_THREADS);
  Info (_T("  Example: %s 25"),ARG_STR_NUM_OF_THREADS);
  Info (_T(""));    
  Info (_T("  %s <time>      Run time for the reentrance test."),
  	                                                        ARG_STR_RUN_TIME);
  Info (_T(""));   
  
}


/*
 * pull the backwards checks params from the command line.
 *
 * dwRunTime can't be null.  On input it points to the default run
 * time in seconds.  On output it point to the user supplied runtime,
 * if this value was given and is correct.  If not it still holds the
 * default run time.
 *
 * returns true on success and false otherwise.  On false dwRunTime is
 * preserved.
 *
 * This prints a message telling the user what run time is being used
 * for the test.
 */
BOOL
handleBackwardsCheckParams (ULONGLONG * pullRunTime)
{
  BOOL returnVal = FALSE;

  if (!pullRunTime)
    {
      return (FALSE);
    }

  BOOL bUserSuppliedParams = FALSE;

  /* 
   * if user doesn't specify the -c param to tux the cmd line global
   * var is null.  In this case don't even try to parse command line
   * args.
   */
  if (g_szDllCmdLine != NULL)
    {
      cTokenControl tokens;

      /* break up command line into tokens */
      if (!tokenize ((TCHAR *)g_szDllCmdLine, &tokens))
	{
	  Error (_T("Couldn't parse command line."));
	  goto cleanAndExit;
	}

      if (getOptionTimeUlonglong (&tokens, ARG_STR_BACKWARDS_CHECK_RUN_TIME,
				   pullRunTime))
	{
	  bUserSuppliedParams = TRUE;	  
	}
    } 
  
  /* convert to realistic time */
  double doTime;
  TCHAR * szUnits;
  realisticTime ((double) *pullRunTime, doTime, &szUnits);

  if (bUserSuppliedParams)
    {
      Info (_T("Using the user supplied test run time, which is %g %s."),
	    doTime, szUnits);
    }
  else
    {	
      /* use default */
      Info (_T("Using the default test run time, which is  %g %s"),
	    doTime, szUnits);
    }

  returnVal = TRUE;
 cleanAndExit:

  return (TRUE);
}





typedef struct
{
  TCHAR * szCmdName;
  sBounds * b;
  BOOL bUsingDefaults;
} sBoundsContainer;








BOOL
getBoundsForDriftTests (cTokenControl * tokens,
			TCHAR * szTestName,
			sBoundsContainer * bounds,
			DWORD sBoundsLength);




/*
 * Returns FALSE if something goes wrong during parsing and 
 * true otherwise.
 *
 * tokens is a non-null pointer to an initialized token class.
 * szTestName is the test name that starts off this string of
 * arguments.  What follows are the names of the arguments and the
 * corresponding bounds.  On input, the bounds are initialized to
 * their default values.  If command line parameters have been given
 * to override these then the specific bounds are reset.  If no
 * parameters are given then then bounds are not touched.
 * 
 * On error the bounds are undefined.
 *
 * The parser is pretty good but will display odd behavior on odd
 * input.
 *
 * The general algorithm is:
 *
 * Move bounds into array of structures to make code cleaner.
 * Check for test name
 * Look for options of the form <optionName> <double> <double>
 * Convert what is found
 * Print out which values (default or user) are being used.
 *
 * In the bounds arrays, doFasterBound must always be numerically
 * greater.  This function preserves these symantics.
 */
BOOL
getBoundsForDriftTests(cTokenControl * tokens, 
		       TCHAR * szTestName,
		       TCHAR * szBound1Name,
		       sBounds * bound1,
		       TCHAR * szBound2Name,
		       sBounds * bound2,
		       TCHAR * szBound3Name,
		       sBounds * bound3)
{
  BOOL returnVal = FALSE;

  /* sanity check on params */
  if (!tokens || !szTestName || !szBound1Name || !bound1 ||
      !szBound2Name || !bound2 || !szBound3Name || !bound3)
    {
      return (FALSE);
    }

  sBoundsContainer bounds[3];

  /* 
   * load values into struct for easy processing.  This allows 
   * searching via for loops as opposed to special casing.
   */
  bounds[0].szCmdName = szBound1Name;
  bounds[0].b = bound1;
  bounds[1].szCmdName = szBound2Name;
  bounds[1].b = bound2;
  bounds[2].szCmdName = szBound3Name;
  bounds[2].b = bound3;
  
  /* set all to default */
  for (DWORD dwPos = 0; dwPos < 3; dwPos++)
    {
      bounds[dwPos].bUsingDefaults = 1;
    }
  
  /*
   * use the generic version
   * This will loads the bounds variables above, and also 
   * set the using defaults flags
   */
  if (!getBoundsForDriftTests (tokens, szTestName,
			      bounds, 3))
    {
      goto cleanAndReturn;
    }

  for (DWORD dwPos = 0; dwPos < 3; dwPos++)
    {
      if (bounds[dwPos].bUsingDefaults)
	{
	  Info (_T("Using default bounds for %s: ratio range is %g to %g."), 
		bounds[dwPos].b->szName, 
		bounds[dwPos].b->doFasterBound, 
		bounds[dwPos].b->doSlowerBound);
	}
      else
	{
	  Info (_T("Using user's bounds for %s: ratio range is %g to %g."), 
		bounds[dwPos].b->szName, 
		bounds[dwPos].b->doFasterBound, 
		bounds[dwPos].b->doSlowerBound);
	}
    }

  returnVal = TRUE;

 cleanAndReturn:
  
  return (returnVal);

}

BOOL
getBoundsForDriftTests (cTokenControl * tokens,
			TCHAR * szTestName,
			sBoundsContainer * bounds,
			DWORD sBoundsLength)
{	
  BOOL returnVal = FALSE;

  /* where we currently are in the token array */
  DWORD dwCurrIndex = 0;

  /*
   * find the os test name.  If we don't find it then using the
   * default values.
   *
   * Order matters in the expression below.  Don't want to call
   * tokens[] with out of range index.
   */
  while ((dwCurrIndex < tokens->listLength()) &&
	 (_tcsicmp (szTestName, (*tokens)[dwCurrIndex]) != 0))
    {
      dwCurrIndex++;
    }

  if (dwCurrIndex >= tokens->listLength())
    {
      returnVal = TRUE;
      goto cleanAndReturn;
    }

  /* 
   * dwCurrIndex should point to the szTestName parameter.  Move to
   * the next one. 
   */
  dwCurrIndex++;

  DWORD dwNumOptionsProcessed = 0;

  /* while we have tokens and have not processed all possible options */
  while ((dwCurrIndex < tokens->listLength()) &&
	 (dwNumOptionsProcessed < 3))
    {
      /* look for bound one param */
      DWORD dwGetOptionReturn = CMD_NOT_FOUND;

      for (DWORD dwPos = 0; 
	   (dwPos < sBoundsLength) && (dwGetOptionReturn == CMD_NOT_FOUND); dwPos++)
	{
	  double doB1, doB2;

	  dwGetOptionReturn = 
	    getOptionPlusTwo (tokens, dwCurrIndex, 
			      bounds[dwPos].szCmdName, &doB1, &doB2);
      
	  switch (dwGetOptionReturn)
	    {
	    case CMD_FOUND_IT:
	      if (!bounds[dwPos].bUsingDefaults)
		{
		  /* we have already seen this option */
		  Error (_T("Already seen option %s on command line."),
			 bounds[dwPos].szCmdName);
		  goto cleanAndReturn;
		}
	      
	      bounds[dwPos].bUsingDefaults = FALSE;

	      if (doB1 > doB2)
		{
		  bounds[dwPos].b->doFasterBound = doB1;
		  bounds[dwPos].b->doSlowerBound = doB2;
		}
	      else
		{
		  bounds[dwPos].b->doFasterBound = doB2;
		  bounds[dwPos].b->doSlowerBound = doB1;
		}

	      dwNumOptionsProcessed ++;

	      break;
	    case CMD_NOT_FOUND:
	      /* do nothing */
	      break;
	    case CMD_ERROR:
	      /* bail out */
	      goto cleanAndReturn;
	    default:
	      IntErr (_T("Bad case statement."));
	      goto cleanAndReturn;
	    }
	}

      dwCurrIndex += 3;
    }


  returnVal = TRUE;

 cleanAndReturn:
  
  return (returnVal);

}


BOOL
getArgsForDriftTests (cTokenControl * tokens, 
		      TCHAR * szSampleSizeName,
		      TCHAR * szRunTimeName,
		      DWORD * pdwSampleSize,
		      DWORD * pdwRunTime)
{
  BOOL returnVal = TRUE;

  if (!tokens || !szSampleSizeName || !szRunTimeName || !pdwSampleSize ||
      !pdwRunTime)
    {
      return (FALSE);
    }

  double doRealisticTime;
  _TCHAR * pszRealisticUnits;

  struct
  {
    TCHAR * szCmdName;
    TCHAR * szFriendlyName;
    DWORD * pdwVal;
  } vals[2];

  vals[0].szCmdName = szSampleSizeName;
  vals[0].pdwVal = pdwSampleSize;
  vals[0].szFriendlyName = _T("sample size");
  vals[1].szCmdName = szRunTimeName;
  vals[1].pdwVal = pdwRunTime;
  vals[1].szFriendlyName = _T("run time");
  
  for (DWORD dwPos = 0; dwPos < 2; dwPos++)
    {
      DWORD dwSizeIndex = 0;

      if (findTokenI (tokens, vals[dwPos].szCmdName, &dwSizeIndex))
	{
	  /* make sure next token exists */
	  if ((dwSizeIndex + 1 == 0) || 
	      (dwSizeIndex + 1 >= tokens->listLength()))
	    {
	      Error (_T("Param %s has no argument."), 
		     vals[dwPos].szCmdName);
	      returnVal = FALSE;
	    }
	  else
	    {
	      dwSizeIndex++;

	      /* is on command line */
	      ULONGLONG ullTimeS;

	      if (!strToSeconds ((*tokens)[dwSizeIndex], &ullTimeS))
		{
		  Error (_T("Error on param %s.  Couldn't convert %s to ")
			 _T("seconds."),
			 vals[dwPos].szCmdName, (*tokens)[dwSizeIndex]);
		  returnVal = FALSE;
		}
	      else
		{
		  if (ullTimeS > (ULONGLONG) MAX_DWORD)
		    {
		      Error (_T("Sample size of %I64u is larger than a DWORD ")
			     _T("value."),
			     ullTimeS);
		      returnVal = FALSE;
		    }
		  else
		    {
		      *(vals[dwPos].pdwVal) = (DWORD) ullTimeS;

		      if (*(vals[dwPos].pdwVal) == 0)
			{
			  Error (_T("Sample size can't be zero."));
			  returnVal = FALSE;
			}	
		      else
			{
			  realisticTime (double (*(vals[dwPos].pdwVal)), 
					 doRealisticTime, &pszRealisticUnits);
			  Info (_T("Using user supplied %s of %g %s."), 
				vals[dwPos].szFriendlyName,
				doRealisticTime, pszRealisticUnits);
			} 
		    } 
		}
	    }
	}
      else
	{
	  /* use default */
	  realisticTime (double (*(vals[dwPos].pdwVal)),
			 doRealisticTime, &pszRealisticUnits);
	  Info (_T("User did not specify a %s.  Using %u s, ")
		_T("which is %g %s."),
		vals[dwPos].szFriendlyName,
		*(vals[dwPos].pdwVal), doRealisticTime, pszRealisticUnits);
	}

    }

  return (returnVal);
}



/*******************************************************************************
 *                        Parse user's date and time
 ******************************************************************************/

// This function parses the command line arguments and returns the 
// user supplied date and time to test the Get and Set real-time functions.
// 
// Arguments: 
// 	Input: None
//  	Output: The time parsed from the command line
//
// Return value:
//    	TRUE if time was parsed correctly, FALSE otherwise

BOOL parseUserDateTime (LPSYSTEMTIME pUserTime)
{
  cTokenControl tokens;

  BOOL returnVal = FALSE;

  if(!pUserTime)
  {
  	goto cleanAndExit;
  }
  
  DWORD dwIndex1 =0;
  DWORD dwIndex2 = 0;
 
  TCHAR *tok = NULL;
  TCHAR *szDateSep = _T("/");
  TCHAR *szTimeSep = _T(":");
  WORD tokArray[7] = {0, 0, 0, 0, 0, 0, 0};   
  ULONGLONG ullVal;	

  Info (_T("")); // Blank line

  if(!(g_szDllCmdLine && tokenize((TCHAR*)g_szDllCmdLine, &tokens)))
  {
  	Error (_T("Couldn't parse the command line."));
	goto cleanAndExit;
  }
    	
  if(!isOptionPresent(&tokens,(ARG_STR_DATE_AND_TIME)))
  {
	Info (_T("No user supplied date and time values."));
	goto cleanAndExit;
  }
	
  findTokenI(&tokens,ARG_STR_DATE_AND_TIME,&dwIndex1);
		
  // Check for correct number of arguments
  // Verify that there are exactly two arguments for the -dateAndTime option. 
  // For this verify that the first two arguments following the option exist 
  // and do not begin with -. Also verify that the argument following this is 
  // either another option or the end of the command line.

  TCHAR *str;
  DWORD checkIndex = ++dwIndex1; // First argument (date)

  // The two date and time arguments exist
  if((dwIndex1+2) > tokens.listLength())
  {
	Error (_T("Insufficient number of arguments for -dateAndTime option"));
	Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
	goto cleanAndExit;
  }	
		
  // These two arguments are not options
  while(checkIndex <= (dwIndex1 + 1))
  {
	str = tokens[checkIndex];
	if(str[0] == '-')
	{
		Error (_T("Insufficient number of arguments for")
			  _T("-dateAndTime option."));
		Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
		goto cleanAndExit;
	}
	checkIndex++;
  }
		
  // If another argument exists, it should be an other option
  if(checkIndex < tokens.listLength())
  {
	str = tokens[checkIndex];
	if(str[0] != '-')
	{
		Error (_T("Too many arguments for -dateAndTime option."));
		Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
		goto cleanAndExit;
	}
  }		
	
  // Parse the date
  Info(_T("The user supplied date is: %s"),tokens[dwIndex1]);
  tok = _tcstok(tokens[dwIndex1], szDateSep);	
  
  while(tok)
  {
    if(dwIndex2 >= 3)
	{
		Error (_T("Incorrect(too many) arguments in date value."));
		Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
		goto cleanAndExit;
	}			
	
	if(!strToULONGLONG(tok,&ullVal))
	{
		Error (_T("Error parsing the date."));
		goto cleanAndExit;
	}			
		
	tokArray[dwIndex2] = (WORD)(ullVal);			
	tok = _tcstok(NULL, szDateSep);
    dwIndex2++;
  }

  if(dwIndex2<3)
  {
	Error (_T("Incorrect(fewer) arguments in date value."));
	Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
	goto cleanAndExit;
  }


  // Parse the time
  Info (_T("The user supplied time is: %s"),tokens[++dwIndex1]);
  tok = _tcstok(tokens[dwIndex1], szTimeSep);
	
  while(tok)
  {
	if(dwIndex2 >= 6)
	{
		Error (_T("Incorrect(too many) arguments in time value."));
		Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
		goto cleanAndExit;
	}				
			
	if(!strToULONGLONG(tok,&ullVal))
	{
		Error (_T("Error parsing the time."));		
		goto cleanAndExit;
	}
			
	tokArray[dwIndex2] = (WORD)(ullVal);
	tok = _tcstok(NULL, szTimeSep);
	dwIndex2++;
  }

  if(dwIndex2<6)
  {
	Error (_T("Incorrect(fewer) arguments in time value."));
	Error (_T("Should be -dateAndTime mm/dd/yyyy hh:mm:ss"));
	goto cleanAndExit;
  }
	   
  pUserTime->wMonth = tokArray[0];
  pUserTime->wDay = tokArray[1];
  pUserTime->wYear = tokArray[2];
  pUserTime->wHour = tokArray[3];
  pUserTime->wMinute = tokArray[4];
  pUserTime->wSecond = tokArray[5];
  pUserTime->wMilliseconds = tokArray[6];

  returnVal = TRUE;
  
 cleanAndExit:
  return (returnVal);
}


/*******************************************************************************
 *                        Parse Thread Info
 ******************************************************************************/

// This function parses the command line arguments and returns the user 
// supplied number of threads and the run time for the Reentrancy of Get and 
// Set real-time test.
//
// Arguments: 
// 		Input: Pointer to number of threads and run time
//  	Output: The number of threads and run time obtained from the
//              command line
//
// Return value:
//    	TRUE if values are present and parsed correctly, FALSE otherwise


BOOL parseThreadInfo (DWORD *pNumThreads, DWORD *pRunTime)
{
  cTokenControl tokens;
  DWORD dwIndex;

  DWORD nThreads = 0,runTime = 0;
  
  BOOL returnVal = FALSE; 

  double doRealisticTime;
  _TCHAR *pszRealisticUnits;
  	
  Info (_T("")); // Blank line

  // Check for null pointer in the input
  if((!pNumThreads)||(!pRunTime))
  {
    Error (_T("Error: Invalid pointer supplied for the number of threads"));
	Error (_T("or the number of iterations."));
	goto cleanAndExit;
  }
  	
  if(!(g_szDllCmdLine && tokenize((TCHAR*)g_szDllCmdLine, &tokens)))
  {
  	Error (_T("Couldn't parse the command line."));
	Error (_T("Using the default value of %u threads"),DEFAULT_REAL_TIME_TEST_NUM_THREADS);
	Error (_T("and the default run time of %u seconds."),DEFAULT_REAL_TIME_TEST_RUN_TIME_SEC);
	goto cleanAndExit;
  }
  

  // Parse number of threads
  if(isOptionPresent(&tokens,(ARG_STR_NUM_OF_THREADS)))
  {
	findTokenI(&tokens,ARG_STR_NUM_OF_THREADS,&dwIndex);
    
  	if(!strToULONGLONG(tokens[++dwIndex],(ULONGLONG*)(&nThreads)))
	{
		Error (_T("Error parsing the number of threads."));		
		Error (_T("Using the default value of %u threads."),
			                               DEFAULT_REAL_TIME_TEST_NUM_THREADS);
	}
    else
    {
	  if(!(nThreads >= 1))
	  {
	    Error (_T("The user supplied number of threads are %u"),nThreads);
	    Error (_T("Acceptable values are greater than or equal to 1."));
		Error (_T("Using the default value of %u threads."),
			                               DEFAULT_REAL_TIME_TEST_NUM_THREADS);
	  }
	  else
	  {
	    Info (_T("The user supplied number of threads are %u"),nThreads);
		(*pNumThreads) = nThreads;
		returnVal = TRUE;
	  }
    }
  }
  else
  {
	Info (_T("User did not supply number of threads, using default value of %u."),
		                                    DEFAULT_REAL_TIME_TEST_NUM_THREADS);	
  }

  realisticTime((double)(DEFAULT_REAL_TIME_TEST_RUN_TIME_SEC),doRealisticTime,&pszRealisticUnits);
  
  // Parse run time
  if(isOptionPresent(&tokens,(ARG_STR_RUN_TIME)))
  {
	findTokenI(&tokens,ARG_STR_RUN_TIME,&dwIndex);
		
   	if(!strToSeconds(tokens[++dwIndex],(ULONGLONG*)(&runTime)))
	{
		Error (_T("Error parsing the run time."));
		Error (_T("Using the default value of %g %s."),doRealisticTime,pszRealisticUnits);		
	}	
	else
	{
	  if(!(runTime >= 1))
	  {
		Error (_T("The user supplied run time is %u seconds."),runTime);
	    Error (_T("Acceptable values are greater than or equal to 1 second."));
		Error (_T("Using the default value of %g %s."),doRealisticTime,pszRealisticUnits);
	  }
	  else
	  {	    
		realisticTime((double)runTime, doRealisticTime, &pszRealisticUnits);
		
	    Info (_T("The test is going to run for the user supplied run time of %g %s"),
                                            doRealisticTime,pszRealisticUnits);
		(*pRunTime) = runTime;
	    returnVal = TRUE;
	  }
	}
  }
  else
  {
	Info (_T("User did not supply the run time, test will run for the default time of %g %s."),
		                                                  doRealisticTime,pszRealisticUnits);
  }  
  
 cleanAndExit:
  return (returnVal);
}
