Commits

Nat Williams committed 3b2c113 Merge

merged

Comments (0)

Files changed (2)

fudge/inspector.py

 
-"""Value inspectors that can be passed to :func:`fudge.Fake.with_args` for more 
-expressive argument matching.  
+"""Value inspectors that can be passed to :func:`fudge.Fake.with_args` for more
+expressive argument matching.
 
-As a mnemonic device, 
+As a mnemonic device,
 an instance of the :class:`fudge.inspector.ValueInspector` is available as "arg" :
 
 .. doctest::
-    
+
     >>> import fudge
     >>> from fudge.inspector import arg
     >>> image = fudge.Fake("image").expects("save").with_args(arg.endswith(".jpg"))
 
-In other words, this declares that the first argument to ``image.save()`` 
+In other words, this declares that the first argument to ``image.save()``
 should end with the suffix ".jpg"
-    
+
 .. doctest::
     :hide:
-    
+
     >>> fudge.clear_expectations()
 
 """
 
 from fudge.util import fmt_val, fmt_dict_vals
 
-__all__ = ['arg']
+__all__ = ['arg', 'arg_not']
 
 class ValueInspector(object):
-    """Dispatches tests to inspect values. 
+    """Dispatches tests to inspect values.
     """
-    
+
+    invert_eq = False
+
+    def _make_value_test(self, test_class, *args, **kwargs):
+        if not self.invert_eq:
+            return test_class(*args, **kwargs)
+        class ValueTestInverter(test_class):
+            def __repr__(wrapper_self):
+                return "(NOT) %s" % test_class.__repr__(wrapper_self)
+            def __eq__(wrapper_self, other):
+                return not test_class.__eq__(wrapper_self, other)
+        return ValueTestInverter(*args, **kwargs)
+
     def any(self):
         """Match any value.
-        
-        This is pretty much just a placeholder for when you 
-        want to inspect multiple arguments but don't care about 
+
+        This is pretty much just a placeholder for when you
+        want to inspect multiple arguments but don't care about
         all of them.
-        
+
         .. doctest::
-            
+
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> db = fudge.Fake("db")
             >>> db = db.expects("transaction").with_args(
             ...             "insert", isolation_level=arg.any())
-            ... 
+            ...
             >>> db.transaction("insert", isolation_level="lock")
             >>> fudge.verify()
-        
+
         This also passes:
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_calls()
-            
+
         .. doctest::
-        
+
             >>> db.transaction("insert", isolation_level="autocommit")
             >>> fudge.verify()
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-        
+
         """
-        return AnyValue()
+        return self._make_value_test(AnyValue)
 
     def any_value(self):
         """**DEPRECATED**: use :func:`arg.any() <fudge.inspector.ValueInspector.any>`
 
     def contains(self, part):
         """Ensure that a value contains some part.
-        
-        This is useful for when you only care that a substring or subelement 
+
+        This is useful for when you only care that a substring or subelement
         exists in a value.
-        
+
         .. doctest::
-            
+
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> addressbook = fudge.Fake().expects("import_").with_args(
             ...                                     arg.contains("Baba Brooks"))
-            ... 
+            ...
             >>> addressbook.import_("Bill Brooks; Baba Brooks; Henry Brooks;")
             >>> fudge.verify()
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-        
-        Since contains() just invokes the __in__() method, checking that a list 
+
+        Since contains() just invokes the __in__() method, checking that a list
         item is present works as expected :
-        
+
         .. doctest::
-            
+
             >>> colorpicker = fudge.Fake("colorpicker")
             >>> colorpicker = colorpicker.expects("select").with_args(arg.contains("red"))
             >>> colorpicker.select(["green","red","blue"])
             >>> fudge.verify()
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-            
+
         """
-        return Contains(part)
-    
+        return self._make_value_test(Contains, part)
+
     def endswith(self, part):
         """Ensure that a value ends with some part.
-        
+
         This is useful for when values with dynamic parts that are hard to replicate.
-        
+
         .. doctest::
-            
+
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> tmpfile = fudge.Fake("tempfile").expects("mkname").with_args(
             ...                                             arg.endswith(".tmp"))
-            ... 
+            ...
             >>> tmpfile.mkname("7AakkkLazUUKHKJgh908JKjlkh.tmp")
             >>> fudge.verify()
-            
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-            
+
         """
-        return Endswith(part)
-    
+        return self._make_value_test(Endswith, part)
+
     def has_attr(self, **attributes):
         """Ensure that an object value has at least these attributes.
-        
+
         This is useful for testing that an object has specific attributes.
-        
+
         .. doctest::
-            
+
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> db = fudge.Fake("db").expects("update").with_args(arg.has_attr(
             ...                                                       first_name="Bob",
             ...                                                       last_name="James" ))
-            ... 
+            ...
             >>> class User:
             ...     first_name = "Bob"
             ...     last_name = "James"
             ...     job = "jazz musician" # this is ignored
-            ... 
+            ...
             >>> db.update(User())
             >>> fudge.verify()
-        
+
         In case of error, the other object's __repr__ will be invoked:
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_calls()
-        
+
         .. doctest::
-            
+
             >>> class User:
             ...     first_name = "Bob"
-            ... 
+            ...
             ...     def __repr__(self):
             ...         return repr(dict(first_name=self.first_name))
-            ... 
+            ...
             >>> db.update(User())
             Traceback (most recent call last):
             ...
             AssertionError: fake:db.update(arg.has_attr(first_name='Bob', last_name='James')) was called unexpectedly with args ({'first_name': 'Bob'})
-            
-            
+
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-            
+
         """
-        return HasAttr(**attributes)
-    
+        return self._make_value_test(HasAttr, **attributes)
+
     def passes_test(self, test):
         """Check that a value passes some test.
-        
-        For custom assertions you may need to create your own callable 
+
+        For custom assertions you may need to create your own callable
         to inspect and verify a value.
-        
+
         .. doctest::
-            
+
             >>> def is_valid(s):
             ...     if s in ('active','deleted'):
             ...         return True
             ...     else:
             ...         return False
-            ... 
+            ...
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> system = fudge.Fake("system")
             >>> system = system.expects("set_status").with_args(arg.passes_test(is_valid))
             >>> system.set_status("active")
             >>> fudge.verify()
-            
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_calls()
-        
-        The callable you pass takes one argument, the value, and should return 
+
+        The callable you pass takes one argument, the value, and should return
         True if it's an acceptable value or False if not.
-        
+
         .. doctest::
-            
+
             >>> system.set_status("sleep") # doctest: +ELLIPSIS
             Traceback (most recent call last):
             ...
             AssertionError: fake:system.set_status(arg.passes_test(<function is_valid at...)) was called unexpectedly with args ('sleep')
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-        
-        If it makes more sense to perform assertions in your test function then 
+
+        If it makes more sense to perform assertions in your test function then
         be sure to return True :
-        
+
             >>> def is_valid(s):
             ...     assert s in ('active','deleted'), (
             ...         "Unexpected status value: %s" % s)
             ...     return True
-            ... 
+            ...
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> system = fudge.Fake("system")
             Traceback (most recent call last):
             ...
             AssertionError: Unexpected status value: sleep
-        
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-            
+
         """
-        return PassesTest(test)
-    
+        return self._make_value_test(PassesTest, test)
+
     def startswith(self, part):
         """Ensure that a value starts with some part.
-        
+
         This is useful for when values with dynamic parts that are hard to replicate.
-        
+
         .. doctest::
-            
+
             >>> import fudge
             >>> from fudge.inspector import arg
             >>> keychain = fudge.Fake("keychain").expects("accept_key").with_args(
             ...                                                     arg.startswith("_key"))
-            ... 
+            ...
             >>> keychain.accept_key("_key-18657yojgaodfty98618652olkj[oollk]")
             >>> fudge.verify()
-            
+
         .. doctest::
             :hide:
-            
+
             >>> fudge.clear_expectations()
-            
+
         """
-        return Startswith(part)
+        return self._make_value_test(Startswith, part)
+
+class NotValueInspector(ValueInspector):
+    invert_eq = True
 
 arg = ValueInspector()
+arg_not = NotValueInspector()
 
 class ValueTest(object):
-    
+
     arg_method = None
     __test__ = False # nose
-    
+
     def __eq__(self, other):
         raise NotImplementedError()
-    
+
     def _repr_argspec(self):
         raise NotImplementedError()
-    
+
     def __str__(self):
         return self._repr_argspec()
-    
+
     def __unicode__(self):
         return self._repr_argspec()
-    
+
     def __repr__(self):
         return self._repr_argspec()
-    
+
     def _make_argspec(self, arg):
         if self.arg_method is None:
             raise NotImplementedError(
         return "arg." + self.arg_method + "(" + arg + ")"
 
 class Stringlike(ValueTest):
-    
+
     def __init__(self, part):
         self.part = part
-        
+
     def _repr_argspec(self):
         return self._make_argspec(fmt_val(self.part))
-    
+
     def stringlike(self, value):
         if isinstance(value, (str, unicode)):
             return value
         else:
             return str(value)
-    
+
     def __eq__(self, other):
         check_stringlike = getattr(self.stringlike(other), self.arg_method)
         return check_stringlike(self.part)
 
 class Startswith(Stringlike):
     arg_method = "startswith"
-    
+
 class Endswith(Stringlike):
     arg_method = "endswith"
 
 class HasAttr(ValueTest):
     arg_method = "has_attr"
-    
+
     def __init__(self, **attributes):
         self.attributes = attributes
-        
+
     def _repr_argspec(self):
         return self._make_argspec(", ".join(sorted(fmt_dict_vals(self.attributes))))
-    
+
     def __eq__(self, other):
         for name, value in self.attributes.items():
             if not hasattr(other, name):
                 return False
             if getattr(other, name) != value:
                 return False
-        
+
         return True
 
 class AnyValue(ValueTest):
     arg_method = "any"
-    
+
     def __eq__(self, other):
         # will match anything:
         return True
-        
+
     def _repr_argspec(self):
         return self._make_argspec("")
 
 class Contains(ValueTest):
     arg_method = "contains"
-    
+
     def __init__(self, part):
         self.part = part
-    
+
     def _repr_argspec(self):
         return self._make_argspec(fmt_val(self.part))
-    
+
     def __eq__(self, other):
         if self.part in other:
             return True
         else:
             return False
-        
+
 class PassesTest(ValueTest):
     arg_method = "passes_test"
-    
+
     def __init__(self, test):
         self.test = test
-    
+
     def __eq__(self, other):
         return self.test(other)
-    
+
     def _repr_argspec(self):
         return self._make_argspec(repr(self.test))
 

fudge/tests/test_inspector.py

 
 import fudge
 from fudge import inspector
-from fudge.inspector import arg
+from fudge.inspector import arg, arg_not
 from fudge import Fake
 
 class TestAnyValue(unittest.TestCase):
-    
+
     def tearDown(self):
         fudge.clear_expectations()
-    
+
     def test_any_value(self):
         db = Fake("db").expects("execute").with_args(arg.any())
         db.execute("delete from foo where 1")
-    
+
     def test_repr(self):
         any = inspector.AnyValue()
         eq_(repr(any), "arg.any()")
-        
+
     def test_str(self):
         any = inspector.AnyValue()
         eq_(str(any), "arg.any()")
-        
+
+
 class TestPassesTest(unittest.TestCase):
-    
+
     def tearDown(self):
         fudge.clear_expectations()
-    
+
     def test_passes(self):
         def isint(v):
             return isinstance(v,int)
         counter = Fake("counter").expects("increment").with_args(arg.passes_test(isint))
         counter.increment(25)
-    
+
     @raises(AssertionError)
     def test_passes_fail(self):
         def is_str(v):
             return isinstance(v,str)
         counter = Fake("counter").expects("set_name").with_args(arg.passes_test(is_str))
         counter.set_name(25)
-    
+
     def test_repr(self):
         class test(object):
             def __call__(self, v):
                 return isinstance(v,int)
             def __repr__(self):
                 return "v is an int"
-                
+
         passes = inspector.PassesTest(test())
         eq_(repr(passes), "arg.passes_test(v is an int)")
-    
+
     def test_str(self):
         class test(object):
             def __call__(self, v):
                 return isinstance(v,int)
             def __repr__(self):
                 return "v is an int"
-                
+
         passes = inspector.PassesTest(test())
         eq_(str(passes), "arg.passes_test(v is an int)")
 
 class TestObjectlike(unittest.TestCase):
-    
+
     def tearDown(self):
         fudge.clear_expectations()
-    
+
     def test_has_attr_ok(self):
         class Config(object):
             size = 12
             color = 'red'
             weight = 'heavy'
-            
+
         widget = Fake("widget").expects("configure")\
                                .with_args(arg.has_attr(size=12,color='red'))
         widget.configure(Config())
-    
+
     @raises(AssertionError)
     def test_has_attr_fail(self):
         class Config(object):
             color = 'red'
-            
+
         widget = Fake("widget").expects("configure")\
                                .with_args(arg.has_attr(size=12))
         widget.configure(Config())
-    
+
     @raises(AssertionError)
     def test_has_attr_fail_wrong_value(self):
         class Config(object):
             color = 'red'
-            
+
         widget = Fake("widget").expects("configure")\
                                .with_args(arg.has_attr(color="green"))
         widget.configure(Config())
-    
+
     def test_objectlike_str(self):
         o = inspector.HasAttr(one=1, two="two")
         eq_(str(o), "arg.has_attr(one=1, two='two')")
-    
+
     def test_objectlike_repr(self):
         o = inspector.HasAttr(one=1, two="two")
         eq_(repr(o), "arg.has_attr(one=1, two='two')")
-    
+
     def test_objectlike_unicode(self):
         o = inspector.HasAttr(one=1, ivan=u"Ivan_Krsti\u0107")
         assert repr(o).startswith("arg.has_attr(ivan=")
         assert repr(o).endswith("'Ivan_Krsti\u0107', one=1)")
-    
+
     def test_objectlike_repr_long_val(self):
         o = inspector.HasAttr(
                 bytes="011110101000101010011111111110000001010100000001110000000011")
-        eq_(repr(o), 
+        eq_(repr(o),
             "arg.has_attr(bytes='011110101000101010011111111110000001010100000...')")
 
 class TestStringlike(unittest.TestCase):
-    
+
     def tearDown(self):
         fudge.clear_expectations()
-    
+
     def test_startswith_ok(self):
         db = Fake("db").expects("execute").with_args(arg.startswith("insert into"))
         db.execute("insert into foo values (1,2,3,4)")
-    
+
     @raises(AssertionError)
     def test_startswith_fail(self):
         db = Fake("db").expects("execute").with_args(arg.startswith("insert into"))
         db.execute("select from")
-    
+
     def test_startswith_ok_uni(self):
         db = Fake("db").expects("execute").with_args(arg.startswith(u"Ivan_Krsti\u0107"))
         db.execute(u"Ivan_Krsti\u0107(); foo();")
-    
+
     def test_startswith_unicode(self):
         p = inspector.Startswith(u"Ivan_Krsti\u0107")
         assert repr(p).startswith("arg.startswith(")
         assert repr(p).endswith("'Ivan_Krsti\u0107')")
-    
+
     def test_endswith_ok(self):
         db = Fake("db").expects("execute").with_args(arg.endswith("values (1,2,3,4)"))
         db.execute("insert into foo values (1,2,3,4)")
-    
+
     def test_endswith_ok_uni(self):
         db = Fake("db").expects("execute").with_args(arg.endswith(u"Ivan Krsti\u0107"))
         db.execute(u"select Ivan Krsti\u0107")
-        
+
     def test_endswith_unicode(self):
         p = inspector.Endswith(u"Ivan_Krsti\u0107")
         assert repr(p).startswith("arg.endswith(")
         assert repr(p).endswith("'Ivan_Krsti\u0107')")
-    
+
     def test_startswith_repr(self):
         p = inspector.Startswith("_start")
         eq_(repr(p), "arg.startswith('_start')")
-    
+
     def test_endswith_repr(self):
         p = inspector.Endswith("_ending")
         eq_(repr(p), "arg.endswith('_ending')")
-    
+
     def test_startswith_str(self):
         p = inspector.Startswith("_start")
         eq_(str(p), "arg.startswith('_start')")
-    
+
     def test_endswith_str(self):
         p = inspector.Endswith("_ending")
         eq_(str(p), "arg.endswith('_ending')")
-    
+
     def test_startswith_str_long_value(self):
         p = inspector.Startswith(
             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
         )
-        eq_(str(p), 
+        eq_(str(p),
             "arg.startswith('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...')" )
-    
+
     def test_endswith_str_long_value(self):
         p = inspector.Endswith(
             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
         )
-        eq_(str(p), 
+        eq_(str(p),
             "arg.endswith('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...')" )
 
 class TestContains(unittest.TestCase):
-    
+
     def tearDown(self):
         fudge.clear_expectations()
-    
+
     def test_contains_str(self):
         db = Fake("db").expects("execute").with_args(arg.contains("table foo"))
         db.execute("select into table foo;")
         db.execute("select * from table foo where bar = 1")
         fudge.verify()
-    
+
     @raises(AssertionError)
     def test_contains_fail(self):
         db = Fake("db").expects("execute").with_args(arg.contains("table foo"))
         db.execute("select into table notyourmama;")
         fudge.verify()
-    
+
     def test_contains_list(self):
         db = Fake("db").expects("execute_statements").with_args(
                                             arg.contains("select * from foo"))
             "drop table foo"
         ])
         fudge.verify()
-        
+
     def test_str(self):
         c = inspector.Contains(":part:")
         eq_(str(c), "arg.contains(':part:')")
-        
+
     def test_str_long_val(self):
         c = inspector.Contains(
             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
         eq_(str(c), "arg.contains('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...')")
-        
+
     def test_repr(self):
         c = inspector.Contains(":part:")
         eq_(repr(c), "arg.contains(':part:')")
-        
+
     def test_unicode(self):
         c = inspector.Contains(u"Ivan_Krsti\u0107")
         assert repr(c).startswith("arg.contains(")
         assert repr(c).endswith("'Ivan_Krsti\u0107')")
-        
-        
+
+class TestMakeValueTest(unittest.TestCase):
+
+    def test_no_invert_just_inits(self):
+        vi = inspector.ValueInspector()
+        vi.invert_eq = False
+        def value_test(*args, **kwargs):
+            return 'hello, friends'
+        ret = vi._make_value_test(value_test, 'asdf', foo='bar')
+        self.assertEqual(ret, 'hello, friends')
+
+    def test_invert_returns_inverter(self):
+        vi = inspector.ValueInspector()
+        vi.invert_eq = True
+        class vt(object):
+            def __eq__(self, other):
+                return True
+        inverter = vi._make_value_test(vt)
+        assert isinstance(inverter, vt)
+        self.assertEqual(inverter.__eq__('whatever'), False)