diff 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
line wrap: on
line diff
--- 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: