From 979ce81f0a275a22a4e9ca875c7f5c36dab7d44a Mon Sep 17 00:00:00 2001 From: prathamesh Date: Fri, 12 Aug 2016 14:59:46 +0530 Subject: Removed for loop from the question template. Once the code question is correct, user must see the message Correct Output for two seconds and then url redirection. But since this was written in a for loop, so multiple requests almost simultaneously were sent by a single user. This caused the server to go down. For now removed for loop. --- yaksh/templates/yaksh/question.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 2d52009..73d851a 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -89,9 +89,7 @@ function call_skip(url) {% if paper.questions_left %} window.setTimeout(function() { - {% for qid in paper.questions.all %} - location.href="{{ URL_ROOT }}/exam/{{ qid.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" - {% endfor %} + location.href="{{ URL_ROOT }}/exam/{{ paper.current_question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" }, 2000); {% else %} window.setTimeout(function() -- cgit From 1f2bce81d2092acf8970d2ac125ecce25225b040 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Fri, 12 Aug 2016 15:21:23 +0530 Subject: Removed --use-mirrors option from pip install in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d362005..7d8ef17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: # command to install dependencies install: - pip install git+https://github.com/FOSSEE/online_test.git#egg=yaksh-0.1 - - pip install -q Django==$DJANGO --use-mirrors + - pip install -q Django==$DJANGO - pip install -q pytz==2016.4 - pip install -q python-social-auth==0.2.19 -- cgit From 3738c8fefa8ac69508bb6daeee045c1f5ea0cb17 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 16:57:03 +0530 Subject: Add test case for code server. This currently fails when multiple threads ask for a code evaluation at the same time. --- requirements.txt | 1 + yaksh/code_server.py | 10 +++- yaksh/tests/__init__.py | 0 yaksh/tests/test_code_server.py | 117 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 yaksh/tests/__init__.py create mode 100644 yaksh/tests/test_code_server.py diff --git a/requirements.txt b/requirements.txt index 5438d8a..bea0017 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ mysql-python==1.2.5 django-taggit==0.18.1 pytz==2016.4 python-social-auth==0.2.19 +tornado diff --git a/yaksh/code_server.py b/yaksh/code_server.py index 2d8567e..a2cd08a 100755 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -62,7 +62,7 @@ class CodeServer(object): """Calls relevant EvaluateCode class based on language to check the answer code """ - code_evaluator = create_evaluator_instance(language, + code_evaluator = create_evaluator_instance(language, test_case_type, json_data, in_dir @@ -107,11 +107,13 @@ class ServerPool(object): queue = Queue(maxsize=len(ports)) self.queue = queue servers = [] + self.processes = [] for port in ports: server = CodeServer(port, queue) servers.append(server) p = Process(target=server.run) p.start() + self.processes.append(p) self.servers = servers # Public Protocol ########## @@ -140,6 +142,12 @@ class ServerPool(object): server.register_instance(self) server.serve_forever() + def stop(self): + """Stop all the code server processes. + """ + for proc in self.processes: + proc.terminate() + ############################################################################### def main(args=None): diff --git a/yaksh/tests/__init__.py b/yaksh/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yaksh/tests/test_code_server.py b/yaksh/tests/test_code_server.py new file mode 100644 index 0000000..18510c6 --- /dev/null +++ b/yaksh/tests/test_code_server.py @@ -0,0 +1,117 @@ +import json +from multiprocessing import Process +try: + from Queue import Queue +except ImportError: + from queue import Queue +from threading import Thread +import unittest + + +from yaksh.code_server import ServerPool, SERVER_POOL_PORT + +from yaksh import settings +from yaksh.xmlrpc_clients import code_server + + +class TestCodeServer(unittest.TestCase): + + @classmethod + def setUpClass(cls): + settings.code_evaluators['python']['standardtestcase'] = \ + "yaksh.python_assertion_evaluator.PythonAssertionEvaluator" + ports = range(8001, 8006) + server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) + cls.server_pool = server_pool + cls.server_proc = p = Process(target=server_pool.run) + p.start() + + + @classmethod + def tearDownClass(cls): + cls.server_pool.stop() + cls.server_proc.terminate() + settings.code_evaluators['python']['standardtestcase'] = \ + "python_assertion_evaluator.PythonAssertionEvaluator" + + def test_inifinite_loop(self): + # Given + testdata = {'user_answer': 'while True: pass', + 'test_case_data': [{'test_case':'assert 1==2'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertFalse(data['success']) + self.assertTrue('infinite loop' in data['error']) + + def test_correct_answer(self): + # Given + testdata = {'user_answer': 'def f(): return 1', + 'test_case_data': [{'test_case':'assert f() == 1'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertTrue(data['success']) + self.assertEqual(data['error'], 'Correct answer') + + def test_wrong_answer(self): + # Given + testdata = {'user_answer': 'def f(): return 1', + 'test_case_data': [{'test_case':'assert f() == 2'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertFalse(data['success']) + self.assertTrue('AssertionError' in data['error']) + + def test_multiple_simultaneous_hits(self): + # Given + results = Queue() + + def run_code(): + """Run an infinite loop.""" + testdata = {'user_answer': 'while True: pass', + 'test_case_data': [{'test_case':'assert 1==2'}]} + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + results.put(json.loads(result)) + + N = 5 + # When + import time + threads = [] + for i in range(N): + t = Thread(target=run_code) + threads.append(t) + t.start() + + for t in threads: + if t.isAlive(): + t.join() + + # Then + self.assertEqual(results.qsize(), N) + for i in range(N): + data = results.get() + self.assertFalse(data['success']) + self.assertTrue('infinite loop' in data['error']) + + +if __name__ == '__main__': + unittest.main() -- cgit From b39eb538c08d4dd6761d6a970aadf58bde58a7ce Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:01:08 +0530 Subject: Use a tornado based server for the pool server. With the previous XMLRPC based server, an XMLRPC server would respond to a request for an available port. This does not work as the server can only take about 2 simultaneous connections. The server pool now uses a HTTP server via tornado which works extremely well. The django code should not change at all as this is an internal change. This change should make the code server far more robust and work for a very large number of simultaneous users. The http server also has a simple status page to indicate the current load. This will not be correct on OSX due to limitations of the multi-processing Queue implementation on OSX. --- .travis.yml | 2 + yaksh/code_server.py | 114 +++++++++++++++++++++++++++++----------- yaksh/tests/test_code_server.py | 39 +++++++++----- yaksh/xmlrpc_clients.py | 21 ++------ 4 files changed, 114 insertions(+), 62 deletions(-) diff --git a/.travis.yml b/.travis.yml index d362005..5e391ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ env: # command to install dependencies install: + - pip install tornado - pip install git+https://github.com/FOSSEE/online_test.git#egg=yaksh-0.1 - pip install -q Django==$DJANGO --use-mirrors - pip install -q pytz==2016.4 @@ -21,3 +22,4 @@ before_install: # command to run tests script: - python manage.py test yaksh + - nosetests -v yaksh/tests \ No newline at end of file diff --git a/yaksh/code_server.py b/yaksh/code_server.py index a2cd08a..e19e9c8 100755 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -1,9 +1,11 @@ #!/usr/bin/env python -"""This server runs an XMLRPC server that can be submitted code and tests -and returns the output. It *should* be run as root and will run as the user -'nobody' so as to minimize any damange by errant code. This can be configured -by editing settings.py to run as many servers as desired. One can also -specify the ports on the command line. Here are examples:: + +"""This server runs an HTTP server (using tornado) and several code servers +using XMLRPC that can be submitted code +and tests and returns the output. It *should* be run as root and will run as +the user 'nobody' so as to minimize any damange by errant code. This can be +configured by editing settings.py to run as many servers as desired. One can +also specify the ports on the command line. Here are examples:: $ sudo ./code_server.py # Runs servers based on settings.py:SERVER_PORTS one server per port given. @@ -17,18 +19,32 @@ All these servers should be running as nobody. This will also start a server pool that defaults to port 50000 and is configurable in settings.py:SERVER_POOL_PORT. This port exposes a `get_server_port` function that returns an available server. + """ -import sys + +# Standard library imports from SimpleXMLRPCServer import SimpleXMLRPCServer -import pwd +import json +from multiprocessing import Process, Queue import os -import stat from os.path import isdir, dirname, abspath, join, isfile +import pwd +import re import signal -from multiprocessing import Process, Queue +import stat import subprocess -import re -import json +import sys + +try: + from urllib import unquote +except ImportError: + # The above import will not work on Python-3.x. + from urllib.parse import unquote + +# Library imports +from tornado.ioloop import IOLoop +from tornado.web import Application, RequestHandler + # Local imports. from settings import SERVER_PORTS, SERVER_POOL_PORT from language_registry import create_evaluator_instance, unpack_json @@ -104,17 +120,30 @@ class ServerPool(object): """ self.my_port = pool_port self.ports = ports - queue = Queue(maxsize=len(ports)) + queue = Queue(maxsize=len(self.ports)) self.queue = queue servers = [] - self.processes = [] - for port in ports: + processes = [] + for port in self.ports: server = CodeServer(port, queue) servers.append(server) p = Process(target=server.run) - p.start() - self.processes.append(p) + processes.append(p) self.servers = servers + self.processes = processes + self.app = self._make_app() + + def _make_app(self): + app = Application([ + (r"/.*", MainHandler, dict(server=self)), + ]) + app.listen(self.my_port) + return app + + def _start_code_servers(self): + for proc in self.processes: + if proc.pid is None: + proc.start() # Public Protocol ########## @@ -122,42 +151,63 @@ class ServerPool(object): """Get available server port from ones in the pool. This will block till it gets an available server. """ - q = self.queue - was_waiting = True if q.empty() else False - port = q.get() - if was_waiting: - print '*'*80 - print "No available servers, was waiting but got server \ - later at %d." % port - print '*'*80 - sys.stdout.flush() - return port + return self.queue.get() + + def get_status(self): + """Returns current queue size and total number of ports used.""" + try: + qs = self.queue.qsize() + except NotImplementedError: + # May not work on OS X so we return a dummy. + qs = len(self.ports) + + return qs, len(self.ports) def run(self): """Run server which returns an available server port where code can be executed. """ - server = SimpleXMLRPCServer(("0.0.0.0", self.my_port)) - self.server = server - server.register_instance(self) - server.serve_forever() + # We start the code servers here to ensure they are run as nobody. + self._start_code_servers() + IOLoop.current().start() def stop(self): """Stop all the code server processes. """ for proc in self.processes: proc.terminate() + IOLoop.current().stop() + + +class MainHandler(RequestHandler): + def initialize(self, server): + self.server = server + + def get(self): + path = self.request.path[1:] + if len(path) == 0: + port = self.server.get_server_port() + self.write(str(port)) + elif path == "status": + q_size, total = self.server.get_status() + result = "%d servers out of %d are free.\n"%(q_size, total) + load = float(total - q_size)/total*100 + result += "Load: %s%%\n"%load + self.write(result) ############################################################################### def main(args=None): - run_as_nobody() if args: - ports = [int(x) for x in args[1:]] + ports = [int(x) for x in args] else: ports = SERVER_PORTS server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) + # This is done *after* the server pool is created because when the tornado + # app calls listen(), it cannot be nobody. + run_as_nobody() + server_pool.run() if __name__ == '__main__': diff --git a/yaksh/tests/test_code_server.py b/yaksh/tests/test_code_server.py index 18510c6..a73f073 100644 --- a/yaksh/tests/test_code_server.py +++ b/yaksh/tests/test_code_server.py @@ -1,17 +1,15 @@ import json -from multiprocessing import Process try: from Queue import Queue except ImportError: from queue import Queue from threading import Thread import unittest - +import urllib from yaksh.code_server import ServerPool, SERVER_POOL_PORT - from yaksh import settings -from yaksh.xmlrpc_clients import code_server +from yaksh.xmlrpc_clients import CodeServerProxy class TestCodeServer(unittest.TestCase): @@ -23,24 +21,26 @@ class TestCodeServer(unittest.TestCase): ports = range(8001, 8006) server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) cls.server_pool = server_pool - cls.server_proc = p = Process(target=server_pool.run) - p.start() - + cls.server_thread = t = Thread(target=server_pool.run) + t.start() @classmethod def tearDownClass(cls): cls.server_pool.stop() - cls.server_proc.terminate() + cls.server_thread.join() settings.code_evaluators['python']['standardtestcase'] = \ "python_assertion_evaluator.PythonAssertionEvaluator" + def setUp(self): + self.code_server = CodeServerProxy() + def test_inifinite_loop(self): # Given testdata = {'user_answer': 'while True: pass', 'test_case_data': [{'test_case':'assert 1==2'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -55,7 +55,7 @@ class TestCodeServer(unittest.TestCase): 'test_case_data': [{'test_case':'assert f() == 1'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -70,7 +70,7 @@ class TestCodeServer(unittest.TestCase): 'test_case_data': [{'test_case':'assert f() == 2'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -87,12 +87,12 @@ class TestCodeServer(unittest.TestCase): """Run an infinite loop.""" testdata = {'user_answer': 'while True: pass', 'test_case_data': [{'test_case':'assert 1==2'}]} - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) results.put(json.loads(result)) - N = 5 + N = 10 # When import time threads = [] @@ -112,6 +112,19 @@ class TestCodeServer(unittest.TestCase): self.assertFalse(data['success']) self.assertTrue('infinite loop' in data['error']) + def test_server_pool_status(self): + # Given + url = "http://localhost:%s/status"%SERVER_POOL_PORT + + # When + data = urllib.urlopen(url).read() + + # Then + expect = 'out of 5 are free' + self.assertTrue(expect in data) + expect = 'Load:' + self.assertTrue(expect in data) + if __name__ == '__main__': unittest.main() diff --git a/yaksh/xmlrpc_clients.py b/yaksh/xmlrpc_clients.py index 7124550..6bfe0d6 100644 --- a/yaksh/xmlrpc_clients.py +++ b/yaksh/xmlrpc_clients.py @@ -3,6 +3,7 @@ import time import random import socket import json +import urllib from settings import SERVER_PORTS, SERVER_POOL_PORT @@ -21,7 +22,7 @@ class CodeServerProxy(object): """ def __init__(self): pool_url = 'http://localhost:%d' % (SERVER_POOL_PORT) - self.pool_server = ServerProxy(pool_url) + self.pool_url = pool_url def run_code(self, language, test_case_type, json_data, user_dir): """Tests given code (`answer`) with the `test_code` supplied. If the @@ -34,7 +35,7 @@ class CodeServerProxy(object): ---------- json_data contains; user_answer : str - The user's answer for the question. + The user's answer for the question. test_code : str The test code to check the user code with. language : str @@ -57,21 +58,7 @@ class CodeServerProxy(object): return result def _get_server(self): - # Get a suitable server from our pool of servers. This may block. We - # try about 60 times, essentially waiting at most for about 30 seconds. - done, count = False, 60 - - while not done and count > 0: - try: - port = self.pool_server.get_server_port() - except socket.error: - # Wait a while try again. - time.sleep(random.random()) - count -= 1 - else: - done = True - if not done: - raise ConnectionError("Couldn't connect to a server!") + port = json.loads(urllib.urlopen(self.pool_url).read()) proxy = ServerProxy('http://localhost:%d' % port) return proxy -- cgit From d1965fcba6e3fb28bbae497d17f9c8b48c094206 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:14:15 +0530 Subject: Move tests.py to test_models.py. --- yaksh/test_models.py | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++ yaksh/tests.py | 676 --------------------------------------------------- 2 files changed, 676 insertions(+), 676 deletions(-) create mode 100644 yaksh/test_models.py delete mode 100644 yaksh/tests.py diff --git a/yaksh/test_models.py b/yaksh/test_models.py new file mode 100644 index 0000000..8bd2dda --- /dev/null +++ b/yaksh/test_models.py @@ -0,0 +1,676 @@ +import unittest +from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ + QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ + StdoutBasedTestCase +import json +from datetime import datetime, timedelta +from django.utils import timezone +import pytz +from django.contrib.auth.models import Group + + +def setUpModule(): + # create user profile + user = User.objects.create_user(username='demo_user', + password='demo', + email='demo@test.com') + User.objects.create_user(username='demo_user2', + password='demo', + email='demo@test.com') + Profile.objects.create(user=user, roll_number=1, institute='IIT', + department='Chemical', position='Student') + student = User.objects.create_user(username='demo_user3', + password='demo', + email='demo3@test.com') + Profile.objects.create(user=student, roll_number=3, institute='IIT', + department='Chemical', position='Student') + + # create a course + course = Course.objects.create(name="Python Course", + enrollment="Enroll Request", creator=user) + + # create 20 questions + for i in range(1, 21): + Question.objects.create(summary='Q%d' % (i), points=1, type='code', user=user) + + # create a quiz + quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz', pass_criteria=0, + language='Python', prerequisite=None, + course=course) + + Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + duration=30, active=False, + attempts_allowed=-1, time_between_attempts=0, + description='demo quiz', pass_criteria=40, + language='Python', prerequisite=quiz, + course=course) + +def tearDownModule(): + User.objects.all().delete() + Question.objects.all().delete() + Quiz.objects.all().delete() + + +############################################################################### +class ProfileTestCases(unittest.TestCase): + def setUp(self): + self.user1 = User.objects.get(pk=1) + self.profile = Profile.objects.get(pk=1) + self.user2 = User.objects.get(pk=3) + + def test_user_profile(self): + """ Test user profile""" + self.assertEqual(self.user1.username, 'demo_user') + self.assertEqual(self.profile.user.username, 'demo_user') + self.assertEqual(int(self.profile.roll_number), 1) + self.assertEqual(self.profile.institute, 'IIT') + self.assertEqual(self.profile.department, 'Chemical') + self.assertEqual(self.profile.position, 'Student') + +############################################################################### +class QuestionTestCases(unittest.TestCase): + def setUp(self): + # Single question details + self.user1 = User.objects.get(pk=1) + self.user2 = User.objects.get(pk=2) + self.question1 = Question(summary='Demo question', + language='Python', + type='Code', + active=True, + test_case_type='standardtestcase', + description='Write a function', + points=1.0, + snippet='def myfunc()', + user=self.user1 + ) + self.question1.save() + + self.question2 = Question(summary='Demo Json', + language='python', + type='code', + active=True, + description='factorial of a no', + test_case_type='standardtestcase', + points=2.0, + snippet='def fact()', + user=self.user2 + ) + self.question2.save() + + self.question1.tags.add('python', 'function') + self.assertion_testcase = StandardTestCase(question=self.question1, + test_case='assert myfunc(12, 13) == 15' + ) + self.upload_test_case = StandardTestCase(question=self.question2, + test_case='assert fact(3) == 6' + ) + self.upload_test_case.save() + self.user_answer = "demo_answer" + self.test_case_upload_data = [{"test_case": "assert fact(3)==6"}] + questions_data = [{"snippet": "def fact()", "active": True, + "points": 1.0, + "description": "factorial of a no", + "language": "Python", "type": "Code", + "test_case_type": "standardtestcase", + "testcase": self.test_case_upload_data, + "summary": "Json Demo"}] + self.json_questions_data = json.dumps(questions_data) + + def test_question(self): + """ Test question """ + self.assertEqual(self.question1.summary, 'Demo question') + self.assertEqual(self.question1.language, 'Python') + self.assertEqual(self.question1.type, 'Code') + self.assertEqual(self.question1.description, 'Write a function') + self.assertEqual(self.question1.points, 1.0) + self.assertTrue(self.question1.active) + self.assertEqual(self.question1.snippet, 'def myfunc()') + tag_list = [] + for tag in self.question1.tags.all(): + tag_list.append(tag.name) + self.assertEqual(tag_list, ['python', 'function']) + + def test_dump_questions_into_json(self): + """ Test dump questions into json """ + question = Question() + question_id = [self.question2.id] + questions = json.loads(question.dump_into_json(question_id, self.user2)) + test_case = self.question2.get_test_cases() + for q in questions: + self.assertEqual(self.question2.summary, q['summary']) + self.assertEqual(self.question2.language, q['language']) + self.assertEqual(self.question2.type, q['type']) + self.assertEqual(self.question2.description, q['description']) + self.assertEqual(self.question2.points, q['points']) + self.assertTrue(self.question2.active) + self.assertEqual(self.question2.snippet, q['snippet']) + self.assertEqual(self.question2.test_case_type, q['test_case_type']) + self.assertEqual([case.get_field_value() for case in test_case], q['testcase']) + + def test_load_questions_from_json(self): + """ Test load questions into database from json """ + question = Question() + result = question.load_from_json(self.json_questions_data, self.user1) + question_data = Question.objects.get(pk=25) + test_case = question_data.get_test_cases() + self.assertEqual(question_data.summary, 'Json Demo') + self.assertEqual(question_data.language, 'Python') + self.assertEqual(question_data.type, 'Code') + self.assertEqual(question_data.description, 'factorial of a no') + self.assertEqual(question_data.points, 1.0) + self.assertTrue(question_data.active) + self.assertEqual(question_data.snippet, 'def fact()') + self.assertEqual(question_data.test_case_type, 'standardtestcase') + self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data) + + +############################################################################### +class QuizTestCases(unittest.TestCase): + def setUp(self): + self.creator = User.objects.get(pk=1) + self.teacher = User.objects.get(pk=2) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) + self.trial_course = Course.objects.create_trial_course(self.creator) + + def test_quiz(self): + """ Test Quiz""" + self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'), + '2015-10-09') + self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'), + '10:08:15') + self.assertEqual(self.quiz1.duration, 30) + self.assertTrue(self.quiz1.active) + self.assertEqual(self.quiz1.description, 'demo quiz') + self.assertEqual(self.quiz1.language, 'Python') + self.assertEqual(self.quiz1.pass_criteria, 0) + self.assertEqual(self.quiz1.prerequisite, None) + + def test_is_expired(self): + self.assertFalse(self.quiz1.is_expired()) + self.assertTrue(self.quiz2.is_expired()) + + def test_has_prerequisite(self): + self.assertFalse(self.quiz1.has_prerequisite()) + self.assertTrue(self.quiz2.has_prerequisite()) + + def test_get_active_quizzes(self): + quizzes = Quiz.objects.get_active_quizzes() + for quiz in quizzes: + self.assertTrue(quiz.active) + + def test_create_trial_quiz(self): + """Test to check if trial quiz is created""" + trial_quiz = Quiz.objects.create_trial_quiz(self.trial_course, + self.creator + ) + self.assertEqual(trial_quiz.course, self.trial_course) + self.assertEqual(trial_quiz.duration, 1000) + self.assertEqual(trial_quiz.description, "trial_questions") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + def test_create_trial_from_quiz_godmode(self): + """Test to check if a copy of original quiz is created in godmode""" + trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz1.id, + self.creator, + True + ) + self.assertEqual(trial_quiz.description, "Trial_orig_id_1_godmode") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.duration, 1000) + self.assertTrue(trial_quiz.active) + self.assertEqual(trial_quiz.end_date_time, + datetime(2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc) + ) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + def test_create_trial_from_quiz_usermode(self): + """Test to check if a copy of original quiz is created in usermode""" + trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz2.id, + self.creator, + False + ) + self.assertEqual(trial_quiz.description, "Trial_orig_id_2_usermode") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.duration, self.quiz2.duration) + self.assertEqual(trial_quiz.active, self.quiz2.active) + self.assertEqual(trial_quiz.start_date_time, + self.quiz2.start_date_time + ) + self.assertEqual(trial_quiz.end_date_time, + self.quiz2.end_date_time + ) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + +############################################################################### +class QuestionPaperTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + # All active questions + self.questions = Question.objects.filter(active=True) + self.quiz = Quiz.objects.get(id=1) + + # create question paper + self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + total_marks=0.0, + shuffle_questions=True + ) + + # add fixed set of questions to the question paper + self.question_paper.fixed_questions.add(self.questions[3], + self.questions[5]) + # create two QuestionSet for random questions + # QuestionSet 1 + self.question_set_1 = QuestionSet.objects.create(marks=2, + num_questions=2 + ) + + # add pool of questions for random sampling + self.question_set_1.questions.add(self.questions[6], + self.questions[7], + self.questions[8], + self.questions[9] + ) + # add question set 1 to random questions in Question Paper + self.question_paper.random_questions.add(self.question_set_1) + + # QuestionSet 2 + self.question_set_2 = QuestionSet.objects.create(marks=3, + num_questions=3 + ) + + # add pool of questions + self.question_set_2.questions.add(self.questions[11], + self.questions[12], + self.questions[13], + self.questions[14] + ) + # add question set 2 + self.question_paper.random_questions.add(self.question_set_2) + + # ip address for AnswerPaper + self.ip = '127.0.0.1' + + self.user = User.objects.get(pk=1) + + self.attempted_papers = AnswerPaper.objects.filter( + question_paper=self.question_paper, + user=self.user + ) + + # For Trial case + self.questions_list = [self.questions[3].id, self.questions[5].id] + trial_course = Course.objects.create_trial_course(self.user) + trial_quiz = Quiz.objects.create_trial_quiz(trial_course, self.user) + + def test_questionpaper(self): + """ Test question paper""" + self.assertEqual(self.question_paper.quiz.description, 'demo quiz') + self.assertSequenceEqual(self.question_paper.fixed_questions.all(), + [self.questions[3], self.questions[5]]) + self.assertTrue(self.question_paper.shuffle_questions) + + def test_update_total_marks(self): + """ Test update_total_marks() method of Question Paper""" + self.assertEqual(self.question_paper.total_marks, 0) + self.question_paper.update_total_marks() + self.assertEqual(self.question_paper.total_marks, 15) + + def test_get_random_questions(self): + """ Test get_random_questions() method of Question Paper""" + random_questions_set_1 = self.question_set_1.get_random_questions() + random_questions_set_2 = self.question_set_2.get_random_questions() + total_random_questions = len(random_questions_set_1 + \ + random_questions_set_2) + self.assertEqual(total_random_questions, 5) + + # To check whether random questions are from random_question_set + questions_set_1 = set(self.question_set_1.questions.all()) + random_set_1 = set(random_questions_set_1) + random_set_2 = set(random_questions_set_2) + boolean = questions_set_1.intersection(random_set_1) == random_set_1 + self.assertTrue(boolean) + self.assertEqual(len(random_set_1), 2) + # To check that the questions are random. + # If incase not random then check that the order is diferent + try: + self.assertFalse(random_set_1 == random_set_2) + except AssertionError: + self.assertTrue(random_questions_set_1 != random_questions_set_2) + + def test_make_answerpaper(self): + """ Test make_answerpaper() method of Question Paper""" + already_attempted = self.attempted_papers.count() + attempt_num = already_attempted + 1 + answerpaper = self.question_paper.make_answerpaper(self.user, self.ip, + attempt_num) + self.assertIsInstance(answerpaper, AnswerPaper) + paper_questions = answerpaper.questions.all() + self.assertEqual(len(paper_questions), 7) + fixed_questions = set(self.question_paper.fixed_questions.all()) + self.assertTrue(fixed_questions.issubset(set(paper_questions))) + answerpaper.passed = True + answerpaper.save() + self.assertFalse(self.question_paper.is_prerequisite_passed(self.user)) + # test can_attempt_now(self): + self.assertFalse(self.question_paper.can_attempt_now(self.user)) + + def test_create_trial_paper_to_test_quiz(self): + trial_paper = QuestionPaper.objects.create_trial_paper_to_test_quiz\ + (trial_quiz, + self.question_paper.id + ) + self.assertEqual(trial_paper.quiz, trial_quiz) + self.assertEqual(trial_paper.fixed_questions.all(), + self.question_paper.fixed_questions.all() + ) + self.assertEqual(trial_paper.random_questions.all(), + self.question_paper.random_questions.all() + ) + + def test_create_trial_paper_to_test_questions(self): + trial_paper = QuestionPaper.objects.\ + create_trial_paper_to_test_questions( + trial_quiz, self.questions_list + ) + self.assertEqual(trial_paper.quiz, trial_quiz) + self.assertEqual(self.questions_list, + self.question_paper.fixed_questions + .values_list("id", flat=True) + ) + + +############################################################################### +class AnswerPaperTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + self.ip = '101.0.0.1' + self.user = User.objects.get(id=1) + self.profile = self.user.profile + self.quiz = Quiz.objects.get(pk=1) + self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) + self.question_paper.save() + self.questions = Question.objects.filter(id__in=[1,2,3]) + self.start_time = timezone.now() + self.end_time = self.start_time + timedelta(minutes=20) + + # create answerpaper + self.answerpaper = AnswerPaper(user=self.user, + question_paper=self.question_paper, + start_time=self.start_time, + end_time=self.end_time, + user_ip=self.ip + ) + self.attempted_papers = AnswerPaper.objects.filter( + question_paper=self.question_paper, + user=self.user + ) + already_attempted = self.attempted_papers.count() + self.answerpaper.attempt_number = already_attempted + 1 + self.answerpaper.save() + self.answerpaper.questions.add(*self.questions) + self.answerpaper.questions_unanswered.add(*self.questions) + self.answerpaper.save() + # answers for the Answer Paper + self.answer_right = Answer(question=Question.objects.get(id=1), + answer="Demo answer", + correct=True, marks=1 + ) + self.answer_wrong = Answer(question=Question.objects.get(id=2), + answer="My answer", + correct=False, + marks=0 + ) + self.answer_right.save() + self.answer_wrong.save() + self.answerpaper.answers.add(self.answer_right) + self.answerpaper.answers.add(self.answer_wrong) + + def test_answerpaper(self): + """ Test Answer Paper""" + self.assertEqual(self.answerpaper.user.username, 'demo_user') + self.assertEqual(self.answerpaper.user_ip, self.ip) + questions = self.answerpaper.get_questions() + num_questions = len(questions) + self.assertSequenceEqual(list(questions), list(self.questions)) + self.assertEqual(num_questions, 3) + self.assertEqual(self.answerpaper.question_paper, self.question_paper) + self.assertEqual(self.answerpaper.start_time, self.start_time) + self.assertEqual(self.answerpaper.status, 'inprogress') + + def test_questions(self): + # Test questions_left() method of Answer Paper + self.assertEqual(self.answerpaper.questions_left(), 3) + # Test current_question() method of Answer Paper + current_question = self.answerpaper.current_question() + self.assertEqual(current_question.id, 1) + # Test completed_question() method of Answer Paper + question = self.answerpaper.completed_question(1) + self.assertEqual(self.answerpaper.questions_left(), 2) + # Test skip() method of Answer Paper + current_question = self.answerpaper.current_question() + self.assertEqual(current_question.id, 2) + next_question_id = self.answerpaper.skip(current_question.id) + self.assertTrue(next_question_id is not None) + self.assertEqual(next_question_id.id, 3) + questions_answered = self.answerpaper.get_questions_answered() + self.assertEqual(questions_answered.count(), 1) + self.assertSequenceEqual(questions_answered, [self.questions[0]]) + questions_unanswered = self.answerpaper.get_questions_unanswered() + self.assertEqual(questions_unanswered.count(), 2) + self.assertSequenceEqual(questions_unanswered, + [self.questions[1], self.questions[2]]) + + def test_update_marks(self): + """ Test update_marks method of AnswerPaper""" + self.answerpaper.update_marks('inprogress') + self.assertEqual(self.answerpaper.status, 'inprogress') + self.assertTrue(self.answerpaper.is_attempt_inprogress()) + self.answerpaper.update_marks() + self.assertEqual(self.answerpaper.status, 'completed') + self.assertEqual(self.answerpaper.marks_obtained, 1.0) + self.assertEqual(self.answerpaper.percent, 33.33) + self.assertTrue(self.answerpaper.passed) + self.assertFalse(self.answerpaper.is_attempt_inprogress()) + + def test_set_end_time(self): + current_time = timezone.now() + self.answerpaper.set_end_time(current_time) + self.assertEqual(self.answerpaper.end_time,current_time) + + def test_get_question_answer(self): + """ Test get_question_answer() method of Answer Paper""" + answered = self.answerpaper.get_question_answers() + first_answer = answered.values()[0][0] + self.assertEqual(first_answer.answer, 'Demo answer') + self.assertTrue(first_answer.correct) + self.assertEqual(len(answered), 2) + + def test_is_answer_correct(self): + self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) + self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1])) + + def test_get_previous_answers(self): + answers = self.answerpaper.get_previous_answers(self.questions[0]) + self.assertEqual(answers.count(), 1) + self.assertTrue(answers[0], self.answer_right) + answers = self.answerpaper.get_previous_answers(self.questions[1]) + self.assertEqual(answers.count(), 1) + self.assertTrue(answers[0], self.answer_wrong) + + def test_set_marks (self): + self.answer_wrong.set_marks(0.5) + self.assertEqual(self.answer_wrong.marks, 0.5) + self.answer_wrong.set_marks(10.0) + self.assertEqual(self.answer_wrong.marks,1.0) + + +############################################################################### +class CourseTestCases(unittest.TestCase): + def setUp(self): + self.course = Course.objects.get(pk=1) + self.creator = User.objects.get(pk=1) + self.student1 = User.objects.get(pk=2) + self.student2 = User.objects.get(pk=3) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) + + + def test_is_creator(self): + """ Test is_creator method of Course""" + self.assertTrue(self.course.is_creator(self.creator)) + + def test_is_self_enroll(self): + """ Test is_self_enroll method of Course""" + self.assertFalse(self.course.is_self_enroll()) + + def test_deactivate(self): + """ Test deactivate method of Course""" + self.course.deactivate() + self.assertFalse(self.course.active) + + def test_activate(self): + """ Test activate method of Course""" + self.course.activate() + self.assertTrue(self.course.active) + + def test_request(self): + """ Test request and get_requests methods of Course""" + self.course.request(self.student1, self.student2) + self.assertSequenceEqual(self.course.get_requests(), + [self.student1, self.student2]) + + def test_enroll_reject(self): + """ Test enroll, reject, get_enrolled and get_rejected methods""" + self.assertSequenceEqual(self.course.get_enrolled(), []) + was_rejected = False + self.course.enroll(was_rejected, self.student1) + self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) + + self.assertSequenceEqual(self.course.get_rejected(), []) + was_enrolled = False + self.course.reject(was_enrolled, self.student2) + self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) + + was_rejected = True + self.course.enroll(was_rejected, self.student2) + self.assertSequenceEqual(self.course.get_enrolled(), + [self.student1, self.student2]) + self.assertSequenceEqual(self.course.get_rejected(), []) + + was_enrolled = True + self.course.reject(was_enrolled, self.student2) + self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) + self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) + + self.assertTrue(self.course.is_enrolled(self.student1)) + + def test_get_quizzes(self): + """ Test get_quizzes method of Courses""" + self.assertSequenceEqual(self.course.get_quizzes(), + [self.quiz1, self.quiz2]) + + def test_add_teachers(self): + """ Test to add teachers to a course""" + self.course.add_teachers(self.student1, self.student2) + self.assertSequenceEqual(self.course.get_teachers(), + [self.student1, self.student2]) + + def test_remove_teachers(self): + """ Test to remove teachers from a course""" + self.course.add_teachers(self.student1, self.student2) + self.course.remove_teachers(self.student1) + self.assertSequenceEqual(self.course.get_teachers(), [self.student2]) + + def test_is_teacher(self): + """ Test to check if user is teacher""" + self.course.add_teachers(self.student2) + result = self.course.is_teacher(self.student2) + self.assertTrue(result) + + def test_create_trial_course(self): + """Test to check if trial course is created""" + # Test for manager method create_trial_course + trial_course = Course.objects.create_trial_course(self.creator) + self.assertEqual(trial_course.name, "trial_course") + self.assertEqual(trial_course.enrollment, "open") + self.assertTrue(trial_course.active) + self.assertEqual(trial_course.students.get(user=self.creator.id), + self.creator + ) + self.assertTrue(trial_course.is_trial) + + +############################################################################### +class TestCaseTestCases(unittest.TestCase): + def setUp(self): + self.user = User.objects.get(pk=1) + self.question1 = Question(summary='Demo question 1', + language='Python', + type='Code', + active=True, + description='Write a function', + points=1.0, + test_case_type="standardtestcase", + user=self.user, + snippet='def myfunc()' + ) + self.question2 = Question(summary='Demo question 2', + language='Python', + type='Code', + active=True, + description='Write to standard output', + points=1.0, + test_case_type="stdoutbasedtestcase", + user=self.user, + snippet='def myfunc()' + ) + self.question1.save() + self.question2.save() + self.assertion_testcase = StandardTestCase( + question=self.question1, + test_case='assert myfunc(12, 13) == 15' + ) + self.stdout_based_testcase = StdoutBasedTestCase( + question=self.question2, + expected_output='Hello World' + ) + self.assertion_testcase.save() + self.stdout_based_testcase.save() + answer_data = {"user_answer": "demo_answer", + "test_case_data": [ + {"test_case": "assert myfunc(12, 13) == 15"} + ] + } + self.answer_data_json = json.dumps(answer_data) + + def test_assertion_testcase(self): + """ Test question """ + self.assertEqual(self.assertion_testcase.question, self.question1) + self.assertEqual(self.assertion_testcase.test_case, + 'assert myfunc(12, 13) == 15') + + def test_stdout_based_testcase(self): + """ Test question """ + self.assertEqual(self.stdout_based_testcase.question, self.question2) + self.assertEqual(self.stdout_based_testcase.expected_output, + 'Hello World' + ) + + def test_consolidate_answer_data(self): + """ Test consolidate answer data model method """ + result = self.question1.consolidate_answer_data( + user_answer="demo_answer" + ) + self.assertEqual(result, self.answer_data_json) diff --git a/yaksh/tests.py b/yaksh/tests.py deleted file mode 100644 index 8bd2dda..0000000 --- a/yaksh/tests.py +++ /dev/null @@ -1,676 +0,0 @@ -import unittest -from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ - QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ - StdoutBasedTestCase -import json -from datetime import datetime, timedelta -from django.utils import timezone -import pytz -from django.contrib.auth.models import Group - - -def setUpModule(): - # create user profile - user = User.objects.create_user(username='demo_user', - password='demo', - email='demo@test.com') - User.objects.create_user(username='demo_user2', - password='demo', - email='demo@test.com') - Profile.objects.create(user=user, roll_number=1, institute='IIT', - department='Chemical', position='Student') - student = User.objects.create_user(username='demo_user3', - password='demo', - email='demo3@test.com') - Profile.objects.create(user=student, roll_number=3, institute='IIT', - department='Chemical', position='Student') - - # create a course - course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=user) - - # create 20 questions - for i in range(1, 21): - Question.objects.create(summary='Q%d' % (i), points=1, type='code', user=user) - - # create a quiz - quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=True, - attempts_allowed=1, time_between_attempts=0, - description='demo quiz', pass_criteria=0, - language='Python', prerequisite=None, - course=course) - - Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=False, - attempts_allowed=-1, time_between_attempts=0, - description='demo quiz', pass_criteria=40, - language='Python', prerequisite=quiz, - course=course) - -def tearDownModule(): - User.objects.all().delete() - Question.objects.all().delete() - Quiz.objects.all().delete() - - -############################################################################### -class ProfileTestCases(unittest.TestCase): - def setUp(self): - self.user1 = User.objects.get(pk=1) - self.profile = Profile.objects.get(pk=1) - self.user2 = User.objects.get(pk=3) - - def test_user_profile(self): - """ Test user profile""" - self.assertEqual(self.user1.username, 'demo_user') - self.assertEqual(self.profile.user.username, 'demo_user') - self.assertEqual(int(self.profile.roll_number), 1) - self.assertEqual(self.profile.institute, 'IIT') - self.assertEqual(self.profile.department, 'Chemical') - self.assertEqual(self.profile.position, 'Student') - -############################################################################### -class QuestionTestCases(unittest.TestCase): - def setUp(self): - # Single question details - self.user1 = User.objects.get(pk=1) - self.user2 = User.objects.get(pk=2) - self.question1 = Question(summary='Demo question', - language='Python', - type='Code', - active=True, - test_case_type='standardtestcase', - description='Write a function', - points=1.0, - snippet='def myfunc()', - user=self.user1 - ) - self.question1.save() - - self.question2 = Question(summary='Demo Json', - language='python', - type='code', - active=True, - description='factorial of a no', - test_case_type='standardtestcase', - points=2.0, - snippet='def fact()', - user=self.user2 - ) - self.question2.save() - - self.question1.tags.add('python', 'function') - self.assertion_testcase = StandardTestCase(question=self.question1, - test_case='assert myfunc(12, 13) == 15' - ) - self.upload_test_case = StandardTestCase(question=self.question2, - test_case='assert fact(3) == 6' - ) - self.upload_test_case.save() - self.user_answer = "demo_answer" - self.test_case_upload_data = [{"test_case": "assert fact(3)==6"}] - questions_data = [{"snippet": "def fact()", "active": True, - "points": 1.0, - "description": "factorial of a no", - "language": "Python", "type": "Code", - "test_case_type": "standardtestcase", - "testcase": self.test_case_upload_data, - "summary": "Json Demo"}] - self.json_questions_data = json.dumps(questions_data) - - def test_question(self): - """ Test question """ - self.assertEqual(self.question1.summary, 'Demo question') - self.assertEqual(self.question1.language, 'Python') - self.assertEqual(self.question1.type, 'Code') - self.assertEqual(self.question1.description, 'Write a function') - self.assertEqual(self.question1.points, 1.0) - self.assertTrue(self.question1.active) - self.assertEqual(self.question1.snippet, 'def myfunc()') - tag_list = [] - for tag in self.question1.tags.all(): - tag_list.append(tag.name) - self.assertEqual(tag_list, ['python', 'function']) - - def test_dump_questions_into_json(self): - """ Test dump questions into json """ - question = Question() - question_id = [self.question2.id] - questions = json.loads(question.dump_into_json(question_id, self.user2)) - test_case = self.question2.get_test_cases() - for q in questions: - self.assertEqual(self.question2.summary, q['summary']) - self.assertEqual(self.question2.language, q['language']) - self.assertEqual(self.question2.type, q['type']) - self.assertEqual(self.question2.description, q['description']) - self.assertEqual(self.question2.points, q['points']) - self.assertTrue(self.question2.active) - self.assertEqual(self.question2.snippet, q['snippet']) - self.assertEqual(self.question2.test_case_type, q['test_case_type']) - self.assertEqual([case.get_field_value() for case in test_case], q['testcase']) - - def test_load_questions_from_json(self): - """ Test load questions into database from json """ - question = Question() - result = question.load_from_json(self.json_questions_data, self.user1) - question_data = Question.objects.get(pk=25) - test_case = question_data.get_test_cases() - self.assertEqual(question_data.summary, 'Json Demo') - self.assertEqual(question_data.language, 'Python') - self.assertEqual(question_data.type, 'Code') - self.assertEqual(question_data.description, 'factorial of a no') - self.assertEqual(question_data.points, 1.0) - self.assertTrue(question_data.active) - self.assertEqual(question_data.snippet, 'def fact()') - self.assertEqual(question_data.test_case_type, 'standardtestcase') - self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data) - - -############################################################################### -class QuizTestCases(unittest.TestCase): - def setUp(self): - self.creator = User.objects.get(pk=1) - self.teacher = User.objects.get(pk=2) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) - self.trial_course = Course.objects.create_trial_course(self.creator) - - def test_quiz(self): - """ Test Quiz""" - self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'), - '2015-10-09') - self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'), - '10:08:15') - self.assertEqual(self.quiz1.duration, 30) - self.assertTrue(self.quiz1.active) - self.assertEqual(self.quiz1.description, 'demo quiz') - self.assertEqual(self.quiz1.language, 'Python') - self.assertEqual(self.quiz1.pass_criteria, 0) - self.assertEqual(self.quiz1.prerequisite, None) - - def test_is_expired(self): - self.assertFalse(self.quiz1.is_expired()) - self.assertTrue(self.quiz2.is_expired()) - - def test_has_prerequisite(self): - self.assertFalse(self.quiz1.has_prerequisite()) - self.assertTrue(self.quiz2.has_prerequisite()) - - def test_get_active_quizzes(self): - quizzes = Quiz.objects.get_active_quizzes() - for quiz in quizzes: - self.assertTrue(quiz.active) - - def test_create_trial_quiz(self): - """Test to check if trial quiz is created""" - trial_quiz = Quiz.objects.create_trial_quiz(self.trial_course, - self.creator - ) - self.assertEqual(trial_quiz.course, self.trial_course) - self.assertEqual(trial_quiz.duration, 1000) - self.assertEqual(trial_quiz.description, "trial_questions") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - def test_create_trial_from_quiz_godmode(self): - """Test to check if a copy of original quiz is created in godmode""" - trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz1.id, - self.creator, - True - ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_1_godmode") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.duration, 1000) - self.assertTrue(trial_quiz.active) - self.assertEqual(trial_quiz.end_date_time, - datetime(2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc) - ) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - def test_create_trial_from_quiz_usermode(self): - """Test to check if a copy of original quiz is created in usermode""" - trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz2.id, - self.creator, - False - ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_2_usermode") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.duration, self.quiz2.duration) - self.assertEqual(trial_quiz.active, self.quiz2.active) - self.assertEqual(trial_quiz.start_date_time, - self.quiz2.start_date_time - ) - self.assertEqual(trial_quiz.end_date_time, - self.quiz2.end_date_time - ) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - -############################################################################### -class QuestionPaperTestCases(unittest.TestCase): - @classmethod - def setUpClass(self): - # All active questions - self.questions = Question.objects.filter(active=True) - self.quiz = Quiz.objects.get(id=1) - - # create question paper - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=0.0, - shuffle_questions=True - ) - - # add fixed set of questions to the question paper - self.question_paper.fixed_questions.add(self.questions[3], - self.questions[5]) - # create two QuestionSet for random questions - # QuestionSet 1 - self.question_set_1 = QuestionSet.objects.create(marks=2, - num_questions=2 - ) - - # add pool of questions for random sampling - self.question_set_1.questions.add(self.questions[6], - self.questions[7], - self.questions[8], - self.questions[9] - ) - # add question set 1 to random questions in Question Paper - self.question_paper.random_questions.add(self.question_set_1) - - # QuestionSet 2 - self.question_set_2 = QuestionSet.objects.create(marks=3, - num_questions=3 - ) - - # add pool of questions - self.question_set_2.questions.add(self.questions[11], - self.questions[12], - self.questions[13], - self.questions[14] - ) - # add question set 2 - self.question_paper.random_questions.add(self.question_set_2) - - # ip address for AnswerPaper - self.ip = '127.0.0.1' - - self.user = User.objects.get(pk=1) - - self.attempted_papers = AnswerPaper.objects.filter( - question_paper=self.question_paper, - user=self.user - ) - - # For Trial case - self.questions_list = [self.questions[3].id, self.questions[5].id] - trial_course = Course.objects.create_trial_course(self.user) - trial_quiz = Quiz.objects.create_trial_quiz(trial_course, self.user) - - def test_questionpaper(self): - """ Test question paper""" - self.assertEqual(self.question_paper.quiz.description, 'demo quiz') - self.assertSequenceEqual(self.question_paper.fixed_questions.all(), - [self.questions[3], self.questions[5]]) - self.assertTrue(self.question_paper.shuffle_questions) - - def test_update_total_marks(self): - """ Test update_total_marks() method of Question Paper""" - self.assertEqual(self.question_paper.total_marks, 0) - self.question_paper.update_total_marks() - self.assertEqual(self.question_paper.total_marks, 15) - - def test_get_random_questions(self): - """ Test get_random_questions() method of Question Paper""" - random_questions_set_1 = self.question_set_1.get_random_questions() - random_questions_set_2 = self.question_set_2.get_random_questions() - total_random_questions = len(random_questions_set_1 + \ - random_questions_set_2) - self.assertEqual(total_random_questions, 5) - - # To check whether random questions are from random_question_set - questions_set_1 = set(self.question_set_1.questions.all()) - random_set_1 = set(random_questions_set_1) - random_set_2 = set(random_questions_set_2) - boolean = questions_set_1.intersection(random_set_1) == random_set_1 - self.assertTrue(boolean) - self.assertEqual(len(random_set_1), 2) - # To check that the questions are random. - # If incase not random then check that the order is diferent - try: - self.assertFalse(random_set_1 == random_set_2) - except AssertionError: - self.assertTrue(random_questions_set_1 != random_questions_set_2) - - def test_make_answerpaper(self): - """ Test make_answerpaper() method of Question Paper""" - already_attempted = self.attempted_papers.count() - attempt_num = already_attempted + 1 - answerpaper = self.question_paper.make_answerpaper(self.user, self.ip, - attempt_num) - self.assertIsInstance(answerpaper, AnswerPaper) - paper_questions = answerpaper.questions.all() - self.assertEqual(len(paper_questions), 7) - fixed_questions = set(self.question_paper.fixed_questions.all()) - self.assertTrue(fixed_questions.issubset(set(paper_questions))) - answerpaper.passed = True - answerpaper.save() - self.assertFalse(self.question_paper.is_prerequisite_passed(self.user)) - # test can_attempt_now(self): - self.assertFalse(self.question_paper.can_attempt_now(self.user)) - - def test_create_trial_paper_to_test_quiz(self): - trial_paper = QuestionPaper.objects.create_trial_paper_to_test_quiz\ - (trial_quiz, - self.question_paper.id - ) - self.assertEqual(trial_paper.quiz, trial_quiz) - self.assertEqual(trial_paper.fixed_questions.all(), - self.question_paper.fixed_questions.all() - ) - self.assertEqual(trial_paper.random_questions.all(), - self.question_paper.random_questions.all() - ) - - def test_create_trial_paper_to_test_questions(self): - trial_paper = QuestionPaper.objects.\ - create_trial_paper_to_test_questions( - trial_quiz, self.questions_list - ) - self.assertEqual(trial_paper.quiz, trial_quiz) - self.assertEqual(self.questions_list, - self.question_paper.fixed_questions - .values_list("id", flat=True) - ) - - -############################################################################### -class AnswerPaperTestCases(unittest.TestCase): - @classmethod - def setUpClass(self): - self.ip = '101.0.0.1' - self.user = User.objects.get(id=1) - self.profile = self.user.profile - self.quiz = Quiz.objects.get(pk=1) - self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) - self.question_paper.save() - self.questions = Question.objects.filter(id__in=[1,2,3]) - self.start_time = timezone.now() - self.end_time = self.start_time + timedelta(minutes=20) - - # create answerpaper - self.answerpaper = AnswerPaper(user=self.user, - question_paper=self.question_paper, - start_time=self.start_time, - end_time=self.end_time, - user_ip=self.ip - ) - self.attempted_papers = AnswerPaper.objects.filter( - question_paper=self.question_paper, - user=self.user - ) - already_attempted = self.attempted_papers.count() - self.answerpaper.attempt_number = already_attempted + 1 - self.answerpaper.save() - self.answerpaper.questions.add(*self.questions) - self.answerpaper.questions_unanswered.add(*self.questions) - self.answerpaper.save() - # answers for the Answer Paper - self.answer_right = Answer(question=Question.objects.get(id=1), - answer="Demo answer", - correct=True, marks=1 - ) - self.answer_wrong = Answer(question=Question.objects.get(id=2), - answer="My answer", - correct=False, - marks=0 - ) - self.answer_right.save() - self.answer_wrong.save() - self.answerpaper.answers.add(self.answer_right) - self.answerpaper.answers.add(self.answer_wrong) - - def test_answerpaper(self): - """ Test Answer Paper""" - self.assertEqual(self.answerpaper.user.username, 'demo_user') - self.assertEqual(self.answerpaper.user_ip, self.ip) - questions = self.answerpaper.get_questions() - num_questions = len(questions) - self.assertSequenceEqual(list(questions), list(self.questions)) - self.assertEqual(num_questions, 3) - self.assertEqual(self.answerpaper.question_paper, self.question_paper) - self.assertEqual(self.answerpaper.start_time, self.start_time) - self.assertEqual(self.answerpaper.status, 'inprogress') - - def test_questions(self): - # Test questions_left() method of Answer Paper - self.assertEqual(self.answerpaper.questions_left(), 3) - # Test current_question() method of Answer Paper - current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 1) - # Test completed_question() method of Answer Paper - question = self.answerpaper.completed_question(1) - self.assertEqual(self.answerpaper.questions_left(), 2) - # Test skip() method of Answer Paper - current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 2) - next_question_id = self.answerpaper.skip(current_question.id) - self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 3) - questions_answered = self.answerpaper.get_questions_answered() - self.assertEqual(questions_answered.count(), 1) - self.assertSequenceEqual(questions_answered, [self.questions[0]]) - questions_unanswered = self.answerpaper.get_questions_unanswered() - self.assertEqual(questions_unanswered.count(), 2) - self.assertSequenceEqual(questions_unanswered, - [self.questions[1], self.questions[2]]) - - def test_update_marks(self): - """ Test update_marks method of AnswerPaper""" - self.answerpaper.update_marks('inprogress') - self.assertEqual(self.answerpaper.status, 'inprogress') - self.assertTrue(self.answerpaper.is_attempt_inprogress()) - self.answerpaper.update_marks() - self.assertEqual(self.answerpaper.status, 'completed') - self.assertEqual(self.answerpaper.marks_obtained, 1.0) - self.assertEqual(self.answerpaper.percent, 33.33) - self.assertTrue(self.answerpaper.passed) - self.assertFalse(self.answerpaper.is_attempt_inprogress()) - - def test_set_end_time(self): - current_time = timezone.now() - self.answerpaper.set_end_time(current_time) - self.assertEqual(self.answerpaper.end_time,current_time) - - def test_get_question_answer(self): - """ Test get_question_answer() method of Answer Paper""" - answered = self.answerpaper.get_question_answers() - first_answer = answered.values()[0][0] - self.assertEqual(first_answer.answer, 'Demo answer') - self.assertTrue(first_answer.correct) - self.assertEqual(len(answered), 2) - - def test_is_answer_correct(self): - self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) - self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1])) - - def test_get_previous_answers(self): - answers = self.answerpaper.get_previous_answers(self.questions[0]) - self.assertEqual(answers.count(), 1) - self.assertTrue(answers[0], self.answer_right) - answers = self.answerpaper.get_previous_answers(self.questions[1]) - self.assertEqual(answers.count(), 1) - self.assertTrue(answers[0], self.answer_wrong) - - def test_set_marks (self): - self.answer_wrong.set_marks(0.5) - self.assertEqual(self.answer_wrong.marks, 0.5) - self.answer_wrong.set_marks(10.0) - self.assertEqual(self.answer_wrong.marks,1.0) - - -############################################################################### -class CourseTestCases(unittest.TestCase): - def setUp(self): - self.course = Course.objects.get(pk=1) - self.creator = User.objects.get(pk=1) - self.student1 = User.objects.get(pk=2) - self.student2 = User.objects.get(pk=3) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) - - - def test_is_creator(self): - """ Test is_creator method of Course""" - self.assertTrue(self.course.is_creator(self.creator)) - - def test_is_self_enroll(self): - """ Test is_self_enroll method of Course""" - self.assertFalse(self.course.is_self_enroll()) - - def test_deactivate(self): - """ Test deactivate method of Course""" - self.course.deactivate() - self.assertFalse(self.course.active) - - def test_activate(self): - """ Test activate method of Course""" - self.course.activate() - self.assertTrue(self.course.active) - - def test_request(self): - """ Test request and get_requests methods of Course""" - self.course.request(self.student1, self.student2) - self.assertSequenceEqual(self.course.get_requests(), - [self.student1, self.student2]) - - def test_enroll_reject(self): - """ Test enroll, reject, get_enrolled and get_rejected methods""" - self.assertSequenceEqual(self.course.get_enrolled(), []) - was_rejected = False - self.course.enroll(was_rejected, self.student1) - self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) - - self.assertSequenceEqual(self.course.get_rejected(), []) - was_enrolled = False - self.course.reject(was_enrolled, self.student2) - self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) - - was_rejected = True - self.course.enroll(was_rejected, self.student2) - self.assertSequenceEqual(self.course.get_enrolled(), - [self.student1, self.student2]) - self.assertSequenceEqual(self.course.get_rejected(), []) - - was_enrolled = True - self.course.reject(was_enrolled, self.student2) - self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) - self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) - - self.assertTrue(self.course.is_enrolled(self.student1)) - - def test_get_quizzes(self): - """ Test get_quizzes method of Courses""" - self.assertSequenceEqual(self.course.get_quizzes(), - [self.quiz1, self.quiz2]) - - def test_add_teachers(self): - """ Test to add teachers to a course""" - self.course.add_teachers(self.student1, self.student2) - self.assertSequenceEqual(self.course.get_teachers(), - [self.student1, self.student2]) - - def test_remove_teachers(self): - """ Test to remove teachers from a course""" - self.course.add_teachers(self.student1, self.student2) - self.course.remove_teachers(self.student1) - self.assertSequenceEqual(self.course.get_teachers(), [self.student2]) - - def test_is_teacher(self): - """ Test to check if user is teacher""" - self.course.add_teachers(self.student2) - result = self.course.is_teacher(self.student2) - self.assertTrue(result) - - def test_create_trial_course(self): - """Test to check if trial course is created""" - # Test for manager method create_trial_course - trial_course = Course.objects.create_trial_course(self.creator) - self.assertEqual(trial_course.name, "trial_course") - self.assertEqual(trial_course.enrollment, "open") - self.assertTrue(trial_course.active) - self.assertEqual(trial_course.students.get(user=self.creator.id), - self.creator - ) - self.assertTrue(trial_course.is_trial) - - -############################################################################### -class TestCaseTestCases(unittest.TestCase): - def setUp(self): - self.user = User.objects.get(pk=1) - self.question1 = Question(summary='Demo question 1', - language='Python', - type='Code', - active=True, - description='Write a function', - points=1.0, - test_case_type="standardtestcase", - user=self.user, - snippet='def myfunc()' - ) - self.question2 = Question(summary='Demo question 2', - language='Python', - type='Code', - active=True, - description='Write to standard output', - points=1.0, - test_case_type="stdoutbasedtestcase", - user=self.user, - snippet='def myfunc()' - ) - self.question1.save() - self.question2.save() - self.assertion_testcase = StandardTestCase( - question=self.question1, - test_case='assert myfunc(12, 13) == 15' - ) - self.stdout_based_testcase = StdoutBasedTestCase( - question=self.question2, - expected_output='Hello World' - ) - self.assertion_testcase.save() - self.stdout_based_testcase.save() - answer_data = {"user_answer": "demo_answer", - "test_case_data": [ - {"test_case": "assert myfunc(12, 13) == 15"} - ] - } - self.answer_data_json = json.dumps(answer_data) - - def test_assertion_testcase(self): - """ Test question """ - self.assertEqual(self.assertion_testcase.question, self.question1) - self.assertEqual(self.assertion_testcase.test_case, - 'assert myfunc(12, 13) == 15') - - def test_stdout_based_testcase(self): - """ Test question """ - self.assertEqual(self.stdout_based_testcase.question, self.question2) - self.assertEqual(self.stdout_based_testcase.expected_output, - 'Hello World' - ) - - def test_consolidate_answer_data(self): - """ Test consolidate answer data model method """ - result = self.question1.consolidate_answer_data( - user_answer="demo_answer" - ) - self.assertEqual(result, self.answer_data_json) -- cgit From 3910794df5de5c2da8d7972bd429d7bb83a0abb3 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:20:16 +0530 Subject: No need to run nosetests again. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f1a5a0..15f85bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,5 +21,4 @@ before_install: # command to run tests script: - - python manage.py test yaksh - - nosetests -v yaksh/tests \ No newline at end of file + - python manage.py test -v yaksh -- cgit From edaffc172a6cf1fcae7e5e3cae16e5aaf84c996b Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:30:02 +0530 Subject: Set correct verbosity level for test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 15f85bd..8ad6c5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,4 @@ before_install: # command to run tests script: - - python manage.py test -v yaksh + - python manage.py test -v 2 yaksh -- cgit From 23ce8a47eeacba027ca35d6f0bcbd3ff78692913 Mon Sep 17 00:00:00 2001 From: ankitjavalkar Date: Fri, 19 Aug 2016 10:58:44 +0530 Subject: - Modify admin.py to include Course model - Modify user.html and complete.html to include id for html elements --- yaksh/admin.py | 3 ++- yaksh/templates/user.html | 2 +- yaksh/templates/yaksh/complete.html | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index c31b99b..2ce3ac4 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -1,5 +1,5 @@ from yaksh.models import Question, Quiz -from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase +from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase, Course from django.contrib import admin admin.site.register(Question) @@ -7,3 +7,4 @@ admin.site.register(TestCase) admin.site.register(StandardTestCase) admin.site.register(StdoutBasedTestCase) admin.site.register(Quiz) +admin.site.register(Course) diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html index 4074656..009dd2f 100644 --- a/yaksh/templates/user.html +++ b/yaksh/templates/user.html @@ -34,7 +34,7 @@