244 lines
10 KiB
Python
244 lines
10 KiB
Python
from os import listdir, mkdir
|
|
from os.path import basename, isdir
|
|
from shutil import rmtree, copyfile
|
|
from threading import Thread
|
|
from xml.dom.minidom import parse
|
|
|
|
from Main.models import *
|
|
from .main import solution_path
|
|
|
|
|
|
def start_new(host):
|
|
in_queue = list(Solution.objects.filter(result='IN QUEUE'))
|
|
if in_queue:
|
|
sol = in_queue[0]
|
|
for s in in_queue:
|
|
dif = s.task.block.priority * 10 + s.task.priority - sol.task.block.priority * 10 - sol.task.priority
|
|
if dif > 0:
|
|
sol = s
|
|
elif dif == 0 and s.id < sol.id:
|
|
sol = s
|
|
Tester(sol, host).test()
|
|
|
|
|
|
def is_project(path):
|
|
return any([x.endswith('.csproj') for x in listdir(path)])
|
|
|
|
|
|
def get_node_value(element):
|
|
return element[0].firstChild.nodeValue
|
|
|
|
|
|
def nunit_path(working_dir):
|
|
return '..{}'.format(sep) * len(working_dir.split(sep)) + 'nunit_console{}nunit3-console.exe'.format(sep)
|
|
|
|
|
|
class Tester:
|
|
|
|
def __init__(self, solution, host):
|
|
self.solution = solution
|
|
self.host = host
|
|
self.working_dir = ''
|
|
self.files = []
|
|
|
|
# функция компиляции
|
|
def build(self, path):
|
|
# решение для UNIX
|
|
# shell('msbuild ' + path + ' /p:Configuration=Debug')
|
|
|
|
# решение для Windows
|
|
cmd = 'dotnet build {} -o {}\\bin\\Debug'.format(path, path)
|
|
with self.solution.log_fs as fs:
|
|
shell(cmd, fs)
|
|
|
|
def build_and_copy(self, path, working_dir):
|
|
if exists(join(path, 'bin', 'Debug')):
|
|
rmtree(join(path, 'bin', 'Debug'))
|
|
self.build(path)
|
|
name = basename(path)
|
|
if not exists(join(path, 'bin', 'Debug')) or not any(
|
|
x.endswith('.exe') for x in listdir(join(path, 'bin', 'Debug'))):
|
|
return False
|
|
self.files.append(basename(path))
|
|
for file in listdir(join(path, 'bin', 'Debug')):
|
|
if exists(join(path, 'bin', 'Debug', file)):
|
|
new_file = join(working_dir, basename(file))
|
|
try:
|
|
copyfile(join(path, 'bin', 'Debug', file), new_file)
|
|
except:
|
|
pass
|
|
else:
|
|
return False
|
|
return True
|
|
|
|
def push(self):
|
|
solution = self.solution
|
|
if solution.result == 'SOLUTION ERROR':
|
|
return
|
|
solution.result = 'IN QUEUE'
|
|
solution.save()
|
|
from Main.models import System
|
|
if len(Solution.objects.filter(result='TESTING')) < int(System.objects.get(key='queue_size').value):
|
|
self.test()
|
|
|
|
def delete_everything(self):
|
|
ssp = solution_path(self.solution.path())
|
|
sln_path = join(ssp, '.idea')
|
|
if exists(sln_path):
|
|
rmtree(sln_path)
|
|
sln_path = join(ssp, '.vs')
|
|
if exists(sln_path):
|
|
rmtree(sln_path)
|
|
sln_path = ssp
|
|
for p in listdir(sln_path):
|
|
if isdir(join(sln_path, p)):
|
|
if exists(join(sln_path, p, 'bin')):
|
|
rmtree(join(sln_path, p, 'bin'))
|
|
if exists(join(sln_path, p, 'obj')):
|
|
rmtree(join(sln_path, p, 'obj'))
|
|
if exists(self.working_dir):
|
|
rmtree(self.working_dir)
|
|
if exists(join(self.solution.path(), 'solution.zip')):
|
|
remove(join(self.solution.path(), 'solution.zip'))
|
|
if exists(join(self.solution.path(), '__MACOSX')):
|
|
rmtree(join(self.solution.path(), '__MACOSX'))
|
|
if exists(join(sln_path, '.DS_Store')):
|
|
remove(join(sln_path, '.DS_Store'))
|
|
if exists(join(sln_path, 'test_folder')):
|
|
rmtree(join(sln_path, 'test_folder'))
|
|
|
|
def nunit_testing(self):
|
|
solution = self.solution
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Building image\n')
|
|
shell('docker build -t solution_{} {}'.format(self.solution.id, self.working_dir))
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Image built successfully\n')
|
|
|
|
def execute():
|
|
with self.solution.log_fs as fs:
|
|
shell('docker run --name solution_container_{} solution_{}'.format(self.solution.id, self.solution.id),
|
|
output=fs)
|
|
|
|
solution.write_log('Running container')
|
|
t = Thread(target=execute)
|
|
t.start()
|
|
t.join(self.solution.task.time_limit / 1000)
|
|
solution.write_log('Running finished')
|
|
with self.solution.log_fs as fs:
|
|
shell('docker cp solution_container_{}:/app/TestResults.xml {}'.format(self.solution.id, self.working_dir),
|
|
fs)
|
|
with self.solution.log_fs as fs:
|
|
shell('docker rm --force solution_container_{}'.format(self.solution.id), fs)
|
|
with self.solution.log_fs as fs:
|
|
shell('docker image rm solution_{}'.format(self.solution.id), fs)
|
|
if not exists(join(self.working_dir, 'TestResults.xml')):
|
|
self.solution.set_result('Time limit')
|
|
solution.write_log('Result file not found in container')
|
|
return
|
|
solution.write_log('Result file found in container')
|
|
try:
|
|
doc = parse(join(self.working_dir, 'TestResults.xml'))
|
|
res = get_node_value(doc.getElementsByTagName('Passed')) + '/' + get_node_value(
|
|
doc.getElementsByTagName('Total'))
|
|
self.solution.details = ''
|
|
for el in doc.getElementsByTagName('Result'):
|
|
self.solution.details += '<h5><b>' + get_node_value(el.getElementsByTagName('MethodName')) + '</b></h5>'
|
|
r = get_node_value(el.getElementsByTagName('Successful'))
|
|
if r == 'true':
|
|
self.solution.details += '<div style="color: green;">Passed</div>'
|
|
else:
|
|
self.solution.details += '<div style="color: red;">Failed</div>'
|
|
mes = get_node_value(el.getElementsByTagName('Message'))
|
|
self.solution.details += '<pre>{}</pre>'.format(mes)
|
|
except:
|
|
solution.write_log('Unknown error')
|
|
res = 'TEST ERROR'
|
|
self.solution.set_result(res)
|
|
|
|
def test(self):
|
|
solution = self.solution
|
|
solution.result = 'TESTING'
|
|
solution.save()
|
|
try:
|
|
if not exists(self.solution.task.tests_path()):
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'No test file found\n')
|
|
solution.set_result('TEST ERROR')
|
|
solution.save()
|
|
self.delete_everything()
|
|
start_new(self.host)
|
|
return
|
|
sln_path = solution_path(join(MEDIA_ROOT, 'solutions', str(solution.id)))
|
|
if sln_path == '':
|
|
solution.set_result('TEST ERROR')
|
|
solution.save()
|
|
self.delete_everything()
|
|
start_new(self.host)
|
|
return
|
|
working_dir = join(sln_path, 'test_folder')
|
|
if exists(working_dir):
|
|
try:
|
|
rmtree(working_dir)
|
|
except:
|
|
remove(working_dir)
|
|
mkdir(working_dir)
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Testing directory created\n')
|
|
for project in listdir(sln_path):
|
|
solution.write_log('Checking if {} is project'.format(project))
|
|
prj = project
|
|
project = join(sln_path, project)
|
|
if isdir(project) and is_project(project) and basename(project) != 'TestsProject':
|
|
if not self.build_and_copy(project, working_dir):
|
|
solution.set_result('Compilation error')
|
|
solution.write_log('Failed to compile project {}'.format(prj))
|
|
solution.save()
|
|
self.delete_everything()
|
|
start_new(self.host)
|
|
return
|
|
dll_path = solution.task.tests_path()
|
|
solution.write_log('Copying test file to working directory')
|
|
copyfile(dll_path, join(working_dir, str(solution.task.id) + '.cs'))
|
|
solution.write_log('Test file copied')
|
|
for file in listdir('SprintTest'):
|
|
try:
|
|
copyfile(join('SprintTest', file), join(working_dir, file))
|
|
except:
|
|
pass
|
|
self.working_dir = working_dir
|
|
build_tests_cmd = 'csc -out:{} -t:library /r:{} /r:{} /r:{} '.format(join(self.working_dir, 'tests.dll'),
|
|
join(self.working_dir,
|
|
'SprintTest.dll'),
|
|
join(working_dir,
|
|
'System.Runtime.dll'),
|
|
join(working_dir,
|
|
'System.Reflection.dll'))
|
|
for file in self.files:
|
|
build_tests_cmd += '/r:{}.dll '.format(join(self.working_dir, file))
|
|
build_tests_cmd += self.solution.task.tests_path()
|
|
if exists(join(self.working_dir, 'tests.dll')):
|
|
remove(join(self.working_dir, 'tests.dll'))
|
|
solution.write_log('Building tests file started')
|
|
with self.solution.log_fs as fs:
|
|
shell(build_tests_cmd, fs)
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Building tests file finished\n')
|
|
if exists(join(self.working_dir, 'tests.dll')):
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Got .dll tests file\n')
|
|
for file in ExtraFile.objects.filter(task=self.solution.task):
|
|
copyfile(file.path, join(working_dir, file.filename))
|
|
self.nunit_testing()
|
|
else:
|
|
solution.set_result('TEST ERROR')
|
|
solution.write_log('Failed to compile tests')
|
|
except:
|
|
solution.set_result('TEST ERROR')
|
|
raise
|
|
with self.solution.log_fs as fs:
|
|
fs.write(b'Unknown error\n')
|
|
solution.save()
|
|
self.delete_everything()
|
|
start_new(self.host)
|