diff options
-rwxr-xr-x | testapp/code_server.py | 128 | ||||
-rw-r--r-- | testapp/exam/forms.py | 1 | ||||
-rw-r--r-- | testapp/exam/models.py | 1 | ||||
-rw-r--r-- | testapp/exam/xmlrpc_clients.py | 1 | ||||
-rw-r--r-- | testapp/scilab_files/test_add.sce | 29 | ||||
-rw-r--r-- | testapp/test_server.py | 46 |
6 files changed, 204 insertions, 2 deletions
diff --git a/testapp/code_server.py b/testapp/code_server.py index 88e374c..792197d 100755 --- a/testapp/code_server.py +++ b/testapp/code_server.py @@ -28,7 +28,7 @@ from os.path import isdir, dirname, abspath, join, isfile import signal from multiprocessing import Process, Queue import subprocess - +import re # Local imports. from settings import SERVER_PORTS, SERVER_TIMEOUT, SERVER_POOL_PORT @@ -658,6 +658,132 @@ class CodeServer(object): if ord(c) is not 26 and ord(c) is not 0: stripped = stripped + c return ''.join(stripped) + + def run_scilab_code(self, answer, test_code, in_dir=None): + """Tests given Scilab function (`answer`) with the `test_code` + supplied. 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). This function also timesout + when the function takes more than SERVER_TIMEOUT seconds to run + to prevent runaway code. + + The testcode 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. + + Returns + ------- + + A tuple: (success, error message). + + """ + if in_dir is not None and isdir(in_dir): + os.chdir(in_dir) + + # Removes all the commands that terminates scilab + answer,i = self._remove_scilab_exit(answer.lstrip()) + + # Throw message if there are commmands that terminates scilab + add_err="" + if i > 0: + add_err = "Please do not use exit, quit and abort commands in your\ + code.\n Otherwise your code will not be evaluated\ + correctly.\n" + + # The file extension should be .sci + submit_f = open('function.sci','w') + submit_f.write(answer) + submit_f.close() + submit_path = abspath(submit_f.name) + + ref_path = test_code.strip() + if not ref_path.startswith('/'): + ref_path = join(MY_DIR, ref_path) + + # Add a new signal handler for the execution of this code. + old_handler = signal.signal(signal.SIGALRM, timeout_handler) + signal.alarm(SERVER_TIMEOUT) + + # Do whatever testing needed. + success = False + try: + 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 + 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. + signal.signal(signal.SIGALRM, old_handler) + + # Delete the created file. + os.remove(submit_path) + + # Cancel the signal if any, see signal.alarm documentation. + signal.alarm(0) + + # Put us back into the server pool queue since we are free now. + self.queue.put(self.port) + + return success, err + + def _remove_scilab_exit(self, string): + """ + Removes exit, quit and abort from the scilab code + """ + new_string = "" + i=0 + for line in string.splitlines(): + new_line = re.sub(r"exit.*$","",line) + new_line = re.sub(r"quit.*$","",new_line) + new_line = re.sub(r"abort.*$","",new_line) + if line != new_line: + i=i+1 + new_string = new_string +'\n'+ new_line + return new_string, i + + def _get_error(self, string): + """ + Fetches only the error from the string. + Returns None if no error. + """ + obj = re.search("!.+\n.+",string); + if obj: + return obj.group() + return None + + def _strip_output(self, out): + """ + Cleans whitespace from the output + """ + strip_out = "Message" + for l in out.split('\n'): + if l.strip(): + strip_out = strip_out+"\n"+l.strip() + return strip_out def run(self): """Run XMLRPC server, serving our methods. diff --git a/testapp/exam/forms.py b/testapp/exam/forms.py index d711f6a..dc19783 100644 --- a/testapp/exam/forms.py +++ b/testapp/exam/forms.py @@ -19,6 +19,7 @@ QUESTION_TYPE_CHOICES = ( ("C", "C Language"), ("C++", "C++ Language"), ("java", "Java Language"), + ("scilab", "Scilab"), ) UNAME_CHARS = letters + "._" + digits diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 713260b..758091f 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -23,6 +23,7 @@ QUESTION_TYPE_CHOICES = ( ("C", "C Language"), ("C++", "C++ Language"), ("java", "Java Language"), + ("scilab", "Scilab"), ) ################################################################################ diff --git a/testapp/exam/xmlrpc_clients.py b/testapp/exam/xmlrpc_clients.py index cc21e62..14ebf27 100644 --- a/testapp/exam/xmlrpc_clients.py +++ b/testapp/exam/xmlrpc_clients.py @@ -26,6 +26,7 @@ class CodeServerProxy(object): "C": "run_c_code", "C++": "run_cplus_code", "java": "run_java_code", + "scilab": "run_scilab_code", } def run_code(self, answer, test_code, user_dir, language): diff --git a/testapp/scilab_files/test_add.sce b/testapp/scilab_files/test_add.sce new file mode 100644 index 0000000..a317cdb --- /dev/null +++ b/testapp/scilab_files/test_add.sce @@ -0,0 +1,29 @@ +mode(-1) +exec("function.sci",-1); +i = 0 +p = add(3,5); +correct = (p == 8); +if correct then + i=i+1 +end +disp("Input submitted 3 and 5") +disp("Expected output 8 got " + string(p)) +p = add(22,-20); +correct = (p==2); +if correct then + i=i+1 +end +disp("Input submitted 22 and -20") +disp("Expected output 2 got " + string(p)) +p =add(91,0); +correct = (p==91); +if correct then + i=i+1 +end +disp("Input submitted 91 and 0") +disp("Expected output 91 got " + string(p)) +if i==3 then + exit(5); +else + exit(3); +end diff --git a/testapp/test_server.py b/testapp/test_server.py index 95f87ef..95dc7b5 100644 --- a/testapp/test_server.py +++ b/testapp/test_server.py @@ -16,7 +16,6 @@ def check_result(result, check='correct answer'): assert result[0], result[1] assert check in result[1].lower(), result[1] - def test_python(): """Test if server runs Python code as expected.""" src = 'while True: pass' @@ -207,6 +206,50 @@ def test_java(): '/tmp', language="java") check_result(result, 'error') +def test_scilab(): + """Test if server runs java code as expected.""" + src = """ + funcprot(0) +function[c]=add(a,b) + c=a+b; +endfunction + """ + result = code_server.run_code(src, 'scilab_files/test_add.sce', + '/tmp', language="scilab") + check_result(result, 'correct answer') + + src = """ + funcprot(0) +function[c]=add(a,b) + c=a-b; +endfunction + """ + result = code_server.run_code(src, 'scilab_files/test_add.sce', + '/tmp', language="scilab") + check_result(result, 'correct answer') + + src = """ + funcprot(0) +function[c]=add(a,b) + c=a+b; +dis( +endfunction + """ + result = code_server.run_code(src, 'scilab_files/test_add.sce', + '/tmp', language="scilab") + check_result(result, 'error') + + src = """ + funcprot(0) +function[c]=add(a,b) + c=a + while(1==1) + end +endfunction + """ + result = code_server.run_code(src, 'scilab_files/test_add.sce', + '/tmp', language="scilab") + check_result(result, 'error') def test_bash(): """Test if server runs Bash code as expected.""" @@ -256,3 +299,4 @@ if __name__ == '__main__': test_c() test_cpp() test_java() + test_scilab() |