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