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 += '
' + get_node_value(el.getElementsByTagName('MethodName')) + '
' r = get_node_value(el.getElementsByTagName('Successful')) if r == 'true': self.solution.details += '
Passed
' else: self.solution.details += '
Failed
' mes = get_node_value(el.getElementsByTagName('Message')) self.solution.details += '
{}
'.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)