comparison upreckon/testcases.py @ 174:e0b2fbd7ebe0

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.
author Oleg Oshmyan <chortos@inbox.lv>
date Sat, 18 Jun 2011 02:55:17 +0100
parents 8198aa2ed20d
children 88e1e6786f67
comparison
equal deleted inserted replaced
170:b993d9257400 174:e0b2fbd7ebe0
11 11
12 import re, sys, tempfile 12 import re, sys, tempfile
13 from subprocess import Popen, PIPE, STDOUT 13 from subprocess import Popen, PIPE, STDOUT
14 14
15 import os 15 import os
16 devnull = open(os.path.devnull, 'w+') 16 devnull = open(os.path.devnull, 'w+b')
17 17
18 class DummySignalIgnorer(object): 18 class DummySignalIgnorer(object):
19 def __enter__(self): pass 19 def __enter__(self): pass
20 def __exit__(self, exc_type, exc_value, traceback): pass 20 def __exit__(self, exc_type, exc_value, traceback): pass
21 signal_ignorer = DummySignalIgnorer() 21 signal_ignorer = DummySignalIgnorer()
177 case.validator = case.problem.config.tester 177 case.validator = case.problem.config.tester
178 178
179 def validate(case, output): 179 def validate(case, output):
180 if not case.validator: 180 if not case.validator:
181 # Compare the output with the reference output 181 # Compare the output with the reference output
182 buffer = refbuffer = crlfhalf = refcrlfhalf = ''.encode()
183 crlf = '\r\n'.encode('ascii')
182 case.open_outfile() 184 case.open_outfile()
183 with case.outfile.open() as refoutput: 185 with case.outfile.open() as refoutput:
184 for line, refline in zip_longest(output, refoutput): 186 while True:
185 if refline is not None and not isinstance(refline, basestring): 187 data = output.read(4096 - len(buffer))
186 line = bytes(line, sys.getdefaultencoding()) 188 refdata = refoutput.read(4096 - len(refbuffer))
187 if line != refline: 189 if not case.problem.config.binary:
190 data, refdata = crlfhalf + data, refcrlfhalf + refdata
191 size, refsize = len(data), len(refdata)
192 if data and data != crlfhalf and data[-1] == crlf[0]:
193 size -= 1
194 crlfhalf = data[-1:]
195 else:
196 crlfhalf = ''.encode()
197 if refdata and refdata != refcrlfhalf and refdata[-1] == crlf[0]:
198 refsize -= 1
199 refcrlfhalf = refdata[-1:]
200 else:
201 refcrlfhalf = ''.encode()
202 data = data[:size].replace(crlf, crlf[1:])
203 data = data.replace(crlf[:1], crlf[1:])
204 refdata = refdata[:refsize].replace(crlf, crlf[1:])
205 refdata = refdata.replace(crlf[:1], crlf[1:])
206 buffer += data
207 refbuffer += refdata
208 if not (buffer or refbuffer or crlfhalf or refcrlfhalf):
209 break
210 size = min(len(buffer), len(refbuffer))
211 if buffer[:size] != refbuffer[:size]:
188 raise WrongAnswer 212 raise WrongAnswer
213 buffer, refbuffer = buffer[size:], refbuffer[size:]
189 return 1 214 return 1
190 elif callable(case.validator): 215 elif callable(case.validator):
191 return case.validator(output) 216 return case.validator(output)
192 else: 217 else:
193 # Call the validator program 218 # Call the validator program
236 contextmgr = CopyDeleting(case, case.infile, inputdatafname) 261 contextmgr = CopyDeleting(case, case.infile, inputdatafname)
237 else: 262 else:
238 inputdatafname = case.problem.config.inname 263 inputdatafname = case.problem.config.inname
239 contextmgr = Copying(case.infile, inputdatafname) 264 contextmgr = Copying(case.infile, inputdatafname)
240 with contextmgr: 265 with contextmgr:
241 with tempfile.TemporaryFile('w+') if options.erase and (not case.validator or callable(case.validator)) else open(case.problem.config.outname, 'w+') as outfile: 266 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:
242 with open(inputdatafname) as infile: 267 with open(inputdatafname) as infile:
243 call(case.problem.config.path, case=case, stdin=infile, stdout=outfile, stderr=devnull) 268 call(case.problem.config.path, case=case, stdin=infile, stdout=outfile, stderr=devnull)
244 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: 269 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0:
245 raise NonZeroExitCode(case.process.returncode) 270 raise NonZeroExitCode(case.process.returncode)
246 case.has_called_back = True 271 case.has_called_back = True
253 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: 278 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0:
254 raise NonZeroExitCode(case.process.returncode) 279 raise NonZeroExitCode(case.process.returncode)
255 case.has_called_back = True 280 case.has_called_back = True
256 callback() 281 callback()
257 try: 282 try:
258 output = open(case.problem.config.outname, 'rU') 283 output = open(case.problem.config.outname, 'rb')
259 except IOError: 284 except IOError:
260 raise CannotReadOutputFile(sys.exc_info()[1]) 285 raise CannotReadOutputFile(sys.exc_info()[1])
261 with output as output: 286 with output as output:
262 return case.validate(output) 287 return case.validate(output)
263 288
273 def test(case, callback): 298 def test(case, callback):
274 case.time_stopped = case.time_started = 0 299 case.time_stopped = case.time_started = 0
275 case.has_called_back = True 300 case.has_called_back = True
276 callback() 301 callback()
277 try: 302 try:
278 output = open(case.problem.config.outname.replace('$', case.id), 'rU') 303 output = open(case.problem.config.outname.replace('$', case.id), 'rb')
279 except IOError: 304 except IOError:
280 raise CannotReadOutputFile(sys.exc_info()[1]) 305 raise CannotReadOutputFile(sys.exc_info()[1])
281 with output as output: 306 with output as output:
282 return case.validate(output) 307 return case.validate(output)
283 308