#!/usr/bin/env python3
# Copyright (c) 2007, 2011 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

import Tac, optparse, sys, signal, os # pylint: disable=deprecated-module

def background( *cmd ): # pylint: disable=inconsistent-return-statements
   pid = os.fork()
   if pid == 0:
      Tac.setpdeathsig( signal.SIGKILL )
      os.execlp( cmd[0], *cmd )
   else:
      return pid

usage = "usage: %prog [options] <command> [args ...]"

parser = optparse.OptionParser(usage=usage)
# Command being run can have options similar to the ones runfor uses.
parser.disable_interspersed_args()

parser.add_option( "-t", "--time",
                   help="How long to run for (in seconds) (defaults to 1)",
                   default=0, type=int )
parser.add_option( "-c", "--continuous", action="store_true",
                   help="Run continuously")
parser.add_option( "-i", "--iterations", help="How many times to run (default 1)",
                   default=None,type=int )
parser.add_option( "-q", "--quiet", help="Quiet all output, except from the test",
                   action='store_true' )
parser.add_option( "-v", "--verbose", help="Add some debugging output",
                   action="store_true")


( options, args ) = parser.parse_args()
iterations = options.iterations
if iterations is None:
   iterations = 1
quiet = options.quiet

if not args:
   parser.error( "You must specify a command to run" )

if options.continuous and options.time:
   parser.error("Both -c and -t options cannot be specified simultaneously")

if not options.continuous and not options.time:
   options.time=1

i = 0

def suffix( i ): # pylint: disable=redefined-outer-name
   if i == 1: return 'st' # pylint: disable=multiple-statements
   elif i == 2: return 'nd' # pylint: disable=multiple-statements
   elif i == 3: return 'rd' # pylint: disable=multiple-statements
   else: return 'th'

timedOut = False
def finish():
   global timedOut
   if child:
      if not quiet:
         if options.iterations:
            # pylint: disable-next=consider-using-f-string
            print( "Iteration %d" % ( i+1 ), end=' ' )
         print( "ran until timeout" )
   if options.iterations:
      timedOut = True
      if child:
         os.kill( child, signal.SIGINT )
      if i < iterations - 1:
         signal.alarm( options.time )
      return
   sys.exit( 0 )

def alarmHandler( signal, frame ): # pylint: disable=redefined-outer-name
   finish()

signal.alarm( options.time )
signal.signal( signal.SIGALRM, alarmHandler )

child = None
def go(args): # pylint: disable=redefined-outer-name
   global timedOut, i
   i=0
   while True:
      global child
      child = background( *args )
      timedOut = False
      while True:
         try:
            pid,rc = os.waitpid(child,0) # pylint: disable=unused-variable
            break
         except OSError as e:
            if e.errno != 4:
               raise
      child = None
      if not timedOut:
         if rc > 0:
            if not quiet:
               print( "On iteration", i+1, args[ 0 ], "exited with error code", rc )
            sys.exit( 1 )
         elif rc < 0:
            if not quiet:
               print( "On iteration", i+1, args[ 0 ],
                      "received a signal with error code", rc )
            sys.exit( 2 )
         elif options.iterations:
            if not quiet:
               # pylint: disable-next=consider-using-f-string
               print( "Iteration %d finished" % ( i+1 ) )
      if options.verbose and not quiet:
         sys.stdout.write( "." )
         # pylint: disable-next=multiple-statements
         if i and i % 50 == 49: print( sys.stdout.write( "\n" ) )
         sys.stdout.flush()
      i += 1
      if i == iterations: break # pylint: disable=multiple-statements

try:
   go(args)
except KeyboardInterrupt:
   if child:
      os.kill(child,signal.SIGINT)

finish()
