simple test implementation for binary operators on selectors tls-sprint
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Mon, 16 Feb 2009 16:24:24 +0100
branchtls-sprint
changeset 630 66ff0b2f7d03
parent 629 59b6542f5729
child 631 99f5852f8604
child 641 ed668804af37
simple test implementation for binary operators on selectors
test/unittest_selectors.py
vregistry.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_selectors.py	Mon Feb 16 16:24:24 2009 +0100
@@ -0,0 +1,80 @@
+"""unit tests for selectors mechanism
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.vregistry import Selector, AndSelector, OrSelector
+
+class _1_(Selector):
+    def __call__(self, *args, **kwargs):
+        return 1
+
+class _0_(Selector):
+    def __call__(self, *args, **kwargs):
+        return 0
+
+def _2_(*args, **kwargs):
+    return 2
+
+
+class SelectorsTC(TestCase):
+    def test_basic_and(self):
+        selector = _1_() & _1_()
+        self.assertEquals(selector(None), 2)
+        selector = _1_() & _0_()
+        self.assertEquals(selector(None), 0)
+        selector = _0_() & _1_()
+        self.assertEquals(selector(None), 0)
+
+    def test_basic_or(self):
+        selector = _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _1_() | _0_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _0_()
+        self.assertEquals(selector(None), 0)
+
+    def test_selector_and_function(self):
+        selector = _1_() & _2_
+        self.assertEquals(selector(None), 3)
+        selector = _2_ & _1_()
+        self.assertEquals(selector(None), 3)
+        
+    def test_three_and(self):
+        selector = _1_() & _1_() & _1_()
+        self.assertEquals(selector(None), 3)
+        selector = _1_() & _0_() & _1_()
+        self.assertEquals(selector(None), 0)
+        selector = _0_() & _1_() & _1_()
+        self.assertEquals(selector(None), 0)
+
+    def test_three_or(self):
+        selector = _1_() | _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _1_() | _0_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _0_() | _0_()
+        self.assertEquals(selector(None), 0)
+
+    def test_composition(self):
+        selector = (_1_() & _1_()) & (_1_() & _1_())
+        self.failUnless(isinstance(selector, AndSelector))
+        self.assertEquals(len(selector.selectors), 4)
+        self.assertEquals(selector(None), 4)
+        selector = (_1_() & _0_()) | (_1_() & _1_())
+        self.failUnless(isinstance(selector, OrSelector))
+        self.assertEquals(len(selector.selectors), 2)
+        self.assertEquals(selector(None), 2)
+
+
+if __name__ == '__main__':
+    unittest_main()
+
--- a/vregistry.py	Mon Feb 16 14:18:13 2009 +0100
+++ b/vregistry.py	Mon Feb 16 16:24:24 2009 +0100
@@ -543,3 +543,65 @@
         return 0
     return selector
 
+
+# selector base classes and operations ########################################
+class Selector(object):
+    """base class for selector classes providing implementation
+    for operators ``&`` and ``|``
+
+    This class is only here to give access to binary operators, the
+    selector logic itself should be implemented in the __call__ method
+    """
+    def __init__(self, *selectors):
+        self.selectors = self.merge_selectors(selectors)
+
+    @classmethod
+    def merge_selectors(cls, selectors):
+        """merge selectors when possible :
+
+        AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4))
+        ==> AndSelector(sel1, sel2, sel3, sel4)
+        """
+        merged_selectors = []
+        for selector in selectors:
+            if isinstance(selector, cls):
+                merged_selectors += selector.selectors
+            else:
+                merged_selectors.append(selector)
+        return merged_selectors
+        
+    def __and__(self, other):
+        return AndSelector(self, other)
+
+    def __or__(self, other):
+        return OrSelector(self, other)
+
+    __ror__ = __or__    # for cases like (function | selector)
+    __rand__ = __and__  # for cases like (function & selector)
+    # XXX (function | function) or (function & function) not managed yet
+
+    def __call__(self, cls, *args, **kwargs):
+        return NotImplementedError("selector %s must implement its logic "
+                                   "in its __call__ method" % self.__class__.__name__)
+
+
+class AndSelector(Selector):
+    """and-chained selectors (formerly known as chainall)"""
+    def __call__(self, cls, *args, **kwargs):
+        score = 0
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if not partscore:
+                return 0
+            score += partscore
+        return score
+
+
+class OrSelector(Selector):
+    """or-chained selectors (formerly known as chainfirst)"""
+    def __call__(self, cls, *args, **kwargs):
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if partscore:
+                return partscore
+        return 0