import os.path
from datetime import datetime, timezone
from django.test import RequestFactory
from django.urls import reverse
from oioioi.base.tests import TestCase, fake_time
from oioioi.contests.models import (
Contest,
ProblemInstance,
ScoreReport,
SubmissionReport,
)
from oioioi.contests.tests import SubmitMixin
from oioioi.problems.models import Problem
from oioioi.quizzes import views
from oioioi.quizzes.models import (
QuestionReport,
Quiz,
QuizAnswer,
QuizAnswerPicture,
QuizQuestion,
QuizQuestionPicture,
QuizSubmission,
)
[docs]class SubmitQuizMixin(SubmitMixin):
[docs] def submit_quiz(self, contest, problem_instance, answers):
"""
Submits a quiz with given answer
:param contest: in what contest to submit
:param problem_instance: indicates which quiz to submit
:param answers: dictionary mapping question ids to:
1) answer id
2) list of answer ids if question is multiple choice
:return response to the request
"""
url = reverse('submit', kwargs={'contest_id': contest.id})
post_data = {
'problem_instance_id': problem_instance.id,
}
for qid in answers:
post_data.update(
{'quiz_' + str(problem_instance.id) + '_q_' + str(qid): answers[qid]}
)
response = self.client.post(url, post_data)
return response
[docs]class TestTextInput(TestCase, SubmitQuizMixin):
[docs] fixtures = [
'test_users',
'test_basic_contest',
'test_problem_instance',
'test_quiz_problem_with_text_input',
]
[docs] def setUp(self):
self.assertTrue(self.client.login(username='test_user'))
[docs] def test_simple_submission(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest,
problem_instance,
{
'1': 'Answer - correct',
'2': 'A',
},
)
self._assertSubmitted(contest, response)
submission = QuizSubmission.objects.get()
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
self.assertEqual(submission_report.score_report.score, 50)
report = SubmissionReport.objects.filter(
submission=submission, status='ACTIVE', kind='NORMAL'
).get()
score_report = ScoreReport.objects.get(submission_report=report)
submission.status = score_report.status
submission.score = score_report.score
[docs] def test_second_possible_answer(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest,
problem_instance,
{
'1': 'Answer - correct',
'2': 'B',
},
)
self._assertSubmitted(contest, response)
submission = QuizSubmission.objects.get()
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
self.assertEqual(submission_report.score_report.score, 50)
[docs] def test_wrong_answer(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest,
problem_instance,
{
'1': 'Answer - wrong',
'2': 'something completely incorrect',
},
)
self._assertSubmitted(contest, response)
submission = QuizSubmission.objects.get()
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
self.assertEqual(submission_report.score_report.score, 0)
[docs]class TestSubmission(TestCase, SubmitQuizMixin):
[docs] fixtures = [
'test_users',
'test_basic_contest',
'test_quiz_problem',
'test_quiz_problem_second',
'test_problem_instance',
]
[docs] def setUp(self):
self.assertTrue(self.client.login(username='test_user'))
[docs] def test_simple_submission(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest, problem_instance, {'1': '1', '2': ('3', '4')}
)
self._assertSubmitted(contest, response)
[docs] def test_empty_multiple_choice(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(contest, problem_instance, {'1': '1', '2': ()})
self._assertSubmitted(contest, response)
[docs] def test_wrong_id(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest,
problem_instance,
{'1': '3', '2': ()}, # answer 3 belongs to question 2
)
self.assertContains(response, "Select a valid choice")
response = self.submit_quiz(
contest,
problem_instance,
{'1': '1337', '2': ()}, # such an answer doesn't exist
)
self.assertContains(response, "Select a valid choice")
[docs] def test_submission_unanswered_question(self):
contest = Contest.objects.get()
problem_instance = ProblemInstance.objects.get(pk=1)
response = self.submit_quiz(
contest,
problem_instance,
{'1': '', '2': ()}, # single-choice questions must have some answer
)
self.assertContains(response, "Answer is required")
[docs]class TestScore(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_quiz_problem',
'test_quiz_problem_second',
'test_problem_instance',
'test_quiz_submission',
]
[docs] def test_multiple_choice_no_correct_answer_score(self):
submission = QuizSubmission.objects.get(pk=1)
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
question_report = QuestionReport.objects.get(
question=3, submission_report=submission_report
)
self.assertEqual(question_report.score, 27)
[docs] def test_all_answers_correct_score(self):
submission = QuizSubmission.objects.get(pk=1)
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
question_report = QuestionReport.objects.get(
question=1, submission_report=submission_report
)
self.assertEqual(question_report.score, 27)
[docs] def test_one_answer_incorrect_score(self):
submission = QuizSubmission.objects.get(pk=1)
controller = submission.problem_instance.controller
controller.judge(submission)
submission_report = SubmissionReport.objects.get(
submission=submission, status="ACTIVE"
)
question_report = QuestionReport.objects.get(
question=2, submission_report=submission_report
)
self.assertEqual(question_report.score, 0)
# Inherits from TestScore class in order to run all tests from parent class after adding
# new questions to a quiz to check if adding new questions does not break judging the old ones.
[docs]class TestScoreRejudgeAfterNewQuestionsAdded(TestScore):
[docs] def setUp(self):
self.quiz = Quiz.objects.get(pk=1)
self.submission = QuizSubmission.objects.get(pk=1)
self.controller = self.submission.problem_instance.controller
self.closed_added_question = QuizQuestion.objects.create(
quiz=self.quiz, question='First added question'
)
QuizAnswer.objects.create(
question=self.closed_added_question,
answer='Only correct answer',
is_correct=True,
)
self.open_added_question = QuizQuestion.objects.create(
question='Second added question', quiz=self.quiz, is_text_input=True
)
QuizAnswer.objects.create(
question=self.open_added_question,
answer='Only correct text answer',
is_correct=True,
)
[docs] def test_closed_added_question_no_given_answer_score(self):
self.controller.judge(self.submission)
submission_report = SubmissionReport.objects.get(
submission=self.submission, status="ACTIVE"
)
question_report = QuestionReport.objects.filter(
question=self.closed_added_question, submission_report=submission_report
)
self.assertFalse(question_report.exists())
[docs] def test_open_added_question_no_given_answer_score(self):
self.controller.judge(self.submission)
submission_report = SubmissionReport.objects.get(
submission=self.submission, status="ACTIVE"
)
question_report = QuestionReport.objects.filter(
question=self.open_added_question, submission_report=submission_report
)
self.assertFalse(question_report.exists())
[docs]class TestSubmissionView(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_quiz_problem',
'test_problem_instance',
'test_quiz_submission',
]
[docs] def setUp(self):
self.assertTrue(self.client.login(username='test_user'))
[docs] def test_question_report(self):
contest = Contest.objects.get()
submission = QuizSubmission.objects.get(pk=1)
kwargs = {'contest_id': contest.id, 'submission_id': submission.id}
response = self.client.get(reverse('submission', kwargs=kwargs))
self.assertContains(response, '27 / 27', count=1)
self.assertContains(response, '0 / 27', count=1)
[docs] def test_submission_score_visible(self):
submission = QuizSubmission.objects.get(pk=1)
kwargs = {
'contest_id': submission.problem_instance.contest.id,
'submission_id': submission.id,
}
expected_score = 50
response = self.client.get(reverse('submission', kwargs=kwargs))
self.assertContains(response, '<td>{}</td>'.format(expected_score), html=True)
[docs] def test_diff_submission_unavailable(self):
submission = QuizSubmission.objects.get(pk=1)
kwargs = {
'contest_id': submission.problem_instance.contest.id,
'submission_id': submission.id,
}
response = self.client.get(reverse('submission', kwargs=kwargs))
self.assertNotContains(response, "Diff submissions")
[docs]class TestEditQuizQuestions(TestCase):
[docs] fixtures = ['test_users', 'test_contest', 'test_quiz_problem']
[docs] def setUp(self):
self.assertTrue(
self.client.login(username='test_user')
) # this user is not an admin
[docs] def test_edit_quiz_questions(self):
# test_user is an author of this problem
problem = Problem.objects.get(pk=1)
url = reverse('oioioiadmin:quizzes_quiz_change', args=[problem.pk])
response = self.client.get(url, follow=True)
self.assertContains(response, 'Add another Quiz Question')
[docs]class TestQuizBleach(TestCase):
[docs] fixtures = [
'test_users',
'test_basic_contest',
'test_quiz_bleach',
'test_problem_instance',
]
[docs] def setUp(self):
self.assertTrue(self.client.login(username='test_user'))
[docs] def test_quizbleach(self):
response = self.client.get(
reverse('submit', kwargs={'contest_id': Contest.objects.get().id})
)
self.assertContains(response, '<pre>Answer - correct</pre>')
self.assertNotContains(
response, '<script src="http://weaselcrow.com/keylogger.js"></script>'
)
[docs]class TestPictures(TestCase):
[docs] fixtures = [
'test_users',
'test_basic_contest',
'test_quiz_problem_pictures',
'test_problem_instance',
]
[docs] def setUp(self):
self.assertTrue(self.client.login(username='test_user'))
[docs] def test_embedding(self):
response = self.client.get(
reverse('submit', kwargs={'contest_id': Contest.objects.get().id})
)
def test(picture):
self.assertContains(response, picture.get_absolute_url())
test(QuizQuestionPicture.objects.get())
test(QuizAnswerPicture.objects.get())
[docs] def test_invalid_mode(self):
response = views.picture_view(RequestFactory().request(), 'z', 1)
self.assertEqual(response.status_code, 404)
[docs] def test_access(self):
url = QuizQuestionPicture.objects.get().get_absolute_url()
with fake_time(datetime(1999, 1, 1, tzinfo=timezone.utc)):
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 403)
[docs] def test_download(self):
cat_path = os.path.join(os.path.dirname(__file__), 'files', 'cat.jpg')
picture = QuizQuestionPicture.objects.get()
picture.file.save('cat', open(cat_path, 'rb'))
picture.save()
response = self.client.get(picture.get_absolute_url(), follow=True)
self.assertEqual(response.status_code, 200)
self.assertTrue(response.streaming)
self.assertEqual(
b''.join(response.streaming_content), open(cat_path, 'rb').read()
)
[docs]class TestQuizProblemView(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_full_package',
'test_problem_instance',
'test_problem_site',
'test_quiz_problem_second',
'test_problem_site_second',
]
[docs] disabled_quiz_tabs = ['Problem statement']
[docs] allowed_quiz_tabs = ['Secret key', 'Settings']
[docs] def test_quiz_tab_visibility(self):
self.assertTrue(self.client.login(username='test_admin'))
quiz = Quiz.objects.get(pk=101)
url = reverse('problem_site', kwargs={'site_key': quiz.problemsite.url_key})
response = self.client.get(url, follow=True)
for (allowed_tab, disabled_tab) in zip(
self.allowed_quiz_tabs, self.disabled_quiz_tabs
):
self.assertContains(response, allowed_tab)
self.assertNotContains(response, disabled_tab)
[docs] def test_normal_problem_tab_visibility(self):
self.assertTrue(self.client.login(username='test_admin'))
problem = Problem.objects.get(pk=1)
url = reverse('problem_site', kwargs={'site_key': problem.problemsite.url_key})
response = self.client.get(url, follow=True)
for tab_name in self.allowed_quiz_tabs + self.disabled_quiz_tabs:
self.assertContains(response, tab_name)