#! /usr/bin/python2
#
# Copyright 2009-2016 The VOTCA Development Team (http://www.votca.org)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

VERSION='1.4.1 '
import sys
import os
import getpass
import socket
import commands as cmds
import numpy as np
import xml.dom.minidom as xmld
import time
import argparse
import re
import math
import datetime

PROGTITLE = 'THE VOTCA::XTP TESTSUITE'
PROGDESCR = 'Performs tests en suite +'
VOTCAHEADER = '''\
==================================================
========   VOTCA (http://www.votca.org)   ========
==================================================

{progtitle}

please submit bugs to bugs@votca.org 
xtp_testsuite, version {version}

'''.format(version=VERSION, progtitle=PROGTITLE)
try:
	VOTCASHARE = os.environ['VOTCASHARE']    
except KeyError:
	print "ERROR: VOTCASHARE not set. Abort."
	print "(You need to source the VOTCARC located in '/your/votca/path/bin/')"
	sys.exit(1)

# =============================================================================
# PROGRAM OPTIONS
# =============================================================================

class XtpHelpFormatter(argparse.HelpFormatter):
	def _format_usage(self, usage, action, group, prefix):
		return VOTCAHEADER

progargs = argparse.ArgumentParser(prog='xtp_testsuite',
    formatter_class=lambda prog: XtpHelpFormatter(prog,max_help_position=70),
	description=PROGDESCR)
# Tests to execute, available tests, XML testsuite file
progargs.add_argument('-e', '--execute',
    dest='execute',   
    action='store', 
    required=False, 
    nargs='*',
    metavar='',
    default=[".*"],
    help = 'Tests to perform, accepts regex (def=".*")')
progargs.add_argument('-l', '--listonly',
	dest='listonly',
	action='store_const',
	const=True,
	default=False,
	help='List all tests available, then quit.')
progargs.add_argument('-x', '--xml',
    dest='testsuite',
    action='store',
    required=False,
    type=str,
    metavar='',
    default= '{share}/xtp/xml/testsuite.xml'.format(share=VOTCASHARE),
    help='Test-suite file (def="$VOTCASHARE/xtp/xml/testsuite.xml")')
# Directories: test, source, reference, targets
progargs.add_argument('-s', '--source',
	dest='sourcedirectory',
	action='store',
	required=False,
	type=str,
	metavar='',
	default='',
	help='Test source input directory (def="source")')
progargs.add_argument('-td', '--testdirectory',
    dest='testdirectory',
    action='store',
    required=False,
    type=str,
    metavar='',
    default='suite',
    help='Test run directory (def="suite")')
progargs.add_argument('-t', '--target',
	dest='target_dir',
	action='store',
	required=False,
	type=str,
	metavar='',
	default='targets',
	help='Directory where to store targets (def="targets")')
progargs.add_argument('-r', '--reference',
	dest='reference_dir',
	action='store',
	required=False,
	type=str,
	metavar='',
	default='reference',
	help='Folder with reference data to compare to (def="reference")')
progargs.add_argument('-g', '--generate',
	dest='generate',
	action='store_const',
	const=True,
	default=False,
	help='Generate reference from targets (def=False)')
progargs.add_argument('-cmp', '--compareonly',
	dest='compareonly',
	action='store_const',
	const=True,
	default=False,
	help='Only compare existing targets to reference (def=False)')
# Verbose, show output, clean-up
progargs.add_argument('-v', '--verbose',
	dest='verbose',
	action='store_const',
	const=True,
	default=False,
	help='The wordy version (def=False)')
progargs.add_argument('-sh', '--showoutput',
	dest='showoutput',
	action='store_const',
	const=True,
	default=False,
	help='Display VOTCA::XTP exec. output (def=False)')
progargs.add_argument('-c', '--clean',
	dest='clean',
	action='store_const',
	const=True,
	default=False,
	help='To clean or not to clean test dir. (def=False)')
progargs.add_argument('-m', '--mailto',
	dest='mailto',
	action='store',
	required=False,
	type=str,
        metavar="",
	default="",
	help='Mail the result. (def=False)')

# OPTIONS & GLOBAL EXECUTION VARIABLES
OPTIONS			 = progargs.parse_args()
TESTSUITEXML		 = OPTIONS.testsuite
TESTDIR			 = OPTIONS.testdirectory
TESTSOURCE		 = OPTIONS.sourcedirectory
VERBOSE			 = OPTIONS.verbose
SILENTEXE		 = not OPTIONS.showoutput
CLEANTESTFOLDER		 = OPTIONS.clean
MAILTO                   = OPTIONS.mailto
USER			 = getpass.getuser()
HOST			 = socket.gethostname()
BASEDIR			 = os.getcwd()

print OPTIONS.mailto
# =============================================================================
# CONVENIENCE FUNCTIONS & CLASSES
# =============================================================================

def okquit(what=''):
	if what != '': print what
	sys.exit(0)
def xxquit(what=''):
	if what != '':
		cprint.Error("ERROR: {what}".format(what=what))
	sys.exit(1)
def sysexe(cmd, silent=False, devfile='/dev/null'):
	if VERBOSE: print "{0}@{1}$ {2}".format(USER, HOST, cmd)
	if silent: cmd += ' >> {0} 2>> {0}'.format(devfile)
	cdx = os.system(cmd)
	#SYSCMDS.write('{cmd} = {cdx}\n'.format(cmd=cmd, cdx=cdx))
	return cdx

def nnprint(msg):
	print "{0:100s} {1}".format(msg, cprint.NN)
def okprint(msg):
	print "{0:100s} {1}".format(msg[0:97], cprint.OK)
def xxprint(msg):
	print "{0:100s} {1}".format(msg, cprint.XX)

def isfloat(item):
	try: float(item); return True
	except: return False
def xmlsplit(string, regex='<|>|/| '):
	return re.split(regex, string)
def sendmail(to, subject):
	print "Send mail to", to, "re:", subject
	sysexe("/usr/bin/echo . | /usr/bin/mail -s '{subject}' -a 'testsuite.txt' {to}".format(subject=subject, to=to))
	return

class CPrint(object):
	def __init__(self):
		self.HEADER = '\033[95m'
		self.OKBLUE = '\033[34m'
		self.LBLUE = '\033[1;34m'
		self.YELLOW = '\033[1;33m'
		self.GREEN = '\033[92m'
		self.WARNING = '\033[93m'
		self.ERROR = '\033[95m'
		self.MAGENTA = '\033[95m'
		self.RED = '\033[91m'
		self.ENDC = '\033[0;1m'
		self.OK = self.GREEN + 'OK' + self.ENDC
		self.XX = self.RED + 'XX' + self.ENDC
		self.NN = self.WARNING + ':/' + self.ENDC
	def Head(self, msg):
		print self.YELLOW+msg+self.ENDC
	def Error(self, msg):
		print self.ERROR+msg+self.ENDC
	def Green(self, msg):
		return self.GREEN+msg+self.ENDC
	def Red(self, msg):
		return self.ERROR+msg+self.ENDC
	def Headerlike(self, msg):
		return self.LBLUE+msg+self.ENDC
	def Disable(self):
		self.HEADER = ''
		self.OKBLUE = ''
		self.LBLUE = ''
		self.YELLOW = ''
		self.GREEN = ''
		self.WARNING = ''
		self.ERROR = ''
		self.MAGENTA = ''
		self.RED = ''
		self.ENDC = ''
		self.OK = 'OK'
		self.XX = 'XX'
		self.NN = ':/'
		return
		
def TestDependency_t1_smaller_t2(t1, t2):
	print "Compare", t1.name, t2.name
	if t1.name in t2.requires:
		if t2.name in t1.requires:
			xxquit("Circular dependencies: "+ t1.name+ " <> "+ t2.name+ " => Abort.")
		return -1
	if t2.name in t1.requires:
		return +1
	else:
		return 0

# =============================================================================
# TEST-SUITE OBJECTS: Suite & Test & Comparator
# =============================================================================

class Suite(object):
	def __init__(self, xmlfile, options):
		self.options = options
		self.xmlfile = xmlfile
		self.basedir = BASEDIR
		# READ IN TESTSUITE CONTROL FILE
		self.tree = xmld.parse(self.xmlfile)		
		self.testNodes = [ node for node in self.tree.getElementsByTagName('test') ]
		self.fileNodes = [ node for node in self.tree.getElementsByTagName('file') ]
		self.exeNodes = [ node for node in self.tree.getElementsByTagName('executable') ]	
		self.tests = [ Test(testNode) for testNode in self.testNodes ]
		self.files = [ GenerateNodeDict(fileNode) for fileNode in self.fileNodes ]
		self.exes = [ GenerateNodeDict(exeNode) for exeNode in self.exeNodes ]
		# DICTIONARY TO REGISTER TESTS WITH A DEDICATED INPUT FOLDER
		self.testinputfolder = {}		
		# GENERATE PLACE HOLDERS FOR COMMAND SUBSTITUTION
		self.placeholders = {}
		for item in self.files:
			key = item['key'].As(str)
			value = item['value'].As(str)
			assert key not in self.placeholders.keys()
			self.placeholders[key] = value
		for item in self.exes:
			key = item['key'].As(str)
			value = item['value'].As(str)
			assert key not in self.placeholders.keys()
			self.placeholders[key] = value
		if 'name' in self.placeholders.keys():
			xxquit("'name' is not accepted as a placeholder for a file or an executable. Abort.")
		# ASSEMBLE EXECUTION COMMANDS FOR INDIVIDUAL TESTS
		for test in self.tests:
			test.AssembleCommands(self.placeholders)	
		# MAKE TESTS ACCESSIBLE BY NAME KEY
		self.testdict = {}
		for test in self.tests:
			if test.name in self.testdict.keys():
				xxquit("Test of name '{name}' already exists. Abort.".format(name=test.name))
			self.testdict[test.name] = test
		return
	def ListTests(self):
		cprint.Head('XML="{xml}" offers the following tests:'.format(xml=self.xmlfile))
		tests_abc = sorted(self.tests, key=lambda t: t.name)
		for test in tests_abc:
			requstr = ''
			#dependencies = self.GetDependencies(test)
			dependencies = test.requires
			for requ in dependencies:
				requstr += requ+' '
			print "  o {name:18s} exe : {exe:20s} dep : {requ}".format(name=test.name, exe=test.execute_cmd.split()[0], requ=requstr)
			#print "  o {name:18s}  REQUIRES={requ}".format(name=test.name, exe=test.execute_cmd, requ=requstr)
		return
	def GetDependencies(self, test):
		required = []
		for req in test.requires:
			required.append(req)
			required_next = self.GetDependencies(self.testdict[req])
			for req_next in required_next:
				if req_next not in required:
					required.append(req_next)
		return required
	def SelectTests(self, matchlist):
		#if executelist == []:
		#	return
		required = []
		# Match regular expression with test names
		executelist = []
		for regex in matchlist:
			for test in self.tests:
				if re.match(regex,test.name):
					executelist.append(test.name)
		# Find all dependencies
		for testkey in executelist:
			try:
				requ_this = self.GetDependencies(self.testdict[testkey])
			except KeyError:
				xxquit("No such test: '{0}'. Abort.".format(testkey))
			for req in requ_this:
				if not req in required: required.append(req)
		rm_tests = []
		for test in self.tests:
			if not test.name in executelist and not test.name in required:
				rm_tests.append(test)	
		for rm in rm_tests:
			self.tests.remove(rm)
		return
	def Run(self):
		cprint.Head('This suite will be running the following tests:')
		for test in self.tests:
			print "  o {name:18s}{exe:20s}".format(name=test.name, exe=test.execute_cmd)		
		# Execute all tests and collect targets
		tested = []
		all_targets = []
		expected_targets = []
		for test in self.tests:
			cprint.Head('RUN '+test.Identify())
			abort = False
			success = False
			lacks = ''
			expected_targets = expected_targets + test.GetTargetNames()
			dependencies = self.GetDependencies(test)
			for name in dependencies:
				if not name in tested:
					abort = True
					lacks += name+' '
			if abort:
				nnprint("  o Requirements not met, lacks {lacks} => abort.".format(lacks=lacks))
			else:
				success, targets = test.Run(self.GetTestInputFolder(test.name))
				for target in targets:
					if target in all_targets:
						nnprint("  o Target already exists: {target}.".format(target=target))
					all_targets.append(target)
				tested.append(test.name)
			if abort:
				xxprint("  o {ident} ABORTED".format(ident=test.name))
			elif not success:
				xxprint("  o {ident} FAILED".format(ident=test.name))
			else:
				okprint("  o {ident} SUCCESS".format(ident=test.name))
			time.sleep(1)		
		# Compile targets into one folder
		target_folder = '{base}/{target}'.format(base=BASEDIR, target=self.options.target_dir)
		if os.path.exists(target_folder):
			sysexe('rm -r {target}'.format(target=target_folder))
		os.mkdir(target_folder)
		for target in all_targets:
			sysexe('mv {target} {folder}/{target}'.format(target=target, folder=target_folder))
		# Generate reference if options demand so
		reference_folder = '{base}/{ref}'.format(base=BASEDIR, ref=self.options.reference_dir)
		if self.options.generate:
			if os.path.exists(reference_folder):
				raw = raw_input("Overwrite reference folder (yes/no)? ")
				if raw != "yes":
					okquit("Reference folder remains as it is. Stop here.")
				else:
					sysexe('rm -r {ref}'.format(ref=reference_folder))
			cdx = sysexe('mv {target} {ref}'.format(target=target_folder, ref=reference_folder))
			if not cdx:
				okquit("Successfully generated reference data, now quit.")
			xxquit("Failed generating reference data, abort.")
		# Ascertain that all expected targets are present, then carry out comparison
		cprint.Head("Verify that all targets are WHERE we expect them to be")
		none_missing = True
		for target in expected_targets:
			if not target in os.listdir(target_folder):
				xxprint("  o Missing target : '{target}'".format(target=target))
				none_missing = False
			else:
				#okprint("  o Confirm target : '{target}'".format(target=target))
				pass
		if none_missing:
			okprint("  o All {0} targets where they should be.".format(len(expected_targets)))
		Comparator(target_folder, reference_folder)
		return
	def RegisterTestInputFolder(self, testname, directory):
		self.testinputfolder[testname] = directory
		return
	def GetTestInputFolder(self, testname):
		try:
			return self.testinputfolder[testname]
		except KeyError:
			return None


class Comparator(object):
	def __init__(self, target_dir, reference_dir):
		self.tolerance = 1e-5
		# Completeness of target_dir already verified
		cprint.Head("Verify that all targets are WHAT we expect them to be:")
		if not os.path.exists(target_dir):
			xxquit("No such target directory: '{0}'".format(target_dir))
		if not os.path.exists(reference_dir):
			xxquit("No such reference directory: '{0}'".format(reference_dir))
		target_items = os.listdir(target_dir)
		target_items.sort()
		reference_items = os.listdir(reference_dir)
		none_different = True
		for item in target_items:
			abs_ref_item = '{0}/{1}'.format(target_dir, item)
			abs_tar_item = '{0}/{1}'.format(reference_dir, item)
			short_ref = abs_ref_item.split('/')[-1]
			short_tar = abs_tar_item.split('/')[-1]
			if not os.path.isfile(abs_tar_item):
				xxprint("Target exists, but reference is missing '{0}'".format(item))				
				continue
			# Binary + Numeric match
			binary_match, binary_what = self.DoFilesMatch_Binary(abs_ref_item, abs_tar_item)
			if not binary_match:
				numeric_match, numeric_what = self.DoFilesMatch_Numeric(abs_ref_item, abs_tar_item)
			else:
				numeric_match, numeric_what = True, 'numeric match'
			if not binary_match or not numeric_match:
				xxprint("  o {0:40s} -> {1:15s} & {2:30s}".format(item[0:40], binary_what, numeric_what))
				none_different = False
			else:
				okprint("  o {0:40s} -> {1:15s} & {2:30s}".format(item[0:40], binary_what, numeric_what))	
		if none_different:
			okprint("  o All {0} targets what they should be.".format(len(target_items)))
		return
	def DoFilesMatch_Binary(self, file1, file2):
		out = cmds.getoutput('diff {0} {1}'.format(file1, file2))
		if out != '':
			return False, 'binary mismatch'
		else:
			return True, 'binary match'
	def DoFilesMatch_Numeric(self, file1, file2):
		def reldiff(v1, v2, eps):
			v1 = float(v1)
			v2 = float(v2)
			if math.isnan(v1): v1 = 0
			if math.isnan(v2): v2 = 0
			if v1 == v2: return 0
			if abs(v1-v2) < eps: return abs(v1-v2)
			if abs(v1) > abs(v2): return abs(v1-v2)/abs(v1)
			return abs(v1-v2)/abs(v2)
		data1 = open(file1).readlines()
		data2 = open(file2).readlines()
		num_mismatch_count = 0
		# Same number of lines
		if len(data1) != len(data2):
			return False, 'number of data lines does not match'
		for d1,d2 in zip(data1,data2):
			s1 = xmlsplit(d1)
			s2 = xmlsplit(d2)
			if len(s1) != len(s2):
				return False, 'number of columns does not match'
			for a,b in zip(s1,s2):
				isfloat_a = isfloat(a)
				isfloat_b = isfloat(b)
				if isfloat_a != isfloat_b:
					num_mismatch_count += 1
					continue
				if isfloat_a == isfloat_b == True:
					rd = reldiff(a, b, self.tolerance)
					if rd > self.tolerance:
						num_mismatch_count += 1
		if num_mismatch_count > 0:
			return False, 'numeric mismatch on {0} counts'.format(num_mismatch_count)
		return True, 'numeric match'


class Test(object):
	def __init__(self, node):
		self.node = node
		self.dict = GenerateNodeDict(node)
		self.name = self['name'].As(str)
		self.description = self['description'].As(str)
		self.execute = self['execute'].As(str)
		self.requires = self['requires'].AsArray(str)
		self.verify = self['verify'].As(str)
		self.output = self['output'].AsArray(str)
		self.logfile = '{name}.log'.format(name=self.name)
		self.execute_cmd = None
		self.verify_cmd = None
		return
	def __getitem__(self, key):
		return self.dict[key]
	def keys(self):
		return self.dict.keys()
	def AssembleCommands(self, placeholders):
		placeholders['name'] = self.name
		self.execute_cmd = self.execute.format(**placeholders)
		self.verify_cmd = self.verify.format(**placeholders)
		placeholders.pop('name')
		return
	def Run(self, inputfolder=None):
		# Copy additional resource from input folder
		if inputfolder != None:
			sysexe('cp {indir}/* .'.format(indir=inputfolder))
		# Execute
		cdx = sysexe(self.execute_cmd, silent=SILENTEXE, devfile=self.logfile)
		if cdx:
			xxprint('  $ {cmd} FAILED'.format(cmd=self.execute_cmd))
		else:
			okprint('  $ {cmd}'.format(cmd=self.execute_cmd))
		# Verify
		if self.verify_cmd.strip() != '':
			cdx = sysexe(self.verify_cmd, silent=SILENTEXE, devfile=self.logfile)
			if cdx:
				xxprint('  $ {cmd} FAILED'.format(cmd=self.verify_cmd))
			else:
				okprint('  $ {cmd}'.format(cmd=self.verify_cmd))
		# Store output
		output_complete = True
		missing = ''
		for item in self.output:
			if not item in os.listdir('./'): 
				output_complete = False
				missing += item+' '
		if not output_complete:
			xxprint("  o Output incomplete, missing: {lacks}".format(lacks=missing))
			return False, []
		else:
			targets = []
			for item in self.output:
				okprint("  o Found target '{target}'".format(target=item))
				target = self.DressTargetName(item)
				sysexe('cp {out} {target}'.format(out=item, target=target))
				targets.append(target)
			return True, targets
	def DressTargetName(self, target):
		return 'test_{name}_{out}'.format(out=target, name=self.name)
	def GetTargetNames(self):
		return [ self.DressTargetName(item) for item in self.output ]
	def Identify(self):
		return "'{name}' : {descr}".format(name=self.name, descr=self.description)
	def IdentifyCmd(self):
		return "{cmd}".format(name=self.name, cmd=self.execute_cmd)
		
# =============================================================================
# XML.DOM.MINIDOM WRAPPERS
# =============================================================================

class XmlNodeValue(object):
	def __init__(self, path, value, attributes):
		self.path = path
		self.value = value
		self.attributes = attributes
	def As(self, typ):
		if typ == np.array:
			sps = self.value.split()
			return typ([ float(sp) for sp in sps ])
		else:
			return typ(self.value)
	def AsArray(self, typ, sep=' ', rep='\t\n'):
		for r in rep:
			self.value = self.value.replace(r, sep)
		sp = self.value.split(sep)
		return [ typ(s) for s in sp if str(s) != '' ]

def CleanXmlTree(node, level='', verbose=False):
	rm = []
	if verbose: print level + node.nodeName
	for child in node.childNodes:		
		CleanXmlTree(child, level=level+'    ', verbose=verbose)		
		if 'text' in child.nodeName:
			if verbose: print level + "Remove", child.nodeName
			rm.append(child)
		else:
			if verbose: print level + "Keep", node.firstChild.nodeName
	for child in rm:
		node.removeChild(child)
	return node

def GenerateNodeDict(node, path='', abbreviate=False):
	path_value = {}
	if node.firstChild == None and '#text' in path: return {}
	elif node.firstChild == None: return { path : XmlNodeValue(path, '', node.attributes) }
	if node.firstChild.nodeValue.split() != []:
		value = XmlNodeValue(path, node.firstChild.nodeValue, node.attributes) 
		path_value[path] = value
		if abbreviate: path_value['.%s' % node.nodeName] = value	
	for child in node.childNodes:
		if path == '': childPath = child.nodeName
		else: childPath = '%s.%s' % (path, child.nodeName)
		add_dict = GenerateNodeDict(child, childPath, abbreviate)
		for key in add_dict.keys():
			path_value[key] = add_dict[key]	
	return path_value

# =============================================================================
# TESTSUITE EXECUTION
# =============================================================================

# Start-up suite & printer
cprint = CPrint()
suite = Suite(TESTSUITEXML, OPTIONS)
if OPTIONS.listonly:
	print '{0}'.format(VOTCAHEADER)
	suite.ListTests()
	okquit()
if OPTIONS.compareonly:
	cprint.Head('{0}: Compare only, then quit.'.format(PROGTITLE))
	abs_tar_folder = '{0}/{1}'.format(BASEDIR, OPTIONS.target_dir)
	abs_ref_folder = '{0}/{1}'.format(BASEDIR, OPTIONS.reference_dir)
	print "  o Target directory    =", abs_tar_folder
	print "  o Reference directory =", abs_ref_folder
	Comparator(abs_tar_folder, abs_ref_folder)
	okquit()
if OPTIONS.sourcedirectory == '':
	progargs.print_help()
	okquit("\nQuit here, because: No source directory set (option -s/--source)")
if OPTIONS.sourcedirectory != '':
	abs_sourcedir = os.path.join(BASEDIR, TESTSOURCE)
	rel_sourcedir = TESTSOURCE
	if not os.path.exists(abs_sourcedir):
		xxquit("Source directory '{src}' does not exist".format(src=abs_sourcedir))
if OPTIONS.mailto != "":
	sys.stdout = open("testsuite.txt","w")
	cprint.Disable()
if OPTIONS.sourcedirectory != '':
	abs_sourcedir = os.path.join(BASEDIR, TESTSOURCE)
	rel_sourcedir = TESTSOURCE
	if not os.path.exists(abs_sourcedir):
		xxquit("Source directory '{src}' does not exist".format(src=abs_sourcedir))
	
suite.SelectTests(OPTIONS.execute)

# Environment tests
cprint.Head("{0}@{1}: Testing your VOTCA environment".format(USER, HOST))
try:
	share = os.environ['VOTCASHARE']
	xmlfiles = os.listdir('{0}/xtp/xml'.format(share))
	xmlshare_xtp = '{0}/xtp/xml'.format(share)
	xmlshare_kmc = '{0}/kmc/xml'.format(share)
	okprint("  o Using VOTCASHARE = '{0}'".format(share))
	if xmlfiles == []:
		xxprint("  o $VOTCASHARE/xtp/xml is empty. Abort.")
	okprint("  o Loading XML options files from $VOTCASHARE/xtp/xml")
except KeyError:
	xxquit("VOTCASHARE not set: You need to source the VOTCARC located in /your/votca/path/bin/")
cprint.Head("{0}@{1}: Testing your VOTCA executables".format(USER, HOST))
xtp_exe = [ exe['value'].As(str) for exe in suite.exes ]
for exe in xtp_exe:
	cdx = sysexe('{exe} --help > /dev/null 2> /dev/null'.format(exe=exe))
	if cdx:
		xxprint("  o {exe} FAILED".format(exe=exe))
		continue
	cdx = sysexe('{exe} --help | grep {exe} > /dev/null 2> /dev/null'.format(exe=exe))
	if cdx:
		xxprint("  o {exe} VERSION ERROR".format(exe=exe))
		continue
	out = cmds.getoutput('%s --help | grep %s' % (exe,exe))
	if out != '': okprint('  o '+out)

# Set-up test directory
if TESTDIR in os.listdir('./'):
	sysexe('rm -r {testdir}'.format(testdir=TESTDIR))
os.mkdir(TESTDIR)
os.chdir(TESTDIR)

# Copy test source (input) files
sysexe('cp -r {src}/* .'.format(src=abs_sourcedir))
sysexe('cp {xmlshare}/*.xml .'.format(xmlshare=xmlshare_xtp))
sysexe('cp {xmlshare}/*.xml .'.format(xmlshare=xmlshare_kmc))

# Register input_*** folders
for test in suite.tests:
	rel_input_folder = 'input_{test}'.format(test=test.name)
	abs_input_folder = '{base}/{testdir}/{rel}'.format(base=BASEDIR, testdir=TESTDIR, rel=rel_input_folder)
	if rel_input_folder in os.listdir('./'):
		suite.RegisterTestInputFolder(test.name, abs_input_folder)

# Run tests
suite.Run()

os.chdir('../')
if TESTDIR in os.listdir('./') and CLEANTESTFOLDER:
	sysexe('rm -r {testdir}'.format(testdir=TESTDIR))

if OPTIONS.mailto != "":
        now = datetime.datetime.now()
	sys.stdout = sys.__stdout__
	sendmail( OPTIONS.mailto, now.strftime("%Y-%m-%d %H:%M") )	
okquit()



