def test_serialize_missing_names_and_labels(self): # Configure rubric criteria and options with no names or labels # This *should* never happen, but if it does, recover gracefully # by assigning unique names and empty labels self._configure_xblock({}) for criterion in self.oa_block.rubric_criteria: del criterion["name"] del criterion["label"] for option in criterion["options"]: del option["name"] del option["label"] xml = serialize_content(self.oa_block) content_dict = parse_from_xml_str(xml) # Verify that all names are unique # and that all labels are empty criterion_names = set() option_names = set() criteria_count = 0 options_count = 0 for criterion in content_dict["rubric_criteria"]: criterion_names.add(criterion["name"]) self.assertEqual(criterion["label"], u"") criteria_count += 1 for option in criterion["options"]: option_names.add(option["name"]) self.assertEqual(option["label"], u"") options_count += 1 self.assertEqual(len(criterion_names), criteria_count) self.assertEqual(len(option_names), options_count)
def xml(self, data, suffix=''): """ Retrieve the XBlock's content definition, serialized as XML. Args: data (dict): Not used Kwargs: suffix (str): Not used Returns: dict with keys 'success' (bool), 'message' (unicode), and 'xml' (unicode) """ try: xml = serialize_content(self) # We do not expect `serialize_content` to raise an exception, # but if it does, handle it gracefully. except Exception as ex: msg = _( 'An unexpected error occurred while loading the problem: {error}' ).format(error=ex.message) logger.error(msg) return {'success': False, 'msg': msg, 'xml': u''} else: return {'success': True, 'msg': '', 'xml': xml}
def test_mutated_criteria_dict(self): self.oa_block.title = "Test title" self.oa_block.rubric_assessments = self.BASIC_ASSESSMENTS self.oa_block.start = None self.oa_block.due = None self.oa_block.submission_start = None self.oa_block.submission_due = None # We have to be really permissive with the data we'll accept. # If the data we're retrieving is somehow corrupted, # Studio authors should still be able to retrive an XML representation # so they can edit and fix the issue. # To test this, we systematically mutate a valid rubric dictionary by # mutating the dictionary, then asserting that we can parse the generated XML. for criteria_dict in self.BASIC_CRITERIA: for mutated_dict in self._dict_mutations(criteria_dict): self.oa_block.rubric_criteria = mutated_dict xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated criteria dict {criteria}\n{ex}".format( criteria=mutated_dict, ex=ex) self.fail(msg)
def test_serialize_missing_names_and_labels(self): # Configure rubric criteria and options with no names or labels # This *should* never happen, but if it does, recover gracefully # by assigning unique names and empty labels self._configure_xblock({}) for criterion in self.oa_block.rubric_criteria: del criterion['name'] del criterion['label'] for option in criterion['options']: del option['name'] del option['label'] xml = serialize_content(self.oa_block) content_dict = parse_from_xml_str(xml) # Verify that all names are unique # and that all labels are empty criterion_names = set() option_names = set() criteria_count = 0 options_count = 0 for criterion in content_dict['rubric_criteria']: criterion_names.add(criterion['name']) self.assertEqual(criterion['label'], '') criteria_count += 1 for option in criterion['options']: option_names.add(option['name']) self.assertEqual(option['label'], '') options_count += 1 self.assertEqual(len(criterion_names), criteria_count) self.assertEqual(len(option_names), options_count)
def test_serialize(self, data): self.oa_block.title = data['title'] self.oa_block.prompt = data['prompt'] self.oa_block.rubric_feedback_prompt = data['rubric_feedback_prompt'] self.oa_block.start = _parse_date(data['start']) self.oa_block.due = _parse_date(data['due']) self.oa_block.submission_start = data['submission_start'] self.oa_block.submission_due = data['submission_due'] self.oa_block.rubric_criteria = data['criteria'] self.oa_block.rubric_assessments = data['assessments'] xml = serialize_content(self.oa_block) # Compare the XML with our expected output # To make the comparison robust, first parse the actual and expected XML # then compare elements/attributes in the tree. try: parsed_actual = etree.fromstring(xml) except (ValueError, etree.XMLSyntaxError): self.fail("Could not parse output XML:\n{}".format(xml)) # Assume that the test data XML is valid; if not, this will raise an error # instead of a test failure. parsed_expected = etree.fromstring("".join(data['expected_xml'])) # Pretty-print and reparse the expected XML pretty_expected = etree.tostring(parsed_expected, pretty_print=True, encoding='utf-8') parsed_expected = etree.fromstring(pretty_expected) # Walk both trees, comparing elements and attributes actual_elements = [el for el in parsed_actual.getiterator()] expected_elements = [el for el in parsed_expected.getiterator()] self.assertEqual( len(actual_elements), len(expected_elements), msg="Incorrect XML output:\nActual: {}\nExpected: {}".format( xml, pretty_expected)) for actual, expected in zip(actual_elements, expected_elements): self.assertEqual(actual.tag, expected.tag) self.assertEqual( actual.text, expected.text, msg= u"Incorrect text for {tag}. Expected '{expected}' but found '{actual}'" .format(tag=actual.tag, expected=expected.text, actual=actual.text)) self.assertItemsEqual( actual.items(), expected.items(), msg= u"Incorrect attributes for {tag}. Expected {expected} but found {actual}" .format(tag=actual.tag, expected=expected.items(), actual=actual.items()))
def test_mutated_field(self, field): self._configure_xblock({}) for mutated_value in [0, "\u9282", None]: setattr(self.oa_block, field, mutated_value) xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated field {field} with value {value}\n{ex}".format( field=field, value=mutated_value, ex=ex) self.fail(msg)
def test_mutated_prompts_dict(self): self._configure_xblock({}) for prompts_list in self.BASIC_PROMPTS: for mutated_list in self._list_mutations(prompts_list): self.oa_block.prompts = mutated_list xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = f"Could not parse mutated prompts list {mutated_list}\n{ex}" self.fail(msg)
def test_mutated_prompts_dict(self): self._configure_xblock({}) for prompts_list in self.BASIC_PROMPTS: for mutated_list in self._list_mutations(prompts_list): self.oa_block.prompts = mutated_list xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated prompts list {prompts}\n{ex}".format(prompts=mutated_list, ex=ex) self.fail(msg)
def test_mutated_assessments_dict(self): self._configure_xblock({}) for assessment_dict in self.BASIC_ASSESSMENTS: for mutated_dict in self._dict_mutations(assessment_dict): self.oa_block.rubric_assessments = [mutated_dict] xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated assessment dict {assessment}\n{ex}".format(assessment=mutated_dict, ex=ex) self.fail(msg)
def test_serialize(self, data): self.oa_block.title = data['title'] self.oa_block.prompt = data['prompt'] self.oa_block.rubric_feedback_prompt = data['rubric_feedback_prompt'] self.oa_block.start = _parse_date(data['start']) self.oa_block.due = _parse_date(data['due']) self.oa_block.submission_start = data['submission_start'] self.oa_block.submission_due = data['submission_due'] self.oa_block.rubric_criteria = data['criteria'] self.oa_block.rubric_assessments = data['assessments'] xml = serialize_content(self.oa_block) # Compare the XML with our expected output # To make the comparison robust, first parse the actual and expected XML # then compare elements/attributes in the tree. try: parsed_actual = etree.fromstring(xml) except (ValueError, etree.XMLSyntaxError): self.fail("Could not parse output XML:\n{}".format(xml)) # Assume that the test data XML is valid; if not, this will raise an error # instead of a test failure. parsed_expected = etree.fromstring("".join(data['expected_xml'])) # Pretty-print and reparse the expected XML pretty_expected = etree.tostring(parsed_expected, pretty_print=True, encoding='utf-8') parsed_expected = etree.fromstring(pretty_expected) # Walk both trees, comparing elements and attributes actual_elements = [el for el in parsed_actual.getiterator()] expected_elements = [el for el in parsed_expected.getiterator()] self.assertEqual( len(actual_elements), len(expected_elements), msg="Incorrect XML output:\nActual: {}\nExpected: {}".format(actual_elements, expected_elements) ) for actual, expected in zip(actual_elements, expected_elements): self.assertEqual(actual.tag, expected.tag) self.assertEqual( actual.text, expected.text, msg=u"Incorrect text for {tag}. Expected '{expected}' but found '{actual}'".format( tag=actual.tag, expected=expected.text, actual=actual.text ) ) self.assertItemsEqual( actual.items(), expected.items(), msg=u"Incorrect attributes for {tag}. Expected {expected} but found {actual}".format( tag=actual.tag, expected=expected.items(), actual=actual.items() ) )
def test_mutated_field(self, field): self._configure_xblock({}) for mutated_value in [0, u"\u9282", None]: setattr(self.oa_block, field, mutated_value) xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated field {field} with value {value}\n{ex}".format( field=field, value=mutated_value, ex=ex ) self.fail(msg)
def test_serialize(self, data): self._configure_xblock(data) xml = serialize_content(self.oa_block) # Compare the XML with our expected output # To make the comparison robust, first parse the actual and expected XML # then compare elements/attributes in the tree. try: parsed_actual = etree.fromstring(xml) except (ValueError, etree.XMLSyntaxError): self.fail(f"Could not parse output XML:\n{xml}") # Assume that the test data XML is valid; if not, this will raise an error # instead of a test failure. parsed_expected = etree.fromstring("".join(data['expected_xml'])) # Pretty-print and reparse the expected XML pretty_expected = etree.tostring(parsed_expected, pretty_print=True, encoding='unicode') parsed_expected = etree.fromstring(pretty_expected) # Walk both trees, comparing elements and attributes actual_elements = list(parsed_actual.getiterator()) expected_elements = list(parsed_expected.getiterator()) self.assertEqual( len(actual_elements), len(expected_elements), msg= f"Incorrect XML output:\nActual: {xml}\nExpected: {pretty_expected}" ) for actual, expected in zip(actual_elements, expected_elements): self.assertEqual(actual.tag, expected.tag) self.assertEqual( actual.text, expected.text, msg= "Incorrect text for {tag}. Expected '{expected}' but found '{actual}'" .format(tag=actual.tag, expected=expected.text, actual=actual.text)) self.assertCountEqual( list(actual.items()), list(expected.items()), msg= "Incorrect attributes for {tag}. Expected {expected} but found {actual}" .format(tag=actual.tag, expected=list(expected.items()), actual=list(actual.items())))
def test_mutated_assessments_dict(self): self.oa_block.title = "Test title" self.oa_block.rubric_criteria = self.BASIC_CRITERIA self.oa_block.start = None self.oa_block.due = None self.oa_block.submission_due = None for assessment_dict in self.BASIC_ASSESSMENTS: for mutated_dict in self._dict_mutations(assessment_dict): self.oa_block.rubric_assessments = [mutated_dict] xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: msg = "Could not parse mutated assessment dict {assessment}\n{ex}".format(assessment=mutated_dict, ex=ex) self.fail(msg)
def test_serialize(self, data): self._configure_xblock(data) xml = serialize_content(self.oa_block) # Compare the XML with our expected output # To make the comparison robust, first parse the actual and expected XML # then compare elements/attributes in the tree. try: parsed_actual = etree.fromstring(xml) except (ValueError, etree.XMLSyntaxError): self.fail("Could not parse output XML:\n{}".format(xml)) # Assume that the test data XML is valid; if not, this will raise an error # instead of a test failure. parsed_expected = etree.fromstring("".join(data["expected_xml"])) # Pretty-print and reparse the expected XML pretty_expected = etree.tostring(parsed_expected, pretty_print=True, encoding="unicode") parsed_expected = etree.fromstring(pretty_expected) # Walk both trees, comparing elements and attributes actual_elements = [el for el in parsed_actual.getiterator()] expected_elements = [el for el in parsed_expected.getiterator()] self.assertEqual( len(actual_elements), len(expected_elements), msg=u"Incorrect XML output:\nActual: {}\nExpected: {}".format(xml, pretty_expected), ) for actual, expected in zip(actual_elements, expected_elements): self.assertEqual(actual.tag, expected.tag) self.assertEqual( actual.text, expected.text, msg=u"Incorrect text for {tag}. Expected '{expected}' but found '{actual}'".format( tag=actual.tag, expected=expected.text, actual=actual.text ), ) self.assertItemsEqual( actual.items(), expected.items(), msg=u"Incorrect attributes for {tag}. Expected {expected} but found {actual}".format( tag=actual.tag, expected=expected.items(), actual=actual.items() ), )
def test_mutated_field(self, field): self.oa_block.rubric_criteria = self.BASIC_CRITERIA self.oa_block.rubric_assessments = self.BASIC_ASSESSMENTS self.oa_block.start = None self.oa_block.due = None self.oa_block.submission_start = None self.oa_block.submission_due = None for mutated_value in [0, u"\u9282", None]: setattr(self.oa_block, field, mutated_value) xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated field {field} with value {value}\n{ex}".format( field=field, value=mutated_value, ex=ex) self.fail(msg)
def test_mutated_assessments_dict(self): self.oa_block.title = "Test title" self.oa_block.rubric_criteria = self.BASIC_CRITERIA self.oa_block.start = None self.oa_block.due = None self.oa_block.submission_start = None self.oa_block.submission_due = None for assessment_dict in self.BASIC_ASSESSMENTS: for mutated_dict in self._dict_mutations(assessment_dict): self.oa_block.rubric_assessments = [mutated_dict] xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: msg = "Could not parse mutated assessment dict {assessment}\n{ex}".format(assessment=mutated_dict, ex=ex) self.fail(msg)
def test_mutated_field(self, field): self.oa_block.rubric_criteria = self.BASIC_CRITERIA self.oa_block.rubric_assessments = self.BASIC_ASSESSMENTS self.oa_block.start = None self.oa_block.due = None self.oa_block.submission_due = None for mutated_value in [0, u"\u9282", None]: setattr(self.oa_block, field, mutated_value) xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: msg = "Could not parse mutated field {field} with value {value}\n{ex}".format( field=field, value=mutated_value, ex=ex ) self.fail(msg)
def test_mutated_criteria_dict(self): self._configure_xblock({}) # We have to be really permissive with the data we'll accept. # If the data we're retrieving is somehow corrupted, # Studio authors should still be able to retrive an XML representation # so they can edit and fix the issue. # To test this, we systematically mutate a valid rubric dictionary by # mutating the dictionary, then asserting that we can parse the generated XML. for criteria_dict in self.BASIC_CRITERIA: for mutated_dict in self._dict_mutations(criteria_dict): self.oa_block.rubric_criteria = mutated_dict xml = serialize_content(self.oa_block) try: etree.fromstring(xml) except Exception as ex: # pylint:disable=W0703 msg = "Could not parse mutated criteria dict {criteria}\n{ex}".format(criteria=mutated_dict, ex=ex) self.fail(msg)
def xml(self, data, suffix=''): """ Retrieve the XBlock's content definition, serialized as XML. Args: data (dict): Not used Kwargs: suffix (str): Not used Returns: dict with keys 'success' (bool), 'message' (unicode), and 'xml' (unicode) """ try: xml = serialize_content(self) # We do not expect `serialize_content` to raise an exception, # but if it does, handle it gracefully. except Exception as ex: msg = _('An unexpected error occurred while loading the problem: {error}').format(error=ex) logger.error(msg) return {'success': False, 'msg': msg, 'xml': u''} else: return {'success': True, 'msg': '', 'xml': xml}