1"""
2XXX: Add tests that check that the type actually works as expected:
3
4* Use struct value as method argument
5* Return struct value from a method
6
7Add tests for nested structs as well (that is assert that NSRect.location is
8an NSPoint, but using our own types)
9"""
10from PyObjCTools.TestSupport import *
11import objc
12from PyObjCTest.structs import *
13from PyObjCTest.fnd import NSObject
14
15
16class TestStructs (TestCase):
17    def testCreateExplicit(self):
18        tp = objc.createStructType("FooStruct", b"{_FooStruct=ffff}", ["a","b","c","d"])
19        self.assertIsInstance(tp, type)
20        self.assertEqual(tp.__typestr__, b"{_FooStruct=ffff}")
21
22        self.assertEqual(tp._fields, ("a", "b", "c", "d"))
23
24        o = tp()
25        self.assertHasAttr(o, 'a')
26        self.assertHasAttr(o, 'b')
27        self.assertHasAttr(o, 'c')
28        self.assertHasAttr(o, 'd')
29
30        self.assertHasAttr(objc.ivar, 'FooStruct')
31        v = objc.ivar.FooStruct()
32        self.assertIsInstance(v, objc.ivar)
33        self.assertEqual(v.__typestr__, tp.__typestr__)
34
35
36    def testNamedTupleAPI(self):
37        Point = objc.createStructType("OCPoint", b"{_OCPoint=dd}", ["x", "y"])
38        Line  = objc.createStructType("OCLine",  b"{_OCLine={_OCPoint=dd}{_OCPoint=dd}}d", ["start", "stop", "width"])
39
40        self.assertEqual(Point._fields, ("x", "y"))
41        self.assertEqual(Line._fields, ("start", "stop", "width"))
42
43        p = Point(3, 4)
44        self.assertEqual(p.x, 3.0)
45        self.assertEqual(p.y, 4.0)
46
47        self.assertEqual(p._asdict(), {"x": 3.0, "y": 4.0})
48
49        p2 = p._replace(y=5)
50        self.assertEqual(p.x, 3.0)
51        self.assertEqual(p.y, 4.0)
52        self.assertEqual(p2.x, 3.0)
53        self.assertEqual(p2.y, 5)
54
55        l = Line(Point(1,2), Point(8,9), 7)
56        self.assertEqual(l.start.x, 1.0)
57        self.assertEqual(l.start.y, 2.0)
58        self.assertEqual(l.stop.x, 8.0)
59        self.assertEqual(l.stop.y, 9.0)
60        self.assertEqual(l.width, 7.0)
61
62        self.assertEqual(l._asdict(),
63            {"start": Point(1,2), "stop":Point(8,9), "width": 7.0})
64
65        l2 = l._replace(stop=Point(3,4), width=0.5)
66        self.assertEqual(l.start.x, 1.0)
67        self.assertEqual(l.start.y, 2.0)
68        self.assertEqual(l.stop.x, 8.0)
69        self.assertEqual(l.stop.y, 9.0)
70        self.assertEqual(l.width, 7.0)
71
72        self.assertEqual(l2.start.x, 1.0)
73        self.assertEqual(l2.start.y, 2.0)
74        self.assertEqual(l2.stop.x, 3.0)
75        self.assertEqual(l2.stop.y, 4.0)
76        self.assertEqual(l2.width, 0.5)
77
78
79    def testCreateImplicit(self):
80        tp = objc.createStructType("BarStruct", b'{_BarStruct="e"f"f"f"g"f"h"f}', None)
81        self.assertIsInstance(tp, type)
82        self.assertEqual(tp.__typestr__, b"{_BarStruct=ffff}")
83
84        o = tp()
85        self.assertHasAttr(o, 'e')
86        self.assertHasAttr(o, 'f')
87        self.assertHasAttr(o, 'g')
88        self.assertHasAttr(o, 'h')
89
90        self.assertEqual(tp._fields, ("e", "f", "g", "h"))
91
92        self.assertRaises(ValueError, objc.createStructType, "Foo2", b'{_Foo=f"a"}', None)
93        self.assertRaises(ValueError, objc.createStructType, "Foo3", b'{_Foo="a"f', None)
94        self.assertRaises(ValueError, objc.createStructType, "Foo4", b'^{_Foo="a"f}', None)
95
96    def testPointerFields(self):
97        # Note: the created type won't be all that useful unless the pointer
98        # happens to be something that PyObjC knows how to deal with, this is
99        # more a check to see if createStructType knows how to cope with
100        # non-trivial types.
101        tp = objc.createStructType("XBarStruct", b'{_XBarStruct="e"^f"f"^f"g"^@"h"f}', None)
102        self.assertIsInstance(tp, type)
103        self.assertEqual(tp.__typestr__, b"{_XBarStruct=^f^f^@f}")
104
105        o = tp()
106        self.assertHasAttr(o, 'e')
107        self.assertHasAttr(o, 'f')
108        self.assertHasAttr(o, 'g')
109        self.assertHasAttr(o, 'h')
110
111    def testEmbeddedFields(self):
112        tp = objc.createStructType("BarStruct", b'{FooStruct="first"i"second"i}', None)
113
114        v = OC_StructTest.createWithFirst_andSecond_(1, 2)
115        self.assertIsInstance(v, tp)
116
117        x = OC_StructTest.sumFields_(v)
118        self.assertEqual(x, v.first + v.second)
119        self.assertEqual(v.first, 1)
120        self.assertEqual(v.second, 2)
121
122        self.assertHasAttr(objc.ivar, 'BarStruct')
123        v = objc.ivar.BarStruct()
124        self.assertEqual(v.__typestr__, b'{FooStruct=ii}')
125
126    def testStructCallback(self):
127        """
128        Regression test for an issue reported on the PyObjC mailinglist.
129        """
130        tp = objc.createStructType("FooStruct", b'{FooStruct="first"i"second"i}', None)
131
132        StructArrayDelegate = objc.informal_protocol(
133            "StructArrayDelegate",
134            [
135                objc.selector(None, b"arrayOf4Structs:",
136                    signature=b"@@:[4{FooStruct=ii}]"),
137            ])
138
139        class OC_PyStruct (NSObject):
140
141            def arrayOf4Structs_(self, value):
142                return value
143
144        self.assertEqual(OC_PyStruct.arrayOf4Structs_.signature, b"@@:[4{FooStruct=" + objc._C_INT + objc._C_INT + b"}]")
145
146        o = OC_PyStruct.alloc().init()
147        v = OC_StructTest.callArrayOf4Structs_(o)
148        self.assertEqual(len(v), 4)
149        for i in range(3):
150            self.assertIsInstance(v[i], tp)
151
152        self.assertEqual(v[0], tp(1, 2))
153        self.assertEqual(v[1], tp(3, 4))
154        self.assertEqual(v[2], tp(5, 6))
155        self.assertEqual(v[3], tp(7, 8))
156
157
158if __name__ == "__main__":
159    main()
160