def test_compare_mixed_types(self): a = CompareDict({'aaa': 2, 'bbb': 3, 'ccc': 'z'}, 'foo') b = CompareDict({'aaa': 'y', 'bbb': 4.0, 'ccc': 5 }, 'foo') expected = set([ Invalid(2, 'y', foo='aaa'), Deviation(-1, 4, foo='bbb'), Invalid('z', 5, foo='ccc'), ]) self.assertEqual(expected, set(a.compare(b)))
def test_compare_mixed_types(self): a = CompareDict({'aaa': 2, 'bbb': 3, 'ccc': 'z'}, 'foo') b = CompareDict({'aaa': 'y', 'bbb': 4.0, 'ccc': 5}, 'foo') expected = set([ Invalid(2, 'y', foo='aaa'), Deviation(-1, 4, foo='bbb'), Invalid('z', 5, foo='ccc'), ]) self.assertEqual(expected, set(a.compare(b)))
def test_ne(self): data1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4} a = CompareDict(data1, 'foo') b = CompareDict(data1, 'foo') self.assertFalse(a != b) data2 = {'a': 1, 'b': 2.5, 'c': 3, 'd': 4} a = CompareDict(data1, 'foo') b = CompareDict(data2, 'foo') self.assertTrue(a != b)
def test_repr(self): expected = "CompareDict({'a': 1}, key_names='foo')" result = CompareDict({'a': 1}, 'foo') self.assertEqual(expected, repr(result)) result = CompareDict({('a',): 1}, ['foo']) # <- Single-item containers. self.assertEqual(expected, repr(result)) # Same "expected" as above. expected = "CompareDict({('a', 'b'): 1}, key_names=['foo', 'bar'])" result = CompareDict({('a', 'b'): 1}, ['foo', 'bar']) self.assertEqual(expected, repr(result))
def assertEqual(self, first, second, msg=None): """Fail if *first* does not satisfy *second* as determined by appropriate validation comparison. If *first* and *second* are comparable, a failure will raise a DataError containing the differences between the two. If the *second* argument is a helper-function (or other callable), it is used as a key which must return True for acceptable values. """ if not isinstance(first, BaseCompare): if isinstance(first, str) or not isinstance(first, collections.Container): first = CompareSet([first]) elif isinstance(first, collections.Set): first = CompareSet(first) elif isinstance(first, collections.Mapping): first = CompareDict(first) if callable(second): equal = first.all(second) default_msg = 'first object contains invalid items' else: equal = first == second default_msg = 'first object does not match second object' if not equal: differences = first.compare(second) self.fail(msg or default_msg, differences)
def test_init(self): data = {'a': 1, 'b': 2, 'c': 3, 'd': 4} x = CompareDict(data, 'foo') # dict. self.assertEqual(data, x) x = CompareDict(list(data.items()), 'foo') # list of tuples. self.assertEqual(data, x) # Non-mapping data (data error). data_list = ['a', 'b', 'c', 'd'] with self.assertRaises(ValueError): x = CompareDict(data_list, 'foo') # Single-item wrapped in collection. data = {('a',): 1, ('b',): 2, ('c',): 3, ('d',): 4} x = CompareDict(data, ['foo']) unwrapped = {'a': 1, 'b': 2, 'c': 3, 'd': 4} self.assertEqual(unwrapped, x)
def test_eq(self): data1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4} a = CompareDict(data1, 'foo') b = CompareDict(data1, 'foo') self.assertTrue(a == b) data2 = {'a': 1, 'b': 2.5, 'c': 3, 'd': 4} a = CompareDict(data1, 'foo') b = CompareDict(data2, 'foo') self.assertFalse(a == b) # Test coersion of mapping. data1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4} a = CompareDict(data1, 'foo') self.assertTrue(a == {'a': 1, 'b': 2, 'c': 3, 'd': 4}) # Test coersion of list of tuples. data1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4} data2 = [ ('a', 1), # <- Should be coerced ('b', 2), # into CompareDict ('c', 3), # internally. ('d', 4) ] a = CompareDict(data1, 'foo') b = data2 self.assertTrue(a == b)
def test_compare_function(self): a = CompareDict({'aaa': 'x', 'bbb': 'y', 'ccc': 'z'}, 'foo') # All True. result = a.compare(lambda x: len(x) == 1) self.assertEqual([], result) # Some False. result = a.compare(lambda a: a in ('x', 'y')) expected = [Invalid('z', foo='ccc')] self.assertEqual(expected, result) # All True, multiple args. a = CompareDict({'aaa': (1, 2), 'bbb': (1, 3), 'ccc': (4, 8)}, 'foo') result = a.compare(lambda x, y: x < y) self.assertEqual([], result) # Some False, multiple args. a = CompareDict({'aaa': (1, 0), 'bbb': (1, 3), 'ccc': (3, 2)}, 'foo') result = a.compare(lambda x, y: x < y) expected = [Invalid((1, 0), foo='aaa'), Invalid((3, 2), foo='ccc')] self.assertEqual(expected, result)
def test_all_fn(self): # All True, single arg key function.. compare_obj = CompareDict({ 'aaa': (1, 2), 'bbb': (1, 3), 'ccc': (4, 8) }, 'foo') result = compare_obj.all(lambda x: x[0] < x[1]) self.assertTrue(result) # Some False, single arg key function.. compare_obj = CompareDict({ 'aaa': (1, 2), 'bbb': (5, 3), 'ccc': (4, 8) }, 'foo') result = compare_obj.all(lambda x: x[0] < x[1]) self.assertFalse(result) # All True, multi-arg key function. compare_obj = CompareDict({ 'aaa': (1, 2), 'bbb': (1, 3), 'ccc': (4, 8) }, 'foo') result = compare_obj.all(lambda x, y: x < y) self.assertTrue(result) # Some False,multi-arg key function. compare_obj = CompareDict({ 'aaa': (1, 2), 'bbb': (5, 3), 'ccc': (4, 8) }, 'foo') result = compare_obj.all(lambda x, y: x < y) self.assertFalse(result)
def test_compare_strings(self): a = CompareDict({'aaa': 'x', 'bbb': 'y', 'ccc': 'z'}, 'foo') b = CompareDict({'aaa': 'x', 'bbb': 'z', 'ccc': 'z'}, 'foo') expected = [Invalid('y', 'z', foo='bbb')] self.assertEqual(expected, a.compare(b))
def test_compare_numbers(self): a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') self.assertEqual([], a.compare(b), ('When there is no difference, ' 'compare should return an empty ' 'list.')) a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-0.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'b' is zero in self/subject. a = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-2.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'bbb' is zero in other/reference. a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(+2, 0, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'bbb' is missing from self/subject. a = CompareDict({'aaa': 1, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-2.5, 2.5, foo='bbb')] # <- QUESTION: This self.assertEqual(expected, a.compare(b)) # deviation looks the # same as 0 vs 2.5. # Is this OK? # 'bbb' is missing from a/subject. a = CompareDict({'aaa': 1, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(None, 0, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'bbb' is empty string in a/subject. a = CompareDict({'aaa': 1, 'bbb': '', 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation('', 0, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'bbb' is missing from b/reference. a = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(0, None, foo='bbb')] self.assertEqual(expected, a.compare(b)) # Test coersion of *other*. a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = {'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4} expected = [Deviation(-0.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b))
def test_make_rows(self): make_set = lambda data: set(frozenset(row.items()) for row in data) # Single-item keys, single-item values. data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') iterable = result.make_rows('bar') expected = [ { 'foo': 'aaa', 'bar': 1 }, { 'foo': 'bbb', 'bar': 2 }, { 'foo': 'ccc', 'bar': 3 }, ] self.assertEqual(make_set(expected), make_set(iterable)) # Composite keys. data = {('aaa', 'xxx'): 1, ('bbb', 'yyy'): 2, ('ccc', 'zzz'): 3} result = CompareDict(data, ['foo', 'bar']) iterable = result.make_rows('baz') expected = [ { 'foo': 'aaa', 'bar': 'xxx', 'baz': 1 }, { 'foo': 'bbb', 'bar': 'yyy', 'baz': 2 }, { 'foo': 'ccc', 'bar': 'zzz', 'baz': 3 }, ] self.assertEqual(make_set(expected), make_set(iterable)) # Composite values. data = {'aaa': ('xxx', 1), 'bbb': ('yyy', 2), 'ccc': ('zzz', 3)} result = CompareDict(data, 'foo') iterable = result.make_rows(['bar', 'baz']) expected = [ { 'foo': 'aaa', 'bar': 'xxx', 'baz': 1 }, { 'foo': 'bbb', 'bar': 'yyy', 'baz': 2 }, { 'foo': 'ccc', 'bar': 'zzz', 'baz': 3 }, ] self.assertEqual(make_set(expected), make_set(iterable)) data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') with self.assertRaises(AssertionError): iterable = result.make_rows(['bar', 'baz']) # Too many *names*. data = {'aaa': (1, 2, 3), 'bbb': (2, 4, 6), 'ccc': (3, 6, 9)} result = CompareDict(data, 'foo') with self.assertRaises(AssertionError): iterable = result.make_rows('bar') # Too few *names*. data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') with self.assertRaises(ValueError): iterable = result.make_rows( 'foo') # 'foo' conflicts with group_by.
def test_compare_numbers(self): a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') self.assertEqual([], a.compare(b), ('When there is no difference, ' 'compare should return an empty ' 'list.')) a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-0.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'b' is zero in self/subject. a = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-2.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'b' is missing from self/subject. a = CompareDict({'aaa': 1, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(-2.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'b' is zero in other/reference. a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'bbb': 0, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(+2, 0, foo='bbb')] self.assertEqual(expected, a.compare(b)) # 'b' is missing from other/reference. a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = CompareDict({'aaa': 1, 'ccc': 3, 'ddd': 4}, 'foo') expected = [Deviation(+2, None, foo='bbb')] self.assertEqual(expected, a.compare(b)) # Test coersion of *other*. a = CompareDict({'aaa': 1, 'bbb': 2, 'ccc': 3, 'ddd': 4}, 'foo') b = {'aaa': 1, 'bbb': 2.5, 'ccc': 3, 'ddd': 4} expected = [Deviation(-0.5, 2.5, foo='bbb')] self.assertEqual(expected, a.compare(b))
def test_make_rows(self): make_set = lambda data: set(frozenset(row.items()) for row in data) # Single-item keys, single-item values. data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') iterable = result.make_rows('bar') expected = [ {'foo': 'aaa', 'bar': 1}, {'foo': 'bbb', 'bar': 2}, {'foo': 'ccc', 'bar': 3}, ] self.assertEqual(make_set(expected), make_set(iterable)) # Composite keys. data = {('aaa', 'xxx'): 1, ('bbb', 'yyy'): 2, ('ccc', 'zzz'): 3} result = CompareDict(data, ['foo', 'bar']) iterable = result.make_rows('baz') expected = [ {'foo': 'aaa', 'bar': 'xxx', 'baz': 1}, {'foo': 'bbb', 'bar': 'yyy', 'baz': 2}, {'foo': 'ccc', 'bar': 'zzz', 'baz': 3}, ] self.assertEqual(make_set(expected), make_set(iterable)) # Composite values. data = {'aaa': ('xxx', 1), 'bbb': ('yyy', 2), 'ccc': ('zzz', 3)} result = CompareDict(data, 'foo') iterable = result.make_rows(['bar', 'baz']) expected = [ {'foo': 'aaa', 'bar': 'xxx', 'baz': 1}, {'foo': 'bbb', 'bar': 'yyy', 'baz': 2}, {'foo': 'ccc', 'bar': 'zzz', 'baz': 3}, ] self.assertEqual(make_set(expected), make_set(iterable)) data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') with self.assertRaises(AssertionError): iterable = result.make_rows(['bar', 'baz']) # Too many *names*. data = {'aaa': (1, 2, 3), 'bbb': (2, 4, 6), 'ccc': (3, 6, 9)} result = CompareDict(data, 'foo') with self.assertRaises(AssertionError): iterable = result.make_rows('bar') # Too few *names*. data = {'aaa': 1, 'bbb': 2, 'ccc': 3} result = CompareDict(data, 'foo') with self.assertRaises(ValueError): iterable = result.make_rows('foo') # 'foo' conflicts with group_by.