1from PyObjCTools.TestSupport import *
2import objc
3import warnings
4import sys
5
6# Most useful systems will at least have 'NSObject'.
7NSObject = objc.lookUpClass('NSObject')
8
9# XXX : This is a really dumb way to detect < 10.3
10if not NSObject.instancesRespondToSelector_('setValue:forKey:'):
11    # Defining protocols in an MH_BUNDLE makes < 10.3 explode
12    OC_TestProtocol = None
13else:
14    from PyObjCTest.protocol import OC_TestProtocol
15
16MyProto = objc.informal_protocol("MyProto", (
17    objc.selector(None, selector="testMethod", signature="I@:", isRequired=1),
18    objc.selector(None, selector="testMethod2:", signature="v@:i", isRequired=0)
19))
20
21class TestInformalProtocols(TestCase):
22
23
24    def testMissingProto(self):
25        class ProtoClass1 (NSObject):
26            def testMethod(self):
27                pass
28        self.assertEquals(ProtoClass1.testMethod.signature, "I@:")
29
30
31    def doIncompleteClass(self):
32        class ProtoClass2 (NSObject, MyProto):
33            def testMethod2_(self, x):
34                pass
35
36    def testIncompleteClass(self):
37        self.assertRaises(TypeError, self.doIncompleteClass)
38
39
40    def testOptional(self):
41        class ProtoClass3 (NSObject, MyProto):
42            def testMethod(self):
43                pass
44
45
46
47if sys.maxint < 2 ** 32:
48    EmptyProtocol = objc.formal_protocol("EmptyProtocol", None, ())
49
50    MyProtocol = objc.formal_protocol("MyProtocol", None, (
51        objc.selector(None, selector="protoMethod", signature="I@:"),
52        objc.selector(None, selector="anotherProto:with:", signature="v@:ii"),
53    ))
54
55    MyOtherProtocol = objc.formal_protocol("MyOtherProtocol",
56            (MyProtocol,), [
57                objc.selector(None, selector="yetAnother:", signature="i@:I")
58            ])
59
60    MyClassProtocol = objc.formal_protocol("MyClassProtocol", None, [
61        objc.selector(None, selector="anAnotherOne:", signature="i@:i"),
62        objc.selector(None, selector="aClassOne:", signature="@@:i", isClassMethod=1),
63    ])
64
65    if OC_TestProtocol is not None:
66
67        class TestFormalOCProtocols(TestCase):
68
69            def testImplementFormalProtocol(self):
70
71                class MyClassNotImplementingProtocol(NSObject):
72                    pass
73
74                self.assert_(not MyClassNotImplementingProtocol.pyobjc_classMethods.conformsToProtocol_(OC_TestProtocol))
75
76                try:
77                    class MyClassNotAlsoImplementingProtocol(NSObject, OC_TestProtocol):
78                        def method1(self): pass
79
80                    self.fail("class not implementing protocol, yet created")
81                except TypeError:
82                    pass
83
84                class MyClassImplementingProtocol(NSObject, OC_TestProtocol):
85                    def method1(self): pass
86                    def method2_(self, a): pass
87
88                self.assert_(MyClassImplementingProtocol.pyobjc_classMethods.conformsToProtocol_(OC_TestProtocol))
89
90
91
92                # The PyObjC implementation of formal protocols is slightly looser
93                # than Objective-C itself: you can inherit part of the protocol
94                # from the superclass.
95                # XXX: not really: you won't inherit the right signatures by default
96
97                class MyClassImplementingHalfOfProtocol(NSObject):
98                        def method1(self): pass
99                        method1 = objc.selector(method1, signature='i@:')
100
101                self.assert_(not MyClassImplementingHalfOfProtocol.pyobjc_classMethods.conformsToProtocol_(OC_TestProtocol))
102
103                class MyClassImplementingAllOfProtocol(MyClassImplementingHalfOfProtocol, OC_TestProtocol):
104                        def method2_(self, v): pass
105
106                self.assert_(MyClassImplementingAllOfProtocol.pyobjc_classMethods.conformsToProtocol_(OC_TestProtocol))
107
108
109
110
111    class TestFormalProtocols (TestCase):
112        # Implement unittests for formal protocols here.
113        #
114
115        def testImplementAnotherObject(self):
116            anObject = NSObject.alloc().init()
117
118            try:
119                class MyClassImplementingAnotherObject(NSObject, anObject):
120                        pass
121                self.fail()
122            except TypeError:
123                pass
124
125            try:
126                class MyClassImplementingAnotherObject(NSObject, 10):
127                        pass
128                self.fail()
129            except TypeError:
130                pass
131
132            try:
133                class MyClassImplementingAnotherObject(NSObject, int):
134                        pass
135                self.fail()
136            except TypeError:
137                pass
138
139        def dont_testDefiningingProtocols(self):
140
141            # Pretty useless, but should work
142
143            self.assert_(MyOtherProtocol.conformsTo_(MyProtocol))
144
145
146            try:
147                class MyClassImplementingMyProtocol(NSObject, MyProtocol):
148                    pass
149
150                # Declare to implement a protocol, but don't do it?
151                self.fail()
152            except TypeError:
153                pass
154
155
156            class MyClassImplementingMyProtocol(NSObject, MyProtocol):
157                def protoMethod(self):
158                    return 1
159
160                def anotherProto_with_(self, a1, a2):
161                    pass
162
163            self.assertEquals(MyClassImplementingMyProtocol.protoMethod.signature, "I@:")
164            self.assertEquals(MyClassImplementingMyProtocol.anotherProto_with_.signature, "v@:ii")
165            self.assert_(MyClassImplementingMyProtocol.pyobjc_classMethods.conformsToProtocol_(MyProtocol))
166
167            class MyClassImplementingMyOtherProtocol(NSObject, MyOtherProtocol):
168                def protoMethod(self): pass
169                def anotherProto_with_(self, a1, a2): pass
170                def yetAnother_(self, a): pass
171
172            self.assertEquals(MyClassImplementingMyOtherProtocol.protoMethod.signature, "I@:")
173            self.assertEquals(MyClassImplementingMyOtherProtocol.anotherProto_with_.signature, "v@:ii")
174            self.assertEquals(MyClassImplementingMyOtherProtocol.yetAnother_.signature, "i@:I")
175            self.assert_(MyClassImplementingMyOtherProtocol.pyobjc_classMethods.conformsToProtocol_(MyProtocol))
176            self.assert_(MyClassImplementingMyOtherProtocol.pyobjc_classMethods.conformsToProtocol_(MyOtherProtocol))
177
178            try:
179                class ImplementingMyClassProtocol(NSObject, MyClassProtocol):
180                    pass
181
182                self.fail()
183            except TypeError:
184                pass
185
186            class ImplementingMyClassProtocol(NSObject, MyClassProtocol):
187                    def anAnotherOne_(self, a):
188                        pass
189
190                    def aClassOne_(self, a):
191                        pass
192
193                    aClassOne_ = classmethod(aClassOne_)
194
195            self.assertEquals(ImplementingMyClassProtocol.anAnotherOne_.signature, 'i@:i')
196            self.assertEquals(ImplementingMyClassProtocol.aClassOne_.isClassMethod, True)
197            self.assertEquals(ImplementingMyClassProtocol.aClassOne_.signature,'@@:i')
198
199            # TODO: protocol with class and instance method with different
200            # signatures.
201            # TODO: should not need to specify classmethod() if it can be
202            # deduced from the protocol
203
204
205        def testIncorrectlyDefiningFormalProtocols(self):
206            # Some bad calls to objc.formal_protocol
207            self.assertRaises(TypeError, objc.formal_protocol, [], None, ())
208            self.assertRaises(TypeError, objc.formal_protocol, 'supers', (NSObject,) , ())
209            self.assertRaises(TypeError, objc.formal_protocol, 'supers', objc.protocolNamed('NSLocking') , ())
210            self.assertRaises(TypeError, objc.formal_protocol, 'supers', [
211                    objc.protocolNamed('NSLocking'),
212                    "hello",
213                ], ())
214            self.assertRaises(TypeError, objc.formal_protocol, 'supers', None, [
215                    objc.selector(None, selector='fooMethod:', signature='v@:i'),
216                    "hello",
217                ])
218
219        def testMethodInfo(self):
220            self.assertEquals(
221                    MyProtocol.descriptionForInstanceMethod_("protoMethod"),
222                        ("protoMethod", "I@:"))
223
224            self.assertEquals(
225                    MyProtocol.descriptionForInstanceMethod_("nosuchmethod"),
226                        None)
227
228            self.assertEquals(
229                    MyClassProtocol.descriptionForClassMethod_("aClassOne:"),
230                        ("aClassOne:", "@@:i"))
231
232            self.assertEquals(
233                    MyClassProtocol.descriptionForClassMethod_("nosuchmethod"),
234                        None)
235
236
237        def dont_testObjCInterface(self):
238            # TODO: tests that access the Objective-C interface of protocols
239            # (those methods should be forwarded to the underlying object, as
240            #  with objc.pyobjc_unicode).
241            # NOTE: This is not very important, the only methods that are not
242            # explicitly wrapped should be compatibility methods that will
243            # cause a warning when called.
244            self.assertEquals(1, 0)
245
246if __name__ == '__main__':
247    main()
248