import course import survey import criterion import grouper import pytest import unittest from course import Student from course import Course from survey import Question, YesNoQuestion, MultipleChoiceQuestion, \ NumericQuestion, CheckboxQuestion, Answer from criterion import InvalidAnswerError, Criterion, HomogeneousCriterion,\ HeterogeneousCriterion, LonelyMemberCriterion from grouper import Group, Grouping from survey import Survey @pytest.fixture def empty_course() -> course.Course: return course.Course('csc148') @pytest.fixture def students() -> list[course.Student]: return [course.Student(1, 'Zoro'), course.Student(2, 'Aaron'), course.Student(3, 'Gertrude'), course.Student(4, 'Yvette')] @pytest.fixture def alpha_grouping(students_with_answers) -> grouper.Grouping: grouping = grouper.Grouping() grouping.add_group(grouper.Group([students_with_answers[0], students_with_answers[3]])) grouping.add_group(grouper.Group([students_with_answers[1], students_with_answers[2]])) return grouping @pytest.fixture def other_alpha_grouping(students_with_answers) -> grouper.Grouping: grouping = grouper.Grouping() grouping.add_group(grouper.Group([students_with_answers[1], students_with_answers[3], students_with_answers[2]])) grouping.add_group(grouper.Group([students_with_answers[0]])) return grouping @pytest.fixture def greedy_grouping(students_with_answers) -> grouper.Grouping: grouping = grouper.Grouping() grouping.add_group(grouper.Group([students_with_answers[1], students_with_answers[3]])) grouping.add_group(grouper.Group([students_with_answers[0], students_with_answers[2]])) return grouping @pytest.fixture def other_greedy_grouping(students_with_answers) -> grouper.Grouping: grouping = grouper.Grouping() grouping.add_group(grouper.Group([students_with_answers[1], ])) grouping.add_group(grouper.Group([students_with_answers[3], students_with_answers[0], students_with_answers[2]])) return grouping @pytest.fixture def sa_grouping(students_with_answers) -> grouper.Grouping: grouping = grouper.Grouping() grouping.add_group(grouper.Group([students_with_answers[2], students_with_answers[0]])) grouping.add_group(grouper.Group([students_with_answers[3], students_with_answers[1]])) return grouping @pytest.fixture def questions() -> list[survey.Question]: return [survey.MultipleChoiceQuestion(1, 'why?', ['a', 'b']), survey.NumericQuestion(2, 'what?', -2, 4), survey.YesNoQuestion(3, 'really?'), survey.CheckboxQuestion(4, 'how?', ['a', 'b', 'c'])] @pytest.fixture def criteria(answers) -> list[criterion.Criterion]: return [criterion.HomogeneousCriterion(), criterion.HeterogeneousCriterion(), criterion.LonelyMemberCriterion(), criterion.HomogeneousCriterion()] @pytest.fixture() def weights() -> list[int]: return [2, 5, 7, 4] @pytest.fixture def answers() -> list[list[survey.Answer]]: return [[survey.Answer('a'), survey.Answer('b'), survey.Answer('a'), survey.Answer('b')], [survey.Answer(0), survey.Answer(4), survey.Answer(-1), survey.Answer(1)], [survey.Answer(True), survey.Answer(False), survey.Answer(True), survey.Answer(True)], [survey.Answer(['a', 'b']), survey.Answer(['a', 'b']), survey.Answer(['a']), survey.Answer(['b'])]] @pytest.fixture def invalid_answers() -> list[list[survey.Answer]]: return [[survey.Answer('a'), survey.Answer('b'), survey.Answer('a'), survey.Answer('b')], [survey.Answer(0), survey.Answer(4), survey.Answer(-1), survey.Answer(1)], [survey.Answer(True), survey.Answer(1), survey.Answer(True), survey.Answer(True)], [survey.Answer(['a', 'b']), survey.Answer(['a', 'b']), survey.Answer(['a']), survey.Answer(['b'])]] @pytest.fixture def students_with_answers(students, questions, answers) -> list[course.Student]: for i, student in enumerate(students): for j, question in enumerate(questions): student.set_answer(question, answers[j][i]) return students @pytest.fixture def students_with_invalid_answers(students, questions, invalid_answers) -> \ list[course.Student]: for i, student in enumerate(students): for j, question in enumerate(questions): student.set_answer(question, invalid_answers[j][i]) return students @pytest.fixture def course_with_students(empty_course, students) -> course.Course: empty_course.enroll_students(students) return empty_course @pytest.fixture def course_with_students_with_answers(empty_course, students_with_answers) -> course.Course: empty_course.enroll_students(students_with_answers) return empty_course @pytest.fixture def survey_(questions, criteria, weights) -> survey.Survey: s = survey.Survey(questions) for i, question in enumerate(questions): s.set_weight(weights[i], question) s.set_criterion(criteria[i], question) return s @pytest.fixture def group(students) -> grouper.Group: return grouper.Group(students) def get_member_ids(grouping: grouper.Grouping) -> set[frozenset[int]]: member_ids = set() for group in grouping.get_groups(): ids = [] for member in group.get_members(): ids.append(member.id) member_ids.add(frozenset(ids)) return member_ids def compare_groupings(grouping1: grouper.Grouping, grouping2: grouper.Grouping) -> None: assert get_member_ids(grouping1) == get_member_ids(grouping2) # You may need to import pytest in order to run your tests. # You are free to import hypothesis and use hypothesis for testing. # This file will not be graded for style with PythonTA ############################################################################### # Task 2 Test cases ############################################################################### class TestStudentClass: def test_initializer(self) -> None: stu1 = Student(1, 'a') assert stu1.id == 1 assert stu1.name == 'a' def test_str(self) -> None: stu1 = Student(1, 'a') assert str(stu1) == 'a' def test_has_answer_valid(self) -> None: stu1 = Student(1, 'a') qn = NumericQuestion(1, 'what?', 1, 10) ans = Answer(4) stu1.set_answer(qn, ans) assert stu1.has_answer(qn) is True def test_has_answer_invalid(self) -> None: stu1 = Student(1, 'a') qn = MultipleChoiceQuestion(1, 'what?', ['a', 'b', 'c']) ans = Answer(4) stu1.set_answer(qn, ans) assert stu1.has_answer(qn) is False def test_set_answer_existing_ans(self) -> None: stu1 = Student(1, 'a') qn = NumericQuestion(1, 'what?', 1, 10) ans1 = Answer(4) ans2 = Answer(5) stu1.set_answer(qn, ans1) stu1.set_answer(qn, ans2) assert stu1.get_answer(qn) == ans2 def test_get_answer_no_qn(self) -> None: stu1 = Student(1, 'a') ans1 = Answer(4) qn1 = NumericQuestion(1, 'what?', 1, 10) qn2 = NumericQuestion(2, 'where?', 1, 10) stu1.set_answer(qn1, ans1) assert stu1.get_answer(qn2) is None ############################################################################### # Task 3 Test cases ############################################################################### class TestCourseClass: def test_initializer(self) -> None: test_course = Course('ABC') assert test_course.name == 'ABC' assert test_course.students == [] def test_enroll_students_valid(self) -> None: test_course = Course('ABC') stu1 = Student(1, 'a') stu2 = Student(2, 'b') test_course.enroll_students([stu1, stu2]) assert test_course.students == [stu1, stu2] def test_enroll_students_duplicates(self) -> None: test_course = Course('ABC') stu1 = Student(1, 'a') stu2 = Student(1, 'b') test_course.enroll_students([stu1, stu2]) assert test_course.students == [] def test_enroll_students_nameless(self) -> None: test_course = Course('ABC') stu1 = Student(1, 'a') stu2 = Student(2, '') test_course.enroll_students([stu1, stu2]) assert test_course.students == [] def test_all_answered_valid(self) -> None: test_course = Course('ABC') qn1 = NumericQuestion(1, 'what?', 1, 10) qn2 = NumericQuestion(2, 'where?', 1, 10) test_survey = Survey([qn1, qn2]) ans1 = Answer(4) ans2 = Answer(5) stu1 = Student(1, 'a') stu1.set_answer(qn1, ans1) stu1.set_answer(qn2, ans2) stu2 = Student(2, 'b') stu2.set_answer(qn1, ans1) stu2.set_answer(qn2, ans2) assert test_course.all_answered(test_survey) is True def test_all_answered_invalid(self) -> None: test_course = Course('ABC') qn1 = NumericQuestion(1, 'what?', 1, 10) qn2 = NumericQuestion(2, 'where?', 1, 10) test_survey = Survey([qn1, qn2]) ans1 = Answer(11) ans2 = Answer(5) stu1 = Student(1, 'a') stu1.set_answer(qn1, ans1) stu1.set_answer(qn2, ans2) stu2 = Student(2, 'b') stu2.set_answer(qn2, ans2) test_course.enroll_students([stu1, stu2]) assert test_course.all_answered(test_survey) is False def test_get_students_order(self) -> None: test_course = Course('ABC') stu1 = Student(3, 'a') stu2 = Student(1, 'b') stu3 = Student(2, 'c') test_course.enroll_students([stu1, stu2, stu3]) assert test_course.get_students() == (stu2, stu3, stu1) ############################################################################### # Task 4 Test cases ############################################################################### class TestQuestion: def test_question_main_class(self) -> None: q = Question(1, 'What?') assert q.id == 1 assert q.text == 'What?' def test_mcq_initializer(self) -> None: mcq = MultipleChoiceQuestion(2, "Choose.", ['a', 'b', 'c', 'd']) assert mcq.id == 2 assert mcq.text == "Choose." def test_mcq_str(self) -> None: mcq = MultipleChoiceQuestion(2, "Choose.", ['a', 'b', 'c', 'd']) expected = str(mcq) s = 'Choose.\n' \ '- a\n' \ '- b\n' \ '- c\n' \ '- d' assert expected == s def test_mcq_validate_answer_valid(self) -> None: mcq = MultipleChoiceQuestion(2, "Choose.", ['a', 'b', 'c', 'd']) answer = Answer('a') assert mcq.validate_answer(answer) is True def test_mcq_validate_answer_invalid(self) -> None: mcq = MultipleChoiceQuestion(2, "Choose.", ['a', 'b', 'c', 'd']) answer = Answer('e') assert mcq.validate_answer(answer) is False def test_numeric_initializer(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) assert num.id == 2 assert num.text == "Enter a number?" def test_numeric_str(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) s = str(num) expected = "Enter a number?" assert expected == s def test_numeric_validate_answer_valid(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) answer1 = Answer(5) answer2 = Answer(10) answer3 = Answer(1) assert num.validate_answer(answer1) is True assert num.validate_answer(answer2) is True assert num.validate_answer(answer3) is True def test_numeric_validate_answer_invalid(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) answer1 = Answer(11) answer2 = Answer(-1) answer3 = Answer(0) assert num.validate_answer(answer1) is False assert num.validate_answer(answer2) is False assert num.validate_answer(answer3) is False def test_numeric_get_similarity_minimum(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) answer1 = Answer(1) answer2 = Answer(10) assert num.get_similarity(answer1, answer2) == 0.0 def test_numeric_get_similarity_maximum(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) answer1 = Answer(10) answer2 = Answer(10) assert num.get_similarity(answer1, answer2) == 1.0 def test_numeric_get_similarity_random(self) -> None: num = NumericQuestion(2, "Enter a number?", 1, 10) answer1 = Answer(5) answer2 = Answer(3) assert num.get_similarity(answer1, answer2) == 1.0 - 2 / 9 def test_yn_initializer(self) -> None: yn = YesNoQuestion(1, "Yes or No?") def test_yn_str(self) -> None: yn = YesNoQuestion(1, "Yes or No?") assert str(yn) == 'Yes or No?\n' \ '- Yes\n' \ '- No' def test_yn_validate_valid(self) -> None: yn = YesNoQuestion(1, "Yes or No?") ans1 = Answer(True) ans2 = Answer(False) assert yn.validate_answer(ans1) is True assert yn.validate_answer(ans2) is True def test_yn_validate_invalid(self) -> None: yn = YesNoQuestion(1, "Yes or No?") ans1 = Answer(1) ans2 = Answer('Yes') assert yn.validate_answer(ans1) is False assert yn.validate_answer(ans2) is False def test_yn_get_similarity_same(self) -> None: yn = YesNoQuestion(1, "Yes or No?") ans1 = Answer(True) ans2 = Answer(True) assert yn.get_similarity(ans1, ans2) == 1.0 def test_yn_get_similarity_different(self) -> None: yn = YesNoQuestion(1, "Yes or No?") ans1 = Answer(False) ans2 = Answer(True) assert yn.get_similarity(ans1, ans2) == 0.0 def test_checkbox_validate_answer(self) -> None: checkbox = CheckboxQuestion(1, "Choose any.", ['a', 'b', 'c', 'd']) answer1 = Answer(['a', 'b', 'c']) answer2 = Answer([]) answer3 = Answer(['a', 'a', 'a']) assert checkbox.validate_answer(answer1) is True assert checkbox.validate_answer(answer2) is False assert checkbox.validate_answer(answer3) is False def test_checkbox_similarity_random(self) -> None: checkbox = CheckboxQuestion(1, "Choose any.", ['a', 'b', 'c', 'd']) answer1 = Answer(['a', 'b', 'c']) answer2 = Answer(['c', 'b', 'd']) assert checkbox.get_similarity(answer1, answer2) == 0.5 def test_checkbox_similarity_different(self) -> None: checkbox = CheckboxQuestion(1, "Choose any.", ['a', 'b', 'c', 'd', 'e', 'f']) answer1 = Answer(['a', 'b', 'c']) answer2 = Answer(['d', 'e', 'f']) assert checkbox.get_similarity(answer1, answer2) == 0.0 def test_checkbox_similarity_same(self) -> None: checkbox = CheckboxQuestion(1, "Choose any.", ['a', 'b', 'c', 'd']) answer1 = Answer(['a', 'b', 'c']) answer2 = Answer(['a', 'b', 'c']) assert checkbox.get_similarity(answer1, answer2) == 1.0 ############################################################################### # Task 5 Test cases ############################################################################### class TestAnswer: def test_answer_is_valid(self) -> None: ans = Answer('a') qn = CheckboxQuestion(1, 'Choose.', ['a', 'b', 'c']) assert ans.is_valid(qn) is True def test_answer_is_invalid(self) -> None: ans = Answer('d') qn = CheckboxQuestion(1, 'Choose.', ['a', 'b', 'c']) assert ans.is_valid(qn) is False ############################################################################### # Task 6 Test cases ############################################################################### class TestCriterion: def test_homogenous_invalid(self, criteria, answers) -> None: q = MultipleChoiceQuestion(1, "Choose.", ['a', 'b', 'c']) hom_criterion = criteria[0] with pytest.raises(InvalidAnswerError): hom_criterion.score_answers(q, answers[-1]) def test_homogenous_none(self, criteria, answers) -> None: q = MultipleChoiceQuestion(1, "Choose.", ['a', 'b', 'c']) hom_criterion = criteria[0] with pytest.raises(InvalidAnswerError): hom_criterion.score_answers(q, answers[-1]) ############################################################################### # Task 7 Test cases ############################################################################### class TestGroup: def test_group_initializer(self) -> None: stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') grp = Group([stu1, stu2, stu3]) members = grp.get_members() assert members[0].name == 'a' assert members[1].name == 'b' assert members[2].name == 'c' def test_group_str(self) -> None: stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') stu4 = Student(4, 'd') grp = Group([stu1, stu2, stu3, stu4]) assert str(grp) == 'a, b, c, d' def test_group_duplicate_students(self) -> None: stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') grp = Group([stu1, stu2, stu3, stu3]) assert str(grp) == 'a, b, c' ############################################################################### # Task 8 Test cases ############################################################################### class TestGrouping: def test_grouping_str(self) -> None: grouping = Grouping() stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') stu4 = Student(4, 'd') stu5 = Student(5, 'e') stu6 = Student(6, 'f') grp1 = Group([stu1, stu2, stu3]) grp2 = Group([stu4, stu5, stu6]) grouping.add_group(grp1) grouping.add_group(grp2) assert str(grouping) == 'Groups\n' \ 'a, b, c\n' \ 'd, e, f' # survey.MultipleChoiceQuestion(1, 'why?', ['a', 'b']), # survey.NumericQuestion(2, 'what?', -2, 4), # survey.YesNoQuestion(3, 'really?'), # survey.CheckboxQuestion(4, 'how?', ['a', 'b', 'c'])] ############################################################################### # Task 9 Test cases ############################################################################### class TestSurvey: def test_survey_len(self) -> None: qn1 = Question(1, 'What?') qn2 = Question(2, 'Who?') qn3 = Question(3, 'Where?') test_survey = Survey([qn1, qn2, qn3]) assert len(test_survey) == 3 def test_survey_contains(self) -> None: qn1 = Question(1, 'What?') qn2 = Question(2, 'Who?') qn3 = Question(3, 'Where?') qn4 = Question(4, 'Why?') test_survey = Survey([qn1, qn2, qn3]) assert test_survey.__contains__(qn1) is True assert test_survey.__contains__(qn4) is False def test_survey_str(self, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2], questions[3]]) assert str(testsurvey) == 'Survey\n' \ '1. why?\n' \ '- a\n' \ '- b\n' \ '2. what?\n' \ '3. really?\n' \ '- Yes\n' \ '- No\n' \ '4. how?\n' \ '- a\n' \ '- b\n' \ '- c' def test_get_questions(self, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2], questions[3]]) assert testsurvey.get_questions() == [questions[0], questions[1], questions[2], questions[3]] def test_set_weight_valid(self, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2], questions[3]]) assert testsurvey.set_weight(5, questions[0]) is True def test_set_weight_invalid(self, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2]]) assert testsurvey.set_weight(5, questions[3]) is False def test_set_criterion_valid(self, criteria, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2], questions[3]]) assert testsurvey.set_criterion(criteria[0], questions[0]) is True def test_set_criterion_invalid(self, criteria, questions) -> None: testsurvey = Survey([questions[0], questions[1], questions[2]]) assert testsurvey.set_criterion(criteria[1], questions[3]) is False def test_score_students(self, survey_, students_with_invalid_answers) -> \ None: score = survey_.score_students(students_with_invalid_answers) assert round(score, 2) == 0.0 def test_score_grouping_sa_grouping(self, survey_, sa_grouping) -> None: score = survey_.score_grouping(sa_grouping) assert round(score, 2) == 2.29 def test_score_grouping_alpha_grouping(self, survey_, alpha_grouping) -> None: score = survey_.score_grouping(alpha_grouping) assert round(score, 2) == 2.0 ############################################################################### # Task 10 Test cases ############################################################################### class TestGrouper: def test_grouper_len(self) -> None: stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') test_group = Group([stu1, stu2, stu3]) assert len(test_group) == 3 def test_grouper_contains(self) -> None: stu1 = Student(1, 'a') stu2 = Student(2, 'b') stu3 = Student(3, 'c') stu4 = Student(1, 'e') test_group = Group([stu1, stu2, stu3]) assert test_group.__contains__(stu4) def test_alpha_make_grouping(self, course_with_students_with_answers, other_alpha_grouping, survey_) -> None: grouper_ = grouper.AlphaGrouper(3) grouping = grouper_.make_grouping(course_with_students_with_answers, survey_) compare_groupings(grouping, other_alpha_grouping) def test_greedy_make_grouping(self, course_with_students_with_answers, greedy_grouping, survey_) -> None: grouper_ = grouper.GreedyGrouper(2) grouping = grouper_.make_grouping(course_with_students_with_answers, survey_) compare_groupings(grouping, greedy_grouping)