1#!/usr/bin/python
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4# or more contributor license agreements.  See the NOTICE file
5# distributed with this work for additional information
6# regarding copyright ownership.  The ASF licenses this file
7# to you under the Apache License, Version 2.0 (the
8# "License"); you may not use this file except in compliance
9# with the License.  You may obtain a copy of the License at
10
11#     http://www.apache.org/licenses/LICENSE-2.0
12
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18
19import zookeeper, zktestbase, unittest, threading, gc
20
21ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"}
22
23class CallbackTest(zktestbase.TestBase):
24    """
25    Test whether callbacks (watchers/completions) are correctly invoked
26    """
27    # to do: startup and teardown via scripts?
28    def setUp(self):
29        zktestbase.TestBase.setUp(self)
30        self.cv = threading.Condition()
31
32    def create_callback(self, callback):
33        """
34        Returns a callable which signals cv and then calls callback
35        """
36        def wrapper(*args, **kwargs):
37            self.cv.acquire()
38            callback(*args, **kwargs)
39            self.cv.notify()
40            self.cv.release()
41        return wrapper
42
43    def test_none_callback(self):
44        """
45        Test that no errors are raised when None is passed as a callback.
46        """
47        self.ensureCreated("/zk-python-none-callback-test","test")
48        # To do this we need to issue two operations, waiting on the second
49        # to ensure that the first completes
50        zookeeper.get(self.handle, "/zk-python-none-callback-test", None)
51        (d,s) = zookeeper.get(self.handle, "/zk-python-none-callback-test")
52        self.assertEqual(d, "test")
53
54    def callback_harness(self, trigger, test):
55        self.callback_flag = False
56        self.cv.acquire()
57        trigger()
58        self.cv.wait(15)
59        test()
60
61    def test_dispatch_types(self):
62        """
63        Test all the various dispatch mechanisms internal to the module.
64        """
65        def dispatch_callback(*args, **kwargs):
66            self.callback_flag = True
67        self.ensureCreated("/zk-python-dispatch-test")
68        self.callback_harness( lambda: zookeeper.adelete(self.handle,
69                                                            "/zk-python-dispatch-test",
70                                                            -1,
71                                                            self.create_callback(dispatch_callback)),
72                                  lambda: self.assertEqual(True, self.callback_flag, "Void dispatch not fired"))
73
74
75        self.ensureCreated("/zk-python-dispatch-test")
76        self.callback_harness( lambda: zookeeper.aexists(self.handle,
77                                                         "/zk-python-dispatch-test",
78                                                         None,
79                                                         self.create_callback(dispatch_callback)),
80                               lambda: self.assertEqual(True, self.callback_flag, "Stat dispatch not fired"))
81
82        self.callback_harness( lambda: zookeeper.aget(self.handle,
83                                                      "/zk-python-dispatch-test",
84                                                      None,
85                                                      self.create_callback(dispatch_callback)),
86                               lambda: self.assertEqual(True, self.callback_flag, "Data dispatch not fired"))
87
88        self.callback_harness( lambda: zookeeper.aget_children(self.handle,
89                                                               "/",
90                                                               None,
91                                                               self.create_callback( dispatch_callback )),
92                               lambda: self.assertEqual(True, self.callback_flag, "Strings dispatch not fired"))
93
94        self.callback_harness( lambda: getattr(zookeeper, 'async')(self.handle,
95                                                                   "/",
96                                                                   self.create_callback( dispatch_callback )),
97                               lambda: self.assertEqual(True, self.callback_flag, "String dispatch not fired"))
98
99        self.callback_harness( lambda: zookeeper.aget_acl(self.handle,
100                                                          "/",
101                                                          self.create_callback( dispatch_callback )),
102                               lambda: self.assertEqual(True, self.callback_flag, "ACL dispatch not fired"))
103
104    def test_multiple_watchers(self):
105        """
106        Test whether multiple watchers are correctly called
107        """
108        cv1, cv2 = threading.Condition(), threading.Condition()
109        def watcher1(*args, **kwargs):
110            cv1.acquire()
111            self.watcher1 = True
112            cv1.notify()
113            cv1.release()
114
115        def watcher2(*args, **kwargs):
116            cv2.acquire()
117            self.watcher2 = True
118            cv2.notify()
119            cv2.release()
120
121        nodename = "/zk-python-multiple-watcher-test"
122        self.ensureCreated(nodename, "test")
123        cv1.acquire()
124        cv2.acquire()
125        zookeeper.get(self.handle, nodename, watcher1)
126        zookeeper.get(self.handle, nodename, watcher2)
127        zookeeper.set(self.handle, nodename, "test")
128        cv1.wait(15)
129        cv2.wait(15)
130        self.assertTrue(self.watcher1 and self.watcher2, "One or more watchers failed to fire")
131
132    def test_lose_scope(self):
133        """
134        The idea is to test that the reference counting doesn't
135        fail when we retain no references outside of the module
136        """
137        self.ensureDeleted("/zk-python-lose-scope-test")
138        self.ensureCreated("/zk-python-lose-scope-test")
139        def set_watcher():
140            def fn(): self.callback_flag = True
141            self.callback_flag = False
142            zookeeper.exists(self.handle, "/zk-python-lose-scope-test",
143                             self.create_callback( lambda handle, type, state, path: fn() )
144                             )
145
146        set_watcher()
147        gc.collect()
148        self.cv.acquire()
149        zookeeper.set(self.handle, "/zk-python-lose-scope-test", "test")
150        self.cv.wait(15)
151        self.assertEqual(self.callback_flag, True)
152
153
154if __name__ == '__main__':
155    unittest.main()
156