diff options
author | ankitjavalkar | 2015-04-30 11:21:49 +0530 |
---|---|---|
committer | ankitjavalkar | 2015-04-30 11:29:35 +0530 |
commit | d8847656ba79e51c96c6e3650374aaf616c375dc (patch) | |
tree | 9e4ca3c29bc082cdd7b6775208544c2da9cee374 | |
parent | 18df6d88a2e1a9dfe7d05ca97b2d69ff0569e088 (diff) | |
download | online_test-d8847656ba79e51c96c6e3650374aaf616c375dc.tar.gz online_test-d8847656ba79e51c96c6e3650374aaf616c375dc.tar.bz2 online_test-d8847656ba79e51c96c6e3650374aaf616c375dc.zip |
Code Review: Code Refactoring
-rw-r--r-- | testapp/exam/bash_code_evaluator.py (renamed from testapp/exam/evaluate_bash_code.py) | 49 | ||||
-rw-r--r-- | testapp/exam/c_cpp_code_evaluator.py (renamed from testapp/exam/evaluate_c_code.py) | 67 | ||||
-rw-r--r-- | testapp/exam/code_evaluator.py (renamed from testapp/exam/evaluate_code.py) | 90 | ||||
-rwxr-xr-x | testapp/exam/code_server.py | 38 | ||||
-rw-r--r-- | testapp/exam/evaluate_cpp_code.py | 48 | ||||
-rw-r--r-- | testapp/exam/evaluate_java_code.py | 50 | ||||
-rw-r--r-- | testapp/exam/java_code_evaluator.py | 166 | ||||
-rw-r--r-- | testapp/exam/language_registry.py | 30 | ||||
-rw-r--r-- | testapp/exam/python_code_evaluator.py (renamed from testapp/exam/evaluate_python_code.py) | 37 | ||||
-rw-r--r-- | testapp/exam/scilab_code_evaluator.py (renamed from testapp/exam/evaluate_scilab_code.py) | 67 | ||||
-rw-r--r-- | testapp/exam/settings.py | 8 | ||||
-rw-r--r-- | testapp/test_server.py | 78 |
12 files changed, 506 insertions, 222 deletions
diff --git a/testapp/exam/evaluate_bash_code.py b/testapp/exam/bash_code_evaluator.py index 2905d65..60f0bb3 100644 --- a/testapp/exam/evaluate_bash_code.py +++ b/testapp/exam/bash_code_evaluator.py @@ -7,32 +7,53 @@ import subprocess import importlib # local imports -from evaluate_code import EvaluateCode -from language_registry import registry +from code_evaluator import CodeEvaluator +# from language_registry import registry -class EvaluateBashCode(EvaluateCode): +class BashCodeEvaluator(CodeEvaluator): """Tests the Bash code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): - submit_path = self.create_submit_code_file('submit.sh') - self.set_file_as_executable(submit_path) + def __init__(self, test_case_data, language, user_answer, + ref_code_path=None, in_dir=None): + super(BashCodeEvaluator, self).__init__(test_case_data, language, user_answer, + ref_code_path, in_dir) + self.submit_path = self.create_submit_code_file('submit.sh') + self.test_case_args = self.setup_code_evaluator() + + def setup_code_evaluator(self): + super(BashCodeEvaluator, self).setup_code_evaluator() + + self.set_file_as_executable(self.submit_path) get_ref_path, get_test_case_path = self.ref_code_path.strip().split(',') get_ref_path = get_ref_path.strip() get_test_case_path = get_test_case_path.strip() ref_path, test_case_path = self.set_test_code_file_path(get_ref_path, get_test_case_path) - success, err = self._check_bash_script(ref_path, submit_path, - test_case_path) + return ref_path, self.submit_path, test_case_path + + + # # Public Protocol ########## + # def evaluate_code(self): + # submit_path = self.create_submit_code_file('submit.sh') + # self.set_file_as_executable(submit_path) + # get_ref_path, get_test_case_path = self.ref_code_path.strip().split(',') + # get_ref_path = get_ref_path.strip() + # get_test_case_path = get_test_case_path.strip() + # ref_path, test_case_path = self.set_test_code_file_path(get_ref_path, + # get_test_case_path) + + # success, err = self._check_bash_script(ref_path, submit_path, + # test_case_path) + + # # Delete the created file. + # os.remove(submit_path) - # Delete the created file. - os.remove(submit_path) + # return success, err - return success, err # Private Protocol ########## - def _check_bash_script(self, ref_path, submit_path, + def check_code(self, ref_path, submit_path, test_case_path=None): """ Function validates student script using instructor script as reference. Test cases can optionally be provided. The first argument @@ -116,4 +137,4 @@ class EvaluateBashCode(EvaluateCode): return False, err -registry.register('bash', EvaluateBashCode) +# registry.register('bash', EvaluateBashCode) diff --git a/testapp/exam/evaluate_c_code.py b/testapp/exam/c_cpp_code_evaluator.py index 0b9e352..d611f96 100644 --- a/testapp/exam/evaluate_c_code.py +++ b/testapp/exam/c_cpp_code_evaluator.py @@ -7,25 +7,32 @@ import subprocess import importlib # local imports -from evaluate_code import EvaluateCode -from language_registry import registry +from code_evaluator import CodeEvaluator +# from language_registry import registry -class EvaluateCCode(EvaluateCode): +class CCppCodeEvaluator(CodeEvaluator): """Tests the C code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): - submit_path = self.create_submit_code_file('submit.c') + def __init__(self, test_case_data, language, user_answer, + ref_code_path=None, in_dir=None): + super(CCppCodeEvaluator, self).__init__(test_case_data, language, user_answer, + ref_code_path, in_dir) + self.submit_path = self.create_submit_code_file('submit.c') + self.test_case_args = self.setup_code_evaluator() + + # Private Protocol ########## + def setup_code_evaluator(self): + super(CCppCodeEvaluator, self).setup_code_evaluator() + get_ref_path = self.ref_code_path ref_path, test_case_path = self.set_test_code_file_path(get_ref_path) - success = False # Set file paths c_user_output_path = os.getcwd() + '/output' c_ref_output_path = os.getcwd() + '/executable' # Set command variables - compile_command = 'g++ {0} -c -o {1}'.format(submit_path, + compile_command = 'g++ {0} -c -o {1}'.format(self.submit_path, c_user_output_path) compile_main = 'g++ {0} {1} -o {2}'.format(ref_path, c_user_output_path, @@ -34,16 +41,43 @@ class EvaluateCCode(EvaluateCode): remove_user_output = c_user_output_path remove_ref_output = c_ref_output_path - success, err = self.check_code(ref_path, submit_path, compile_command, - compile_main, run_command_args, - remove_user_output, remove_ref_output) + return ref_path, self.submit_path, compile_command, compile_main, run_command_args, remove_user_output, remove_ref_output + def teardown_code_evaluator(self): # Delete the created file. - os.remove(submit_path) - - return success, err + super(CCppCodeEvaluator, self).teardown_code_evaluator() + os.remove(self.submit_path) + + # # Public Protocol ########## + # def evaluate_code(self): + # submit_path = self.create_submit_code_file('submit.c') + # get_ref_path = self.ref_code_path + # ref_path, test_case_path = self.set_test_code_file_path(get_ref_path) + # success = False + + # # Set file paths + # c_user_output_path = os.getcwd() + '/output' + # c_ref_output_path = os.getcwd() + '/executable' + + # # Set command variables + # compile_command = 'g++ {0} -c -o {1}'.format(submit_path, + # c_user_output_path) + # compile_main = 'g++ {0} {1} -o {2}'.format(ref_path, + # c_user_output_path, + # c_ref_output_path) + # run_command_args = [c_ref_output_path] + # remove_user_output = c_user_output_path + # remove_ref_output = c_ref_output_path + + # success, err = self.check_code(ref_path, submit_path, compile_command, + # compile_main, run_command_args, + # remove_user_output, remove_ref_output) + + # # Delete the created file. + # os.remove(submit_path) + + # return success, err - # Public Protocol ########## def check_code(self, ref_code_path, submit_code_path, compile_command, compile_main, run_command_args, remove_user_output, remove_ref_output): @@ -122,7 +156,6 @@ class EvaluateCCode(EvaluateCode): return success, err - # Public Protocol ########## def remove_null_substitute_char(self, string): """Returns a string without any null and substitute characters""" stripped = "" @@ -132,4 +165,4 @@ class EvaluateCCode(EvaluateCode): return ''.join(stripped) -registry.register('c', EvaluateCCode) +# registry.register('c', EvaluateCCode) diff --git a/testapp/exam/evaluate_code.py b/testapp/exam/code_evaluator.py index b9892ed..3cc7374 100644 --- a/testapp/exam/evaluate_code.py +++ b/testapp/exam/code_evaluator.py @@ -9,7 +9,6 @@ from multiprocessing import Process, Queue import subprocess import re import json -import importlib # Local imports. from settings import SERVER_PORTS, SERVER_TIMEOUT, SERVER_POOL_PORT @@ -53,7 +52,7 @@ def delete_signal_handler(): ############################################################################### # `TestCode` class. ############################################################################### -class EvaluateCode(object): +class CodeEvaluator(object): """Tests the code obtained from Code Server""" def __init__(self, test_case_data, language, user_answer, ref_code_path=None, in_dir=None): @@ -65,9 +64,9 @@ class EvaluateCode(object): self.user_answer = user_answer self.ref_code_path = ref_code_path self.in_dir = in_dir + self.test_case_args = None # Public Protocol ########## - @classmethod def from_json(cls, language, json_data, in_dir): json_data = json.loads(json_data) @@ -79,7 +78,53 @@ class EvaluateCode(object): in_dir) return instance - def run_code(self): + # def run_code(self): + # """Tests given code (`answer`) with the test cases based on + # given arguments. + + # The ref_code_path is a path to the reference code. + # The reference code will call the function submitted by the student. + # The reference code will check for the expected output. + + # If the path's start with a "/" then we assume they are absolute paths. + # If not, we assume they are relative paths w.r.t. the location of this + # code_server script. + + # If the optional `in_dir` keyword argument is supplied it changes the + # directory to that directory (it does not change it back to the original + # when done). + + # Returns + # ------- + + # A tuple: (success, error message). + # """ + # self._change_dir(self.in_dir) + + # # Add a new signal handler for the execution of this code. + # prev_handler = self.create_signal_handler() + # success = False + + # # Do whatever testing needed. + # try: + # success, err = self.evaluate_code() #pass *list where list is a list of args obtained from setup + + # except TimeoutException: + # err = self.timeout_msg + # except: + # type, value = sys.exc_info()[:2] + # err = "Error: {0}".format(repr(value)) + # finally: + # # Set back any original signal handler. + # self.set_original_signal_handler(prev_handler) + + # # Cancel the signal + # self.delete_signal_handler() + + # result = {'success': success, 'error': err} + # return result + + def code_evaluator(self): """Tests given code (`answer`) with the test cases based on given arguments. @@ -100,34 +145,47 @@ class EvaluateCode(object): A tuple: (success, error message). """ + + self.setup_code_evaluator() + success, err = self.evaluate_code(self.test_case_args) + self.teardown_code_evaluator() + + result = {'success': success, 'error': err} + return result + + # Public Protocol ########## + def setup_code_evaluator(self): self._change_dir(self.in_dir) + def evaluate_code(self, args): # Add a new signal handler for the execution of this code. prev_handler = create_signal_handler() success = False + args = args or [] # Do whatever testing needed. try: - success, err = self.evaluate_code() + success, err = self.check_code(*args) except TimeoutException: err = self.timeout_msg except: - type, value = sys.exc_info()[:2] + _type, value = sys.exc_info()[:2] err = "Error: {0}".format(repr(value)) finally: # Set back any original signal handler. set_original_signal_handler(prev_handler) + return success, err + + def teardown_code_evaluator(self): # Cancel the signal delete_signal_handler() - result = {'success': success, 'error': err} - return result - - def evaluate_code(self): - raise NotImplementedError("evaluate_code method not implemented") + def check_code(self): + raise NotImplementedError("check_code method not implemented") + # Private Protocol ########## def create_submit_code_file(self, file_name): """ Write the code (`answer`) to a file and set the file path""" submit_f = open(file_name, 'w') @@ -184,8 +242,14 @@ class EvaluateCode(object): raise return proc_compile, err - # Private Protocol ########## - def _change_dir(self, in_dir): if in_dir is not None and isdir(in_dir): os.chdir(in_dir) + + def remove_null_substitute_char(self, string): + """Returns a string without any null and substitute characters""" + stripped = "" + for c in string: + if ord(c) is not 26 and ord(c) is not 0: + stripped = stripped + c + return ''.join(stripped) diff --git a/testapp/exam/code_server.py b/testapp/exam/code_server.py index 697b131..c621dcd 100755 --- a/testapp/exam/code_server.py +++ b/testapp/exam/code_server.py @@ -32,13 +32,13 @@ import json import importlib # Local imports. from settings import SERVER_PORTS, SERVER_TIMEOUT, SERVER_POOL_PORT -from language_registry import registry -from evaluate_python_code import EvaluatePythonCode -from evaluate_c_code import EvaluateCCode -from evaluate_cpp_code import EvaluateCppCode -from evaluate_java_code import EvaluateJavaCode -from evaluate_scilab_code import EvaluateScilabCode -from evaluate_bash_code import EvaluateBashCode +from language_registry import set_registry +# from evaluate_python_code import EvaluatePythonCode +# from evaluate_c_code import EvaluateCCode +# from evaluate_cpp_code import EvaluateCppCode +# from evaluate_java_code import EvaluateJavaCode +# from evaluate_scilab_code import EvaluateScilabCode +# from evaluate_bash_code import EvaluateBashCode MY_DIR = abspath(dirname(__file__)) @@ -69,24 +69,15 @@ class CodeServer(object): """Calls relevant EvaluateCode class based on language to check the answer code """ - evaluate_code_instance = self.create_class_instance(language, - json_data, in_dir) - - result = evaluate_code_instance.run_code() + code_evaluator = self._create_evaluator_instance(language, json_data, + in_dir) + result = code_evaluator.code_evaluator() # Put us back into the server pool queue since we are free now. self.queue.put(self.port) return json.dumps(result) - # Public Protocol ########## - def create_class_instance(self, language, json_data, in_dir): - """Create instance of relevant EvaluateCode class based on language""" - cls = registry.get_class(language) - instance = cls.from_json(language, json_data, in_dir) - return instance - - # Public Protocol ########## def run(self): """Run XMLRPC server, serving our methods.""" server = SimpleXMLRPCServer(("localhost", self.port)) @@ -95,6 +86,15 @@ class CodeServer(object): self.queue.put(self.port) server.serve_forever() + # Private Protocol ########## + def _create_evaluator_instance(self, language, json_data, in_dir): + """Create instance of relevant EvaluateCode class based on language""" + set_registry() + registry = get_registry() + cls = registry.get_class(language) + instance = cls.from_json(language, json_data, in_dir) + return instance + ############################################################################### # `ServerPool` class. diff --git a/testapp/exam/evaluate_cpp_code.py b/testapp/exam/evaluate_cpp_code.py deleted file mode 100644 index 987d041..0000000 --- a/testapp/exam/evaluate_cpp_code.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -import traceback -import pwd -import os -from os.path import join, isfile -import subprocess -import importlib - -# local imports -from evaluate_c_code import EvaluateCCode -from evaluate_code import EvaluateCode -from language_registry import registry - - -class EvaluateCppCode(EvaluateCCode, EvaluateCode): - """Tests the C code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): - submit_path = self.create_submit_code_file('submitstd.cpp') - get_ref_path = self.ref_code_path - ref_path, test_case_path = self.set_test_code_file_path(get_ref_path) - success = False - - # Set file paths - c_user_output_path = os.getcwd() + '/output' - c_ref_output_path = os.getcwd() + '/executable' - - # Set command variables - compile_command = 'g++ {0} -c -o {1}'.format(submit_path, - c_user_output_path) - compile_main = 'g++ {0} {1} -o {2}'.format(ref_path, - c_user_output_path, - c_ref_output_path) - run_command_args = c_ref_output_path - remove_user_output = c_user_output_path - remove_ref_output = c_ref_output_path - - success, err = self.check_code(ref_path, submit_path, compile_command, - compile_main, run_command_args, - remove_user_output, remove_ref_output) - - # Delete the created file. - os.remove(submit_path) - - return success, err - - -registry.register('cpp', EvaluateCppCode) diff --git a/testapp/exam/evaluate_java_code.py b/testapp/exam/evaluate_java_code.py deleted file mode 100644 index d04be4e..0000000 --- a/testapp/exam/evaluate_java_code.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -import traceback -import pwd -import os -from os.path import join, isfile -import subprocess -import importlib - -# local imports -from evaluate_c_code import EvaluateCCode -from evaluate_code import EvaluateCode -from language_registry import registry - - -class EvaluateJavaCode(EvaluateCCode, EvaluateCode): - """Tests the C code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): - submit_path = self.create_submit_code_file('Test.java') - ref_path, test_case_path = self.set_test_code_file_path(self.ref_code_path) - success = False - - # Set file paths - java_student_directory = os.getcwd() + '/' - java_ref_file_name = (ref_path.split('/')[-1]).split('.')[0] - - # Set command variables - compile_command = 'javac {0}'.format(submit_path), - compile_main = ('javac {0} -classpath ' - '{1} -d {2}').format(ref_path, - java_student_directory, - java_student_directory) - run_command_args = "java -cp {0} {1}".format(java_student_directory, - java_ref_file_name) - remove_user_output = "{0}{1}.class".format(java_student_directory, - 'Test') - remove_ref_output = "{0}{1}.class".format(java_student_directory, - java_ref_file_name) - - success, err = self.check_code(ref_path, submit_path, compile_command, - compile_main, run_command_args, - remove_user_output, remove_ref_output) - - # Delete the created file. - os.remove(submit_path) - - return success, err - - -registry.register('java', EvaluateJavaCode) diff --git a/testapp/exam/java_code_evaluator.py b/testapp/exam/java_code_evaluator.py new file mode 100644 index 0000000..4a80acd --- /dev/null +++ b/testapp/exam/java_code_evaluator.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +import traceback +import pwd +import os +from os.path import join, isfile +import subprocess +import importlib + +# local imports +# from c_code_evaluator import CCodeEvaluator +from code_evaluator import CodeEvaluator +# from language_registry import registry + + +class JavaCodeEvaluator(CodeEvaluator): + """Tests the Java code obtained from Code Server""" + def __init__(self, test_case_data, language, user_answer, + ref_code_path=None, in_dir=None): + super(JavaCodeEvaluator, self).__init__(test_case_data, language, user_answer, + ref_code_path, in_dir) + self.submit_path = self.create_submit_code_file('Test.java') + self.test_case_args = self.setup_code_evaluator() + + # Private Protocol ########## + def setup_code_evaluator(self): + super(JavaCodeEvaluator, self).setup_code_evaluator() + + ref_path, test_case_path = self.set_test_code_file_path(self.ref_code_path) + + # Set file paths + java_student_directory = os.getcwd() + '/' + java_ref_file_name = (ref_path.split('/')[-1]).split('.')[0] + + # Set command variables + compile_command = 'javac {0}'.format(submit_path), + compile_main = ('javac {0} -classpath ' + '{1} -d {2}').format(ref_path, + java_student_directory, + java_student_directory) + run_command_args = "java -cp {0} {1}".format(java_student_directory, + java_ref_file_name) + remove_user_output = "{0}{1}.class".format(java_student_directory, + 'Test') + remove_ref_output = "{0}{1}.class".format(java_student_directory, + java_ref_file_name) + + return ref_path, submit_path, compile_command, compile_main, run_command_args, remove_user_output, remove_ref_output + + def teardown_code_evaluator(self): + # Delete the created file. + super(JavaCodeEvaluator, self).teardown_code_evaluator() + os.remove(self.submit_path) + + + # Public Protocol ########## + # def evaluate_code(self): + # submit_path = self.create_submit_code_file('Test.java') + # ref_path, test_case_path = self.set_test_code_file_path(self.ref_code_path) + # success = False + + # # Set file paths + # java_student_directory = os.getcwd() + '/' + # java_ref_file_name = (ref_path.split('/')[-1]).split('.')[0] + + # # Set command variables + # compile_command = 'javac {0}'.format(submit_path), + # compile_main = ('javac {0} -classpath ' + # '{1} -d {2}').format(ref_path, + # java_student_directory, + # java_student_directory) + # run_command_args = "java -cp {0} {1}".format(java_student_directory, + # java_ref_file_name) + # remove_user_output = "{0}{1}.class".format(java_student_directory, + # 'Test') + # remove_ref_output = "{0}{1}.class".format(java_student_directory, + # java_ref_file_name) + + # success, err = self.check_code(ref_path, submit_path, compile_command, + # compile_main, run_command_args, + # remove_user_output, remove_ref_output) + + # # Delete the created file. + # os.remove(submit_path) + + # return success, err + + def check_code(self, ref_code_path, submit_code_path, compile_command, + compile_main, run_command_args, remove_user_output, + remove_ref_output): + """ Function validates student code using instructor code as + reference.The first argument ref_code_path, is the path to + instructor code, it is assumed to have executable permission. + The second argument submit_code_path, is the path to the student + code, it is assumed to have executable permission. + + Returns + -------- + + returns (True, "Correct answer") : If the student function returns + expected output when called by reference code. + + returns (False, error_msg): If the student function fails to return + expected output when called by reference code. + + Returns (False, error_msg): If mandatory arguments are not files or + if the required permissions are not given to the file(s). + + """ + if not isfile(ref_code_path): + return False, "No file at %s or Incorrect path" % ref_code_path + if not isfile(submit_code_path): + return False, 'No file at %s or Incorrect path' % submit_code_path + + success = False + # output_path = os.getcwd() + '/output' + ret = self.compile_command(compile_command) + proc, stdnt_stderr = ret + # if self.language == "java": + stdnt_stderr = self.remove_null_substitute_char(stdnt_stderr) + + # Only if compilation is successful, the program is executed + # And tested with testcases + if stdnt_stderr == '': + ret = self.compile_command(compile_main) + proc, main_err = ret + # if self.language == "java": + main_err = self.remove_null_substitute_char(main_err) + + if main_err == '': + ret = self.run_command(run_command_args, stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + proc, stdout, stderr = ret + if proc.returncode == 0: + success, err = True, "Correct answer" + else: + err = stdout + "\n" + stderr + os.remove(remove_ref_output) + else: + err = "Error:" + try: + error_lines = main_err.splitlines() + for e in error_lines: + if ':' in e: + err = err + "\n" + e.split(":", 1)[1] + else: + err = err + "\n" + e + except: + err = err + "\n" + main_err + os.remove(remove_user_output) + else: + err = "Compilation Error:" + try: + error_lines = stdnt_stderr.splitlines() + for e in error_lines: + if ':' in e: + err = err + "\n" + e.split(":", 1)[1] + else: + err = err + "\n" + e + except: + err = err + "\n" + stdnt_stderr + + return success, err + + +# registry.register('java', EvaluateJavaCode) diff --git a/testapp/exam/language_registry.py b/testapp/exam/language_registry.py index 4d56de2..8700d32 100644 --- a/testapp/exam/language_registry.py +++ b/testapp/exam/language_registry.py @@ -1,17 +1,31 @@ -#!/usr/bin/env python +from settings import language_register +registry = None -class LanguageRegistry(object): +def set_registry(): + globals registry = _LanguageRegistry() + +def get_registry(): + return registry + +class _LanguageRegistry(object): def __init__(self): - self._registry = {} + for language, module in language_register.iteritems(): + self._register[language] = None # Public Protocol ########## def get_class(self, language): - return self._registry[language] + if not self._register[language]: + self._register[language] = language_register[language] - # Public Protocol ########## - def register(self, language, cls): - self._registry[language] = cls + cls = self._register[language] + module_name, class_name = cls.split(".") + # load the module, will raise ImportError if module cannot be loaded + get_module = importlib.import_module(module_name) + # get the class, will raise AttributeError if class cannot be found + get_class = getattr(get_module, class_name) + return get_class + # def register(self, language, cls): + # self._register[language] = cls -registry = LanguageRegistry() diff --git a/testapp/exam/evaluate_python_code.py b/testapp/exam/python_code_evaluator.py index 8892ae6..05a5063 100644 --- a/testapp/exam/evaluate_python_code.py +++ b/testapp/exam/python_code_evaluator.py @@ -6,14 +6,14 @@ from os.path import join import importlib # local imports -from evaluate_code import EvaluateCode -from language_registry import registry +from code_evaluator import CodeEvaluator +# from language_registry import registry -class EvaluatePythonCode(EvaluateCode): +class PythonCodeEvaluator(CodeEvaluator): """Tests the Python code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): + # Private Protocol ########## + def check_code(self): success = False try: @@ -37,6 +37,31 @@ class EvaluatePythonCode(EvaluateCode): del tb return success, err + # # Public Protocol ########## + # def evaluate_code(self): + # success = False + + # try: + # tb = None + # test_code = self._create_test_case() + # submitted = compile(self.user_answer, '<string>', mode='exec') + # g = {} + # exec submitted in g + # _tests = compile(test_code, '<string>', mode='exec') + # exec _tests in g + # except AssertionError: + # type, value, tb = sys.exc_info() + # info = traceback.extract_tb(tb) + # fname, lineno, func, text = info[-1] + # text = str(test_code).splitlines()[lineno-1] + # err = "{0} {1} in: {2}".format(type.__name__, str(value), text) + # else: + # success = True + # err = 'Correct answer' + + # del tb + # return success, err + # Private Protocol ########## def _create_test_case(self): """ @@ -57,4 +82,4 @@ class EvaluatePythonCode(EvaluateCode): return test_code -registry.register('python', EvaluatePythonCode) +# registry.register('python', EvaluatePythonCode) diff --git a/testapp/exam/evaluate_scilab_code.py b/testapp/exam/scilab_code_evaluator.py index 899abc9..073fbcb 100644 --- a/testapp/exam/evaluate_scilab_code.py +++ b/testapp/exam/scilab_code_evaluator.py @@ -7,16 +7,33 @@ import re import importlib # local imports -from evaluate_code import EvaluateCode -from language_registry import registry +from code_evaluator import CodeEvaluator +# from language_registry import registry -class EvaluateScilabCode(EvaluateCode): +class ScilabCodeEvaluator(CodeEvaluator): """Tests the Scilab code obtained from Code Server""" - # Public Protocol ########## - def evaluate_code(self): - submit_path = self.create_submit_code_file('function.sci') - ref_path, test_case_path = self.set_test_code_file_path() + def __init__(self, test_case_data, language, user_answer, + ref_code_path=None, in_dir=None): + super(ScilabCodeEvaluator, self).__init__(test_case_data, language, user_answer, + ref_code_path, in_dir) + self.submit_path = self.create_submit_code_file('function.sci') + self.test_case_args = self.setup_code_evaluator() + + # Private Protocol ########## + def setup_code_evaluator(self): + super(ScilabCodeEvaluator, self).setup_code_evaluator() + + ref_path, test_case_path = self.set_test_code_file_path(self.ref_code_path) + + return ref_path, # Return as a tuple + + def teardown_code_evaluator(self): + # Delete the created file. + super(ScilabCodeEvaluator, self).teardown_code_evaluator() + os.remove(self.submit_path) + + def check_code(self, ref_path): success = False cmd = 'printf "lines(0)\nexec(\'{0}\',2);\nquit();"'.format(ref_path) @@ -39,11 +56,39 @@ class EvaluateScilabCode(EvaluateCode): else: err = add_err + stderr - # Delete the created file. - os.remove(submit_path) - return success, err + # # Public Protocol ########## + # def evaluate_code(self): + # submit_path = self.create_submit_code_file('function.sci') + # ref_path, test_case_path = self.set_test_code_file_path() + # success = False + + # cmd = 'printf "lines(0)\nexec(\'{0}\',2);\nquit();"'.format(ref_path) + # cmd += ' | timeout 8 scilab-cli -nb' + # ret = self.run_command(cmd, + # shell=True, + # stdout=subprocess.PIPE, + # stderr=subprocess.PIPE) + # proc, stdout, stderr = ret + + # # Get only the error. + # stderr = self._get_error(stdout) + # if stderr is None: + # # Clean output + # stdout = self._strip_output(stdout) + # if proc.returncode == 5: + # success, err = True, "Correct answer" + # else: + # err = add_err + stdout + # else: + # err = add_err + stderr + + # # Delete the created file. + # os.remove(submit_path) + + # return success, err + # Private Protocol ########## def _remove_scilab_exit(self, string): """ @@ -83,4 +128,4 @@ class EvaluateScilabCode(EvaluateCode): return strip_out -registry.register('scilab', EvaluateScilabCode) +# registry.register('scilab', EvaluateScilabCode) diff --git a/testapp/exam/settings.py b/testapp/exam/settings.py index 682516f..497a620 100644 --- a/testapp/exam/settings.py +++ b/testapp/exam/settings.py @@ -18,3 +18,11 @@ SERVER_TIMEOUT = 2 # reason set this to the root you have to serve at. In the above example # host.org/foo/exam set URL_ROOT='/foo' URL_ROOT = '' + +language_register = {"python": "python_code_evaluator", + "c": "c_cpp_code_evaluator", + "cpp": "c_cpp_code_evaluator", + "java": "java_evaluator", + "bash": "bash_evaluator", + "scilab": "scilab_evaluator", + } diff --git a/testapp/test_server.py b/testapp/test_server.py index 39995e1..9319d5b 100644 --- a/testapp/test_server.py +++ b/testapp/test_server.py @@ -1,12 +1,15 @@ import unittest import os -from exam import evaluate_c_code, evaluate_cpp_code, evaluate_java_code, evaluate_python_code, evaluate_scilab_code, evaluate_bash_code -from exam.language_registry import registry +# from exam import evaluate_c_code, evaluate_cpp_code, evaluate_java_code, evaluate_python_code, evaluate_scilab_code, evaluate_bash_code +from exam import c_cpp_code_evaluator, bash_code_evaluator, python_code_evaluator, scilab_code_evaluator, java_code_evaluator +from exam.language_registry import set_registry, get_registry from exam.settings import SERVER_TIMEOUT + class RegistryTestCase(unittest.TestCase): def setUp(self): - self.registry_object = registry + set_registry() + self.registry_object = get_registry() def test_set_register(self): self.registry_object.register("demo_language", "demo_object") @@ -17,6 +20,9 @@ class RegistryTestCase(unittest.TestCase): cls = self.registry_object.get_class("demo_language") self.assertEquals(cls, "demo_object") + def tearDown(self): + self.registry_object = None + ############################################################################### class PythonEvaluationTestCases(unittest.TestCase): @@ -33,8 +39,8 @@ class PythonEvaluationTestCases(unittest.TestCase): def test_correct_answer(self): user_answer = "def add(a, b):\n\treturn a + b""" - get_class = evaluate_python_code.EvaluatePythonCode(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) - result = get_class.run_code() + get_class = python_code_evaluator.PythonCodeEvaluator(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") @@ -46,8 +52,8 @@ class PythonEvaluationTestCases(unittest.TestCase): "pos_args": ["3", "2"], "kw_args": {} }] - get_class = evaluate_python_code.EvaluatePythonCode(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) - result = get_class.run_code() + get_class = python_code_evaluator.PythonCodeEvaluator(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertEqual(result.get("error"), "AssertionError in: assert add(3, 2) == 5") @@ -59,8 +65,8 @@ class PythonEvaluationTestCases(unittest.TestCase): "pos_args": ["3", "2"], "kw_args": {} }] - get_class = evaluate_python_code.EvaluatePythonCode(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) - result = get_class.run_code() + get_class = python_code_evaluator.PythonCodeEvaluator(self.test_case_data, self.language, user_answer, ref_code_path=None, in_dir=None) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertEquals(result.get("error"), self.timeout_msg) @@ -78,24 +84,24 @@ class CEvaluationTestCases(unittest.TestCase): def test_correct_answer(self): user_answer = "int add(int a, int b)\n{return a+b;}" - get_class = evaluate_c_code.EvaluateCCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") def test_compilation_error(self): user_answer = "int add(int a, int b)\n{return a+b}" - get_class = evaluate_c_code.EvaluateCCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertTrue("Compilation Error" in result.get("error")) def test_infinite_loop(self): user_answer = "int add(int a, int b)\n{while(1>0){}}" - get_class = evaluate_c_code.EvaluateCCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertEquals(result.get("error"), self.timeout_msg) @@ -114,24 +120,24 @@ class CppEvaluationTestCases(unittest.TestCase): def test_correct_answer(self): user_answer = "int add(int a, int b)\n{return a+b;}" - get_class = evaluate_cpp_code.EvaluateCppCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") def test_compilation_error(self): user_answer = "int add(int a, int b)\n{return a+b}" - get_class = evaluate_cpp_code.EvaluateCppCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertTrue("Compilation Error" in result.get("error")) def test_infinite_loop(self): user_answer = "int add(int a, int b)\n{while(1>0){}}" - get_class = evaluate_cpp_code.EvaluateCppCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = c_cpp_code_evaluator.CCppCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertEquals(result.get("error"), self.timeout_msg) @@ -149,25 +155,25 @@ class BashEvaluationTestCases(unittest.TestCase): def test_correct_answer(self): user_answer = "#!/bin/bash\n[[ $# -eq 2 ]] && echo $(( $1 + $2 )) && exit $(( $1 + $2 ))" - get_class = evaluate_bash_code.EvaluateBashCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = bash_code_evaluator.BashCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") def test_error(self): user_answer = "#!/bin/bash\n[[ $# -eq 2 ]] && echo $(( $1 - $2 )) && exit $(( $1 - $2 ))" - get_class = evaluate_bash_code.EvaluateBashCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() - + get_class = bash_code_evaluator.BashCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() + self.assertFalse(result.get("success")) self.assertTrue("Error" in result.get("error")) def test_infinite_loop(self): user_answer = "#!/bin/bash\nwhile [ 1 ] ; do echo "" > /dev/null ; done" - get_class = evaluate_bash_code.EvaluateBashCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() - + get_class = bash_code_evaluator.BashCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() + self.assertFalse(result.get("success")) self.assertEquals(result.get("error"), self.timeout_msg) @@ -198,7 +204,7 @@ class JavaEvaluationTestCases(unittest.TestCase): self.assertFalse(result.get("success")) self.assertTrue("Error" in result.get("error")) - def test_infinite_loop(self): + def test_infinite_loop(self): user_answer = "class Test {\n\tint square_num(int a) {\n\t\twhile(0==0){\n\t\t}\n\t}\n}" get_class = evaluate_java_code.EvaluateJavaCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) result = get_class.run_code() @@ -217,24 +223,24 @@ class ScilabEvaluationTestCases(unittest.TestCase): def test_correct_answer(self): user_answer = "funcprot(0)\nfunction[c]=add(a,b)\n\tc=a+b;\nendfunction" - get_class = evaluate_scilab_code.EvaluateScilabCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = scilab_code_evaluator.ScilabCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") def test_correct_answer_2(self): user_answer = "funcprot(0)\nfunction[c]=add(a,b)\n\tc=a-b;\nendfunction" - get_class = evaluate_scilab_code.EvaluateScilabCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = scilab_code_evaluator.ScilabCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertTrue(result.get("success")) self.assertEqual(result.get("error"), "Correct answer") def test_error(self): user_answer = "funcprot(0)\nfunction[c]=add(a,b)\n\t c=a+b;\ndis(\tendfunction" - get_class = evaluate_java_code.EvaluateJavaCode(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) - result = get_class.run_code() + get_class = scilab_code_evaluator.ScilabCodeEvaluator(self.test_case_data, self.language, user_answer, self.ref_code_path, self.in_dir) + result = get_class.code_evaluator() self.assertFalse(result.get("success")) self.assertTrue("Error" in result.get("error")) |