first commit
This commit is contained in:
528
python/datasource.py
Executable file
528
python/datasource.py
Executable file
@@ -0,0 +1,528 @@
|
||||
|
||||
import logging, os, sys, socket, time, xmlrpclib, bz2, Cookie
|
||||
import unittest
|
||||
from engineconfig import getConfig
|
||||
from entity import Submit, Problem, TestCase, PresetCode, DataFile
|
||||
|
||||
class DataSourceError(Exception): pass
|
||||
|
||||
class DataSource:
|
||||
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
self.config = getConfig()
|
||||
self.logger = logging.getLogger('main')
|
||||
|
||||
self.judge_id = self.driver.get_judge_id()
|
||||
|
||||
def _get_config_retry(self):
|
||||
ret = self.config.retry
|
||||
if ret < 1: ret = 1
|
||||
return ret
|
||||
|
||||
def _get_config_retry_wait(self):
|
||||
ret = self.config.retry_wait
|
||||
if ret < 0.1: ret = 0.1
|
||||
return ret
|
||||
|
||||
def _do_action(self, func, args):
|
||||
retry = self._get_config_retry()
|
||||
retry_wait = self._get_config_retry_wait()
|
||||
ret = None
|
||||
while retry > 0:
|
||||
try:
|
||||
ret = func(*args)
|
||||
except DataSourceError, e:
|
||||
self.logger.exception("DataSourceError")
|
||||
if retry > 0:
|
||||
time.sleep(retry_wait)
|
||||
retry = retry - 1
|
||||
continue
|
||||
else:
|
||||
self.logger.info('Quit after retried.')
|
||||
sys.exit(1)
|
||||
break
|
||||
return ret
|
||||
|
||||
def get_submits(self, limit):
|
||||
func = self.driver.get_submits
|
||||
args = (self.judge_id, limit)
|
||||
rows = self._do_action(func, args)
|
||||
ret = []
|
||||
if rows:
|
||||
for row in rows:
|
||||
ret.append(Submit(self, row))
|
||||
return ret
|
||||
|
||||
def reset_submits(self):
|
||||
func = self.driver.reset_submits
|
||||
args = (self.judge_id,)
|
||||
return self._do_action(func, args)
|
||||
|
||||
def get_problem(self, pid):
|
||||
func = self.driver.get_problem
|
||||
args = (pid, )
|
||||
return Problem(self, self._do_action(func, args))
|
||||
|
||||
def get_tests(self, pid, full):
|
||||
func = self.driver.get_tests
|
||||
args = (pid, full)
|
||||
rows = self._do_action(func, args)
|
||||
ret = []
|
||||
if rows:
|
||||
for row in rows:
|
||||
ret.append(TestCase(self, row))
|
||||
return ret
|
||||
|
||||
def get_test(self, tid, raw = True):
|
||||
func = self.driver.get_test
|
||||
args = (tid, )
|
||||
row = self._do_action(func, args)
|
||||
if raw:
|
||||
return row
|
||||
else:
|
||||
return TestCase(self, row)
|
||||
|
||||
def get_presetcodes(self, pid, lang):
|
||||
func = self.driver.get_presetcodes
|
||||
args = (pid, lang)
|
||||
rows = self._do_action(func, args)
|
||||
return map(lambda row: PresetCode(self, row), rows)
|
||||
|
||||
def get_datafiles(self, pid):
|
||||
func = self.driver.get_datafiles
|
||||
args = (pid, )
|
||||
rows = self._do_action(func, args)
|
||||
return map(lambda row: DataFile(self, row), rows)
|
||||
|
||||
def get_datafile_data(self, datafileid):
|
||||
func = self.driver.get_datafile_data
|
||||
args = (datafileid, )
|
||||
return self._do_action(func, args)
|
||||
|
||||
def update_submits_status(self, sids, status):
|
||||
func = self.driver.update_submits_status
|
||||
if not isinstance(sids, (tuple, list)):
|
||||
sids = (sids, )
|
||||
args = (sids, status)
|
||||
self.logger.debug('set submits %s status to %s', sids.__str__(), status)
|
||||
return self._do_action(func, args)
|
||||
|
||||
def update_submit_compilemessage(self, sid, msg):
|
||||
func = self.driver.update_submit_compilemessage
|
||||
args = (sid, msg)
|
||||
return self._do_action(func, args)
|
||||
|
||||
def get_submit_status(self, sid):
|
||||
func = self.driver.get_submit_status
|
||||
args = (sid, )
|
||||
return self._do_action(func, args)
|
||||
|
||||
def get_submit_compilemessage(self, sid):
|
||||
func = self.driver.get_submit_compilemessage
|
||||
args = (sid, )
|
||||
return self._do_action(func, args)
|
||||
|
||||
def update_submit_test_results(self, sid, results):
|
||||
func = self.driver.update_submit_test_results
|
||||
args = (sid, results)
|
||||
self.logger.info('update_submits_status: submit %s, %d records' % \
|
||||
(sid, len(results)))
|
||||
return self._do_action(func, args)
|
||||
|
||||
def get_submit(self, sid):
|
||||
func = self.driver.get_submit
|
||||
args = (sid, )
|
||||
ret = self._do_action(func, args)
|
||||
return Submit(self, ret)
|
||||
|
||||
class JspAuthTransport(xmlrpclib.Transport):
|
||||
def __init__(self):
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
self.__cookies = Cookie.SmartCookie()
|
||||
|
||||
def request(self, host, handler, request_body, verbose=0):
|
||||
# issue XML-RPC request
|
||||
|
||||
h = self.make_connection(host)
|
||||
if verbose:
|
||||
h.set_debuglevel(1)
|
||||
|
||||
self.send_request(h, handler, request_body)
|
||||
self.__sendJsessionCookie(h)
|
||||
self.send_host(h, host)
|
||||
self.send_user_agent(h)
|
||||
self.send_content(h, request_body)
|
||||
|
||||
errcode, errmsg, headers = h.getreply()
|
||||
|
||||
if errcode != 200:
|
||||
raise xmlrpclib.ProtocolError(
|
||||
host + handler,
|
||||
errcode, errmsg,
|
||||
headers
|
||||
)
|
||||
self.verbose = verbose
|
||||
self.__processCookies(headers)
|
||||
return self.parse_response(h.getfile())
|
||||
|
||||
|
||||
def get_jsession_id(self):
|
||||
if self.__cookies.has_key('MoodleSession'):
|
||||
return self.__cookies['MoodleSession'].value
|
||||
return None
|
||||
|
||||
|
||||
def __sendJsessionCookie(self, connection):
|
||||
if self.__cookies.has_key('MoodleSession'):
|
||||
connection.putheader(
|
||||
'Cookie',
|
||||
'MoodleSession=%s' % self.__cookies['MoodleSession'].value)
|
||||
if self.__cookies.has_key('MoodleSessionTest'):
|
||||
connection.putheader(
|
||||
'Cookie',
|
||||
'MoodleSessionTest=%s' % self.__cookies['MoodleSessionTest'].value)
|
||||
|
||||
|
||||
def __processCookies(self, headers):
|
||||
if headers.getheader('Set-Cookie'):
|
||||
self.__cookies.load(headers.getheader('Set-Cookie'))
|
||||
|
||||
|
||||
def send_content(self, connection, request_body):
|
||||
connection.putheader("Content-Type", "text/xml")
|
||||
connection.putheader("Content-Length", str(len(request_body)))
|
||||
|
||||
connection.endheaders()
|
||||
if request_body:
|
||||
connection.send(request_body)
|
||||
|
||||
class XmlRpcDataSource:
|
||||
|
||||
def __init__(self, url):
|
||||
self.transport = JspAuthTransport()
|
||||
self.server = xmlrpclib.Server(url, transport = self.transport)
|
||||
self.config = getConfig()
|
||||
self.logger = logging.getLogger('main')
|
||||
|
||||
def get_judge_id(self):
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.get_judge_id()
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_judge_id')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def reset_submits(self, judgeid):
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.reset_submits(judgeid)
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to reset_submits')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_submits(self, judgeid, limit):
|
||||
while True:
|
||||
try:
|
||||
submits = self.server.oj.get_submits(judgeid, limit)
|
||||
for submit in submits:
|
||||
if not isinstance(submit['code'], (str, unicode)):
|
||||
submit['code'] = submit['code'].__str__()
|
||||
return submits
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
time.sleep(self.config.retry_wait)
|
||||
self.logger.exception('Failed to get_submits')
|
||||
|
||||
def get_problem(self, problemid):
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.get_problem(problemid)
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_problem')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_tests(self, problemid, full):
|
||||
while True:
|
||||
try:
|
||||
tests = self.server.oj.get_tests(problemid, full)
|
||||
for test in tests:
|
||||
if not isinstance(test['input'], (str, unicode)):
|
||||
test['input'] = test['input'].__str__()
|
||||
if not isinstance(test['output'], (str, unicode)):
|
||||
test['output'] = test['output'].__str__()
|
||||
self.logger.debug('Got %d test case(s)', len(tests))
|
||||
return tests
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_tests')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_test(self, testid):
|
||||
while True:
|
||||
try:
|
||||
test = self.server.oj.get_gztest(testid)
|
||||
if not isinstance(test['input'], (str, unicode)):
|
||||
test['input'] = test['input'].__str__()
|
||||
if not isinstance(test['output'], (str, unicode)):
|
||||
test['output'] = test['output'].__str__()
|
||||
test['input'] = bz2.decompress(test['input'])
|
||||
test['output'] = bz2.decompress(test['output'])
|
||||
return test
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_tests')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_presetcodes(self, problemid, lang):
|
||||
while True:
|
||||
try:
|
||||
codes = self.server.oj.get_presetcodes(problemid, lang)
|
||||
for code in codes:
|
||||
if not isinstance(code['code'], (str, unicode)):
|
||||
code['code'] = code['code'].__str__()
|
||||
self.logger.debug('Got %d presetcodes', len(codes))
|
||||
return codes
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_presetcodes')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_datafiles(self, problemid):
|
||||
while True:
|
||||
try:
|
||||
files = self.server.oj.get_datafiles(problemid)
|
||||
self.logger.debug('Got %d datafiles', len(files))
|
||||
return files
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_datafiles')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_datafile_data(self, datafileid):
|
||||
while True:
|
||||
try:
|
||||
data = self.server.oj.get_datafile_data(datafileid)
|
||||
return str(data)
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_datafiles')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def update_submit_compilemessage(self, id, compilemsg):
|
||||
compilemsg = xmlrpclib.Binary(compilemsg)
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.update_submit_compilemessage(
|
||||
id, compilemsg)
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to update_submit_compilemessage')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def update_submit_test_results(self, id, results):
|
||||
for r in results:
|
||||
if not isinstance(r['stdout'], (str, unicode)): r['stdout'] = ''
|
||||
if not isinstance(r['stderr'], (str, unicode)): r['stderr'] = ''
|
||||
r['stdout'] = xmlrpclib.Binary(r['stdout'])
|
||||
r['stderr'] = xmlrpclib.Binary(r['stderr'])
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.update_submit_test_results(id, results)
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to update_submit_compilemessage')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def update_submits_status(self, submitids, newstatus):
|
||||
while True:
|
||||
mc = xmlrpclib.MultiCall(self.server)
|
||||
for id in submitids:
|
||||
mc.oj.update_submit_status(id, newstatus)
|
||||
try:
|
||||
return mc()
|
||||
except xmlrpclib.Error, e:
|
||||
raise DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to update_submits_status')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_submit_status(self, sid):
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.get_submit_status(sid)
|
||||
except xmlrpclib.Error, e:
|
||||
return DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_submit_status')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_submit_compilemessage(self, sid):
|
||||
while True:
|
||||
try:
|
||||
msg = self.server.oj.get_submit_compilemessage(sid)
|
||||
if isinstance(msg, (str, unicode)):
|
||||
return msg
|
||||
else:
|
||||
return msg.__str__()
|
||||
except xmlrpclib.Error, e:
|
||||
return DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_submit_status')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
def get_submit(self, sid):
|
||||
while True:
|
||||
try:
|
||||
return self.server.oj.get_submit(sid)
|
||||
except xmlrpclib.Error, e:
|
||||
return DataSourceError(e)
|
||||
except socket.error, e:
|
||||
self.logger.exception('Failed to get_submit')
|
||||
time.sleep(self.config.retry_wait)
|
||||
|
||||
class DataSourceTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
execfile(os.path.join('..', 'testdata', 'test_config.py'))
|
||||
self.config = getConfig()
|
||||
self.datasource = self.config.datasources[0]
|
||||
self.dbname = os.path.join('..', 'testdata', self.config.testdb)
|
||||
|
||||
|
||||
def testGetSubmits(self):
|
||||
submits = self.datasource.get_submits(4)
|
||||
self.assertTrue(len(submits), 4)
|
||||
s = submits[0]
|
||||
self.assertEqual(s.id, '0000000001')
|
||||
self.assertEqual(s.problem_id, '0000000001')
|
||||
self.assertEqual(s.language, 'gcc-3.3')
|
||||
self.assertEqual(s.code, '')
|
||||
s = submits[1]
|
||||
self.assertEqual(s.code, '#include <stdio.h>')
|
||||
|
||||
def testGetSubmit(self):
|
||||
s = self.datasource.get_submit(1)
|
||||
self.assertEqual(s.id, '0000000001')
|
||||
self.assertEqual(s.problem_id, '0000000001')
|
||||
self.assertEqual(s.language, 'gcc-3.3')
|
||||
self.assertEqual(s.code, '')
|
||||
|
||||
def testResetSubmits(self):
|
||||
self.datasource.reset_submits()
|
||||
|
||||
def testGetProblem(self):
|
||||
p = self.datasource.get_problem('0000000001')
|
||||
self.assertEqual(p.id, '0000000001')
|
||||
|
||||
def testGetTests(self):
|
||||
tests = self.datasource.get_tests('0000000001', True)
|
||||
self.assertTrue(len(tests), 2)
|
||||
t = tests[0]
|
||||
self.assertEqual(t.id, '0000000001')
|
||||
self.assertEqual(t.problem_id, '0000000001')
|
||||
self.assertEqual(t.timemodified, 0)
|
||||
self.assertEqual(t.input, '1 2')
|
||||
self.assertEqual(t.output, '3')
|
||||
self.assertEqual(t.timelimit, 10)
|
||||
self.assertEqual(t.memlimit, 1024 * 1024)
|
||||
|
||||
def testGetTest(self):
|
||||
t = self.datasource.get_test('0000000001', False)
|
||||
self.assertEqual(t.id, '0000000001')
|
||||
self.assertEqual(t.problem_id, '0000000001')
|
||||
self.assertEqual(t.timemodified, 0)
|
||||
self.assertEqual(t.input, '1 2')
|
||||
self.assertEqual(t.output, '3')
|
||||
self.assertEqual(t.timelimit, 10)
|
||||
self.assertEqual(t.memlimit, 1024 * 1024)
|
||||
|
||||
def testUpdateSubmitCompileMessage(self):
|
||||
self.datasource.update_submit_compilemessage('0000000001', 'hello')
|
||||
msg = self.datasource.get_submit_compilemessage('0000000001')
|
||||
self.assertEqual(msg, 'hello')
|
||||
self.datasource.update_submit_compilemessage('0000000001', '')
|
||||
|
||||
def testUpdateSubmitStatus(self):
|
||||
self.datasource.update_submits_status(('0000000004', '0000000001'),
|
||||
'compiling')
|
||||
s = self.datasource.get_submit_status('0000000001')
|
||||
self.assertEqual(s, 'compiling')
|
||||
s = self.datasource.get_submit_status('0000000004')
|
||||
self.assertEqual(s, 'compiling')
|
||||
s = self.datasource.get_submit_status('0000000002')
|
||||
self.assertEqual(s, 'new')
|
||||
self.datasource.update_submits_status(('0000000004', '0000000001'), 'new')
|
||||
s = self.datasource.get_submit_status('0000000001')
|
||||
self.assertEqual(s, 'new')
|
||||
|
||||
def testUpdateSubmitTestResults(self):
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
conn = sqlite.connect(self.dbname)
|
||||
cur = conn.cursor()
|
||||
cur.execute('DELETE FROM submit_test_result')
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
id = '0000000001'
|
||||
r1 = { 'test_id' : '0000000001',
|
||||
'stdout' : '3',
|
||||
'stderr' : '',
|
||||
'exitcode' : 0,
|
||||
'signal' : 0,
|
||||
'timeused' : 0,
|
||||
'memused' : 0}
|
||||
r2 = { 'test_id' : '0000000002',
|
||||
'stdout' : '4',
|
||||
'stderr' : 'abc',
|
||||
'exitcode' : 0,
|
||||
'signal' : 11,
|
||||
'timeused' : 1,
|
||||
'memused' : 2}
|
||||
self.datasource.update_submit_test_results(id, (r1, r2))
|
||||
|
||||
conn = sqlite.connect(self.dbname)
|
||||
cur = conn.cursor()
|
||||
cur.execute('SELECT * FROM submit_test_result')
|
||||
rows = cur.fetchall()
|
||||
self.assertEqual(len(rows), 2)
|
||||
cur.close()
|
||||
conn.close()
|
||||
row = rows[0]
|
||||
self.assertEqual(row[1], 1) #sid
|
||||
self.assertEqual(row[2], 1) #tid
|
||||
self.assertEqual(row[3], '3') #stdout
|
||||
self.assertEqual(row[4], '') #stderr
|
||||
self.assertEqual(row[5], 0) #exitcode
|
||||
self.assertEqual(row[6], 0) #signal
|
||||
self.assertEqual(row[7], 0) #timeused
|
||||
self.assertEqual(row[8], 0) #memused
|
||||
row = rows[1]
|
||||
self.assertEqual(row[1], 1) #sid
|
||||
self.assertEqual(row[2], 2) #tid
|
||||
self.assertEqual(row[3], '4') #stdout
|
||||
self.assertEqual(row[4], 'abc') #stderr
|
||||
self.assertEqual(row[5], 0) #exitcode
|
||||
self.assertEqual(row[6], 11) #signal
|
||||
self.assertEqual(row[7], 1) #timeused
|
||||
self.assertEqual(row[8], 2) #memused
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/datasource.pyc
Normal file
BIN
python/datasource.pyc
Normal file
Binary file not shown.
69
python/engine.py
Executable file
69
python/engine.py
Executable file
@@ -0,0 +1,69 @@
|
||||
|
||||
import logging, os, thread, threading, time, Queue
|
||||
from engineconfig import getConfig
|
||||
class JudgeEngine:
|
||||
|
||||
def __init__(self):
|
||||
self.quit_event = threading.Event()
|
||||
self.test_queue = Queue.Queue()
|
||||
|
||||
self.config = getConfig()
|
||||
self.quit_event.clear()
|
||||
self.logger = logging.getLogger('main')
|
||||
|
||||
def run(self):
|
||||
# one thread mode is good for debugging
|
||||
if self.config.test_threads == 1:
|
||||
thread.start_new_thread(self.transport, ())
|
||||
self.test()
|
||||
else:
|
||||
for i in range(self.config.test_threads):
|
||||
thread.start_new_thread(self.test, ())
|
||||
self.transport()
|
||||
|
||||
for ds in self.config.datasources:
|
||||
ds.reset_submits()
|
||||
self.logger.info('main thread quit')
|
||||
return
|
||||
|
||||
def transport(self):
|
||||
total = 0
|
||||
while not self.quit_event.isSet():
|
||||
self.logger.info(
|
||||
"%d submit in test queue, %d processed" % \
|
||||
(self.test_queue.qsize(), total))
|
||||
c = 16 - self.test_queue.qsize()
|
||||
if c > 0:
|
||||
submits = self.config.datasources[0].get_submits(c)
|
||||
for submit in submits:
|
||||
self.logger.info('Add submit %s to queue' % submit.id)
|
||||
self.test_queue.put(submit)
|
||||
total = total + len(submits)
|
||||
time.sleep(self.config.fetch_interval)
|
||||
self.logger.info("fetch thread quit")
|
||||
return
|
||||
|
||||
def test(self):
|
||||
while not self.quit_event.isSet():
|
||||
#pdb.set_trace()
|
||||
try:
|
||||
submit = self.test_queue.get(True, self.config.fetch_interval)
|
||||
except Queue.Empty:
|
||||
continue
|
||||
tester = self.config.get_tester(submit.language)
|
||||
if tester:
|
||||
tester.test(submit)
|
||||
else:
|
||||
submit.set_status('compile_failed')
|
||||
submit.set_compilemessage('No compiler specified')
|
||||
self.logger.info("test thread quit")
|
||||
return
|
||||
|
||||
def quit(self, signum, frame):
|
||||
self.logger.info('signal receive: %d' % signum)
|
||||
self.quit_event.set()
|
||||
return
|
||||
|
||||
class JudgeError(Exception): pass
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/engine.pyc
Normal file
BIN
python/engine.pyc
Normal file
Binary file not shown.
239
python/engineconfig.py
Executable file
239
python/engineconfig.py
Executable file
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python2.6
|
||||
|
||||
import string
|
||||
from string import split
|
||||
from os import path
|
||||
from Queue import Queue
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
class EngineConfig:
|
||||
|
||||
def __init__(self):
|
||||
self.profile = 'default'
|
||||
self.test_threads = 2
|
||||
self.retry = 10
|
||||
self.retry_wait = 5
|
||||
self.datasources = []
|
||||
self.fetch_interval = 1
|
||||
self.mips = 1000
|
||||
self.languages = {}
|
||||
self.maxmem = 65536
|
||||
self.maxtime = 300
|
||||
self.compile_timelimit = 30
|
||||
self.output_size_limit = 1024 * 1024 * 2
|
||||
self.output_sendback_size_limit = 1024 * 64
|
||||
self.logdir = 'log'
|
||||
self.debug = False
|
||||
self.judgescript_wait = 10
|
||||
self.python = '/usr/bin/python3'
|
||||
self.no_cleanup = False
|
||||
self.datadir = path.join('data', self.profile)
|
||||
self.rundir_root = '/var/lib/bitoj'
|
||||
|
||||
self.runas = Queue()
|
||||
self.setup_testers()
|
||||
|
||||
# Init runas with ojrun?? users
|
||||
pa = Popen(['getent', 'passwd'], stdout=PIPE)
|
||||
pb = Popen(['awk', '-F:', '/^ojrun[0-9][0-9]:/{print $1}'],
|
||||
stdin=pa.stdout, stdout=PIPE)
|
||||
output = pb.communicate()[0]
|
||||
if output:
|
||||
for user in string.split(output, '\n'):
|
||||
user = string.strip(user)
|
||||
if user: self.runas.put(user)
|
||||
pa.wait()
|
||||
pb.wait()
|
||||
|
||||
def add_tester(self, profile, tester):
|
||||
self.languages[profile] = tester
|
||||
|
||||
def get_tester(self, profile):
|
||||
if self.languages.has_key(profile):
|
||||
return self.languages[profile]
|
||||
|
||||
def add_datasource(self, ds):
|
||||
from datasource import DataSource
|
||||
self.datasources.append(DataSource(ds))
|
||||
|
||||
def add_xmlrpc_datasource(self, url):
|
||||
from datasource import XmlRpcDataSource
|
||||
self.add_datasource(XmlRpcDataSource(url))
|
||||
|
||||
def setup_testers(self):
|
||||
|
||||
default_compileguard = (
|
||||
'<judgehome>/scripts/compile-guard', ' <datadir>'
|
||||
)
|
||||
default_runguard = split(
|
||||
'/usr/bin/sudo -u <user> <judgehome>/scripts/binary-guard ' +
|
||||
'-e <extraproc> ' +
|
||||
'-t <timelimit> -T 5 -m <maxmem> -d <rundir> -o <statfile> -p -x',
|
||||
' '
|
||||
)
|
||||
maxmem_runguard = split(
|
||||
'/usr/bin/sudo -u <user> <judgehome>/scripts/binary-guard ' +
|
||||
'-e <extraproc> ' +
|
||||
'-t <timelimit> -T 5 -m <maxmem> -d <rundir> -o <statfile> -p -x',
|
||||
' '
|
||||
)
|
||||
java_runguard = split(
|
||||
'/usr/bin/sudo -u <user> ' +
|
||||
'<judgehome>/scripts/java-guard -t <timelimit> -T 5 -m 262144 ' +
|
||||
'-e <extraproc> ' +
|
||||
'-d <rundir> -o <statfile> -p -x', ' '
|
||||
)
|
||||
python_runguard = split(
|
||||
'<judgehome>/scripts/python-guardd ' +
|
||||
'-t <timelimit> -T 5 -m <maxmem> -d <rundir> -o <statfile> -p -x',
|
||||
' '
|
||||
)
|
||||
mono_runguard = split(
|
||||
'/usr/bin/sudo -u <user> ' +
|
||||
'<judgehome>/scripts/mono-guard -t <timelimit> -T 5 -m <maxmem> ' +
|
||||
'-e <extraproc> ' +
|
||||
'-d <rundir> -o <statfile> -p -x', ' '
|
||||
)
|
||||
bash_runguard = split(
|
||||
'/usr/bin/sudo -u <user> <judgehome>/scripts/bash-guard ' +
|
||||
'-e <extraproc> ' +
|
||||
'-t <timelimit> -T 5 -m <maxmem> -d <rundir> -o <statfile> -p -x',
|
||||
' '
|
||||
)
|
||||
default_compare = (
|
||||
'<judgehome>/scripts/compare-guard', '<language>', '<codefile>',
|
||||
'<stdinfile>', '<stdoutfile>', '<resultfile>'
|
||||
)
|
||||
|
||||
# Options for testers
|
||||
from tester import SimpleTester, ComboTester
|
||||
cbc = SimpleTester(source = 'main.c',
|
||||
target = 'main',
|
||||
compile = ('<judgehome>/scripts/gcc-3.3-bc',),
|
||||
run = ('<datadir>/main', ),
|
||||
runenv = {
|
||||
'GCC_BOUNDS_OPTS' : '-no-message -no-statistics -no-check-mmap',
|
||||
},
|
||||
basemem = {'Data' : 48, 'Stack' : 84 },
|
||||
compileguard = default_compileguard,
|
||||
runguard = maxmem_runguard,
|
||||
)
|
||||
|
||||
cnobc = SimpleTester(
|
||||
source = 'main.c', target = 'main',
|
||||
compile = ('<judgehome>/scripts/gcc-3.3-nobc',),
|
||||
run = ('<datadir>/main', ),
|
||||
runenv = {},
|
||||
basemem = {'Data' : 28, 'Stack' : 84 },
|
||||
compileguard = default_compileguard,
|
||||
runguard = default_runguard,
|
||||
)
|
||||
|
||||
gcc33 = ComboTester()
|
||||
gcc33.add_tester(cbc, has_timelimit = False, has_memlimit = False,
|
||||
check_result = True, is_lasttest = False)
|
||||
gcc33.add_tester(cnobc, has_timelimit = True, has_memlimit = True,
|
||||
check_result = True, is_lasttest = True)
|
||||
gcc33.comparecmd = default_compare
|
||||
gcc33.source = 'main.c'
|
||||
|
||||
gxx33 = SimpleTester(
|
||||
source = 'main.cpp', target = 'main',
|
||||
compile = ('<judgehome>/scripts/g++-3.3',),
|
||||
run = ('<datadir>/main',),
|
||||
runenv = {},
|
||||
basemem = {'Data' : 52, 'Stack' : 84 },
|
||||
compileguard = default_compileguard,
|
||||
runguard = default_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
j2se15 = SimpleTester(
|
||||
source = 'Main.java', target = 'Main.class',
|
||||
compile = ('<judgehome>/scripts/javac-1.5',),
|
||||
run = split('/usr/bin/java -cp <datadir> -Xms8M -Xmx64M Main'),
|
||||
runenv = {},
|
||||
basemem = {'RSS' : 7560 },
|
||||
baseproc = 8,
|
||||
compileguard = default_compileguard,
|
||||
runguard = java_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
j2se16 = SimpleTester(
|
||||
source = 'Main.java', target = 'Main.class',
|
||||
compile = ('<judgehome>/scripts/javac-1.6',),
|
||||
run = split('/usr/bin/java -cp <datadir> -Xms8M -Xmx64M Main'),
|
||||
runenv = {},
|
||||
basemem = {'RSS' : 7560 },
|
||||
baseproc = 8,
|
||||
compileguard = default_compileguard,
|
||||
runguard = java_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
fpc = SimpleTester(
|
||||
source = 'main.pas', target = 'main',
|
||||
compile = ('<judgehome>/scripts/fpc',),
|
||||
run = ('<datadir>/main', ),
|
||||
runenv = {},
|
||||
basemem = {'Data' : 32, 'Stack' : 84 },
|
||||
compileguard = default_compileguard,
|
||||
runguard = default_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
python3 = SimpleTester(
|
||||
source = 'main.py', target = 'main.py',
|
||||
compile = ('/bin/true',),
|
||||
run = ('/usr/bin/python3', '<datadir>/main.py'),
|
||||
runenv = {},
|
||||
basemem = {'RSS' : 2048 },
|
||||
compileguard = (),
|
||||
runguard = python_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
gmcs20 = SimpleTester(
|
||||
source = 'main.cs', target = 'main.exe',
|
||||
compile = ('<judgehome>/scripts/gmcs-2.0',),
|
||||
run = ('/usr/bin/mono', '<datadir>/main.exe'),
|
||||
runenv = { 'MONO_SHARED_DIR' : '<datadir>' },
|
||||
basemem = {'RSS' : 8192 },
|
||||
compileguard = (),
|
||||
runguard = mono_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
bash3 = SimpleTester(
|
||||
source = 'main.sh', target = 'main.sh',
|
||||
compile = ('/bin/true',),
|
||||
run = ('/bin/bash', '<datadir>/main.sh'),
|
||||
runenv = {},
|
||||
basemem = {'RSS' : 2048 },
|
||||
extraproc = 3,
|
||||
compileguard = (),
|
||||
runguard = bash_runguard,
|
||||
comparecmd = default_compare,
|
||||
)
|
||||
|
||||
self.add_tester('gcc-3.3', gcc33)
|
||||
self.add_tester('gcc-3.3-nobc', cnobc)
|
||||
self.add_tester('gcc-3.3-bc', cbc)
|
||||
self.add_tester('g++-3.3', gxx33)
|
||||
self.add_tester('java-1.5', j2se15)
|
||||
self.add_tester('java-1.6', j2se16)
|
||||
self.add_tester('fpc-2.2', fpc)
|
||||
self.add_tester('python3', python3)
|
||||
self.add_tester('gmcs-2.0', gmcs20)
|
||||
self.add_tester('bash-3', bash3)
|
||||
|
||||
config = None
|
||||
|
||||
def getConfig():
|
||||
global config
|
||||
if not config:
|
||||
config = EngineConfig()
|
||||
return config
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/engineconfig.pyc
Normal file
BIN
python/engineconfig.pyc
Normal file
Binary file not shown.
271
python/entity.py
Executable file
271
python/entity.py
Executable file
@@ -0,0 +1,271 @@
|
||||
|
||||
import string, os, stat, threading, logging, bz2
|
||||
import unittest
|
||||
|
||||
from engineconfig import getConfig
|
||||
from judgescript import InternalJudge, ExternalJudge
|
||||
|
||||
class Problem:
|
||||
|
||||
def __init__(self, datasource, row):
|
||||
self.tests = None
|
||||
self.presetcodes = {}
|
||||
self.datafiles = None
|
||||
|
||||
self.datasource = datasource
|
||||
self.id = row['id']
|
||||
self.timemodified = row['timemodified']
|
||||
self.vcode = row['validator_code']
|
||||
if not isinstance(self.vcode, (str, unicode)):
|
||||
self.vcode = self.vcode.__str__()
|
||||
self.vtype = row['validator_type']
|
||||
self.vlang = row['validator_lang']
|
||||
self.gcode = row['generator_code']
|
||||
if not isinstance(self.gcode, (str, unicode)):
|
||||
self.gcode = self.vcode.__str__()
|
||||
self.gtype = row['generator_type']
|
||||
self.standard_code = row['standard_code']
|
||||
|
||||
if row.has_key('input_filename') and row['input_filename']:
|
||||
self.input_filename = row['input_filename']
|
||||
else:
|
||||
self.input_filename = None
|
||||
if row.has_key('output_filename') and row['output_filename']:
|
||||
self.output_filename = row['output_filename']
|
||||
else:
|
||||
self.output_filename = None
|
||||
|
||||
def get_judge_script(self):
|
||||
if self.vtype == 'comparetext':
|
||||
return InternalJudge()
|
||||
elif self.vtype == 'comparetextwithpe':
|
||||
return InternalJudge(allowpe = True)
|
||||
elif self.vtype == 'comparefile':
|
||||
code = open('scripts/compare-file.py').read()
|
||||
return ExternalJudge(self.id, 'python-2.5', code)
|
||||
elif self.vtype == 'customized':
|
||||
return ExternalJudge(self.id, self.vlang, self.vcode)
|
||||
|
||||
def get_testcases(self):
|
||||
if not self.tests:
|
||||
self.tests = self.datasource.get_tests(self.id, False)
|
||||
for testcase in self.tests:
|
||||
testcase.problem = self
|
||||
return self.tests
|
||||
|
||||
def get_input_filename(self):
|
||||
return self.input_filename
|
||||
|
||||
def get_output_filename(self):
|
||||
return self.output_filename
|
||||
|
||||
def get_presetcodes(self, lang):
|
||||
if not self.presetcodes.has_key(lang):
|
||||
codes = self.datasource.get_presetcodes(self.id, lang)
|
||||
for code in codes: code.problem = self
|
||||
self.presetcodes[lang] = codes
|
||||
return self.presetcodes[lang]
|
||||
|
||||
def get_datafiles(self):
|
||||
if not self.datafiles:
|
||||
self.datafiles = self.datasource.get_datafiles(self.id)
|
||||
return self.datafiles
|
||||
|
||||
class Submit:
|
||||
|
||||
def __init__(self, datasource, row):
|
||||
self.datasource = datasource
|
||||
self.problem = None
|
||||
self.id = row['id']
|
||||
self.problem_id = row['problem_id']
|
||||
self.language = row['language']
|
||||
self.code = row['code']
|
||||
|
||||
def get_problem(self):
|
||||
if not self.problem:
|
||||
self.problem = self.datasource.get_problem(self.problem_id)
|
||||
return self.problem
|
||||
|
||||
def get_testcases(self):
|
||||
return self.get_problem().get_testcases()
|
||||
|
||||
def get_judge_script(self):
|
||||
return self.get_problem().get_judge_script()
|
||||
|
||||
def get_input_filename(self):
|
||||
return self.get_problem().get_input_filename()
|
||||
|
||||
def get_output_filename(self):
|
||||
return self.get_problem().get_output_filename()
|
||||
|
||||
def get_presetcodes(self):
|
||||
return self.get_problem().get_presetcodes(self.language)
|
||||
|
||||
def set_compilemessage(self, msg):
|
||||
return self.datasource.update_submit_compilemessage(self.id, msg)
|
||||
|
||||
def set_status(self, newstatus):
|
||||
return self.datasource.update_submits_status(self.id, newstatus)
|
||||
|
||||
def get_status(self):
|
||||
return self.datasource.get_submit_status(self.id)
|
||||
|
||||
def get_compilemessage(self):
|
||||
return self.datasource.get_submit_compilemessage(self.id)
|
||||
|
||||
def update_test_results(self, results):
|
||||
config = getConfig()
|
||||
|
||||
newresults = []
|
||||
for r in results:
|
||||
# stdout
|
||||
f = file(r[4], 'r')
|
||||
r[4] = f.read(config.output_sendback_size_limit)
|
||||
f.close()
|
||||
# stderr
|
||||
f = file(r[5], 'r')
|
||||
r[5] = f.read(config.output_sendback_size_limit)
|
||||
f.close()
|
||||
|
||||
# strip stdout and stderr send back to datasource
|
||||
# preventing post data too big
|
||||
if len(r[4]) > config.output_sendback_size_limit:
|
||||
r[4] = r[4][0:config.output_sendback_size_limit]
|
||||
if len(r[5]) > config.output_sendback_size_limit:
|
||||
r[5] = r[5][0:config.output_sendback_size_limit]
|
||||
|
||||
newresults.append({ 'test_id' : r[0],
|
||||
'judge_result' : r[1],
|
||||
'exitcode' : r[2],
|
||||
'signal' : r[3],
|
||||
'stdout' : r[4],
|
||||
'stderr' : r[5],
|
||||
'timeused' : r[6],
|
||||
'memused' : r[7] })
|
||||
|
||||
return self.datasource.update_submit_test_results(self.id, newresults)
|
||||
|
||||
class TestCase:
|
||||
|
||||
write_lock = threading.Lock()
|
||||
|
||||
def __init__(self, datasource, row):
|
||||
config = getConfig()
|
||||
logger = logging.getLogger('main')
|
||||
self.id = row['id']
|
||||
self.problem_id = row['problem_id']
|
||||
self.timemodified = row['timemodified']
|
||||
#self.input = string.replace(row['input'], '\r\n', '\n')
|
||||
#self.output = string.replace(row['output'], '\r\n', '\n')
|
||||
self.timelimit = row['timelimit']
|
||||
if not self.timelimit:
|
||||
self.timelimit = 1
|
||||
self.memlimit = row['memlimit']
|
||||
if not self.memlimit:
|
||||
self.memlimit = config.maxmem
|
||||
if row.has_key('nproc') and row['nproc']:
|
||||
self.nproc = string.atoi(row['nproc'])
|
||||
else:
|
||||
self.nproc = 0
|
||||
|
||||
config = getConfig()
|
||||
testdir = os.path.join(config.datadir, 'testcase')
|
||||
if not os.path.exists(testdir): os.mkdir(testdir)
|
||||
|
||||
self.infile = os.path.join(testdir, self.id + '.in')
|
||||
self.outfile = os.path.join(testdir, self.id + '.out')
|
||||
|
||||
TestCase.write_lock.acquire()
|
||||
|
||||
if os.path.exists(self.infile):
|
||||
inmtime = os.stat(self.infile)[stat.ST_MTIME]
|
||||
else:
|
||||
inmtime = 0
|
||||
if os.path.exists(self.outfile):
|
||||
outmtime = os.stat(self.outfile)[stat.ST_MTIME]
|
||||
else:
|
||||
outmtime = 0
|
||||
logger.debug("inmtime=%d outmtime=%d timemodified=%d" % (inmtime, outmtime, self.timemodified))
|
||||
if inmtime <= self.timemodified or outmtime <= self.timemodified:
|
||||
logger.debug('Creating input/output file %s and %s' % (self.infile, self.outfile))
|
||||
row = datasource.get_test(self.id)
|
||||
input = string.replace(row['input'], '\r\n', '\n')
|
||||
output = string.replace(row['output'], '\r\n', '\n')
|
||||
|
||||
f = file(self.infile, 'w')
|
||||
f.write(input)
|
||||
if len(input) > 0 and input[-1] != '\n': f.write('\n')
|
||||
f.close()
|
||||
|
||||
f = file(self.outfile, 'w')
|
||||
f.write(output)
|
||||
if len(output) > 0 and output[-1] != '\n': f.write('\n')
|
||||
f.close()
|
||||
logger.debug('Finished')
|
||||
else:
|
||||
logger.debug('Skip input/output file creation')
|
||||
|
||||
TestCase.write_lock.release()
|
||||
|
||||
def get_input_file(self):
|
||||
return self.infile
|
||||
|
||||
def get_output_file(self):
|
||||
return self.outfile
|
||||
|
||||
class PresetCode:
|
||||
|
||||
def __init__(self, datasource, row):
|
||||
self.datasource = datasource
|
||||
self.problem = None
|
||||
|
||||
self.id = row['id']
|
||||
self.name = row['name']
|
||||
self.code = row['code']
|
||||
self.isheader = row['isheader']
|
||||
|
||||
class DataFile:
|
||||
|
||||
write_lock = threading.Lock()
|
||||
|
||||
def __init__(self, datasource, row):
|
||||
self.datasource = datasource
|
||||
self.problem = None
|
||||
|
||||
# Data passed from datasource
|
||||
self.id = row['id']
|
||||
self.problem_id = row['problem_id']
|
||||
self.filename = row['filename']
|
||||
self.type = row['type']
|
||||
self.timemodified = row['timemodified']
|
||||
|
||||
# Save datafile
|
||||
config = getConfig()
|
||||
testdir = os.path.join(config.datadir, 'testcase')
|
||||
if not os.path.exists(testdir): os.mkdir(testdir)
|
||||
self.absolute_path = os.path.join(
|
||||
testdir, self.problem_id + '.' + self.filename)
|
||||
|
||||
DataFile.write_lock.acquire()
|
||||
|
||||
mtime = 0
|
||||
if os.path.exists(self.absolute_path):
|
||||
mtime = os.stat(self.absolute_path)[stat.ST_MTIME]
|
||||
if mtime < self.timemodified:
|
||||
data = datasource.get_datafile_data(self.id)
|
||||
data = bz2.decompress(data)
|
||||
if self.type == 'text':
|
||||
f = open(self.absolute_path, 'w')
|
||||
f.write(string.replace(data, '\r\n', '\n'))
|
||||
f.close()
|
||||
else:
|
||||
f = open(self.absolute_path, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
DataFile.write_lock.release()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/entity.pyc
Normal file
BIN
python/entity.pyc
Normal file
Binary file not shown.
286
python/judgescript.py
Executable file
286
python/judgescript.py
Executable file
@@ -0,0 +1,286 @@
|
||||
|
||||
import string, os, time, tempfile, logging
|
||||
import unittest
|
||||
|
||||
from engineconfig import getConfig
|
||||
|
||||
class InternalJudge:
|
||||
|
||||
def __init__(self, allowpe = False):
|
||||
self.logger = logging.getLogger('main')
|
||||
self.allowpe = allowpe
|
||||
|
||||
def judge(self, sid, tid, tin, tout, result, errfile, rundir = None):
|
||||
"""Judge if the result correct.
|
||||
|
||||
@param sid String submit id
|
||||
@param tid String testcase id
|
||||
@param tin String filename of the standard input
|
||||
@param tout String filename of the standard output
|
||||
@param result String filename of the result
|
||||
"""
|
||||
|
||||
return self.compare_file(tin, tout, result, self.allowpe)
|
||||
|
||||
def compare_file(self, input, output, result, allowpe):
|
||||
fo = file(output, 'rb'); fr = file(result, 'rb')
|
||||
|
||||
if not allowpe:
|
||||
r = 'AC'
|
||||
while r == 'AC':
|
||||
so = fo.read(8192); sr = fr.read(8192)
|
||||
if so == '' and sr == '': break
|
||||
if so != sr: r = 'WA'
|
||||
else:
|
||||
so = fo.read(); sr = fr.read()
|
||||
r = self.compare_string(so, sr)
|
||||
|
||||
fo.close(); fr.close()
|
||||
return r
|
||||
|
||||
def compare_string(self, output, result):
|
||||
if output == result: return 'AC'
|
||||
|
||||
outnum = ''
|
||||
retnum = ''
|
||||
for c in output:
|
||||
if c in string.digits: outnum += c
|
||||
for c in result:
|
||||
if c in string.digits: retnum += c
|
||||
self.logger.debug('numbers in output: %s' % outnum)
|
||||
self.logger.debug('numbers in result: %s' % retnum)
|
||||
if len(outnum) > 0 and len(retnum) > 0 and outnum == retnum:
|
||||
return 'PE'
|
||||
|
||||
return 'WA'
|
||||
|
||||
class ExternalJudge:
|
||||
|
||||
def __init__(self, problemid, vlang, vcode):
|
||||
"""Constructor.
|
||||
|
||||
@param pid String problem id
|
||||
@param vlang String validator language
|
||||
@param vcode String validator code
|
||||
"""
|
||||
self.config = getConfig()
|
||||
self.logger = logging.getLogger('main')
|
||||
self.problemid = problemid
|
||||
self.lang = vlang
|
||||
self.code = vcode
|
||||
|
||||
# save validator code
|
||||
tester = self.config.get_tester(self.lang)
|
||||
datadir = os.path.join(self.config.datadir, 'validator', problemid)
|
||||
if not os.path.exists(datadir):
|
||||
os.makedirs(datadir)
|
||||
self.comparecmd = tester.comparecmd
|
||||
self.codefile = os.path.abspath(os.path.join(datadir, tester.source))
|
||||
f = file(self.codefile, 'w')
|
||||
f.write(string.replace(vcode, '\r\n', '\n'))
|
||||
if len(vcode) > 0 and vcode[-1] != '\n': f.write('\n')
|
||||
f.close()
|
||||
|
||||
self.logger.debug("Save validator code as %s" % self.codefile)
|
||||
|
||||
def judge(self, sid, tid, tin, tout, result, errfile, rundir = None):
|
||||
"""Judge if the result correct.
|
||||
|
||||
@param sid String submit id
|
||||
@param tid String testcase id
|
||||
@param tin String filename of the standard input
|
||||
@param tout String filename of the standard output
|
||||
@param result String filename of the result
|
||||
@param rundir String in which dir the judge script should be started
|
||||
"""
|
||||
|
||||
rfiledir = os.path.join(self.config.datadir, sid)
|
||||
rfile = os.path.join(rfiledir, tid + '.rst')
|
||||
if not os.path.exists(rfiledir):
|
||||
os.mkdir(rfiledir)
|
||||
|
||||
cmd = []
|
||||
for s in self.comparecmd:
|
||||
s = s.replace('<judgehome>', self.config.judgehome)
|
||||
s = s.replace('<language>', self.lang)
|
||||
s = s.replace('<codefile>', self.codefile)
|
||||
s = s.replace('<stdinfile>', tin)
|
||||
s = s.replace('<stdoutfile>', tout)
|
||||
s = s.replace('<resultfile>', result)
|
||||
cmd.append(s)
|
||||
self.logger.debug("Run validator as %s" % cmd.__str__())
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
os.close(1)
|
||||
os.open(rfile, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0666)
|
||||
os.close(2)
|
||||
os.open(errfile, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0666)
|
||||
|
||||
if rundir: os.chdir(rundir)
|
||||
os.execv(cmd[0], cmd)
|
||||
print 'WA'
|
||||
|
||||
remaintime = self.config.judgescript_wait
|
||||
pid1 = 0
|
||||
while remaintime > 0:
|
||||
pid1, status = os.waitpid(pid, os.WNOHANG)
|
||||
if pid1 > 0:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
remaintime -= 0.1
|
||||
|
||||
while pid1 == 0:
|
||||
try:
|
||||
os.kill(pid, 9)
|
||||
except os.OSError, e:
|
||||
pass
|
||||
pid1, status = os.waitpid(pid, os.WNOHANG)
|
||||
|
||||
f = file(rfile, 'r')
|
||||
ret = string.strip(f.readline())
|
||||
f.close()
|
||||
if ret != 'AC' and ret != 'PE': ret = 'WA'
|
||||
return ret
|
||||
|
||||
|
||||
class InternalJudgeTest(unittest.TestCase):
|
||||
"""Internal Judge Test Case."""
|
||||
|
||||
def testWithoutPE(self):
|
||||
ij = InternalJudge()
|
||||
output = 'hello world\n'
|
||||
result = 'hello world\n'
|
||||
self.assertEqual(ij.compare_string(output, result), 'AC')
|
||||
output = 'hello world'
|
||||
self.assertEqual(ij.compare_string(output, result), 'WA')
|
||||
result = 'hello world'
|
||||
self.assertEqual(ij.compare_string(output, result), 'AC')
|
||||
result = 'hello world '
|
||||
self.assertEqual(ij.compare_string(output, result), 'WA')
|
||||
|
||||
def testWithPE(self):
|
||||
ij = InternalJudge(True)
|
||||
input = ''
|
||||
output = 'hello world\n'
|
||||
result = 'hello world\n'
|
||||
self.assertEqual(ij.compare_string(output, result), 'AC')
|
||||
result = 'hello worl\n'
|
||||
self.assertEqual(ij.compare_string(output, result), 'WA')
|
||||
output = '1 2 3 4 5\n'
|
||||
result = '1\n2\n3\n4\n5\n'
|
||||
self.assertEqual(ij.compare_string(output, result), 'PE')
|
||||
result = '12345\n'
|
||||
self.assertEqual(ij.compare_string(output, result), 'PE')
|
||||
result = '1 2 3 4 5'
|
||||
self.assertEqual(ij.compare_string(output, result), 'PE')
|
||||
|
||||
class ExternalJudgeTest(unittest.TestCase):
|
||||
"""External Judge Test Case."""
|
||||
|
||||
def setUp(self):
|
||||
config = getConfig()
|
||||
config.judgehome = '../'
|
||||
config.datadir = os.path.join('..', 'testdata')
|
||||
|
||||
self.tin = tempfile.NamedTemporaryFile('w')
|
||||
self.tin.write('3')
|
||||
self.tin.flush()
|
||||
self.tout = tempfile.NamedTemporaryFile('w')
|
||||
self.tout.write('3')
|
||||
self.tout.flush()
|
||||
self.rst = tempfile.NamedTemporaryFile('w')
|
||||
self.rst.write('6')
|
||||
self.rst.flush()
|
||||
self.err = tempfile.NamedTemporaryFile('w')
|
||||
self.err.close()
|
||||
self.tin.close()
|
||||
self.tout.close()
|
||||
self.rst.close()
|
||||
|
||||
def testGCC33(self):
|
||||
code = """
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("AC\\n");
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'gcc-3.3', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testGPP33(self):
|
||||
code = """
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
int main()
|
||||
{
|
||||
cout << "AC" << endl;
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'g++-3.3', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testFPC(self):
|
||||
code = """
|
||||
program main;
|
||||
|
||||
begin
|
||||
write('AC');
|
||||
end.
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'fpc-2.2', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testJava(self):
|
||||
code = """
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("AC");
|
||||
}
|
||||
}
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'java-1.5', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
self.j = ExternalJudge('p1', 'java-1.6', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testPython(self):
|
||||
code = """public class Main
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
System.Console.WriteLine("AC");
|
||||
}
|
||||
}
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'gmcs-2.0', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testPython(self):
|
||||
code = """#!/usr/bin/env python
|
||||
|
||||
print 'AC'
|
||||
"""
|
||||
self.j = ExternalJudge('p1', 'python-2.5', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
def testBash(self):
|
||||
code = """echo AC"""
|
||||
self.j = ExternalJudge('p1', 'bash-3', code)
|
||||
x = self.j.judge('s1', 't1', self.tin.name, self.tout.name, self.rst.name, self.err.name)
|
||||
self.assertEquals('AC', x)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/judgescript.pyc
Normal file
BIN
python/judgescript.pyc
Normal file
Binary file not shown.
21
python/ojunit.py
Executable file
21
python/ojunit.py
Executable file
@@ -0,0 +1,21 @@
|
||||
import unittest, logging, os, sys
|
||||
|
||||
from engineconfig import getConfig
|
||||
|
||||
class OJTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
logger = logging.getLogger('main')
|
||||
hdlr = logging.StreamHandler()
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
||||
hdlr.setFormatter(formatter)
|
||||
logger.addHandler(hdlr)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
execfile(os.path.join('..', 'testdata', 'test_config.py'))
|
||||
self.config = getConfig()
|
||||
self.ds = self.config.datasources[0]
|
||||
self.dbname = os.path.join('..', 'testdata', self.config.testdb)
|
||||
|
||||
self.logger = logger
|
||||
BIN
python/ojunit.pyc
Normal file
BIN
python/ojunit.pyc
Normal file
Binary file not shown.
829
python/tester.py
Executable file
829
python/tester.py
Executable file
@@ -0,0 +1,829 @@
|
||||
|
||||
import math, os, resource, signal, string, sys, threading, logging, time, pickle
|
||||
import shutil, Queue
|
||||
import unittest
|
||||
import pdb
|
||||
from engineconfig import getConfig
|
||||
from ojunit import *
|
||||
|
||||
class TesterBase:
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger('main')
|
||||
|
||||
def get_datadir(self, submit):
|
||||
config = getConfig()
|
||||
|
||||
# get datadir
|
||||
datadir = os.path.abspath(config.datadir)
|
||||
if not os.path.exists(datadir): os.mkdir(datadir)
|
||||
|
||||
# get filenames
|
||||
datadir = os.path.join(datadir, submit.id)
|
||||
if not os.path.exists(datadir): os.mkdir(datadir)
|
||||
|
||||
return datadir
|
||||
|
||||
def get_rundir(self, submit):
|
||||
config = getConfig()
|
||||
return os.path.join(config.rundir_root, submit.user, 'run')
|
||||
|
||||
class SimpleTester(TesterBase):
|
||||
|
||||
has_timelimit = True
|
||||
has_memlimit = True
|
||||
check_result = True
|
||||
is_lasttest = True
|
||||
|
||||
def __init__(self,
|
||||
source, target,
|
||||
compile = None, compileenv = {},
|
||||
run = None, runenv = {}, basemem = 0,
|
||||
baseproc = 0, extraproc = 0,
|
||||
compileguard = None, runguard = None, comparecmd = None):
|
||||
TesterBase.__init__(self)
|
||||
self.source = source
|
||||
self.target = target
|
||||
self.compilecmd = compile
|
||||
self.compileenv = compileenv
|
||||
self.runcmd = run
|
||||
self.runenv = runenv
|
||||
self.basemem = basemem
|
||||
self.baseproc = baseproc
|
||||
self.extraproc = extraproc
|
||||
self.compileguard = compileguard
|
||||
self.runguard = runguard
|
||||
self.comparecmd = comparecmd
|
||||
|
||||
def test(self, submit):
|
||||
#pdb.set_trace()
|
||||
if not self.prepare(submit):
|
||||
self.logger.info('self.prepare')
|
||||
submit.set_status('compile_failed')
|
||||
submit.set_compilemessage('Failed to delete source/target file')
|
||||
return False
|
||||
|
||||
submit.set_status('compiling')
|
||||
if not self.compile(submit):
|
||||
self.logger.info('self.compile')
|
||||
submit.set_status('compile_failed')
|
||||
self.cleanup(submit)
|
||||
return False
|
||||
|
||||
submit.set_status('running')
|
||||
testcases = submit.get_testcases()
|
||||
ret = []
|
||||
for testcase in testcases:
|
||||
r = self.run(submit, testcase)
|
||||
ret.append(r)
|
||||
self.logger.debug('run result: ' + r.__str__())
|
||||
|
||||
passed = True
|
||||
if self.check_result:
|
||||
for r in ret:
|
||||
if r[1] == 'AC' or not self.has_timelimit and r[1] == 'TLE':
|
||||
pass
|
||||
else:
|
||||
passed = False
|
||||
if not passed or self.is_lasttest:
|
||||
submit.update_test_results(ret)
|
||||
submit.set_status('finish')
|
||||
|
||||
self.cleanup(submit)
|
||||
|
||||
return passed
|
||||
|
||||
def prepare(self, submit):
|
||||
datadir = self.get_datadir(submit)
|
||||
|
||||
# write presetcodes to dir
|
||||
presetcodes = submit.get_presetcodes()
|
||||
for presetcode in presetcodes:
|
||||
pcname = os.path.join(datadir, presetcode.name)
|
||||
if os.path.exists(pcname):
|
||||
try:
|
||||
os.unlink(pcname)
|
||||
except OSError, e:
|
||||
self.logger.exception(
|
||||
"Failed to delete presetcode file %s" % pcname)
|
||||
return False
|
||||
f = open(pcname, 'w')
|
||||
f.write(string.replace(presetcode.code, '\r\n', '\n'))
|
||||
f.write('\n');
|
||||
f.close()
|
||||
|
||||
# delete existing source and target file
|
||||
datadirsource = os.path.join(datadir, self.source)
|
||||
datadirtarget = os.path.join(datadir, self.target)
|
||||
if os.path.exists(datadirsource):
|
||||
try:
|
||||
os.unlink(datadirsource)
|
||||
except OSError, e:
|
||||
self.logger.exception("Failed to delete source")
|
||||
return False
|
||||
if os.path.exists(datadirtarget):
|
||||
try:
|
||||
os.unlink(datadirtarget)
|
||||
except OSError, e:
|
||||
self.logger.exception("Failed to delete target")
|
||||
return False
|
||||
|
||||
# preprocess source code
|
||||
code = string.replace(submit.code, '\r\n', '\n')
|
||||
code = string.replace(code, chr(0x1a), '') # char generated by tc
|
||||
code = string.replace(code, 'getch()', '')
|
||||
code = string.replace(code, 'getch ()', '')
|
||||
code = string.replace(code, 'getch ( )', '')
|
||||
|
||||
code = string.replace(code, '\r\n', '\n')
|
||||
# write source to disk
|
||||
f = open(datadirsource, 'w')
|
||||
f.write(code)
|
||||
if len(submit.code) > 0 and submit.code[-1] != '\n': f.write('\n')
|
||||
f.close()
|
||||
|
||||
# setup rundir
|
||||
config = getConfig()
|
||||
try:
|
||||
submit.user = config.runas.get_nowait()
|
||||
except Queue.Empty:
|
||||
self.logger.exception("No runas user left, please create more!")
|
||||
return False
|
||||
rundir = self.get_rundir(submit)
|
||||
|
||||
if not os.path.exists(os.path.dirname(rundir)):
|
||||
os.mkdir(os.path.dirname(rundir))
|
||||
|
||||
if os.path.exists(rundir):
|
||||
try:
|
||||
self._remove(rundir)
|
||||
except OSError, e:
|
||||
self.logger.exception("Failed to delete rundir")
|
||||
config.runas.put(submit.user)
|
||||
return False
|
||||
os.mkdir(rundir)
|
||||
os.chmod(rundir, 0775)
|
||||
|
||||
return True
|
||||
|
||||
def cleanup(self, submit, force = False):
|
||||
datadir = self.get_datadir(submit)
|
||||
rundir = self.get_rundir(submit)
|
||||
config = getConfig()
|
||||
if not config.no_cleanup or force:
|
||||
if os.path.exists(datadir):
|
||||
self._remove(datadir)
|
||||
if os.path.exists(rundir):
|
||||
try:
|
||||
self._remove(rundir)
|
||||
except OSError:
|
||||
os.system('/usr/bin/sudo -u %s /bin/rm -rf %s' % (submit.user, rundir))
|
||||
|
||||
config.runas.put(submit.user)
|
||||
|
||||
def compile(self, submit):
|
||||
config = getConfig()
|
||||
datadir = self.get_datadir(submit)
|
||||
cmd = []
|
||||
if self.compileguard:
|
||||
for s in self.compileguard:
|
||||
s = string.replace(s, '<judgehome>', config.judgehome)
|
||||
s = string.replace(s, '<datadir>', datadir)
|
||||
cmd.append(s)
|
||||
if self.compilecmd:
|
||||
for s in self.compilecmd:
|
||||
s = string.replace(s, '<judgehome>', config.judgehome)
|
||||
s = string.replace(s, '<datadir>', datadir)
|
||||
cmd.append(s)
|
||||
cmd.append(self.source)
|
||||
for code in submit.get_presetcodes():
|
||||
if not code.isheader:
|
||||
cmd.append(code.name)
|
||||
self.logger.debug(string.join(cmd, '_'))
|
||||
|
||||
errfile = os.path.join(datadir, 'compile.err')
|
||||
|
||||
(exitcode, sig, timeused, memused) = \
|
||||
self._execute(submit, cmd, timelimit = config.compile_timelimit,
|
||||
infile = None, outfile = None, errfile = errfile,
|
||||
env = self.compileenv)
|
||||
|
||||
compilemsg = None
|
||||
if os.path.exists(errfile):
|
||||
f = file(errfile, 'r')
|
||||
compilemsg = string.join(f.readlines(), '')
|
||||
f.close()
|
||||
|
||||
if compilemsg:
|
||||
submit.set_compilemessage(compilemsg)
|
||||
|
||||
if exitcode == 0:
|
||||
self.logger.info('submit %s compile success' % submit.id)
|
||||
else:
|
||||
self.logger.info('submit %s compile failed' % submit.id)
|
||||
self.logger.debug(compilemsg)
|
||||
|
||||
return exitcode == 0
|
||||
|
||||
def run(self, submit, testcase):
|
||||
config = getConfig()
|
||||
datadir = self.get_datadir(submit) #dir save input and result
|
||||
rundir = self.get_rundir(submit) #dir program runs in
|
||||
|
||||
infile = testcase.get_input_file()
|
||||
outfile = os.path.join(datadir, testcase.id + '.out')
|
||||
errfile = os.path.join(datadir, testcase.id + '.err')
|
||||
statfile = os.path.join(rundir, testcase.id + '.stat')
|
||||
|
||||
# If submit input filename or output filename is provided,
|
||||
# no input or output redirect will happen.
|
||||
submit_input_filename = submit.get_input_filename()
|
||||
submit_output_filename = submit.get_output_filename()
|
||||
if submit_input_filename:
|
||||
shutil.copyfile(infile, os.path.join(rundir, submit_input_filename))
|
||||
_infile = infile
|
||||
infile = None
|
||||
if submit_output_filename:
|
||||
_outfile = outfile
|
||||
outfile = None
|
||||
|
||||
# Create data file symolic links
|
||||
datafiles = submit.get_problem().get_datafiles()
|
||||
for datafile in datafiles:
|
||||
targetname = os.path.join(rundir, datafile.filename)
|
||||
try:
|
||||
shutil.copyfile(datafile.absolute_path, targetname)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Use extra process setting on testcase if it exist
|
||||
extraproc = self.baseproc + (testcase.nproc if testcase.nproc else self.extraproc)
|
||||
|
||||
cmd = []
|
||||
if self.runguard:
|
||||
for s in self.runguard:
|
||||
s = s.replace('<judgehome>', config.judgehome)
|
||||
s = s.replace('<extraproc>', '%d' % extraproc)
|
||||
s = s.replace('<timelimit>', '%d' % (testcase.timelimit))
|
||||
s = s.replace('<maxmem>', '%d' % config.maxmem)
|
||||
s = s.replace('<rundir>', rundir)
|
||||
s = s.replace('<user>', submit.user)
|
||||
s = s.replace('<statfile>', statfile)
|
||||
cmd.append(s)
|
||||
if self.runcmd:
|
||||
for s in self.runcmd:
|
||||
s = s.replace('<judgehome>', config.judgehome)
|
||||
s = s.replace('<datadir>', datadir)
|
||||
s = s.replace('<user>', submit.user)
|
||||
cmd.append(s)
|
||||
if self.runenv:
|
||||
for k in self.runenv.keys():
|
||||
s = self.runenv[k]
|
||||
s = s.replace('<judgehome>', config.judgehome)
|
||||
s = s.replace('<datadir>', datadir)
|
||||
s = s.replace('<user>', submit.user)
|
||||
self.runenv[k] = s
|
||||
self.logger.debug(string.join(cmd, ' ') + ' ' + str(self.runenv))
|
||||
|
||||
(exitcode, sig, timeused, memused) = \
|
||||
self._execute(submit, cmd, timelimit = testcase.timelimit * 10,
|
||||
infile = infile, outfile = outfile,
|
||||
errfile = errfile, env = self.runenv,
|
||||
rlimit_fsize = config.output_size_limit + 2,
|
||||
statfile = statfile)
|
||||
if submit_input_filename:
|
||||
infile = _infile
|
||||
if submit_output_filename:
|
||||
outfile = _outfile
|
||||
try:
|
||||
shutil.copyfile(os.path.join(rundir, submit_output_filename),
|
||||
outfile)
|
||||
except IOError:
|
||||
f = file(outfile, 'w')
|
||||
f.close()
|
||||
|
||||
ret = [testcase.id, exitcode, sig, outfile, errfile, timeused, memused]
|
||||
if timeused > testcase.timelimit:
|
||||
ret.insert(1, 'TLE')
|
||||
return ret
|
||||
if memused > testcase.memlimit:
|
||||
ret.insert(1, 'MLE')
|
||||
return ret
|
||||
|
||||
if exitcode == 125:
|
||||
ret.insert(1, 'JSE')
|
||||
return ret
|
||||
elif exitcode == 126:
|
||||
ret.insert(1, 'RFC')
|
||||
return ret
|
||||
elif exitcode == 127:
|
||||
ret.insert(1, 'JGE')
|
||||
return ret
|
||||
|
||||
if sig == signal.SIGABRT or sig == signal.SIGSEGV:
|
||||
ret.insert(1, 'RE')
|
||||
return ret
|
||||
elif sig == signal.SIGKILL or sig == signal.SIGXCPU:
|
||||
ret.insert(1, 'TLE')
|
||||
return ret
|
||||
elif sig == signal.SIGXFSZ:
|
||||
ret.insert(1, 'OLE')
|
||||
return ret
|
||||
elif sig == signal.SIGFPE:
|
||||
ret.insert(1, 'FPE')
|
||||
return ret
|
||||
elif sig > 0:
|
||||
ret.insert(1, 'KS')
|
||||
return ret
|
||||
|
||||
s = os.stat(outfile)
|
||||
if s.st_size > config.output_size_limit:
|
||||
ret.insert(1, 'OLE')
|
||||
return ret
|
||||
|
||||
js = submit.get_judge_script()
|
||||
ret.insert(1, js.judge(submit.id, testcase.id,
|
||||
os.path.abspath(testcase.get_input_file()),
|
||||
os.path.abspath(testcase.get_output_file()),
|
||||
os.path.abspath(outfile),
|
||||
os.path.abspath(errfile), rundir))
|
||||
return ret
|
||||
|
||||
def _execute(self, submit, command, timelimit = 1,
|
||||
infile = None, outfile = None, errfile = None, env = {},
|
||||
rlimit_fsize = -1, statfile = None):
|
||||
|
||||
pid = os.fork()
|
||||
|
||||
if pid == 0:
|
||||
if rlimit_fsize > 0:
|
||||
resource.setrlimit(resource.RLIMIT_FSIZE,
|
||||
(rlimit_fsize, rlimit_fsize))
|
||||
|
||||
# child process
|
||||
if infile:
|
||||
try:
|
||||
os.close(0)
|
||||
os.open(infile, os.O_RDONLY)
|
||||
except Exception, e:
|
||||
print e
|
||||
sys.exit(125)
|
||||
if outfile:
|
||||
try:
|
||||
os.close(1)
|
||||
os.open(outfile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666)
|
||||
except Exception, e:
|
||||
print e
|
||||
sys.exit(125)
|
||||
if errfile:
|
||||
try:
|
||||
os.close(2)
|
||||
os.open(errfile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666)
|
||||
except Exception, e:
|
||||
print e
|
||||
sys.exit(125)
|
||||
|
||||
#os.chdir(self.get_datadir(submit))
|
||||
os.execve(command[0], command, env)
|
||||
sys.exit(125)
|
||||
|
||||
# parent process
|
||||
pid, status = os.waitpid(pid, 0)
|
||||
timeused = 0; memused = 0; sig = 0; exitcode = 0
|
||||
|
||||
# get this child sig and exitcode
|
||||
if os.WIFEXITED(status): exitcode = os.WEXITSTATUS(status)
|
||||
if os.WIFSIGNALED(status): sig = os.WTERMSIG(status)
|
||||
|
||||
# read information form statfile
|
||||
if statfile:
|
||||
try:
|
||||
stat = pickle.load(file(statfile, 'r'))
|
||||
exitcode = stat['exitcode']
|
||||
sig = stat['sig']
|
||||
timeused = stat['timeused']
|
||||
memused = 0
|
||||
if self.basemem.has_key('RSS'):
|
||||
memused += stat['memrss'] - self.basemem['RSS']
|
||||
if self.basemem.has_key('Data'):
|
||||
memused += stat['memdata'] - self.basemem['Data']
|
||||
if self.basemem.has_key('Stack'):
|
||||
memused += stat['memstack'] - self.basemem['Stack']
|
||||
memused = max(0, memused)
|
||||
except Exception, e:
|
||||
self.logger.exception(e)
|
||||
self.logger.error("Failed to read statfile: %s" % statfile)
|
||||
exitcode = 127 # judge script error
|
||||
|
||||
return (exitcode, sig, timeused, memused)
|
||||
|
||||
def _remove(self, top):
|
||||
for root, dirs, files in os.walk(top, topdown=False):
|
||||
for name in files:
|
||||
os.remove(os.path.join(root, name))
|
||||
for name in dirs:
|
||||
os.rmdir(os.path.join(root, name))
|
||||
os.rmdir(top)
|
||||
|
||||
class ComboTester:
|
||||
|
||||
testers = []
|
||||
|
||||
def add_tester(self, tester,
|
||||
has_timelimit = True, has_memlimit = True,
|
||||
check_result = True, is_lasttest = False):
|
||||
tester.has_timelimit = has_timelimit
|
||||
tester.has_memlimit = has_memlimit
|
||||
tester.check_result = check_result
|
||||
tester.is_lasttest = is_lasttest
|
||||
self.testers.append(tester)
|
||||
|
||||
def test(self, submit):
|
||||
r = True
|
||||
for t in self.testers:
|
||||
r = t.test(submit)
|
||||
if not r:
|
||||
break
|
||||
return r
|
||||
|
||||
class SimpleTesterTestCase(OJTestCase):
|
||||
|
||||
@classmethod
|
||||
def suite(clazz):
|
||||
tests = ( 'testSteps', 'testNoSource',
|
||||
'testAC', 'testWA', 'testZero',
|
||||
'testBinary', 'testCE', 'testOLE', 'testTLE',
|
||||
'testTargetDeleted',
|
||||
)
|
||||
return unittest.TestSuite(map(SimpleTesterTestCase, tests))
|
||||
|
||||
def setUp(self):
|
||||
OJTestCase.setUp(self)
|
||||
self.st = self.config.languages['gcc-3.3-nobc']
|
||||
|
||||
def testSteps(self):
|
||||
self.logger.info("TESTING: Steps")
|
||||
submit = self.ds.get_submit(3)
|
||||
|
||||
self.st.prepare(submit)
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
self.assertTrue(os.path.isdir(datadir))
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.source)))
|
||||
|
||||
submit.set_status('compiling')
|
||||
r = self.st.compile(submit)
|
||||
self.assertTrue(r)
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.target)))
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, 'compile.err')))
|
||||
|
||||
submit.set_status('running')
|
||||
testcases = submit.get_testcases()
|
||||
|
||||
r = self.st.run(submit, testcases[0])
|
||||
self.assertEqual(r[0], testcases[0].id)
|
||||
self.assertEqual(r[1], 'AC')
|
||||
self.assertEqual(r[2], 0)
|
||||
self.assertEqual(r[3], 0)
|
||||
f = file(r[4], 'r')
|
||||
o = string.join(f.readlines(), '\n')
|
||||
f.close()
|
||||
self.assertEqual(o, '3\n')
|
||||
f = file(r[5], 'r')
|
||||
o = string.join(f.readlines(), '\n')
|
||||
f.close()
|
||||
self.assertEqual(o, '')
|
||||
|
||||
r = self.st.run(submit, testcases[1])
|
||||
f = file(r[4], 'r')
|
||||
o = string.join(f.readlines(), '\n')
|
||||
f.close()
|
||||
self.assertEqual(o, '4\n')
|
||||
|
||||
self.st.cleanup(submit)
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir))
|
||||
|
||||
def testAC(self):
|
||||
self.logger.info("TESTING: AC")
|
||||
submit = self.ds.get_submit(3)
|
||||
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
r = self.st.test(submit)
|
||||
self.assertTrue(r) # passed
|
||||
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup
|
||||
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
conn = sqlite.connect(self.dbname)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute('SELECT * FROM submit_test_result WHERE sid=?', (submit.id, ))
|
||||
rows = cur.fetchall()
|
||||
cur.execute('DELETE FROM submit_test_result')
|
||||
conn.commit()
|
||||
|
||||
self.assertEqual(len(rows), 2)
|
||||
|
||||
# test if the submit status change
|
||||
cur.execute('SELECT status FROM submit WHERE id=?', (submit.id, ))
|
||||
rows = cur.fetchall()
|
||||
self.assertEqual(rows[0][0], 10)
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
def testNoSource(self):
|
||||
self.logger.info("TESTING: No Source")
|
||||
submit = self.ds.get_submit(1)
|
||||
|
||||
# the code of this submit is empty
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
r = self.st.test(submit)
|
||||
self.assertFalse(r)
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup
|
||||
self.assertTrue(submit.get_status(), 'compile_failed')
|
||||
|
||||
def testNoTarget(self):
|
||||
self.logger.info("TESTING: No Target")
|
||||
submit = self.ds.get_submit(1)
|
||||
|
||||
# the code of this submit generate nothing
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
r = self.st.test(submit)
|
||||
self.assertFalse(r)
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup
|
||||
self.assertTrue(submit.get_status(), 'compile_failed')
|
||||
|
||||
def testCE(self):
|
||||
self.logger.info("TESTING: CE")
|
||||
submit = self.ds.get_submit(7)
|
||||
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
r = self.st.test(submit)
|
||||
self.assertFalse(r) # not passed
|
||||
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup
|
||||
|
||||
self.assertTrue(len(submit.get_compilemessage()) > 0)
|
||||
self.assertTrue(submit.get_status(), 'compile_failed')
|
||||
|
||||
def testWA(self):
|
||||
self.logger.info("TESTING: WA")
|
||||
submit = self.ds.get_submit(4)
|
||||
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
r = self.st.test(submit)
|
||||
self.assertFalse(r) # not passed
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir)) # cleanup
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
conn = sqlite.connect(self.dbname)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute('SELECT * FROM submit_test_result')
|
||||
rows = cur.fetchall()
|
||||
cur.execute('DELETE FROM submit_test_result')
|
||||
conn.commit()
|
||||
|
||||
self.assertEqual(len(rows), 2)
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
def testTargetDeleted(self):
|
||||
self.logger.info("TESTING: Target Deleted")
|
||||
submit = self.ds.get_submit(3)
|
||||
|
||||
self.st.prepare(submit)
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
self.assertTrue(os.path.isdir(datadir))
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.source)))
|
||||
|
||||
submit.set_status('compiling')
|
||||
r = self.st.compile(submit)
|
||||
self.assertTrue(r)
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, self.st.target)))
|
||||
self.assertTrue(os.path.isfile(os.path.join(datadir, 'compile.err')))
|
||||
|
||||
self.st.cleanup(submit, True)
|
||||
|
||||
submit.set_status('running')
|
||||
testcases = submit.get_testcases()
|
||||
r = self.st.run(submit, testcases[0])
|
||||
self.assertNotEqual(r[1], 'AC')
|
||||
|
||||
def testZero(self):
|
||||
self.logger.info("TESTING: ZERO")
|
||||
submit = self.ds.get_submit(5)
|
||||
|
||||
# this program output '\0'
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
self.st.prepare(submit)
|
||||
r = self.st.compile(submit)
|
||||
self.assertTrue(r)
|
||||
testcases = submit.get_testcases()
|
||||
r = self.st.run(submit, testcases[0])
|
||||
f = file(os.path.join(datadir, '0000000001.out'))
|
||||
o = string.join(f.readlines(), '\n')
|
||||
f.close()
|
||||
self.assertEqual(o, '3\0\n')
|
||||
|
||||
self.st.cleanup(submit)
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir))
|
||||
|
||||
def testBinary(self):
|
||||
self.logger.info("TESTING: BINARY")
|
||||
submit = self.ds.get_submit(6)
|
||||
|
||||
# this program output '0xbb'
|
||||
datadir = os.path.join(self.config.datadir, submit.id)
|
||||
self.st.prepare(submit)
|
||||
r = self.st.compile(submit)
|
||||
self.assertTrue(r)
|
||||
testcases = submit.get_testcases()
|
||||
r = self.st.run(submit, testcases[0])
|
||||
f = file(os.path.join(datadir, '0000000001.out'))
|
||||
o = string.join(f.readlines(), '\n')
|
||||
f.close()
|
||||
self.assertEqual(o, '\xbb')
|
||||
|
||||
self.st.cleanup(submit)
|
||||
self.assertFalse(not self.config.no_cleanup and os.path.exists(datadir))
|
||||
|
||||
def testOLE(self):
|
||||
self.logger.info("TESTING: OLE")
|
||||
submit = self.ds.get_submit(10)
|
||||
|
||||
self.st.prepare(submit)
|
||||
r = self.st.compile(submit)
|
||||
self.assertTrue(r, "Submit compile failed")
|
||||
ts = submit.get_testcases()
|
||||
r = self.st.run(submit, ts[0])
|
||||
self.assertEqual(r[1], 'OLE')
|
||||
|
||||
def testTLE(self):
|
||||
self.logger.info("TESTING: TLE")
|
||||
submit = self.ds.get_submit(11)
|
||||
|
||||
self.st.prepare(submit)
|
||||
self.st.compile(submit)
|
||||
ts = submit.get_testcases()
|
||||
r = self.st.run(submit, ts[0])
|
||||
self.assertEqual(r[1], 'TLE')
|
||||
|
||||
class ComboTesterTestCase(OJTestCase):
|
||||
|
||||
@classmethod
|
||||
def suite(clazz):
|
||||
tests = ( 'testGCC33', )
|
||||
return unittest.TestSuite(map(ComboTesterTestCase, tests))
|
||||
|
||||
def testGCC33(self):
|
||||
self.ct = self.config.languages['gcc-3.3']
|
||||
submits = self.ds.get_submits(8)
|
||||
r = self.ct.test(submits[2])
|
||||
self.assertTrue(r)
|
||||
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
conn = sqlite.connect(self.dbname)
|
||||
cur = conn.cursor()
|
||||
cur.execute('DELETE FROM submit_test_result')
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
class LanguageTestCase(OJTestCase):
|
||||
|
||||
@classmethod
|
||||
def suite(clazz):
|
||||
tests = ( 'testGCC33NOBC', 'testGCC33BC', 'testGXX33',
|
||||
'testJava5', 'testJava6',
|
||||
'testFreePascal20',
|
||||
'testPython25',
|
||||
)
|
||||
return unittest.TestSuite(map(LanguageTestCase, tests))
|
||||
|
||||
def setUp(self):
|
||||
OJTestCase.setUp(self)
|
||||
|
||||
def testGCC33NOBC(self):
|
||||
st = self.config.languages['gcc-3.3-nobc']
|
||||
submit = self.ds.get_submit(1001)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'GCC NOBC test failed')
|
||||
|
||||
def testGCC33BC(self):
|
||||
st = self.config.languages['gcc-3.3-bc']
|
||||
submit = self.ds.get_submit(1001)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'GCC WITH BC test failed')
|
||||
|
||||
def testGXX33(self):
|
||||
st = self.config.languages['g++-3.3']
|
||||
submit = self.ds.get_submit(1002)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'GCC WITH BC test failed')
|
||||
|
||||
def testJava5(self):
|
||||
st = self.config.languages['java-1.6']
|
||||
submit = self.ds.get_submit(1004)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'Java 5 test failed')
|
||||
|
||||
def testJava6(self):
|
||||
st = self.config.languages['java-1.6']
|
||||
submit = self.ds.get_submit(1004)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'Java 6 test failed')
|
||||
|
||||
def testFreePascal20(self):
|
||||
st = self.config.languages['fpc-2.0']
|
||||
submit = self.ds.get_submit(1005)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'FreePascal 2.0 test failed')
|
||||
|
||||
def testPython25(self):
|
||||
st = self.config.languages['python-2.5']
|
||||
submit = self.ds.get_submit(1006)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'Python 2.5 test failed')
|
||||
|
||||
class SecurityTestCase(OJTestCase):
|
||||
"""
|
||||
Security test including:
|
||||
|
||||
* File Permission: only rundir are allowed to visit
|
||||
* Forked Process: can oj kill all these process
|
||||
* Daemon Process: can oj kill the daemon process
|
||||
* Sleep: can oj kill sleep process
|
||||
* Network: network usage should be forbidden
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def suite(clazz):
|
||||
tests = (#'testGCC33FilePermission', 'testJavaFilePermission',
|
||||
#'testPython25FilePermission',
|
||||
# 'testSleep',
|
||||
'testFork', 'testDaemon',
|
||||
)
|
||||
return unittest.TestSuite(map(SecurityTestCase, tests))
|
||||
|
||||
def testGCC33FilePermission(self):
|
||||
st = self.config.languages['gcc-3.3-nobc']
|
||||
submit = self.ds.get_submit(2311)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'gcc 3.3 no bc security test failed')
|
||||
|
||||
def testJavaFilePermission(self):
|
||||
st = self.config.languages['java-1.6']
|
||||
submit = self.ds.get_submit(2331)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'Java 1.6 security test failed')
|
||||
|
||||
def testPython25FilePermission(self):
|
||||
st = self.config.languages['python-2.5']
|
||||
submit = self.ds.get_submit(2361)
|
||||
r = st.test(submit)
|
||||
self.assertTrue(r, 'Python 2.5 security test failed')
|
||||
|
||||
def testFork(self):
|
||||
st = self.config.languages['gcc-3.3-nobc']
|
||||
submit = self.ds.get_submit(2412)
|
||||
st.prepare(submit)
|
||||
r = st.compile(submit)
|
||||
self.assertTrue(r, "Failed")
|
||||
ts = submit.get_testcases()
|
||||
r = st.run(submit, ts[0])
|
||||
self.assertEqual(r[1], 'TLE', "Sleep test failed")
|
||||
|
||||
def testDaemon(self):
|
||||
st = self.config.languages['gcc-3.3-nobc']
|
||||
submit = self.ds.get_submit(2413)
|
||||
st.prepare(submit)
|
||||
r = st.compile(submit)
|
||||
self.assertTrue(r, "Failed")
|
||||
ts = submit.get_testcases()
|
||||
r = st.run(submit, ts[0])
|
||||
self.assertEqual(r[1], 'TLE', "Sleep test failed")
|
||||
|
||||
def testSleep(self):
|
||||
st = self.config.languages['gcc-3.3-nobc']
|
||||
submit = self.ds.get_submit(2411)
|
||||
st.prepare(submit)
|
||||
r = st.compile(submit)
|
||||
self.assertTrue(r, "Failed")
|
||||
ts = submit.get_testcases()
|
||||
r = st.run(submit, ts[0])
|
||||
self.assertEqual(r[1], 'TLE', "Sleep test failed")
|
||||
|
||||
def testNetwork(self):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
#unittest.main()
|
||||
suite = unittest.TestSuite()
|
||||
#suite.addTests(LanguageTestCase.suite())
|
||||
#suite.addTests(SimpleTesterTestCase.suite())
|
||||
#suite.addTest(ComboTesterTestCase.suite())
|
||||
suite.addTest(SecurityTestCase.suite())
|
||||
unittest.TextTestRunner().run(suite)
|
||||
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
||||
BIN
python/tester.pyc
Normal file
BIN
python/tester.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user