# HG changeset patch # User Oleg Oshmyan # Date 1308362117 -3600 # Node ID e0b2fbd7ebe0ea337c9fbd4cfaa5f7fdc516f969 # Parent b993d9257400c4ad375447880a462ad256d579a6 Improved built-in output validator; added conf. var. binary The built-in output validator now reads blocks rather than lines, which should make it faster. There is also a new configuration variable called binary, which defaults to False and specifies whether the built-in output validator should not try to translate line breaks. Finally, when binary is false, the built-in output validator now translates line breaks even if it the reference output is in a tape archive. diff -r b993d9257400 -r e0b2fbd7ebe0 upreckon/compat.py --- a/upreckon/compat.py Thu Jun 16 01:24:10 2011 +0100 +++ b/upreckon/compat.py Sat Jun 18 02:55:17 2011 +0100 @@ -1,6 +1,6 @@ # Copyright (c) 2010-2011 Chortos-2 -# A compatibility layer for Python 2.5+. This is what lets test.py +# A compatibility layer for Python 2.5+. This is what lets Upreckon # run on all versions of Python starting with 2.5, including Python 3. # A few notes regarding some compatibility-driven peculiarities diff -r b993d9257400 -r e0b2fbd7ebe0 upreckon/config.py --- a/upreckon/config.py Thu Jun 16 01:24:10 2011 +0100 +++ b/upreckon/config.py Sat Jun 18 02:55:17 2011 +0100 @@ -31,6 +31,7 @@ 'groupweight': {}, 'pointmap': {}, 'stdio': False, + 'binary': False, 'dummyinname': '', 'dummyoutname': '', 'tester': None, diff -r b993d9257400 -r e0b2fbd7ebe0 upreckon/files.py --- a/upreckon/files.py Thu Jun 16 01:24:10 2011 +0100 +++ b/upreckon/files.py Sat Jun 18 02:55:17 2011 +0100 @@ -60,7 +60,6 @@ member.name = target self.file.extract(member) - # TODO: somehow automagically emulate universal line break support def open(self, name): return self.file.extractfile(name) @@ -117,7 +116,7 @@ self.file.extract(member) def open(self, name): - return self.file.open(name, 'rU') + return self.file.open(name, 'r') def exists(self, queried_name): if not hasattr(self, '_namelist'): @@ -232,7 +231,7 @@ else: return contextlib.closing(file) else: - return open(self.real_path, 'rU') + return open(self.real_path, 'rb') def copy(self, target): if self.archive: diff -r b993d9257400 -r e0b2fbd7ebe0 upreckon/testcases.py --- a/upreckon/testcases.py Thu Jun 16 01:24:10 2011 +0100 +++ b/upreckon/testcases.py Sat Jun 18 02:55:17 2011 +0100 @@ -13,7 +13,7 @@ from subprocess import Popen, PIPE, STDOUT import os -devnull = open(os.path.devnull, 'w+') +devnull = open(os.path.devnull, 'w+b') class DummySignalIgnorer(object): def __enter__(self): pass @@ -179,13 +179,38 @@ def validate(case, output): if not case.validator: # Compare the output with the reference output + buffer = refbuffer = crlfhalf = refcrlfhalf = ''.encode() + crlf = '\r\n'.encode('ascii') case.open_outfile() with case.outfile.open() as refoutput: - 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: + while True: + data = output.read(4096 - len(buffer)) + refdata = refoutput.read(4096 - len(refbuffer)) + if not case.problem.config.binary: + data, refdata = crlfhalf + data, refcrlfhalf + refdata + size, refsize = len(data), len(refdata) + if data and data != crlfhalf and data[-1] == crlf[0]: + size -= 1 + crlfhalf = data[-1:] + else: + crlfhalf = ''.encode() + if refdata and refdata != refcrlfhalf and refdata[-1] == crlf[0]: + refsize -= 1 + refcrlfhalf = refdata[-1:] + else: + refcrlfhalf = ''.encode() + data = data[:size].replace(crlf, crlf[1:]) + data = data.replace(crlf[:1], crlf[1:]) + refdata = refdata[:refsize].replace(crlf, crlf[1:]) + refdata = refdata.replace(crlf[:1], crlf[1:]) + buffer += data + refbuffer += refdata + if not (buffer or refbuffer or crlfhalf or refcrlfhalf): + break + size = min(len(buffer), len(refbuffer)) + if buffer[:size] != refbuffer[:size]: raise WrongAnswer + buffer, refbuffer = buffer[size:], refbuffer[size:] return 1 elif callable(case.validator): return case.validator(output) @@ -238,7 +263,7 @@ inputdatafname = case.problem.config.inname contextmgr = Copying(case.infile, inputdatafname) with contextmgr: - with tempfile.TemporaryFile('w+') if options.erase and (not case.validator or callable(case.validator)) else open(case.problem.config.outname, 'w+') as outfile: + with tempfile.TemporaryFile('w+b') if options.erase and (not case.validator or callable(case.validator)) else open(case.problem.config.outname, 'w+b') as outfile: with open(inputdatafname) as infile: call(case.problem.config.path, case=case, stdin=infile, stdout=outfile, stderr=devnull) if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: @@ -255,7 +280,7 @@ case.has_called_back = True callback() try: - output = open(case.problem.config.outname, 'rU') + output = open(case.problem.config.outname, 'rb') except IOError: raise CannotReadOutputFile(sys.exc_info()[1]) with output as output: @@ -275,7 +300,7 @@ case.has_called_back = True callback() try: - output = open(case.problem.config.outname.replace('$', case.id), 'rU') + output = open(case.problem.config.outname.replace('$', case.id), 'rb') except IOError: raise CannotReadOutputFile(sys.exc_info()[1]) with output as output: