#  $Id$
#
#  Copyright (c) 2003-2010 Greg Landrum and Rational Discovery LLC
#
#   @@ All Rights Reserved @@
#  This file is part of the RDKit.
#  The contents are covered by the terms of the BSD license
#  which is included in the file license.txt, found at the root
#  of the RDKit source tree.
#
from __future__ import print_function
from rdkit import RDConfig
import os,sys,time
try:
  import subprocess
except ImportError:
  subprocess=None

TEST_FAILED=-1
TEST_PASSED=0

def RunTest(exeName,args,extras):
  if exeName=="python":
    exeName=RDConfig.pythonExe
  args = args.split(' ')  

  startDir = os.getcwd()
  if 'dir' in extras:
    os.chdir(extras['dir'])
  expectedReturn = extras.get('returns',0)
  if not subprocess:
    raise NotImplementedError('cannot run tests if the subprocess module is not available.')
  else:
    try:
      retVal = subprocess.call([exeName]+list(args))
    except OSError:
      print("Could not find executable: %s."%exeName, file=sys.stderr)
      return TEST_FAILED
  if 'dir' in extras:
    os.chdir(startDir)
  if retVal!=expectedReturn:
    return TEST_FAILED
  else:
    return TEST_PASSED

def RunScript(script,doLongTests,verbose):
  # support python 2.7 style -f argument for failfast
  if sys.argv[-1] == '-f':
    # setting environment allows this setting to recursively pass to all child
    # processes
    os.environ['PYTHON_TEST_FAILFAST'] = '1'
  if len(sys.argv)==3 and sys.argv[1]=='--testDir':
    os.chdir(sys.argv[2])
  # -------------------------------------------------------
  # this is pretty funny.  Whatever directory we started python in
  # will be in the search path, and if we've changed to another
  # directory, that'll be there too.  HOWEVER... the starting
  # directory will be searched first (at least in python2.2), so we
  # need to make sure that '.' is at the front of the search path
  if sys.path[0] != '.':
    sys.path = ['.']+sys.path
  script = script.split('.py')[0]
  mod = __import__(script)
  try:
    tests = mod.tests
  except AttributeError:
    return [],0
    
  longTests = []
  if doLongTests:
    try:
      longTests = mod.longTests
    except AttributeError:
      pass

  failed = []
  for i, entry in enumerate(tests):
    try:
      exeName,args,extras  = entry
    except ValueError:
      print('bad entry:',entry)
      sys.exit(-1)
    try:
      res = RunTest(exeName,args,extras)
    except:
      import traceback
      traceback.print_exc()
      res = TEST_FAILED
    if res != TEST_PASSED:
      failed.append((exeName,args,extras))
      # check failfast setting
      if os.environ.get('PYTHON_TEST_FAILFAST', '') == '1':
        # return immediately
        sys.stderr.write("Exiting from %s\n" % str([exeName]+list(args)))
        return failed, i + 1
  for i, (exeName,args,extras) in enumerate(longTests):
    res = RunTest(exeName,args,extras)
    if res != TEST_PASSED:
      failed.append((exeName,args,extras))
      if os.environ.get('PYTHON_TEST_FAILFAST', '') == '1':
        # return immediately
        sys.stderr.write("Exitng from %s\n" % str([exeName]+list(args)))
        return failed, len(tests) + i + 1
  
  nTests = len(tests)+len(longTests)
  del sys.modules[script]
  return failed,nTests
  
def ReportResults(script,failedTests,nTests,runTime,verbose,dest):
  if not nTests:
    dest.write('!-!-!-!-!-!-!-!-!-!-!\n')
    dest.write('\tScript: %s.  No tests run!\n'%(script))
  elif not len(failedTests):
    dest.write('-----------------\n')
    dest.write('\tScript: %s.  Passed %d tests in %.2f seconds\n'%(script,nTests,runTime))
  else:
    dest.write('!-!-!-!-!-!-!-!-!-!-!\n')
    dest.write('\tScript: %s.  Failed %d (of %d) tests in %.2f seconds\n'%(script,len(failedTests),nTests,runTime))
    if verbose:
      for exeName,args,extras in failedTests:
        dirName = extras.get('dir','.')
        dirName = os.path.abspath(dirName)
        dest.write('\t\t(%s): %s %s\n'%(dirName,exeName,args))

  
if __name__=='__main__':
  import getopt
  args,extras = getopt.getopt(sys.argv[1:],'lv')
  doLongTests = 0
  verbose = 1
  for arg,val in args:
    if arg == '-l':
      doLongTests=1
    elif arg == '-v':
      verbose=0
  
  pwd = os.getcwd()
  totNumFailed=0
  totNumRun=0
  failures = []
  timeAccum = 0.0
  for script in extras:
    try:
      open(script,'r')
    except IOError:
      sys.stderr.write('ERROR: Test script %s could not be opened.\n'%(script))
    else:
      dirName = os.path.dirname(script)
      scriptBase = os.path.basename(script)
      if dirName:
        os.chdir(dirName)
      try:
        t1 = time.time()
        failed,nTests=RunScript(scriptBase,doLongTests,verbose)
        t2 = time.time()
      except ImportError:
        import traceback
        traceback.print_exc()
        sys.stderr.write('ERROR: Could not import test script %s\n'%(script))
      else:
        runTime = t2-t1
        ReportResults(script,failed,nTests,runTime,verbose,sys.stderr)
        timeAccum += runTime
      if dirName:
        os.chdir(pwd)
      if len(extras)>1:
        totNumFailed += len(failed)
        totNumRun += nTests
        if len(failed):
          failures.append(script)

  if totNumRun>1:
    sys.stderr.write('\n\n-*-*-*-*-*-*- Test Results Summary -*-*-*-*-*-*-\n')
    sys.stderr.write('\t\tTotal run time: %.2f seconds\n'%(timeAccum))
    if totNumFailed:
      sys.stderr.write('!!!!!---  %d Failures in %d tests  ---!!!!!\n'%(totNumFailed,totNumRun))
      sys.stderr.write('\tModules with failures:\n')
      for failure in failures:
        sys.stderr.write('\t\t%s\n'%failure)
    else:
      sys.stderr.write('  All %d tests (in %d modules) passed\n'%(totNumRun,len(extras)))
  sys.exit(totNumFailed)
      
        
    
        
