view 2.00/test-svn.py @ 28:3d535503161f

hasattr -> try
author Oleg Oshmyan <chortos@inbox.lv>
date Wed, 24 Nov 2010 01:42:39 +0000
parents 5bbb68833868
children f90bd2d1a12b
line wrap: on
line source

#! /usr/bin/env python
# Copyright (c) 2009-2010 Chortos-2 <chortos@inbox.lv>

from __future__ import division, with_statement
import optparse, sys, compat

def import_error(e):
	say('Error: your installation of test.py is incomplete;', str(e).lower() + '.', file=sys.stderr)
	sys.exit(3)

from compat import *

# $Rev$
version = '2.00.0 (SVN r$$REV$$)'
parser = optparse.OptionParser(version='test.py '+version, epilog='Python 2.5 or newer is required.')
parser.add_option('-1', dest='legacy', action='store_true', default=False, help='handle configuration files in a way more compatible with test.py 1.x')
parser.add_option('-u', '--update', dest='update', action='store_true', default=False, help='update the installed test.py to the latest publicly available version')
parser.add_option('-p', '--problem', dest='problems', metavar='PROBLEM', action='append', help='test only the PROBLEM (this option can be specified more than once with different problem names, all of which will be tested)')
parser.add_option('-m', '--copy-io', dest='copyonly', action='store_true', default=False, help='create a copy of the input/output files of the last test case for manual testing and exit')
parser.add_option('-x', '--auto-exit', dest='pause', action='store_false', default=True, help='do not wait for a key to be pressed after finishing testing')
parser.add_option('-s', '--save-io', dest='erase', action='store_false', default=True, help='do not delete the copies of input/output files after the last test case; create copies of input files and store output in files even if the solution uses standard I/O; delete the stored input/output files if the solution uses standard I/O and the -c/--cleanup option is specified')
parser.add_option('-t', '--detect-time', dest='autotime', action='store_true', default=False, help='spend a second detecting the most precise time measurement function')
parser.add_option('--no-time-limits', dest='no_maxtime', action='store_true', default=False, help='disable all time limits')

options, args = parser.parse_args()
parser.destroy()
del parser

if options.update:
	try:
		urllib, urlread = compat.import_urllib()
	except ImportError:
		sys.exit('Error: the urllib Python module is missing. Without it, an automatic update is impossible.')
	
	latesttext = urlread('http://chortos.selfip.net/~astiob/test.py/version.txt')
	latest = latesttext.split('.')
	installed = version.split('.')
	update = None
	
	if latest[0] > installed[0]:
		update = 'major'
	elif latest[0] == installed[0]:
		if latest[1] > installed[1]:
			update = 'feature'
		elif latest[1] == installed[1]:
			if latest[2] > installed[2]:
				update = 'bug-fixing'
			elif latest[2] == installed[2]:
				say('You are using the latest publicly available version of test.py.')
				sys.exit()
	
	if not update:
		say('Your copy of test.py is newer than the publicly available version.')
		sys.exit()
	
	say('A ' + update + ' update to test.py is available. Downloading...')
	sys.stdout.flush()
	urllib.urlretrieve('http://chortos.selfip.net/~astiob/test.py/test.py', sys.argv[0])
	say('Downloaded and installed. Now you are using test.py ' + latesttext + '.')
	sys.exit()

import config, itertools, os, subprocess, sys, time

if options.autotime:
	# This is really a dirty hack that assumes that sleep() does not spend
	# the CPU time of the current process and that if clock() measures
	# wall-clock time, then it is more precise than time() is. Both these
	# assumptions are true on all platforms I have tested this on so far,
	# but I am not aware of any guarantee that they will both be true
	# on every other platform.
	c = time.clock()
	time.sleep(1)
	c = time.clock() - c
	if int(c + .5) == 1:
		clock = time.clock
	else:
		clock = time.time
elif sys.platform == 'win32':
	clock = time.clock
else:
	clock = time.time

try:
	from testcases import pause
except ImportError:
	pause = None

try:
	globalconf = config.load_global()

	# Do this check here so that if we have to warn them, we do it as early as possible
	if options.pause and not pause and not hasattr(globalconf, 'pause'):
		if os.name == 'posix':
			globalconf.pause = 'read -s -n 1'
			say('Warning: configuration variable pause is not defined; it was devised automatically but the choice might be incorrect, so test.py might exit immediately after the testing is completed.', file=sys.stderr)
			sys.stderr.flush()
		elif os.name == 'nt':
			globalconf.pause = 'pause'
		else:
			sys.exit('Error: configuration variable pause is not defined and cannot be devised automatically.')

	try:
		from problem import *
	except ImportError:
		import_error(sys.exc_info()[1])

	# Support single-problem configurations
	if globalconf.tasknames is None:
		shouldprintnames = False
		globalconf.multiproblem = False
		globalconf.tasknames = os.path.curdir,
	else:
		globalconf.multiproblem = True
		# TODO: erase the commented part? if it has a tasknames variable, it is by definition multi-problem
		shouldprintnames = True
		# try:
		# 	shouldprintnames = len(globalconf.tasknames) > 1
		# except Exception:
		# 	# Try to retrieve the first two problem names and cache them on success
		# 	globalconf.tasknames = iter(globalconf.tasknames)
		# 	try:
		# 		try:
		# 			first = next(globalconf.tasknames)
		# 		except NameError:
		# 			# Python 2.5 lacks the next() built-in
		# 			first = globalconf.tasknames.next()
		# 	except StopIteration:
		# 		globalconf.tasknames = ()
		# 		shouldprintnames = False
		# 	else:
		# 		try:
		# 			try:
		# 				second = next(globalconf.tasknames)
		# 			except NameError:
		# 				second = globalconf.tasknames.next()
		# 		except StopIteration:
		# 			globalconf.tasknames = first,
		# 			shouldprintnames = False
		# 		else:
		# 			globalconf.tasknames = itertools.chain((first, second), globalconf.tasknames)
		# 			shouldprintnames = True

	ntasks = 0
	nfulltasks = 0
	maxscore = 0
	realscore = 0

	for taskname in (globalconf.tasknames if not options.problems else options.problems):
		problem = Problem(taskname)
		
		if ntasks and not options.copyonly: say()
		if shouldprintnames: say(taskname)
		
		if options.copyonly:
			problem.copytestdata()
		else:
			real, max = problem.test()
		
		ntasks += 1
		nfulltasks += real == max
		realscore += real
		maxscore += max

	if options.copyonly:
		sys.exit()

	if ntasks != 1:
		say()
		say('Grand grand total: %g/%g weighted points; %d/%d problems solved fully' % (realscore, maxscore, nfulltasks, ntasks))
except KeyboardInterrupt:
	sys.exit('Exiting due to a keyboard interrupt.')

if options.pause:
	say('Press any key to exit...')
	sys.stdout.flush()
	
	if pause:
		pause()
	elif callable(globalconf.pause):
		globalconf.pause()
	else:
		with open(os.devnull, 'w') as devnull:
			subprocess.call(globalconf.pause, stdout=devnull, stderr=subprocess.STDOUT)