Commits

Ronald Oussoren  committed 53a80d0

Add tests for blocks that return a large struct (NSRect)

The tests that actually call these blocks are disabled for
now because they crash the interpreter (because PyObjC doesn't
have the code needed to support these)

  • Participants
  • Parent commits 75d37c4

Comments (0)

Files changed (4)

File pyobjc-core/Modules/objc/block_support.m

     PyObject* invoke_cleanup;
 };
 
-extern const char* PyObjCBlock_GetSignature(void* _block)
+const char*
+PyObjCBlock_GetSignature(void* _block)
 {
     struct block_literal* block = (struct block_literal*)_block;
     struct block_descriptor_basic* descriptor = (struct block_descriptor_basic*)block->descriptor;
-    int offset = 0;
+    size_t offset = 0;
 
-    offset = 0;
     if (block->flags & BLOCK_HAS_COPY_DISPOSE) {
         offset += 2;
     }
+
     if (block->flags & BLOCK_HAS_SIGNATURE) {
         return descriptor->rest[offset];
     }
     return 0;
 }
 
+
 _block_func_ptr
 PyObjCBlock_GetFunction(void* block)
 {

File pyobjc-core/Modules/objc/module.m

     return Py_None;
 }
 
+static PyObject*
+block_signature(PyObject* mod __attribute__((__unused__)), PyObject* block)
+{
+    if (!PyObjCObject_Check(block) || !PyObjCObject_IsBlock(block)) {
+        PyErr_SetString(PyExc_ValueError, "Not a block");
+        return NULL;
+    }
+
+    const char* sig = PyObjCBlock_GetSignature(PyObjCObject_GetObject(block));
+    if (sig == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return PyBytes_FromString(sig);
+}
+
+
+
 static PyMethodDef mod_methods[] = {
     {
         "propertiesForClass",
         METH_VARARGS,
         "_block_call(block, signature, args, kwds) -> retval" },
 
+    { "_block_signature", (PyCFunction)block_signature,
+        METH_O, "return signature string for a block, or None" },
     { "_typestr2typestr", (PyCFunction)typestr2typestr,
         METH_O, "private function" },
 

File pyobjc-core/Modules/objc/test/block.m

 
 -(int(^)(void))getIntBlock;
 -(double(^)(double,double))getFloatBlock;
+-(NSRect(^)(double, double, double, double))getStructBlock;
 -(void)callIntBlock:(void(^)(int))block withValue:(int)value;
 -(double)callDoubleBlock:(double(^)(double, double))block withValue:(double)v1 andValue:(double)v2;
 #endif
 
 #if PyObjC_BUILD_RELEASE >= 1006 && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 2)
 
+-(NSRect(^)(double, double, double, double))getStructBlock
+{
+    return [[^(double a, double b, double c, double d) {
+        return NSMakeRect(a, b, c, d);
+    } copy] autorelease];
+}
+
 -(int(^)(void))getIntBlock
 {
     return [[^{ return 42; } copy] autorelease];
 {
     return block(v1, v2);
 }
+
+-(NSRect)callStructBlock: (NSRect(^)(double, double, double, double))block
+                   withA:(double)a b:(double)b c:(double)c d:(double)d
+{
+    return block(a, b, c, d);
+}
+
 #endif
 
 @end

File pyobjc-core/PyObjCTest/test_blocks.py

 from PyObjCTools.TestSupport import *
 from PyObjCTest.block import OCTestBlock
 import objc
+import sys
+
+if sys.maxsize > 2 ** 32:
+    NSRect_tp = b'{CGRect={CGPoint=dd}{CGSize=dd}}'
+else:
+    NSRect_tp = b'{_NSRect={_NSPoint=ff}{_NSSize=ff}}'
 
 objc.parseBridgeSupport('''\
     <?xml version='1.0'?>
         self.assertEqual(obj.callDoubleBlock_withValue_andValue_(callback, 2.0, 3.5), 7.0)
         self.assertEqual(obj.callDoubleBlock_withValue_andValue_(callback, 2.5, 10), 25.0)
 
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockToObjC3(self):
+        return
+
+        obj = OCTestBlock.alloc().init()
+
+        lst = []
+        def callback(a, b, c, d):
+            return ((a, b), (c, d))
+
+        v = obj.callStructBlock_withA_b_c_d_(callback, 1.5, 2.5, 3.5, 4.5)
+        print(v)
+
 
     @min_os_level('10.6')
     @onlyIf(blocksEnabled, "no blocks")
         value = block(2.5, 7.0)
         self.assertEqual(value, 9.5)
 
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockFromObjC3(self):
+        obj = OCTestBlock.alloc().init()
+
+        return
+
+        block = obj.getStructBlock()
+        value = block(1, 2, 3, 4)
+        self.assertEqual(len(value), 4)
+        self.assertEqual(list(value), ((1.0, 2.0), (3.0, 4.0)))
+
+
+    @min_os_level('10.6')
+    @onlyIf(blocksEnabled, "no blocks")
+    def testBlockSignatures(self):
+        obj = OCTestBlock.alloc().init()
+
+        block = obj.getFloatBlock()
+        sig = objc.splitSignature(objc._block_signature(block))
+        self.assertEqual(sig,  (objc._C_DBL, objc._C_ID + b'?', objc._C_DBL, objc._C_DBL))
+
+        block = obj.getStructBlock()
+        sig = objc.splitSignature(objc._block_signature(block))
+        self.assertEqual(sig,  (NSRect_tp, objc._C_ID + b'?', objc._C_DBL, objc._C_DBL, objc._C_DBL, objc._C_DBL))
+
+
+
 if __name__ == "__main__":
     main()