def assert_bool_diff(assert_method, frame_locals): # type: (str, dict) -> tuple hint = None expected = assert_method == "assertTrue" actual = frame_locals["expr"] if not isinstance(actual, bool): booly = "falsy" if assert_method == "assertTrue" else "truthy" hint = "{expr} is {booly}".format( expr=deleted_text(pformat(actual)), booly=deleted_text(booly), ) return pformat(expected), pformat(actual), hint
def assert_has_calls_diff(assert_method, mock_instance, mock_name, frame_locals): # type: (str, Mock, str, dict) -> tuple hint = None expected_calls = frame_locals["expected"] # expected is a normal list, mock call_args_list is a CallList so we coerce to a # normal list for consistent formatting expected = pformat(expected_calls, width=1).replace("call", mock_name) actual = pformat(list(mock_instance.call_args_list), width=1).replace("call", mock_name) if not mock_instance.call_count == len(expected_calls): expected_line = MOCK_CALL_COUNT_MSG.format( padding=PADDED_NEWLINE, label=header_text("Expected: "), mock_name=header_text(mock_name), num=deleted_text(len(expected_calls)), ) actual_line = MOCK_CALL_COUNT_MSG.format( padding=" " * 12, label=header_text("Actual: "), mock_name=header_text(mock_name), num=inserted_text(mock_instance.call_count), ) hint = "\n".join([ "expected and actual call counts differ", expected_line, actual_line, ]) return expected, actual, hint
def build_args_diff(expected, actual): # type: (tuple, tuple) -> tuple """ Loops through expected/actual arg tuples and builds out the diff between each individual pair of args. :param expected: the expected called args tuple :param actual: the actual called args tuple :return: colorized, formatted diff str """ i = 0 expected_args = [] actual_args = [] hints = [] expected_length = len(expected) actual_length = len(actual) while not (i == expected_length or i == actual_length): a = expected[i] b = actual[i] if type(expected) is not type(actual): hints.append( "Arg {num} expected type: {etype}, actual type: {atype}". format( num=i + 1, etype=deleted_text(type(expected)), atype=inserted_text(type(actual)), )) act, exp = build_split_diff(pformat(b), pformat(a)) expected_args.append("\n".join(exp)) actual_args.append("\n".join(act)) i += 1 # handle different arg lengths if i == actual_length and i < expected_length: for remaining_arg in expected[i:]: expected_args.append(deleted_text(pformat(remaining_arg))) if i == expected_length and i < actual_length: for remaining_arg in actual[i:]: actual_args.append(inserted_text(pformat(remaining_arg))) return expected_args, actual_args
def build_split_diff(lhs_repr, rhs_repr): # type: (str, str) -> tuple """ Copy pasted from pytest-clarity. Compares string representations of expected and actual, building the colorized diff output for consumption. :param lhs_repr: the string representation of the "left" i.e. expected :param rhs_repr: the string representation of the "right" i.e. actual :return: tuple of the "left" and "right" colorized newline separated strings """ lhs_out, rhs_out = "", "" matcher = difflib.SequenceMatcher(None, lhs_repr, rhs_repr) for op, i1, i2, j1, j2 in matcher.get_opcodes(): lhs_substring_lines = lhs_repr[i1:i2].splitlines() rhs_substring_lines = rhs_repr[j1:j2].splitlines() for i, lhs_substring in enumerate(lhs_substring_lines): if op == "replace": lhs_out += inserted_text(lhs_substring) elif op == "delete": lhs_out += inserted_text(lhs_substring) elif op == "insert": lhs_out += Colour.stop + lhs_substring elif op == "equal": lhs_out += Colour.stop + lhs_substring if i != len(lhs_substring_lines) - 1: lhs_out += "\n" for j, rhs_substring in enumerate(rhs_substring_lines): if op == "replace": rhs_out += deleted_text(rhs_substring) elif op == "insert": rhs_out += deleted_text(rhs_substring) elif op == "equal": rhs_out += Colour.stop + rhs_substring if j != len(rhs_substring_lines) - 1: rhs_out += "\n" return lhs_out.splitlines(), rhs_out.splitlines()
def get_assert_equal_diff(assert_method, frame_locals): # type: (str, dict) -> tuple expected_key, actual_key = FRAME_LOCALS_EXPECTED_ACTUAL_KEYS[assert_method] hint = None expected_value = frame_locals[expected_key] actual_value = frame_locals[actual_key] expected_type = type(expected_value) actual_type = type(actual_value) expected_pformat_kwargs = {} # type: dict actual_pformat_kwargs = {} # type: dict not_comparison = { "assertNotEqual": ("!=", "=="), "assertIsNot": ("is not", "is"), }.get(assert_method) if not_comparison is not None: expected_op, actual_op = not_comparison comparison = partial( "{expected} {op} {actual}".format, expected=pformat(expected_value), actual=pformat(actual_value), ) expected = comparison(op=expected_op) actual = comparison(op=actual_op) return expected, actual, hint if isinstance(expected_value, dict): expected_pformat_kwargs["width"] = 1 if isinstance(actual_value, dict): actual_pformat_kwargs["width"] = 1 if expected_type is not actual_type: hint_expected = TYPE_MISMATCH_HINT_MSG.format( padding=PADDED_NEWLINE, label=header_text("Expected:"), vtype=deleted_text(expected_type), ) hint_actual = TYPE_MISMATCH_HINT_MSG.format( padding=" " * 12, label=header_text("Actual:"), vtype=inserted_text(actual_type), ) hint = "\n".join([ "expected and actual are different types", hint_expected, hint_actual, ]) expected = pformat(expected_value, **expected_pformat_kwargs) actual = pformat(actual_value, **actual_pformat_kwargs) return expected, actual, hint
def assert_is_instance_diff(assert_method, frame_locals): # type: (str, dict) -> tuple hint = None expected = frame_locals["cls"] actual_value = frame_locals["obj"] actual = actual_value.__class__.__mro__ hint_verb = { "assertIsInstance": "is not", "assertNotIsInstance": "is", }[assert_method] hint = "{actual} {verb} an instance of {expected}.".format( actual=header_text(actual_value), verb=deleted_text(hint_verb), expected=header_text(expected), ) return pformat(expected, width=1), pformat(actual, width=1), hint