def test_adapt_db_expected_list(self): """Test for adapting dictionaries in initial_db and expected_result to unitary lists """ collection = Collection(name_md='Colección', position=8, description_md='Colección de pruebas', author=None) collection.save() # Problems with dictionaries or already with [dict] problems = [ SelectProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=dict()), DMLProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=dict()), FunctionProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=dict()), ProcProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=dict()), TriggerProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=dict()), SelectProblem(title_html="titulo", text_html="enunciado", initial_db=[dict()], collection=collection, author=None, expected_result=[dict()]), # already has the right representation ] for prob in problems: prob.save() adapt_db_result_to_list() for prob in Problem.objects.all().select_subclasses(): self.assertIs(type(prob.initial_db), list) self.assertIs(type(prob.initial_db[0]), dict) self.assertIs(type(prob.expected_result), list) self.assertIs(type(prob.expected_result[0]), dict) for prob in Problem.objects.all(): prob.delete() # Problems with wrong types in initial_db or expected_result wrong_problems = [ SelectProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=3), DMLProblem(title_html="titulo", text_html="enunciado", initial_db=6, collection=collection, author=None, expected_result=dict()), FunctionProblem(title_html="titulo", text_html="enunciado", initial_db=[], collection=collection, author=None, expected_result=dict()), ProcProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=[3]), TriggerProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=[]), TriggerProblem(title_html="titulo", text_html="enunciado", initial_db=dict(), collection=collection, author=None, expected_result=[dict(), dict(), False]), ] # Tries every wrong program individually (removing it after checking the exception) for prob in wrong_problems: prob.save() with self.assertRaises(TypeError): adapt_db_result_to_list() prob.delete()
def test_individual_zip(self): """Load problem data from valid ZIP""" curr_path = os.path.dirname(__file__) zip_select_path = os.path.join(curr_path, self.ZIP_FOLDER, self.SELECT_OK) zip_dml_path = os.path.join(curr_path, self.ZIP_FOLDER, self.DML_OK) zip_function_path = os.path.join(curr_path, self.ZIP_FOLDER, self.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, self.ZIP_FOLDER, self.PROC_OK) zip_trigger_path = os.path.join(curr_path, self.ZIP_FOLDER, self.TRIGGER_OK) zip_discriminant_path = os.path.join(curr_path, self.ZIP_FOLDER, self.DISCRIMINANT_OK) select_problem = SelectProblem(zipfile=zip_select_path) dml_problem = DMLProblem(zipfile=zip_dml_path) function_problem = FunctionProblem(zipfile=zip_function_path) proc_problem = ProcProblem(zipfile=zip_proc_path) trigger_problem = TriggerProblem(zipfile=zip_trigger_path) discriminant_problem = DiscriminantProblem( zipfile=zip_discriminant_path) for problem in [ select_problem, dml_problem, function_problem, proc_problem, trigger_problem, discriminant_problem ]: self.assertFalse(problem.text_md) problem.clean() self.assertTrue(problem.text_md) self.assertTrue('.html' in problem.template()) self.assertTrue(str(problem)) self.assertEqual(problem.language, 'es')
def test_plsql_correct(self): """Accepted submissions to function/procedure/trigger problem""" curr_path = os.path.dirname(__file__) zip_function_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.PROC_OK) zip_trigger_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.TRIGGER_OK) client = Client() collection = create_collection('Colleccion de prueba AAA') user = create_user('54522', 'antonio') client.login(username='******', password='******') function_problem = FunctionProblem(zipfile=zip_function_path, collection=collection, author=user) proc_problem = ProcProblem(zipfile=zip_proc_path, collection=collection, author=user) trigger_problem = TriggerProblem(zipfile=zip_trigger_path, collection=collection, author=user) for problem in [function_problem, proc_problem, trigger_problem]: problem.clean() problem.save() submit_url = reverse('judge:submit', args=[problem.pk]) response = client.post(submit_url, {'code': problem.solution}, follow=True) self.assertEqual(response.json()['veredict'], VeredictCode.AC)
def test_show_problems(self): """Shows a problem of each type""" curr_path = os.path.dirname(__file__) zip_select_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.SELECT_OK) zip_dml_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.DML_OK) zip_function_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.PROC_OK) zip_trigger_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.TRIGGER_OK) collection = create_collection('Colleccion de prueba XYZ') user = create_user('5555', 'pepe') select_problem = SelectProblem(zipfile=zip_select_path, collection=collection, author=user) dml_problem = DMLProblem(zipfile=zip_dml_path, collection=collection, author=user) function_problem = FunctionProblem(zipfile=zip_function_path, collection=collection, author=user) proc_problem = ProcProblem(zipfile=zip_proc_path, collection=collection, author=user) trigger_problem = TriggerProblem(zipfile=zip_trigger_path, collection=collection, author=user) client = Client() client.login(username='******', password='******') for problem in [select_problem, dml_problem, function_problem, proc_problem, trigger_problem]: problem.clean() problem.save() problem_url = reverse('judge:problem', args=[problem.pk]) response = client.get(problem_url, follow=True) self.assertEqual(response.status_code, 200) self.assertIn(problem.title_html, response.content.decode('utf-8'))
def test_load_hint(self): """Test to check if hints.md is loaded correctly""" curr_path = os.path.dirname(__file__) zip_dml_path = os.path.join(curr_path, self.ZIP_FOLDER, self.DML_HINTS) zip_function_path = os.path.join(curr_path, self.ZIP_FOLDER, self.FUNCTION_HINTS) zip_proc_path = os.path.join(curr_path, self.ZIP_FOLDER, self.PROC_HINTS) zip_trigger_path = os.path.join(curr_path, self.ZIP_FOLDER, self.TRIGGER_HINTS) zip_discriminant_path = os.path.join(curr_path, self.ZIP_FOLDER, self.DISCRIMINANT_HINTS) collection = create_collection('Coleccion 1') dml = DMLProblem(zipfile=zip_dml_path, collection=collection) function = FunctionProblem(zipfile=zip_function_path, collection=collection) proc = ProcProblem(zipfile=zip_proc_path, collection=collection) trigger = TriggerProblem(zipfile=zip_trigger_path, collection=collection) discriminant = DiscriminantProblem(zipfile=zip_discriminant_path, collection=collection) hints_expected1 = (3, 'descripcion pista 1') hints_expected2 = (5, 'descripcion pista 2') hints_expected3 = (10, 'descripcion pista 3') for problem in [dml, function, proc, trigger, discriminant]: problem.clean() problem.save() hints = Hint.objects.filter(problem=problem).order_by('num_submit') self.assertEqual(hints.count(), 3) self.assertEqual(hints_expected1[0], hints[0].num_submit) self.assertEqual(hints_expected1[1], hints[0].text_md) self.assertEqual(hints_expected2[0], hints[1].num_submit) self.assertEqual(hints_expected2[1], hints[1].text_md) self.assertEqual(hints_expected3[0], hints[2].num_submit) self.assertEqual(hints_expected3[1], hints[2].text_md)
def test_zip_errors(self): """ValidationError when loading ZIP in all problem types""" curr_path = os.path.dirname(__file__) # Select problems for filename in [ self.SELECT_MISSING_FILES, self.SELECT_EMPTY_TITLE, self.SELECT_TEXT_DECODE ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = SelectProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean) # DML problems for filename in [ self.DML_MISSING_FILES, self.DML_BAD_NUM_STMT, self.DML_TEXT_DECODE ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = DMLProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean) # Function problems for filename in [ self.FUNCTION_MISSING_FILES, self.FUNCTION_EMPTY_TITLE, self.FUNCTION_TEXT_DECODE ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = FunctionProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean) # Procedure problems for filename in [ self.PROC_MISSING_FILES, self.PROC_EMPTY_TITLE, self.PROC_TEXT_DECODE ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = ProcProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean) # Trigger problems for filename in [ self.TRIGGER_MISSING_FILES, self.TRIGGER_EMPTY_TITLE, self.TRIGGER_TEXT_DECODE, self.TRIGGER_BAD_INSERT ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = TriggerProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean) # Discriminant problems for filename in [ self.DISCRIMINANT_MISSING_FILES, self.DISCRIMINANT_BAD_STMT, self.DISCRIMINANT_TEXT_DECODE ]: zip_path = os.path.join(curr_path, self.ZIP_FOLDER, filename) problem = DiscriminantProblem(zipfile=zip_path) self.assertRaises(ValidationError, problem.clean)
def test_download(self): """Download the script of a problem (CREATE + INSERT)""" curr_path = os.path.dirname(__file__) zip_select_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.SELECT_OK) zip_dml_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.DML_OK) zip_function_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.PROC_OK) zip_trigger_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.TRIGGER_OK) collection = create_collection('Colleccion de prueba AAA') user = create_user('54522', 'antonio') select_problem = SelectProblem(zipfile=zip_select_path, collection=collection, author=user) dml_problem = DMLProblem(zipfile=zip_dml_path, collection=collection, author=user) function_problem = FunctionProblem(zipfile=zip_function_path, collection=collection, author=user) proc_problem = ProcProblem(zipfile=zip_proc_path, collection=collection, author=user) trigger_problem = TriggerProblem(zipfile=zip_trigger_path, collection=collection, author=user) client = Client() client.login(username='******', password='******') for problem in [ select_problem, dml_problem, function_problem, proc_problem, trigger_problem ]: problem.clean() problem.save() url = reverse('judge:create_insert', args=[problem.pk]) response = client.get(url, follow=True) script = problem.create_sql + '\n\n' + problem.insert_sql self.assertEqual( response.get('Content-Disposition'), "attachment; filename=create_insert.sql", ) self.assertEqual(response.get('Content-Type'), "application/sql") self.assertEqual(response.content.decode('UTF-8'), script) self.assertEqual(response.status_code, 200)
def test_trigger_problem_language(self): """Test to check if language in trigger problem page displays correctly""" curr_path = os.path.dirname(__file__) client = Client() zip_trigger_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.TRIGGER_OK) collection = create_collection('Colleccion de prueba XYZ') user = create_user('5555', 'pepe') problem = TriggerProblem(zipfile=zip_trigger_path, collection=collection, author=user) problem.clean() problem.save() client.login(username='******', password='******') client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'en'}) url = reverse('judge:problem', args=[problem.pk]) response = client.get(url, follow=True) self.assertIn('Executed statements', response.content.decode('utf-8')) self.assertIn('Expected result', response.content.decode('utf-8')) client.cookies.load({settings.LANGUAGE_COOKIE_NAME: 'es'}) url = reverse('judge:problem', args=[problem.pk]) response = client.get(url, follow=True) self.assertEqual(response.status_code, 200) self.assertIn('Sentencias ejecutadas', response.content.decode('utf-8')) self.assertIn('Resultado esperado', response.content.decode('utf-8'))
def test_zip_other_type(self): """Loading a problem data from a ZIP of a different type must raise a ValidationError""" curr_path = os.path.dirname(__file__) zip_select_path = os.path.join(curr_path, self.ZIP_FOLDER, self.SELECT_OK) zip_dml_path = os.path.join(curr_path, self.ZIP_FOLDER, self.DML_OK) zip_function_path = os.path.join(curr_path, self.ZIP_FOLDER, self.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, self.ZIP_FOLDER, self.PROC_OK) zip_trigger_path = os.path.join(curr_path, self.ZIP_FOLDER, self.TRIGGER_OK) select_problem = SelectProblem(zipfile=zip_dml_path) dml_problem = DMLProblem(zipfile=zip_function_path) function_problem = FunctionProblem(zipfile=zip_proc_path) proc_problem = ProcProblem(zipfile=zip_trigger_path) trigger_problem = TriggerProblem(zipfile=zip_select_path) discriminant_problem = TriggerProblem(zipfile=zip_select_path) for problem in [ select_problem, dml_problem, function_problem, proc_problem, trigger_problem, discriminant_problem ]: self.assertRaises(ValidationError, problem.clean)
def test_no_type(self): """Loading problem details form a ZIP with a JSON file without type field""" curr_path = os.path.dirname(__file__) zip_path = os.path.join(curr_path, self.ZIP_FOLDER, self.NO_TYPE) select_problem = SelectProblem(zipfile=zip_path) dml_problem = DMLProblem(zipfile=zip_path) function_problem = FunctionProblem(zipfile=zip_path) proc_problem = ProcProblem(zipfile=zip_path) trigger_problem = TriggerProblem(zipfile=zip_path) discriminant_problem = DiscriminantProblem(zipfile=zip_path) for problem in [ select_problem, dml_problem, function_problem, proc_problem, trigger_problem, discriminant_problem ]: self.assertRaises(ValidationError, problem.clean)
def test_trigger(self): """Tests for TriggerProblem.judge()""" collection = Collection() collection.save() create = '''CREATE TABLE Club( CIF CHAR(9) PRIMARY KEY, -- No puede ser NULL Nombre VARCHAR2(40) NOT NULL UNIQUE, Sede VARCHAR2(30) NOT NULL, Num_Socios NUMBER(10,0) NOT NULL, CONSTRAINT NumSociosPositivos CHECK (Num_Socios >= 0) );''' insert = """INSERT INTO Club VALUES ('11111111X', 'Real Madrid CF', 'Concha Espina', 70000); INSERT INTO Club VALUES ('11111112X', 'Futbol Club Barcelona', 'Aristides Maillol', 80000); INSERT INTO Club VALUES ('11111113X', 'PSG', 'Rue du Commandant Guilbaud', 1000);""" solution = """ CREATE OR REPLACE TRIGGER IncrementaNumSocios BEFORE INSERT OR UPDATE ON Club FOR EACH ROW DECLARE numClubes NUMBER; BEGIN SELECT 4000 INTO numClubes FROM DUAL; :NEW.Num_Socios := :NEW.Num_Socios + numClubes * 100; END;""" tests = """ INSERT INTO Club VALUES ('22222222X', 'FALSO', 'Calle', 30000); UPDATE Club SET Num_Socios = 70000 WHERE CIF = '11111113X';""" # Time-limit tle = """ CREATE OR REPLACE TRIGGER IncrementaNumSocios BEFORE INSERT OR UPDATE ON Club FOR EACH ROW DECLARE numClubes NUMBER; IN_TIME INT := 11; --num seconds v_now DATE; BEGIN LOOP EXIT WHEN v_now + (IN_TIME * (1/86400)) <= SYSDATE; END LOOP; SELECT 4000 INTO numClubes FROM DUAL; :NEW.Num_Socios := :NEW.Num_Socios + numClubes * 100; END;""" # Error in the trigger code compile_error = """ CREATE OR REPLACE TRIGGER IncrementaNumSocios BEFORE INSERT OR UPDATE ON Club FOR EACH ROW DECLARE numClubes NUMBER; BEGIN SELECT 4000 INTO numClubes FROM DUAL; :NEW.Num_Socios := :NEW.Num_Socios + numClubes 100; -- missing operator END;""" # Division by zero runtime_error = """ CREATE OR REPLACE TRIGGER IncrementaNumSocios BEFORE INSERT OR UPDATE ON Club FOR EACH ROW DECLARE numClubes NUMBER; BEGIN SELECT 4000 INTO numClubes FROM DUAL; :NEW.Num_Socios := :NEW.Num_Socios + numClubes / 0; END;""" wrong_answer = """ CREATE OR REPLACE TRIGGER IncrementaNumSocios BEFORE INSERT OR UPDATE ON Club FOR EACH ROW DECLARE numClubes NUMBER; BEGIN SELECT 4000 INTO numClubes FROM DUAL; :NEW.Num_Socios := :NEW.Num_Socios + numClubes * 10000; END;""" oracle = OracleExecutor.get() problem = TriggerProblem(title_md='Test Function', text_md='bla bla bla', create_sql=create, insert_sql=insert, collection=collection, author=None, solution=solution, tests=tests) problem.clean() # Needed to compute extra HTML fields and solutions problem.save() # Time-limit self.assert_executor_exception(lambda: problem.judge(tle, oracle), OracleStatusCode.TLE_USER_CODE) # Error when compiling user function self.assert_executor_exception( lambda: problem.judge(compile_error, oracle), OracleStatusCode.COMPILATION_ERROR) # Error when invoking user function self.assert_executor_exception( lambda: problem.judge(runtime_error, oracle), OracleStatusCode.EXECUTE_USER_CODE) # Correct solution self.assertEqual(problem.judge(solution, oracle)[0], VeredictCode.AC) # Incorrect solution self.assertEqual( problem.judge(wrong_answer, oracle)[0], VeredictCode.WA)
def test_compile_error(self): """Submitting code for a function/procedure/trigger with a compile error does resturn a OracleStatusCode.COMPILATION_ERROR""" curr_path = os.path.dirname(__file__) zip_function_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.FUNCTION_OK) zip_proc_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.PROC_OK) zip_trigger_path = os.path.join(curr_path, ParseTest.ZIP_FOLDER, ParseTest.TRIGGER_OK) client = Client() collection = create_collection('Colleccion de prueba AAA') user = create_user('54522', 'antonio') client.login(username='******', password='******') function_problem = FunctionProblem(zipfile=zip_function_path, collection=collection, author=user) proc_problem = ProcProblem(zipfile=zip_proc_path, collection=collection, author=user) trigger_problem = TriggerProblem(zipfile=zip_trigger_path, collection=collection, author=user) funct_compile_error = """ CREATE OR REPLACE FUNCTION golesLocal(resultado VARCHAR2) RETURN NUMBER IS posGuion NUMBER; golesStr VARCHAR2(3); BEGIN posGuion := INSTR(resultado, '-') -- Missing ';' golesStr := SUBSTR(resultado, 0, posGuion - 1); RETURN TO_NUMBER(golesStr); END;""" proc_compile_error = """ CREATE OR REPLACE PROCEDURE actualiza_socios(club_cif CHAR) IS CURSOR cr_partidos IS SELECT CL.Nombre AS ClubL, CV.Nombre AS ClubV, COUNT(*) AS NAsistentes FROM Enfrenta JOIN Asiste USING(CIF_local, CIF_visitante) JOIN Club CL ON CL.CIF = CIF_Local JOIN Club CV ON CV.CIF = CIF_visitante WHERE CIF_local = club_cif OR CIF_visitante = club_cif GROUP BY CIF_local, CIF_visitante, CL.Nombre, CV.Nombre; incrPartido NUMBER; incrTotal NUMBER := 0; nPartido NUMBER := 1; nombreClub VARCHAR2(200); BEGIN SELECT Nombre INTO nombreClub FROM Club WHERE CIF = club_cif; FOR partido IN cr_partidos LOOP IF partido.NAsistentes > 3 THEN incrPartido := 100; ELSIF partido.NAsistentes > 1 -- Missing 'THEN' incrPartido := 10; ELSE incrPartido := 0; END IF; incrTotal := incrTotal + incrPartido; nPartido := nPartido + 1; END LOOP; UPDATE Club SET Num_Socios = Num_Socios + incrTotal WHERE CIF = club_cif; END;""" trigger_compile_error = """ CREATE OR REPLACE TRIGGER DispCantidadFinanciacion BEFORE INSERT OR UPDATE ON Financia FOR EACH ROW DECLARE numJug NUMBER; BEGIN SELECT COUNT(*) INTO numJug FROM Jugador WHERE CIF = :NEW.CIF_C; IF numJug >= 2 THEN :NEW.Cantidad := :NEW.Cantidad 1.25; -- Missing operator END IF; END;""" for (problem, code) in [(function_problem, funct_compile_error), (proc_problem, proc_compile_error), (trigger_problem, trigger_compile_error)]: problem.clean() problem.save() submit_url = reverse('judge:submit', args=[problem.pk]) response = client.post(submit_url, {'code': code}, follow=True) self.assertEqual(response.json()['veredict'], VeredictCode.WA) self.assertIn('error', response.json()['feedback']) self.assertIn('compil', response.json()['feedback'])