def test_disabled_code(): source, indent_corrections = undo_amendments( "# Some code\n" "a = b + c\n" "### with disabled_code()\n" "### a += 1\n" "### \n" "###\n" " ### \n" " ###\n" " ### a -= 1\n" "a += 1\n" ) assert source == ( "# Some code\n" "a = b + c\n" "with disabled_code()\n" " a += 1\n" "\n" "\n" " \n" " \n" " a -= 1\n" "a += 1\n" ) assert indent_corrections == { 3: 4, 4: 4, 5: 4, 6: 3, 7: 4, 8: 3, 9: 4, }
def test_not_in_spec_line(): source, indent_corrections = undo_amendments( "# Some code\n" "a = 1\n" "with foo(): ## Not in spec\n" " foobar() ##not in SPEC at all!\n" "b = 2\n" ) assert source == ( "# Some code\n" "a = 1\n" "# with foo(): ## Not in spec\n" "# foobar() ##not in SPEC at all!\n" "b = 2\n" ) assert indent_corrections == {}
def test_not_in_spec_block(): source, indent_corrections = undo_amendments( "# Some code\n" "a = 1\n" "## Begin not in spec\n" "with foo():\n" " foobar()\n" " ## end NOT in spec at all either!\n" "b = 2\n" ) assert source == ( "# Some code\n" "a = 1\n" "# ## Begin not in spec\n" "# with foo():\n" "# foobar()\n" "# ## end NOT in spec at all either!\n" "b = 2\n" ) assert indent_corrections == {}
def test_single_hashes_dont_cause_changes(): source, indent_corrections = undo_amendments( "# Some code\n" "a = 1 # Not in spec\n" "# Begin not in spec\n" "with foo():\n" " foobar()\n" "# End not in spec\n" "b = 2\n" ) assert source == ( "# Some code\n" "a = 1 # Not in spec\n" "# Begin not in spec\n" "with foo():\n" " foobar()\n" "# End not in spec\n" "b = 2\n" ) assert indent_corrections == {}
def compare_sources(ref_source, imp_source, comparator): """ Compare two Python sources, one containing a reference VC-2 pseudocode function and the other containing an implementation. Parameters ========== ref_source : str The reference VC-2 pseudocode implementation of a function. imp_source : str The implementation of the same function used in :py:mod:`vc2_conformance`. Will be pre-processed using :py:func:`verification.amendment_comments.undo_amendments` prior to comparison. comparator : :py:class:`verification.node_comparator.NodeComparator` The comparator instance to use to test for equivalence. Returns ======= True or :py:class:`Difference` True is returned if the implementations are equal (according to the supplied comparator). Otherwise a :py:class:`Difference` is returned enumerating the differences. Raises ======= :py:exc:`ComparisonError` """ try: imp_source, implementation_indent_offsets = undo_amendments(imp_source) except TokenError as e: raise ComparisonError(e.args[0], imp_row=e.args[1][0], imp_col=e.args[1][1]) except BadAmendmentCommentError as e: raise ComparisonError( "Unrecognised amendment comment {!r}".format(e.comment), imp_row=e.row, imp_col=e.col, ) except UnmatchedNotInSpecBlockError as e: raise ComparisonError( "No matching '## Begin not in spec'", imp_row=e.row, ) except UnclosedNotInSpecBlockError as e: raise ComparisonError( "'## Begin not in spec' block not closed", imp_row=e.row, ) try: ref_ast = ast.parse(ref_source) except SyntaxError as e: raise ComparisonError(e.msg, ref_row=e.lineno, ref_col=e.offset) try: imp_ast = ast.parse(imp_source) except SyntaxError as e: raise ComparisonError( e.msg, imp_row=e.lineno, imp_col=e.offset + implementation_indent_offsets.get(e.lineno, 0), ) match = comparator.compare(ref_ast, imp_ast) if match is True: return match message = str(match) ref_row = None ref_col = None imp_row = None imp_col = None if isinstance(match, NodeTypesDiffer): message = "Mismatched {} vs. {}".format( type(match.n1).__name__, type(match.n2).__name__, ) ref_row, ref_col = match.n1_row_col imp_row, imp_col = match.n2_row_col elif isinstance(match, NodeFieldsDiffer): message = "Different {} values".format(match.field) ref_row, ref_col = match.n1_row_col imp_row, imp_col = match.n2_row_col elif isinstance(match, NodeFieldLengthsDiffer): if len(match.v1) > len(match.v2): num = len(match.v1) - len(match.v2) message = "{} extra value{} in reference's {}".format( num, "s" if num != 1 else "", match.field, ) else: num = len(match.v2) - len(match.v1) message = "{} extra value{} in implementation's {}".format( num, "s" if num != 1 else "", match.field, ) ref_row, ref_col = match.n1_row_col imp_row, imp_col = match.n2_row_col elif isinstance(match, NodeListFieldsDiffer): message = "Different {} at index {} ({!r} and {!r})".format( match.field, match.index, match.v1[match.index], match.v2[match.index], ) ref_row, ref_col = match.n1_row_col imp_row, imp_col = match.n2_row_col return Difference( message=message, ref_row=ref_row, ref_col=ref_col, imp_row=imp_row, imp_col=(imp_col + implementation_indent_offsets.get(imp_row, 0) if imp_row is not None and imp_col is not None else imp_col), )
def test_no_amendment_comments(): source, indent_corrections = undo_amendments("# Some code\n" "a = b + c\n") assert source == ("# Some code\n" "a = b + c\n") assert indent_corrections == {}
def test_unclosed_not_in_spec_block(): with pytest.raises(UnclosedNotInSpecBlockError): undo_amendments("## Begin not in spec")
def test_unmatched_not_in_spec_block(): with pytest.raises(UnmatchedNotInSpecBlockError): undo_amendments("## End not in spec")
def test_bad_amendment_comment(): with pytest.raises(BadAmendmentCommentError): undo_amendments("## Not a valid one...") with pytest.raises(BadAmendmentCommentError): undo_amendments("###Space after hashes is required")
def test_bad_tokenization(): with pytest.raises(tokenize.TokenError): undo_amendments("'''")
def test_empty(): assert undo_amendments("") == ("", {})