Mercurial > ~astiob > upreckon > hgweb
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 |