from pysys.constants import *
from pysys.basetest import BaseTest
from pysys.utils.filegrep import filegrep

class XpybuildBaseTest(BaseTest):
	def xpybuild(self, args=None, buildfile='test.xpybuild.py', shouldFail=False, stdouterr='xpybuild'):
		"""
		Runs xpybuild against the specified buildfile or test.xpybuild.py from the 
		input dir. Produces output in the <testoutput>/build-output folder. 
		
		@param shouldFail: by default, the test will abort if the build fails. 
		Set this to True if the build is expected to fail in which case 
		the test will abort if it succeeds, and this method will return a 
		string identifying the target or overall failure message if not. 
		
		@returns the failure message string if shouldFail=True, otherwise nothing
		"""
		stdout,stderr=self.allocateUniqueStdOutErr(stdouterr)
		args = args or []
		try:
			try:
				environs = {}
				if PLATFORM != 'win32':
					pythonhome = os.path.dirname(sys.executable)
					if pythonhome.endswith('bin'): pythonhome = os.path.dirname(pythonhome)
					# python doesn't seem to launch on linux without PYTHONHOME being set
					environs['PYTHONHOME'] = pythonhome
					environs['PYTHONPATH'] = ''
				args = 	[
#					PROJECT.rootdir+'/../xpybuild.py', 
					PROJECT.XPYBUILD,
					'-f', os.path.join(self.input, buildfile), 
					'--logfile', os.path.join(self.output, stdout.replace('.out', '')+'.log'), 
					'-J', # might as well run in parallel to speed things up and help find race conditions
					'OUTPUT_DIR=%s'%self.output+'/build-output']+args
				if os.getenv('XPYBUILD_PPROFILE',None):
					self.log.info('Enabling Python per-line pprofile')
					args = [os.environ['XPYBUILD_PPROFILE'], '--out', 'profileoutput.py', 
						'--include', os.getenv('XPYBUILD_PPROFILE_REGEX', '.*xpybuild.*'), '--exclude', '.*', #'--verbose'
						]+args
					assert args[0].endswith('py'), args[0] # use the script path not the dir
					assert os.path.exists(args[0]), args[0]
					self.log.info('   see %s', os.path.normpath(self.output+'/profileoutput.py'))

				result = self.startProcess(sys.executable, args, 
					environs=environs, 
					stdout=stdout, stderr=stderr, displayName=('xpybuild %s'%' '.join(args)).strip(), 
					abortOnError=True, ignoreExitStatus=shouldFail)
				if shouldFail and result != 0: raise Exception('Build failed as expected')
			finally:
				self.logFileContents(stdout, tail=True) or self.logFileContents(stderr, tail=True)
		
		except AssertionError as e:
			self.log.exception('Assertion error: ')
			raise
		except Exception as e:
			m = None
			try:
				# these give the best messages
				m = filegrep(stdout, '(Target FAILED: .*)', returnMatch=True)
				if not m: m = filegrep(stdout, '(XPYBUILD FAILED: .*)', returnMatch=True)
				if m: m = m.group(1)
			except Exception, e2:
				if shouldFail: raise e2 # this is fatal if we need the error message
				self.log.exception('Error handling block failed: ')
			if not m: self.log.warning('Caught exception running build: %s', e)
			m = m or '<unknown failure>'

			if shouldFail: 
				self.log.info('Build failed as expected; message is: %s', m)
				return m
			else:
				self.abort(BLOCKED, 'Build failed unexpectedly: %s'%m)
		else:
			if shouldFail:
				self.abort(FAILED, 'build was expected to fail but succeeded')
		
		return None

@@ -1532,16 +1532,322 @@
 rgs%0A%09%09%09%09
+pythoncoverage = getattr(self, 'PYTHON_COVERAGE', '')=='true'%0A%09%09%09%09if pythoncoverage:%0A%09%09%09%09%09self.log.info('Enabling Python code coverage')%0A%09%09%09%09%09args = %5B'-m', 'coverage', 'run', '--source=%25s'%25PROJECT.XPYBUILD_ROOT%5D+args%0A%09%09%09%09%09self.log.info('   see %25s', os.path.normpath(self.output+'/profileoutput.py'))%0A%09%09%09%09el
 if os.ge
@@ -2634,16 +2634,348 @@
 ected')%0A
+%09%09%09%09if pythoncoverage:%0A%09%09%09%09%09self.startProcess(sys.executable, %5B'-m', 'coverage', 'html'%5D, %0A%09%09%09%09%09environs=environs, %0A%09%09%09%09%09stdout='python-coverage.out', stderr='python-coverage.err', displayName='python coverage', %0A%09%09%09%09%09abortOnError=True, ignoreExitStatus=False)%0A%09%09%09%09%09self.log.info('See %25s'%25os.path.normpath(self.output+'/htmlcov'))%0A%0A
 %09%09%09final

add option for running python code coverage as part of a testcase (if you have the coverage package installed)