[Checkins] SVN: gocept.selenium/trunk/src/gocept/selenium/ refactored and fixed fall-back rules for assertions, improved failure messages

Thomas Lotze tl at gocept.com
Wed Mar 17 03:03:47 EDT 2010


Log message for revision 110012:
  refactored and fixed fall-back rules for assertions, improved failure messages

Changed:
  U   gocept.selenium/trunk/src/gocept/selenium/selenese.py
  U   gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_selenese.py

-=-
Modified: gocept.selenium/trunk/src/gocept/selenium/selenese.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/selenese.py	2010-03-17 02:17:26 UTC (rev 110011)
+++ gocept.selenium/trunk/src/gocept/selenium/selenese.py	2010-03-17 07:03:47 UTC (rev 110012)
@@ -274,53 +274,86 @@
 
     def __getattr__(self, name):
         requested_name = name
+        def _getattr(name):
+            try:
+                return getattr(self, name)
+            except AttributeError:
+                raise AttributeError(requested_name)
+
+        # Generate a number of assertions that aren't implemented directly.
+        # Apply a fall-back chain that first implements waitFor* using
+        # corresponding assert* and maps verify* methods to corresponding
+        # assert*, then implements negative assert* by negating their positive
+        # counterparts. Generate only these three kinds of assertions. After
+        # each fall-back step, getattr() is called to make use of all directly
+        # implemented methods. If a method cannot be looked up in spite of the
+        # fall-back, raise an AttributeError that reports the method name
+        # actually used by client code.
         if name.startswith('waitFor'):
             name = name.replace('waitFor', 'assert', 1)
-            assertion = getattr(self, name)
+            assertion = _getattr(name)
             return (lambda *args, **kw:
                     self._waitFor(assertion, *args, **kw))
 
+        if name.startswith('verify'):
+            name = name.replace('verify', 'assert', 1)
+            return _getattr(name)
+
+        if not name.startswith('assert'):
+            raise AttributeError(requested_name)
+
         if 'Not' in name:
             name = name.replace('Not', '', 1)
+            assertion = _getattr(name)
+            return (lambda *args, **kw:
+                    self._negate(assertion, requested_name, *args, **kw))
 
-        if name.startswith('verify'):
-            name = name.replace('verify', 'assert', 1)
+        # Positive assertions are synthesised by looking up a getter method
+        # for a value, getting the value and evaluating it in an appropriate
+        # way. Getters may be named either get* or is*.
+        try:
+            getter = _getattr(name.replace('assert', 'get', 1))
+        except AttributeError:
+            getter = _getattr(name.replace('assert', 'is', 1))
 
-        if name.startswith('assert'):
-            getter_name = name.replace('assert', 'get', 1)
-            getter = getattr(self, getter_name, None)
-            if getter is None:
-                getter_name = name.replace('assert', 'is', 1)
-                getter = getattr(self, getter_name, None)
-            if getter is None:
-                raise AttributeError(requested_name)
-            if getter.assert_type == 'pattern':
-                return lambda pattern: self._assert_pattern(
-                    getter, requested_name, pattern)
-            elif getter.assert_type == 'locator':
-                return lambda locator: self._assert(
-                    getter, requested_name, locator)
-            elif getter.assert_type == 'locator_pattern':
-                return lambda locator, pattern: self._assert_pattern(
-                    getter, requested_name, pattern, locator)
-            elif getter.assert_type is None:
-                return lambda: self._assert(getter, requested_name)
+        if getter.assert_type == 'pattern':
+            return lambda pattern: self._assert_pattern(
+                getter, requested_name, pattern)
+        elif getter.assert_type == 'locator':
+            return lambda locator: self._assert(
+                getter, requested_name, locator)
+        elif getter.assert_type == 'locator_pattern':
+            return lambda locator, pattern: self._assert_pattern(
+                getter, requested_name, pattern, locator)
+        elif getter.assert_type is None:
+            return lambda: self._assert(getter, requested_name)
+        else:
+            raise ValueError('Unknown assert type %r for selenese method %r.'
+                             % (getter.assert_type, requested_name))
 
-        raise AttributeError(requested_name)
-
     def _assert(self, getter, name, *args, **kw):
-        if ('Not' not in name) ^ bool(getter(*args, **kw)):
+        value = getter(*args, **kw)
+        if not value:
             raise self.failureException(
-                'not valid: %s(%r, %r)' % (name, args, kw))
+                'Failed: %s -> %r' %
+                (self._call_repr(name, *args, **kw), value))
 
     def _assert_pattern(self, getter, name, pattern, *args):
         result = getter(*args)
-        if ('Not' not in name) ^ bool(
-                selenese_pattern_equals(result, pattern)):
+        if not selenese_pattern_equals(result, pattern):
             raise self.failureException(
-                '%r did not match expected %r'
-                % (result, pattern))
+                'Expected: %r, got: %r from %s' %
+                (pattern, result, self._call_repr(name, *args)))
 
+    def _negate(self, assertion, name, *args, **kw):
+        try:
+            assertion(*args, **kw)
+        except self.failureException:
+            return
+        else:
+            raise self.failureException(
+                'Failed: ' + self._call_repr(name, *args, **kw))
+
     def _waitFor(self, assertion, *args, **kw):
         start = time.time()
         while True:
@@ -329,12 +362,18 @@
             except self.failureException, e:
                 if time.time() - start > self.timeout:
                     raise self.failureException(
-                        'Timed out waiting for: %s' % e.args[0])
+                        'Timed out. %s' % e.args[0])
             else:
                 break
             time.sleep(0.1)
 
+    def _call_repr(self, name, *args, **kw):
+        return '%s(%s)' % (
+            name,
+            ', '.join(map(repr, args) +
+                      ['%s=%r' % item for item in sorted(kw.items())]))
 
+
 def match_glob(text, pattern):
     pattern = re.escape(pattern)
     pattern = pattern.replace(r'\*', '.*')

Modified: gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_selenese.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_selenese.py	2010-03-17 02:17:26 UTC (rev 110011)
+++ gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_selenese.py	2010-03-17 07:03:47 UTC (rev 110012)
@@ -14,6 +14,7 @@
 
 from gocept.selenium.selenese import selenese_pattern_equals as match
 from gocept.selenium.selenese import camelcase_to_underscore
+import gocept.selenium.selenese
 import gocept.selenium.ztk.testing
 import unittest
 import time
@@ -54,6 +55,55 @@
         self.assertEquals('foo_bar', camelcase_to_underscore('fooBar'))
 
 
+class NonexistentNameTest(unittest.TestCase):
+
+    def setUp(self):
+        class TestCase(object):
+            failureException = None
+
+        class Selenese(gocept.selenium.selenese.Selenese):
+            def get_without_assert_type(self):
+                pass
+            @gocept.selenium.selenese.assert_type('wrong_type')
+            def get_with_wrong_assert_type(self):
+                pass
+
+        self.selenese = Selenese(None, TestCase())
+
+    def assertError(self, error, name, expected_msg):
+        try:
+            getattr(self.selenese, name)
+        except error, e:
+            msg = e.args[0]
+        self.assertEquals(expected_msg, msg)
+
+    def assertAttributeError(self, name):
+        self.assertError(AttributeError, name, name)
+
+    def test_nonexistent_name(self):
+        self.assertAttributeError('a_nonexistent_name')
+        self.assertAttributeError('assert_a_nonexistent_name')
+
+    def test_waitfor_verify(self):
+        self.assertAttributeError('waitFor_a_nonexistent_name')
+        self.assertAttributeError('verify_a_nonexistent_name')
+
+    def test_not(self):
+        self.assertAttributeError('a_Notexistent_name')
+        self.assertAttributeError('assert_a_Notexistent_name')
+        self.assertAttributeError('waitFor_a_Notexistent_name')
+        self.assertAttributeError('verify_a_Notexistent_name')
+
+    def test_broken_assert_type(self):
+        self.assertError(AttributeError,
+                         'assert_without_assert_type',
+                         "'function' object has no attribute 'assert_type'")
+        self.assertError(ValueError,
+                         'assert_with_wrong_assert_type',
+                         "Unknown assert type 'wrong_type' for "
+                         "selenese method 'assert_with_wrong_assert_type'.")
+
+
 class AssertionTest(gocept.selenium.ztk.testing.TestCase):
 
     def test_wait_for(self):



More information about the checkins mailing list