Mercurial > ~astiob > upreckon > hgweb
diff test.py @ 49:245150080c48 1.20 1.20.3
Converted 1.20 into a branch
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Sun, 19 Dec 2010 23:23:24 +0200 |
parents | 1.20/test.py@5bfa23cd638d |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test.py Sun Dec 19 23:23:24 2010 +0200 @@ -0,0 +1,899 @@ +#! /usr/bin/python +# Copyright (c) 2009, 2010 Chortos-2 <chortos@inbox.lv> + +import os, sys, shutil, time, subprocess, filecmp, optparse, signal, tempfile, tarfile, zipfile + +parser = optparse.OptionParser(version='test.py 1.20.3', usage='usage: %prog [options] [problem names] [[path/to/]solution-app] [test case numbers]\n\nTest case numbers can be specified in plain text or as a Python expression\nif there is only one positional argument.\n\nOnly problem names listed in testconf.py are recognized.') +parser.add_option('-e', '--exclude', dest='exclude', action='append', help='test case number(s) to exclude, as a Python expression; multiple -e options can be supplied') +parser.add_option('-c', '--cleanup', dest='clean', action='store_true', default=False, help='delete the copies of input/output files and exit') +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('-m', '--copy-io', dest='copyonly', action='store_true', default=False, help='only create a copy of the input/output files of the last test case for manual testing; to delete them, use options -cs') +parser.add_option('-x', '--auto-exit', dest='pause', action='store_false', default=True, help='do not wait for a key to be pressed when finished testing') +parser.add_option('-p', '--python', action='store_true', default=False, help='always parse all positional arguments as a single Python expression (including the first argument even if it names an executable file)') +parser.add_option('-t', '--detect-time', dest='autotime', action='store_true', default=False, help='spend a second detecting the most precise time measurement function') + +options, args = parser.parse_args() +parser.destroy() +del parser + +globals1 = set(globals()) + +# Initialize some configuration variables with default values +tasknames = ('.',) +maxtime = 0 +tests = () +dummies = () +testsexcluded = () +padwithzeroestolength = 0 +taskweight = 100 +pointmap = {} +stdio = False +dummyinname = '' +dummyoutname = '' +tester = '' + +def exectestconf_helper(name): + if os.path.isfile('tests.tar'): + f = tarfile.open('tests.tar') + try: + exec f.extractfile(name).read() in globals() + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.zip'): + f = zipfile.ZipFile('tests.zip') + try: + exec f.open(name, 'rU').read() in globals() + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tgz'): + f = tarfile.open('tests.tgz') + try: + exec f.extractfile(name).read() in globals() + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.gz'): + f = tarfile.open('tests.tar.gz') + try: + exec f.extractfile(name).read() in globals() + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tbz2'): + f = tarfile.open('tests.tbz2') + try: + exec f.extractfile(name).read() in globals() + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.bz2'): + f = tarfile.open('tests.tar.bz2') + try: + exec f.extractfile(name).read() in globals() + f.close() + return True + except KeyError: + f.close() + return False + +try: + execfile('testconf.py') +except IOError, error: + exc_info = sys.exc_info()[2] + try: + execfile('tests/testconf.py') + except IOError: + if not exectestconf_helper('testconf.py'): + raise IOError, (error.errno, 'The configuration file is missing', error.filename), exc_info + del exc_info + +globals2 = set(globals()) +globals2.remove('globals1') +globals2 -= globals1 +del globals1 + +shared = {} +g = globals() +for k in globals2: + shared[k] = g[k] + +newtasknames = [] +while len(args) and args[0] in tasknames: + newtasknames.append(args[0]) + del args[0] +if len(newtasknames): + tasknames = newtasknames + +scoresumoveralltasks = 0 +scoremaxoveralltasks = 0 +ntasks = 0 +nfulltasks = 0 +cwd = '' # At any time this is either '' or taskname + '/' + +if options.autotime: + c = time.clock() + time.sleep(1) + c = time.clock() - c + if int(c + .99999) == 1: + clock = time.clock + else: + clock = time.time +elif os.name == 'nt': + clock = time.clock +else: + clock = time.time + +if options.copyonly: + options.erase = False + +def existstestcase_helper(name): + if os.path.isfile('tests.tar'): + f = tarfile.open('tests.tar') + try: + f.getmember(name) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.zip'): + f = zipfile.ZipFile('tests.zip') + try: + f.getinfo(name) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tgz'): + f = tarfile.open('tests.tgz') + try: + f.getmember(name) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.gz'): + f = tarfile.open('tests.tar.gz') + try: + f.getmember(name) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tbz2'): + f = tarfile.open('tests.tbz2') + try: + f.getmember(name) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.bz2'): + f = tarfile.open('tests.tar.bz2') + try: + f.getmember(name) + f.close() + return True + except KeyError: + f.close() + return False + +def existstestcase(name): + if os.path.isfile('tests/' + taskname + '/' + name) or os.path.isfile('tests/' + name): + return True + if cwd and (os.path.isfile(oldcwd + '/tests/' + cwd + name) or os.path.isfile(oldcwd + '/tests/' + name)): + return True + if existstestcase_helper(taskname + '/' + name) or existstestcase_helper(name): + return True + if cwd: + os.chdir(oldcwd) + if existstestcase_helper(cwd + name) or existstestcase_helper(name): + os.chdir(cwd) + return True + os.chdir(cwd) + return False + +def opentestcase_helper(name): + if os.path.isfile('tests.tar'): + f = tarfile.open('tests.tar') + try: + c = f.extractfile(name) + return c + except KeyError: + f.close() + if os.path.isfile('tests.zip'): + f = zipfile.ZipFile('tests.zip') + try: + c = f.open(name, 'rU') + f.close() + return c + except KeyError: + f.close() + if os.path.isfile('tests.tgz'): + f = tarfile.open('tests.tgz') + try: + c = f.extractfile(name) + return c + except KeyError: + f.close() + if os.path.isfile('tests.tar.gz'): + f = tarfile.open('tests.tar.gz') + try: + c = f.extractfile(name) + return c + except KeyError: + f.close() + if os.path.isfile('tests.tbz2'): + f = tarfile.open('tests.tbz2') + try: + c = f.extractfile(name) + return c + except KeyError: + f.close() + if os.path.isfile('tests.tar.bz2'): + f = tarfile.open('tests.tar.bz2') + try: + c = f.extractfile(name) + return c + except KeyError: + f.close() + return None + +def opentestcase(name): + if os.path.isfile('tests/' + taskname + '/' + name): + return open('tests/' + taskname + '/' + name, 'rU') + elif os.path.isfile('tests/' + name): + return open('tests/' + name, 'rU') + f = opentestcase_helper(taskname + '/' + name) + if not f: + f = opentestcase_helper(name) + if f: + return f + if cwd: + if os.path.isfile(oldcwd + '/tests/' + cwd + name): + return open(oldcwd + '/tests/' + cwd + name, 'rU') + elif os.path.isfile(oldcwd + '/tests/' + name): + return open(oldcwd + '/tests/' + name, 'rU') + os.chdir(oldcwd) + f = opentestcase_helper(cwd + name) + if not f: + f = opentestcase_helper(name) + os.chdir(cwd) + if f: + return f + raise KeyError, 'The test-case-defining file \'' + name + '\' cannot be found' + +def copytestcase_helper(name, target): + if os.path.isfile('tests.tar'): + f = tarfile.open('tests.tar') + try: + m = f.getmember(name) + m.name = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.zip'): + if not target.startswith('/'): + f = zipfile.ZipFile('tests.zip') + try: + m = f.getinfo(name) + m.filename = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + else: + oldcwd = os.getcwdu() + os.chdir('/') + f = zipfile.ZipFile(oldcwd + '/tests.zip') + try: + m = f.getinfo(name) + m.filename = os.path.relpath(target) + f.extract(m) + f.close() + os.chdir(oldcwd) + return True + except KeyError: + f.close() + os.chdir(oldcwd) + if os.path.isfile('tests.tgz'): + f = tarfile.open('tests.tgz') + try: + m = f.getmember(name) + m.name = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.gz'): + f = tarfile.open('tests.tar.gz') + try: + m = f.getmember(name) + m.name = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tbz2'): + f = tarfile.open('tests.tbz2') + try: + m = f.getmember(name) + m.name = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + if os.path.isfile('tests.tar.bz2'): + f = tarfile.open('tests.tar.bz2') + try: + m = f.getmember(name) + m.name = target + f.extract(m) + f.close() + return True + except KeyError: + f.close() + return False + +def copytestcase(name, target): + if os.path.isfile('tests/' + taskname + '/' + name): + shutil.copyfile('tests/' + taskname + '/' + name, target) + return + elif os.path.isfile('tests/' + name): + shutil.copyfile('tests/' + name, target) + return + if copytestcase_helper(taskname + '/' + name, target) or copytestcase_helper(name, target): + return + if cwd: + if os.path.isfile(oldcwd + '/tests/' + cwd + name): + shutil.copyfile(oldcwd + '/tests/' + cwd + name, target) + return + elif os.path.isfile(oldcwd + '/tests/' + name): + shutil.copyfile(oldcwd + '/tests/' + name, target) + return + os.chdir(oldcwd) + if copytestcase_helper(cwd + name, target) or copytestcase_helper(name, target): + os.chdir(cwd) + return + os.chdir(cwd) + raise KeyError, 'The test-case-defining file \'' + name + '\' cannot be found' + +# Always chdir if the directory exists but use any existing config +def chdir_and_exec_testconf(): + global cwd + cwd = '' + if os.path.isdir(taskname): + os.chdir(taskname) + if taskname != '.': + cwd = taskname + '/' + try: + execfile('testconf.py', globals()) + return + except IOError: + pass + if not cwd: + if os.path.isfile('tests/' + taskname + '/testconf.py'): + execfile('tests/' + taskname + '/testconf.py', globals()) + return + if os.path.isfile('tests/testconf.py'): + execfile('tests/testconf.py', globals()) + return + if exectestconf_helper(taskname + '/testconf.py') or exectestconf_helper('testconf.py'): + return + if cwd: + os.chdir(oldcwd) + if os.path.isfile('tests/' + cwd + 'testconf.py'): + execfile('tests/' + cwd + 'testconf.py', globals()) + os.chdir(cwd) + return + if os.path.isfile('tests/testconf.py'): + execfile('tests/testconf.py', globals()) + os.chdir(cwd) + return + if exectestconf_helper(cwd + 'testconf.py') or exectestconf_helper('testconf.py'): + os.chdir(cwd) + return + if os.path.isfile('testconf.py'): + execfile('testconf.py', globals()) + os.chdir(cwd) + return + os.chdir(cwd) + elif os.path.isfile('testconf.py'): + execfile('testconf.py', globals()) + return + raise KeyError, 'The configuration file for task ' + taskname + ' is missing' + +try: + name + namedefined = True +except Exception: + namedefined = False + +for taskname in tasknames: + if ntasks: + print + + try: + if len(tasknames) > 1: + print taskname + except Exception: + if taskname != '.' or ntasks: + print taskname + + try: del inname + except NameError: pass + try: del outname + except NameError: pass + try: del ansname + except NameError: pass + + if not namedefined and taskname != '.': + name = './' + taskname + for k in shared: + g[k] = shared[k] + + oldcwd = os.getcwdu() + chdir_and_exec_testconf() + + if options.clean: + try: + if not stdio or tester: + if not tester: + inname + outname + if tester: + ansname + except NameError, error: + raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] + if not options.erase: + try: + inname = inname.replace('%', taskname) + except NameError: + inname = taskname + '.in' + try: + outname = outname.replace('%', taskname) + except NameError: + outname = taskname + '.out' + try: + ansname = ansname.replace('%', taskname) + except NameError: + ansname = taskname + '.ans' + else: + inname = inname.replace('%', taskname) + outname = outname.replace('%', taskname) + if tester: + ansname = ansname.replace('%', taskname) + if not stdio or tester or not options.erase: + if os.path.exists(inname): os.remove(inname) + if os.path.exists(outname): os.remove(outname) + if (tester or not options.erase) and ansname: + if os.path.exists(ansname): os.remove(ansname) + continue + + try: + name + except NameError, error: + if str(error).count('name') == 1: + raise NameError, 'configuration ' + str(error), sys.exc_info()[2] + else: + raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] + + try: + if not stdio: + inname + outname + testcaseinname + if tester: + outname + if ansname: + testcaseoutname + else: + testcaseoutname + except NameError, error: + raise NameError, 'configuration ' + str(error).replace('name ', 'variable ', 1), sys.exc_info()[2] + + if not options.erase: + try: + inname + except NameError: + inname = taskname + '.in' + try: + outname + except NameError: + outname = taskname + '.out' + try: + ansname + except NameError: + ansname = taskname + '.ans' + + if options.pause: + try: + pause + except NameError, error: + if os.name == 'posix': + pause = 'read -s -n 1' + print 'Configuration ' + str(error).replace('name ', 'variable ') + '; it was devised automatically but the choice might be incorrect, so test.py might exit immediately after the testing is complete.' + elif os.name == 'nt': + pause = 'pause' + else: + raise NameError, 'configuration ' + str(error).replace('name ', 'variable ') + ' and cannot be devised automatically', sys.exc_info()[2] + + if not dummyinname: + dummyinname = testcaseinname + if not dummyoutname and (not tester or ansname): + dummyoutname = testcaseoutname + + dummyinname = dummyinname.replace('%', taskname) + dummyoutname = dummyoutname.replace('%', taskname) + testcaseinname = testcaseinname.replace('%', taskname) + if not stdio or not options.erase: + inname = inname.replace('%', taskname) + outname = outname.replace('%', taskname) + try: + ansname = ansname.replace('%', taskname) + except NameError: + pass + if tester: + try: inname = inname.replace('%', taskname) + except NameError: pass + outname = outname.replace('%', taskname) + if ansname: + ansname = ansname.replace('%', taskname) + testcaseoutname = testcaseoutname.replace('%', taskname) + else: + testcaseoutname = testcaseoutname.replace('%', taskname) + + if isinstance(padwithzeroestolength, tuple): + padwithzeroestolength, paddummieswithzeroestolength = padwithzeroestolength + else: + paddummieswithzeroestolength = padwithzeroestolength + + if options.python: + dummies = () + s = ' '.join(args) + tests = eval(s) + try: + tests.__iter__ + except AttributeError: + tests = (tests,) + elif len(args): + if os.path.exists(args[0]): + name = args[0] + del args[0] + if len(args) > 1: + dummies = () + tests = args + elif len(args): + dummies = () + s = args[0] + if len(s) < padwithzeroestolength: + s = s.zfill(padwithzeroestolength) + if existstestcase(testcaseinname.replace('$', s)): + tests = (s,) + else: + try: + tests = eval(args[0]) + try: + tests.__iter__ + except AttributeError: + tests = (tests,) + except Exception: + tests = (s,) + + if options.exclude: + testsexcluded = [] + for i in options.exclude: + v = eval(i) + try: + testsexcluded.extend(v) + except TypeError: + testsexcluded.append(v) + + # Windows doesn't like paths beginning with .\ and not ending with an extension + name = os.path.normcase(name) + if name.startswith('.\\'): + name = name[2:] + + newpointmap = {} + + for i in pointmap: + try: + for j in i: + newpointmap[j] = pointmap[i] + except TypeError: + newpointmap[i] = pointmap[i] + + pointmap = newpointmap + + if maxtime > 0: + strmaxtime = '/%.3f' % maxtime + else: + strmaxtime = '' + + padoutputtolength = 0 + ntests = [] + + for j in dummies: + try: + j.__iter__ + except AttributeError: + j = (j,) + ntests.append((j, True)) + for i in j: + s = str(i) + if len(s) < paddummieswithzeroestolength: + s = s.zfill(paddummieswithzeroestolength) + s = 'sample ' + s + if padoutputtolength < len(s): + padoutputtolength = len(s) + + for j in tests: + try: + j.__iter__ + except AttributeError: + j = (j,) + ntests.append((j, False)) + for i in j: + s = str(i) + if len(s) < padwithzeroestolength: + s = s.zfill(padwithzeroestolength) + if padoutputtolength < len(s): + padoutputtolength = len(s) + + tests = ntests + score = maxpoints = ncorrect = ntotal = ncorrectvalued = nvalued = 0 + + if options.copyonly: + j, isdummy = tests[-1] + if isdummy: + realinname = dummyinname + realoutname = dummyoutname + else: + realinname = testcaseinname + realoutname = testcaseoutname + for i in j: + if i in testsexcluded and not isdummy: + continue + s = str(i) + if isdummy: + if len(s) < paddummieswithzeroestolength: + s = s.zfill(paddummieswithzeroestolength) + else: + if len(s) < padwithzeroestolength: + s = s.zfill(padwithzeroestolength) + copytestcase(realinname.replace('$', s), inname) + if ansname: + copytestcase(realoutname.replace('$', s), ansname) + continue + + for j, isdummy in tests: + ncorrectgrp = 0 + ntotalgrp = 0 + scoregrp = 0 + maxpointsgrp = 0 + if isdummy: + realinname = dummyinname + realoutname = dummyoutname + else: + realinname = testcaseinname + realoutname = testcaseoutname + for i in j: + if i in testsexcluded and not isdummy: + continue + ntotalgrp += 1 + s = str(i) + if isdummy: + npoints = 0 + if len(s) < paddummieswithzeroestolength: + s = s.zfill(paddummieswithzeroestolength) + spref = 'sample ' + else: + npoints = pointmap.get(None, 1) + npoints = pointmap.get(i, npoints) + maxpointsgrp += npoints + if npoints: + nvalued += 1 + if len(s) < padwithzeroestolength: + s = s.zfill(padwithzeroestolength) + spref = '' + print ' ' * (padoutputtolength - len(spref + s)) + spref + s + ':', + sys.stdout.flush() + outputdata = open(os.devnull, 'w') + if stdio: + f = tempfile.NamedTemporaryFile(delete=False) + inputdatafname = f.name + f.close() + copytestcase(realinname.replace('$', s), inputdatafname) + inputdata = open(inputdatafname, 'rU') + if options.erase: + tempoutput = tempfile.TemporaryFile('w+') + else: + tempoutput = open(outname, 'w+') + try: + proc = subprocess.Popen(name, stdin=inputdata, stdout=tempoutput, stderr=outputdata, universal_newlines=True) + except OSError, error: + raise OSError, 'The program to be tested cannot be launched: ' + str(error), sys.exc_info()[2] + else: + if os.path.exists(outname): + os.remove(outname) + copytestcase(realinname.replace('$', s), inname) + try: + proc = subprocess.Popen(name, stdin=outputdata, stdout=outputdata, stderr=outputdata, universal_newlines=True) + except OSError, error: + raise OSError, 'The program to be tested cannot be launched: ' + str(error), sys.exc_info()[2] + cl = clock() + if maxtime > 0: + while 1: + proc.poll() + elapsed = clock() - cl + if proc.returncode == None: + if elapsed >= maxtime: + print '%.3f%s s, 0/%d, time limit exceeded' % (elapsed, strmaxtime, npoints) + sys.stdout.flush() + while proc.returncode == None: + try: + proc.terminate() + except OSError: + pass + except AttributeError: + try: + os.kill(proc.pid, signal.SIGTERM) + except Exception: + pass + proc.poll() + outputdata.close() + if stdio: + tempoutput.close() + break + else: + print '%.3f%s s,' % (elapsed, strmaxtime), + sys.stdout.flush() + elapsed = 0 + if stdio: + tempoutput.seek(0) + lines = tempoutput.readlines() + tempoutput.close() + break + if elapsed >= maxtime: + continue + else: + data = proc.communicate() + elapsed = clock() - cl + print '%.3f%s s,' % (elapsed, strmaxtime), + sys.stdout.flush() + if stdio: + tempoutput.seek(0) + lines = tempoutput.readlines() + tempoutput.close() + outputdata.close() + if stdio: + inputdata.close() + try: + os.unlink(inputdatafname) + except Exception: + pass + if proc.returncode > 0: + print '0/%d, non-zero return code %d' % (npoints, proc.returncode) + sys.stdout.flush() + elif proc.returncode < 0: + print '0/%d, terminated by signal %d' % (npoints, -proc.returncode) + sys.stdout.flush() + else: + if not tester: + if stdio: + outputdata = opentestcase(realoutname.replace('$', s)) + r = 0 + data = outputdata.read().splitlines(True) + if len(lines) != len(data): + r = 1 + else: + for i in zip(lines, data): + if i[0] != i[1]: + r = 1 + break + outputdata.close() + else: + try: + inputdata = open(outname, 'rU') + except IOError: + print '0/%g, output file not created or not readable' % npoints + sys.stdout.flush() + r = None + else: + outputdata = opentestcase(realoutname.replace('$', s)) + r = 0 + lines = inputdata.readlines() + data = outputdata.read().splitlines(True) + if len(lines) != len(data): + r = 1 + else: + for i in zip(lines, data): + if i[0] != i[1]: + r = 1 + break + inputdata.close() + outputdata.close() + else: + if ansname: + copytestcase(realoutname.replace('$', s), ansname) + if stdio: + try: copytestcase(realinname.replace('$', s), inname) + except NameError: pass + outputdata = open(outname, 'w') + outputdata.writelines(lines) + outputdata.close() + try: + proc = subprocess.Popen(tester, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + except OSError, error: + raise OSError, 'The tester application cannot be launched: ' + str(error), sys.exc_info()[2] + data = proc.communicate() + r = proc.returncode + if tester and data[0]: + data = ''.join((' (', data[0].strip(), ')')) + else: + data = '' + if r: + print '0/%g, wrong answer%s' % (npoints, data) + sys.stdout.flush() + elif r == 0: + print '%g/%g, OK%s' % (npoints, npoints, data) + sys.stdout.flush() + scoregrp += npoints + ncorrectgrp += 1 + if npoints: + ncorrectvalued += 1 + if ntotalgrp: + if scoregrp < maxpointsgrp: + scoregrp = 0 + if ntotalgrp > 1: + print 'Group total: %d/%d tests; %g/%g points' % (ncorrectgrp, ntotalgrp, scoregrp, maxpointsgrp) + sys.stdout.flush() + ncorrect += ncorrectgrp + ntotal += ntotalgrp + score += scoregrp + maxpoints += maxpointsgrp + + if options.erase: + if not stdio or tester: + if os.path.exists(inname): os.remove(inname) + if os.path.exists(outname): os.remove(outname) + if tester and ansname: + if os.path.exists(ansname): os.remove(ansname) + elif stdio: + copytestcase(realinname.replace('$', s), inname) + copytestcase(realoutname.replace('$', s), ansname) + if nvalued != ntotal: + print 'Grand total: %d/%d tests (%d/%d valued); %g/%g points; weighted score: %g/%g' % (ncorrect, ntotal, ncorrectvalued, nvalued, score, maxpoints, (score*taskweight/maxpoints if not score*taskweight%maxpoints else float(score*taskweight)/maxpoints) if maxpoints else 0, taskweight) + else: + print 'Grand total: %d/%d tests; %g/%g points; weighted score: %g/%g' % (ncorrect, ntotal, score, maxpoints, (score*taskweight/maxpoints if not score*taskweight%maxpoints else float(score*taskweight)/maxpoints) if maxpoints else 0, taskweight) + + scoresumoveralltasks += (score*taskweight/maxpoints if not score*taskweight%maxpoints else float(score*taskweight)/maxpoints) if maxpoints else 0 + scoremaxoveralltasks += taskweight + ntasks += 1 + nfulltasks += int((score == maxpoints) if maxpoints else (taskweight == 0)) + + os.chdir(oldcwd) + +if options.clean or options.copyonly: + sys.exit() + +if ntasks != 1: + print + print 'Grand grand total: %g/%g weighted points; %d/%d problems solved fully' % (scoresumoveralltasks, scoremaxoveralltasks, nfulltasks, ntasks) + +if options.pause: + print 'Press any key to exit... ', + sys.stdout.flush() + os.system(pause + ' >' + os.devnull)