def test_dict_diffs_not_equal(self): d1 = {1: 'a', 2: 'b', 3: 'c'} d2 = {1: 'a', 2: 'b', 3: 'd'} d3 = {1: 'a', 2: 'b', 3: 'e'} diff_a = diff(d1, d2) diff_b = diff(d1, d3) self.assertFalse(diffs_are_equal(diff_a, diff_b))
def test_dict_diffs_are_equal(self): # these should get seeded differently, fairly regularly in python 3 # the diffs will be equivalent, but ordering of DiffItems will differ d1 = {-1: 'y', 0: 'z', 1: 'a', 2: 'b', 3: 'c', 4: 'e'} d2 = {1: 'a', 2: 'b', 3: 'd', 4: 'f'} diff_a = diff(d1, d2) diff_b = diff(d1, d2) self.assertTrue(diffs_are_equal(diff_a, diff_b))
def test_ordered_dicts_are_out_of_order(self): d = {1: 'a', 2: 'b', 7: 'c', 3: 'd'} diff_a = diff( OrderedDict(sorted(d.items(), key=lambda k: k[0])), OrderedDict(sorted(d.items(), key=lambda k: k[0]))) diff_b = diff( OrderedDict(sorted(d.items(), key=lambda k: k[1])), OrderedDict(sorted(d.items(), key=lambda k: k[1]))) self.assertFalse(diffs_are_equal(diff_a, diff_b))
def test_diff_item_is_a_nested_diff(self): dict1 = {1: 'ab'} dict2 = {1: 'bc'} diff_obj = diff(dict1, dict2) nested_diff = diff('ab', 'bc', _depth=1) diff_item = MappingDiffItem( unchanged, 1, changed, nested_diff) expected_diff_output = '{} {}'.format(changed(' '), diff_item) self.assertEqual( diff_obj.context_blocks[0].__str__(), expected_diff_output)
def test_context_banner_is_correct_for_sequences(self): ''' Context banners should contain the information you need the two original sequences such that you only get the items contained within the displayed context block. ''' seq1 = [0, 1, 2, 3, 0] seq2 = [0, 4, 2, 5, 0] # the useful context for this diff is the slice 1:4 in both sequences s1_start = s2_start = '1' s1_end = s2_end = '4' diff_obj = diff(seq1, seq2) expected_banner = [ '@@ {}{},{} {}{},{} @@'.format( remove('-'), remove(s1_start), remove(s1_end), insert('+'), insert(s2_start), insert(s2_end)) ] expected_diff_items = [ '{} {}'.format(remove('-'), remove('1')), '{} {}'.format(insert('+'), insert('4')), '{} {}'.format(unchanged(' '), unchanged('2')), '{} {}'.format(remove('-'), remove('3')), '{} {}'.format(insert('+'), insert('5')) ] expected_diff_output = '\n'.join(expected_banner + expected_diff_items) # expected_diff_output is unicode type, convert to str for comparison self.assertEqual( diff_obj.context_blocks[0].__str__(), str(expected_diff_output))
def test_empty_diff(self): set1 = set() set2 = set() diff_obj = diff(set1, set2) expected_diff_output = '{}\n{}'.format( unchanged('{!s}('.format(type(set1))), unchanged(')')) self.assertEqual(diff_obj.__str__(), expected_diff_output)
def test_no_differences(self): diff_obj = diff([1, 2, 3], [1, 2, 3]) diffs = [ DiffItem(unchanged, 1, (0, 1, 0, 1)), DiffItem(unchanged, 2, (1, 2, 1, 2)), DiffItem(unchanged, 3, (2, 3, 2, 3))] expected_diff = Diff(list, diffs) self.assertEqual(diff_obj, expected_diff)
def test_string_is_term_width(self): a = '' b = 'a' * (term.width - 1) d = diff(a, b) expected_str = [ unchanged('{!s}('.format(type(a))), '@@ {}{},{} {}{},{} @@'.format( remove('-'), remove('0'), remove('0'), insert('+'), insert('0'), insert('{}'.format(term.width - 1))), ' ' + ('{}'.format(insert('+')) * (term.width - 1)), ' ' + ('{}'.format(insert('a')) * (term.width - 1)), unchanged(')') ] self.assertEqual(d.__str__(), '\n'.join(expected_str))
def test_can_diff_set_type(self): fs1 = frozenset([1, 2, 3]) fs2 = frozenset([2, 3, 4]) diff_obj = diff(fs1, fs2) diffs = [ DiffItem(remove, 1), DiffItem(unchanged, 2), DiffItem(unchanged, 3), DiffItem(insert, 4)] expected_diff = Diff(frozenset, diffs) expected_diff.context_blocks = [ expected_diff.ContextBlock(frozenset, diffs)] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(fs1, diff_obj), fs2)
def test_single_char_edge_case(self): d1 = 'a' d2 = 'b' diff_obj = diff(d1, d2) diffs = [ DiffItem(remove, 'a', (0, 1, 0, 0)), DiffItem(insert, 'b', (1, 1, 0, 1)), ] expected_diff = Diff(str, diffs) expected_diff.context_blocks = [ expected_diff.ContextBlock(str, diffs) ] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(d1, diff_obj), d2)
def test_can_diff_sequence_type(self): ThreeDPoint = namedtuple('ThreeDPoint', ['x', 'y', 'z']) p1 = ThreeDPoint(0, 0, 0) p2 = ThreeDPoint(0, 0, 1) diff_obj = diff(p1, p2) diffs = [ DiffItem(unchanged, 0, (0, 1, 0, 1)), DiffItem(unchanged, 0, (1, 2, 1, 2)), DiffItem(remove, 0, (2, 3, 2, 2)), DiffItem(insert, 1, (3, 3, 2, 3))] expected_diff = Diff(type(p1), diffs) expected_diff.context_blocks = [ expected_diff.ContextBlock(type(p1), diffs[2:])] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(p1, diff_obj), p2)
def test_strings_display_on_single_line(self): a = 'this' b = 'that' d = diff(a, b) expected_str = [ unchanged('{!s}('.format(type(a))), '@@ {}{},{} {}{},{} @@'.format( remove('-'), remove('2'), remove('4'), insert('+'), insert('2'), insert('4')), ' {}{}{}{}'.format( remove('-'), remove('-'), insert('+'), insert('+')), ' {}{}{}{}'.format( remove('i'), remove('s'), insert('a'), insert('t')), unchanged(')') ] self.assertEqual(d.__str__(), '\n'.join(expected_str))
def test_can_diff_mapping_type(self): d1 = {'d': 1, 'c': 2, 'b': 3} d2 = {'d': 1, 'c': 2, 'a': 3} # sort by values od1 = OrderedDict(sorted(d1.items(), key=lambda i: i[1])) od2 = OrderedDict(sorted(d2.items(), key=lambda i: i[1])) diff_obj = diff(od1, od2) diffs = [ MappingDiffItem(unchanged, 'd', unchanged, 1), MappingDiffItem(unchanged, 'c', unchanged, 2), MappingDiffItem(remove, 'b', remove, 3), MappingDiffItem(insert, 'a', insert, 3)] expected_diff = Diff(OrderedDict, diffs) expected_diff.context_blocks = [ expected_diff.ContextBlock(OrderedDict, diffs[2:])] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(od1, diff_obj), od2)
def test_no_context_banner_for_non_sequence(self): set1 = {1, 2} set2 = {'a', 'b'} diff_obj = diff(set1, set2) expected_diff_items = [ '{} {}'.format(remove('-'), remove('1')), '{} {}'.format(remove('-'), remove('2')), '{} {}'.format(insert('+'), insert('a')), '{} {}'.format(insert('+'), insert('b')) ] # allow the expected output to be unordered actual_string = diff_obj.context_blocks[0].__str__() actual_items = actual_string.split('\n') if sys.version_info.major >= 3: self.assertCountEqual(expected_diff_items, actual_items) else: self.assertItemsEqual(expected_diff_items, actual_items)
def test_context_limit_is_adjustable(self): map1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4} map2 = {'a': 2, 'b': 2, 'c': 3, 'e': 4} diff_obj = diff(map1, map2, context_limit=1) diffs = [ MappingDiffItem(remove, 'd', remove, 4), MappingDiffItem(unchanged, 'a', remove, 1), MappingDiffItem(unchanged, 'a', insert, 2), MappingDiffItem(unchanged, 'c', unchanged, 3), MappingDiffItem(unchanged, 'b', unchanged, 2), MappingDiffItem(insert, 'e', insert, 4)] expected_diff = Diff(dict, diffs, context_limit=1) expected_diff.context_blocks = [ expected_diff.ContextBlock(dict, diffs[0:3]), expected_diff.ContextBlock(dict, diffs[5:])] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(map1, diff_obj), map2)
def test_only_context_blocks_are_displayed(self): a = [1, 0, 0, 0, 0, 1] b = [2, 0, 0, 0, 0, 2] diff_obj = diff(a, b) expected_diff_output = [ unchanged('{!s}('.format(type(a))), '@@ {}{},{} {}{},{} @@'.format( remove('-'), remove('0'), remove('1'), insert('+'), insert('0'), insert('1')), '{} {}'.format(remove('-'), remove('1')), '{} {}'.format(insert('+'), insert('2')), '@@ {}{},{} {}{},{} @@'.format( remove('-'), remove('5'), remove('6'), insert('+'), insert('5'), insert('6')), '{} {}'.format(remove('-'), remove('1')), '{} {}'.format(insert('+'), insert('2')), unchanged(')') ] self.assertEqual(diff_obj.__str__(), '\n'.join(expected_diff_output))
def test_recursive_diff(self): struct1 = [1, {'a': {'a', 'b'}}] struct2 = [1, {'a': {'b'}}] diff_obj = diff(struct1, struct2) depth_2_diffs = [ DiffItem(remove, 'a'), DiffItem(unchanged, 'b')] diff_depth_2 = Diff(set, depth_2_diffs, depth=2) diff_depth_2.context_blocks = [ diff_depth_2.ContextBlock(set, [diff_depth_2.diffs[0]], depth=2)] depth_1_diffs = [ MappingDiffItem( unchanged, 'a', changed, diff_depth_2)] diff_depth_1 = Diff(dict, depth_1_diffs, depth=1) diff_depth_1.context_blocks = [ diff_depth_1.ContextBlock(dict, diff_depth_1.diffs, depth=1)] diffs = [ DiffItem(unchanged, 1, (0, 1, 0, 1)), DiffItem(changed, diff_depth_1, (1, 2, 1, 2))] expected_diff = Diff(list, diffs) expected_diff.context_blocks = [ expected_diff.ContextBlock(list, [diffs[1]])] self.assertEqual(diff_obj, expected_diff) self.assertEqual(patch(struct1, diff_obj), struct2)
def test_len_diff(self): self.assertEqual(len(diff([1, 2], [1, 2])), 2)
def test_diff_evaluates_false_when_all_items_are_unchanged(self): self.assertFalse(bool(diff({1: [1, 2]}, {1: [1, 2]})))
def test_diff_slicing(self): d = diff('aabcdef', 'abcdef') s = d[1:] self.assertEqual(s, Diff(str, d.diffs[1:]))
def test_depth_is_adjustable(self): diff_obj = diff([1, 2], [2, 3, 4], _depth=3) self.assertEqual(diff_obj.depth, 3)
def test_empty_diff(self): diff_obj = diff((), ()) self.assertEqual(diff_obj, Diff(tuple, []))
def test_diff_index(self): d = diff('aabcdef', 'abcdef') i = 0 self.assertEqual(d[i], d.diffs[i])
def test_getattr_fail(self): d = diff('aaa', 'bbb') with self.assertRaises(TypeError): d['a']
def test_iterate_over_diff(self): d = diff([1, 2, 3, 4], [2, 3, 4, 5]) diff_items = [di for di in d] self.assertEqual(tuple(diff_items), d.diffs)
def test_diffs_with_different_depths_compare_equal(self): d1 = diff([1, 2, 3], [2, 3]) d2 = diff({'a': [1, 2, 3]}, {'a': [2, 3]}) self.assertEqual(d1, d2.diffs[0].value)
def test_diff_evaluates_true_when_contain_non_unchanged_items(self): self.assertTrue(bool(diff('baaaaaaa', 'aaaaaaaa')))
def test_sequence_diffs_are_equal(self): diff_a = diff([1, 2, 3], [2, 3, 4]) diff_b = diff([1, 2, 3], [2, 3, 4]) self.assertTrue(diffs_are_equal(diff_a, diff_b))