summaryrefslogtreecommitdiff
path: root/testapp
diff options
context:
space:
mode:
Diffstat (limited to 'testapp')
-rwxr-xr-xtestapp/code_server.py128
-rw-r--r--testapp/exam/forms.py1
-rw-r--r--testapp/exam/models.py1
-rw-r--r--testapp/exam/xmlrpc_clients.py1
-rw-r--r--testapp/scilab_files/test_add.sce29
-rw-r--r--testapp/test_server.py46
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..d22a022 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 scilab 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()