"""Tests the capa shuffle and name-masking.""" import textwrap import unittest from xmodule.capa.responsetypes import LoncapaProblemError from xmodule.capa.tests.helpers import mock_capa_system, new_loncapa_problem class CapaShuffleTest(unittest.TestCase): """Capa problem tests for shuffling and choice-name masking.""" def setUp(self): super().setUp() self.system = mock_capa_system() def test_shuffle_4_choices(self): """Verify shuffling and name-masking for four choices.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Donut """ ) problem = new_loncapa_problem(xml_str, seed=0) # shuffling 4 things with seed of 0 yields: B A C D # Check that the choices are shuffled the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Banana'.*'Apple'.*'Chocolate'.*'Donut'.*\].*
") # Check that choice name masking is enabled and that unmasking works response = list(problem.responders.values())[0] assert not response.has_mask() assert response.unmask_order() == ["choice_1", "choice_0", "choice_2", "choice_3"] assert the_html == problem.get_html(), "should be able to call get_html() twice" def test_shuffle_custom_names(self): """Verify shuffling preserves custom choice names.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Donut """ ) problem = new_loncapa_problem(xml_str, seed=0) # B A C D # Check that the custom name= names come through response = list(problem.responders.values())[0] assert not response.has_mask() assert response.has_shuffle() assert response.unmask_order() == ["choice_0", "choice_aaa", "choice_1", "choice_ddd"] def test_shuffle_different_seed(self): """Check that shuffling produces different order with a different seed.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Donut """ ) problem = new_loncapa_problem(xml_str, seed=341) # yields D A B C the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Donut'.*'Apple'.*'Banana'.*'Chocolate'.*\].*
") def test_shuffle_1_choice(self): """Verify shuffling behavior with only one choice.""" xml_str = textwrap.dedent( """ Apple """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Apple'.*\].*
") response = list(problem.responders.values())[0] assert not response.has_mask() assert response.has_shuffle() assert response.unmask_order() == ["choice_0"] def test_shuffle_6_choices(self): """Test shuffling with six choices to ensure proper randomization.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Zonut Eggplant Filet Mignon """ ) problem = new_loncapa_problem(xml_str, seed=0) # yields: C E A B D F # Donut -> Zonut to show that there is not some hidden alphabetic ordering going on the_html = problem.get_html() self.assertRegex( the_html, r"
.*\[.*'Chocolate'.*'Eggplant'.*'Apple'.*'Banana'.*'Zonut'.*'Filet Mignon'.*\].*
" ) def test_shuffle_false(self): """Verify that shuffle='false' keeps original order.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Donut """ ) problem = new_loncapa_problem(xml_str) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Apple'.*'Banana'.*'Chocolate'.*'Donut'.*\].*
") response = list(problem.responders.values())[0] assert not response.has_mask() assert not response.has_shuffle() def test_shuffle_fixed_head_end(self): """Ensure choices fixed at the head remain in place during shuffle.""" xml_str = textwrap.dedent( """ Alpha Beta A B C D """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() # Alpha Beta held back from shuffle (head end) self.assertRegex(the_html, r"
.*\[.*'Alpha'.*'Beta'.*'B'.*'A'.*'C'.*'D'.*\].*
") def test_shuffle_fixed_tail_end(self): """Ensure choices fixed at the tail remain in place during shuffle.""" xml_str = textwrap.dedent( """ A B C D Alpha Beta """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() # Alpha Beta held back from shuffle (tail end) self.assertRegex(the_html, r"
.*\[.*'B'.*'A'.*'C'.*'D'.*'Alpha'.*'Beta'.*\].*
") def test_shuffle_fixed_both_ends(self): """Ensure choices fixed at both ends remain in place during shuffle.""" xml_str = textwrap.dedent( """ Alpha Beta A B C D Psi Omega """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Alpha'.*'Beta'.*'B'.*'A'.*'C'.*'D'.*'Psi'.*'Omega'.*\].*
") def test_shuffle_fixed_both_ends_thin(self): """Test shuffling with only three choices, two of which are fixed.""" xml_str = textwrap.dedent( """ Alpha A Omega """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'Alpha'.*'A'.*'Omega'.*\].*
") def test_shuffle_fixed_all(self): """Verify that all fixed choices remain in order with no shuffle.""" xml_str = textwrap.dedent( """ A B C """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'A'.*'B'.*'C'.*\].*
") def test_shuffle_island(self): """A fixed 'island' choice not at the head or tail end gets lumped into the tail end.""" xml_str = textwrap.dedent( """ A Mid C Mid D """ ) problem = new_loncapa_problem(xml_str, seed=0) the_html = problem.get_html() self.assertRegex(the_html, r"
.*\[.*'A'.*'Mid'.*'Mid'.*'C'.*'D'.*\].*
") def test_multiple_shuffle_responses(self): """Check shuffling for multiple choice groups in the same problem.""" xml_str = textwrap.dedent( """ Apple Banana Chocolate Donut

Here is some text

A B C D
""" ) problem = new_loncapa_problem(xml_str, seed=0) orig_html = problem.get_html() assert orig_html == problem.get_html(), "should be able to call get_html() twice" html = orig_html.replace("\n", " ") # avoid headaches with .* matching self.assertRegex( html, r"
.*\[.*'Banana'.*'Apple'.*'Chocolate'.*'Donut'.*\].*
.*" + r"
.*\[.*'C'.*'A'.*'D'.*'B'.*\].*
", ) # Look at the responses in their authored order responses = sorted(list(problem.responders.values()), key=lambda resp: int(resp.id[resp.id.rindex("_") + 1 :])) assert not responses[0].has_mask() assert responses[0].has_shuffle() assert responses[1].has_shuffle() assert responses[0].unmask_order() == ["choice_1", "choice_0", "choice_2", "choice_3"] assert responses[1].unmask_order() == ["choice_2", "choice_0", "choice_3", "choice_1"] def test_shuffle_not_with_answerpool(self): """Raise error if shuffle and answer-pool are both used.""" xml_str = textwrap.dedent( """ A Mid C Mid D """ ) with self.assertRaisesRegex(LoncapaProblemError, "shuffle and answer-pool"): new_loncapa_problem(xml_str)