Mercurial > ~astiob > upreckon > hgweb
diff 2.00/testcases.py @ 25:b500e117080e
Bug fixes and overhead reduction
Added the --problem/-p option. (WARNING: not the same as the -p option of test.py 1.x.) The problem names supplied are not validated.
Added zip_longest to compat.py.
Experimental: problem names are now _always_ printed for multi-problem sets.
Overhead: Escape presses are now checked only once every .15 seconds (at least kbhit() on Windows is very slow).
Overhead: sleep(0) is now called in the time-control-and-Escape-watching loop (at least on Windows, it immediately transfers control to some waiting thread).
Bug fix: compat.py now overwrites built-ins only while including testconfs (--help was broken in Python 2).
Bug fix: ReadDeleting in config.py now closes the file it opens (especially important on Windows, where open files cannot be deleted).
Bug fix: added callable to compat.py (it is absent from Python 3).
Bug fix: the default (built-in) output validator now properly handles unwanted trailing data.
Bug fix: testconfs in custom archives no more raise NameError.
Bug fix: if a validator program cannot be launched, CannotStartValidator is now raised instead of the fatal OSError.
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Thu, 23 Sep 2010 23:05:58 +0000 |
parents | c23d81f4a1a3 |
children | 5bbb68833868 |
line wrap: on
line diff
--- a/2.00/testcases.py Thu Sep 23 00:11:24 2010 +0000 +++ b/2.00/testcases.py Thu Sep 23 23:05:58 2010 +0000 @@ -59,9 +59,9 @@ atexit.register(cleanup) del cleanup tty.setcbreak(sys.stdin.fileno()) - def canceled(): - while select.select((sys.stdin,), (), (), 0)[0]: - if sys.stdin.read(1) == '\33': + def canceled(select=select.select, stdin=sys.stdin, read=sys.stdin.read): + while select((stdin,), (), (), 0)[0]: + if read(1) == '\33': return True return False def init_canceled(): @@ -70,14 +70,14 @@ def pause(): sys.stdin.read(1) else: - def canceled(): - while msvcrt.kbhit(): - c = msvcrt.getch() + def canceled(kbhit=msvcrt.kbhit, getch=msvcrt.getch): + while kbhit(): + c = getch() if c == '\33': return True elif c == '\0': # Let's hope no-one is fiddling with this - msvcrt.getch() + getch() return False def init_canceled(): while msvcrt.kbhit(): @@ -298,29 +298,11 @@ # Compare the output with the reference output case.open_outfile() with case.outfile.open() as refoutput: - for line, refline in zip(output, refoutput): - if not isinstance(refline, basestring): + for line, refline in zip_longest(output, refoutput): + if refline is not None and not isinstance(refline, basestring): line = bytes(line, sys.getdefaultencoding()) if line != refline: raise WrongAnswer - try: - try: - next(output) - except NameError: - output.next() - except StopIteration: - pass - else: - raise WrongAnswer - try: - try: - next(refoutput) - except NameError: - refoutput.next() - except StopIteration: - pass - else: - raise WrongAnswer return 1 elif callable(case.validator): return case.validator(output) @@ -330,10 +312,12 @@ if case.problem.config.ansname: case.open_outfile() case.outfile.copy(case.problem.config.ansname) - case.process = Popen(case.validator, stdin=devnull, stdout=PIPE, stderr=STDOUT, universal_newlines=True, bufsize=-1) + try: + case.process = Popen(case.validator, stdin=devnull, stdout=PIPE, stderr=STDOUT, universal_newlines=True, bufsize=-1) + except OSError: + raise CannotStartValidator(sys.exc_info()[1]) comment = case.process.communicate()[0].strip() - lower = comment.lower() - match = re.match(r'(ok|correct|wrong(?:(?:\s|_)*answer)?)(?:$|\s+|[.,!:]+\s*)', lower) + match = re.match(r'(?i)(ok|correct|wrong(?:(?:\s|_)*answer)?)(?:$|\s+|[.,!:]+\s*)', comment) if match: comment = comment[match.end():] if not case.problem.config.maxexitcode: @@ -375,11 +359,12 @@ # FIXME: 2.5 lacks the delete parameter with tempfile.NamedTemporaryFile(delete=False) as f: inputdatafname = f.name - context = CopyDeleting(case, case.infile, inputdatafname) + contextmgr = CopyDeleting(case, case.infile, inputdatafname) else: inputdatafname = case.problem.config.inname - context = Copying(case.infile, inputdatafname) - with context: + contextmgr = Copying(case.infile, inputdatafname) + with contextmgr: + # FIXME: this U doesn't do anything good for the child process, does it? with open(inputdatafname, 'rU') as infile: with tempfile.TemporaryFile('w+') if options.erase and not case.validator else open(case.problem.config.outname, 'w+') as outfile: # TODO: make sure outfile.file is passed to Popen if needed @@ -393,14 +378,22 @@ except OSError: raise CannotStartTestee(sys.exc_info()[1]) case.time_started = clock() + time_next_check = case.time_started + .15 if not case.maxtime: while True: exitcode, now = case.process.poll(), clock() if exitcode is not None: case.time_stopped = now break - elif canceled(): - raise CanceledByUser + # For some reason (probably Microsoft's fault), + # msvcrt.kbhit() is slow as hell + else: + if now >= time_next_check: + if canceled(): + raise CanceledByUser + else: + time_next_check = now + .15 + time.sleep(0) else: time_end = case.time_started + case.maxtime while True: @@ -410,8 +403,13 @@ break elif now >= time_end: raise TimeLimitExceeded - elif canceled(): - raise CanceledByUser + else: + if now >= time_next_check: + if canceled(): + raise CanceledByUser + else: + time_next_check = now + .15 + time.sleep(0) if config.globalconf.force_zero_exitcode and case.process.returncode: raise NonZeroExitCode(case.process.returncode) callback() @@ -430,14 +428,20 @@ except OSError: raise CannotStartTestee(sys.exc_info()[1]) case.time_started = clock() + time_next_check = case.time_started + .15 if not case.maxtime: while True: exitcode, now = case.process.poll(), clock() if exitcode is not None: case.time_stopped = now break - elif canceled(): - raise CanceledByUser + else: + if now >= time_next_check: + if canceled(): + raise CanceledByUser + else: + time_next_check = now + .15 + time.sleep(0) else: time_end = case.time_started + case.maxtime while True: @@ -447,8 +451,13 @@ break elif now >= time_end: raise TimeLimitExceeded - elif canceled(): - raise CanceledByUser + else: + if now >= time_next_check: + if canceled(): + raise CanceledByUser + else: + time_next_check = now + .15 + time.sleep(0) if config.globalconf.force_zero_exitcode and case.process.returncode: raise NonZeroExitCode(case.process.returncode) callback()