1from PyObjCTools.TestSupport import *
2from CoreFoundation import *
3import errno, time, os, socket, sys
4
5from test_cfsocket import onTheNetwork
6
7
8class TestStream (TestCase):
9    def testTypes(self):
10        self.assertIsCFType(CFReadStreamRef)
11        self.assertIsCFType(CFWriteStreamRef)
12
13    def testConstants(self):
14        self.assertEqual(kCFStreamStatusNotOpen , 0)
15        self.assertEqual(kCFStreamStatusOpening , 1)
16        self.assertEqual(kCFStreamStatusOpen , 2)
17        self.assertEqual(kCFStreamStatusReading , 3)
18        self.assertEqual(kCFStreamStatusWriting , 4)
19        self.assertEqual(kCFStreamStatusAtEnd , 5)
20        self.assertEqual(kCFStreamStatusClosed , 6)
21        self.assertEqual(kCFStreamStatusError , 7)
22        self.assertEqual(kCFStreamEventNone , 0)
23        self.assertEqual(kCFStreamEventOpenCompleted , 1)
24        self.assertEqual(kCFStreamEventHasBytesAvailable , 2)
25        self.assertEqual(kCFStreamEventCanAcceptBytes , 4)
26        self.assertEqual(kCFStreamEventErrorOccurred , 8)
27        self.assertEqual(kCFStreamEventEndEncountered , 16)
28        self.assertEqual(kCFStreamErrorDomainCustom , -1)
29        self.assertEqual(kCFStreamErrorDomainPOSIX , 1)
30        self.assertEqual(kCFStreamErrorDomainMacOSStatus , 2)
31        self.assertIsInstance(kCFStreamPropertyDataWritten, unicode)
32        self.assertIsInstance(kCFStreamPropertyAppendToFile, unicode)
33        self.assertIsInstance(kCFStreamPropertyFileCurrentOffset, unicode)
34        self.assertIsInstance(kCFStreamPropertySocketNativeHandle, unicode)
35        self.assertIsInstance(kCFStreamPropertySocketRemoteHostName, unicode)
36        self.assertIsInstance(kCFStreamPropertySocketRemotePortNumber, unicode)
37
38    def testStructs(self):
39        o = CFStreamError()
40        self.assertHasAttr(o, 'domain')
41        self.assertHasAttr(o, 'error')
42
43    def testGetTypeID(self):
44        v = CFReadStreamGetTypeID()
45        self.assertIsInstance(v, (int, long))
46        v = CFWriteStreamGetTypeID()
47        self.assertIsInstance(v, (int, long))
48
49    def testReadStream(self):
50        strval = b"hello world"
51        self.assertArgHasType(CFReadStreamCreateWithBytesNoCopy, 1, b'n^v')
52        self.assertArgSizeInArg(CFReadStreamCreateWithBytesNoCopy, 1, 2)
53        stream = CFReadStreamCreateWithBytesNoCopy(None,
54                strval, len(strval), kCFAllocatorNull)
55        self.assertIsInstance(stream, CFReadStreamRef)
56        r, buf = CFReadStreamRead(stream, None, 10)
57        self.assertEqual(r, -1)
58        self.assertEqual(buf, b'')
59
60        self.assertResultIsCFRetained(CFReadStreamCopyError)
61        err  = CFReadStreamCopyError(stream)
62        if err is not None:
63            self.assertIsInstance(err, CFErrorRef)
64        status = CFReadStreamGetStatus(stream)
65        self.assertIsInstance(status, (int, long))
66        self.assertEqual(status, kCFStreamStatusNotOpen)
67
68        self.assertResultIsBOOL(CFReadStreamOpen)
69        r = CFReadStreamOpen(stream)
70        self.assertIs(r, True)
71        status = CFReadStreamGetStatus(stream)
72        self.assertIsInstance(status, (int, long))
73        self.assertEqual(status, kCFStreamStatusOpen)
74
75        self.assertResultIsBOOL(CFReadStreamHasBytesAvailable)
76        r = CFReadStreamHasBytesAvailable(stream)
77        self.assertIs(r, True)
78        self.assertArgHasType(CFReadStreamRead, 1, b'o^v')
79        self.assertArgSizeInArg(CFReadStreamRead, 1, 2)
80        self.assertArgSizeInResult(CFReadStreamRead, 1)
81        r, buf = CFReadStreamRead(stream, None, 5)
82        self.assertEqual(r, 5)
83        self.assertEqual(buf, b"hello")
84
85        r, buf = CFReadStreamRead(stream, None, 10)
86        self.assertEqual(r, 6)
87        self.assertEqual(buf, b" world")
88
89        r = CFReadStreamHasBytesAvailable(stream)
90        self.assertIs(r, False)
91        r = CFReadStreamClose(stream)
92        self.assertIs(r, None)
93        status = CFReadStreamGetStatus(stream)
94        self.assertIsInstance(status, (int, long))
95        self.assertEqual(status, kCFStreamStatusClosed)
96
97
98        del stream
99
100        self.assertResultIsCFRetained(CFReadStreamCreateWithFile)
101        stream = CFReadStreamCreateWithFile(None,
102                    CFURLCreateWithString(None, u"file:///etc/shells", None))
103        self.assertIsInstance(stream, CFReadStreamRef)
104        r = CFReadStreamOpen(stream)
105        self.assertIs(r, True)
106        status = CFReadStreamGetStatus(stream)
107        self.assertIsInstance(status, (int, long))
108        self.assertEqual(status, kCFStreamStatusOpen)
109
110        r, buf = CFReadStreamRead(stream, None, 5)
111        self.assertEqual(r, 5)
112        self.assertIsInstance(buf, bytes)
113        self.assertResultSizeInArg(CFReadStreamGetBuffer, 2)
114        self.assertResultHasType(CFReadStreamGetBuffer, b'^v')
115        self.assertArgIsOut(CFReadStreamGetBuffer, 2)
116        buf, numBytes = CFReadStreamGetBuffer(stream, 20, None)
117        if buf is objc.NULL:
118            self.assertEqual(numBytes, 0)
119        else:
120            self.assertIsInstance(buf, str)
121            self.assertEqual(numBytes, len(buf))
122
123        val = CFReadStreamCopyProperty(stream, kCFStreamPropertyFileCurrentOffset)
124        self.assertEqual(val, 5)
125
126        r = CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, 10)
127        self.assertIs(r, True)
128        val = CFReadStreamCopyProperty(stream, kCFStreamPropertyFileCurrentOffset)
129        self.assertEqual(val, 10)
130
131        err = CFReadStreamGetError(stream)
132        self.assertIsInstance(err, CFStreamError)
133        self.assertEqual(err.domain , 0)
134        self.assertEqual(err.error , 0)
135    def testWriteStream(self):
136        import array
137        a = array.array('b', b" "*20)
138
139        # XXX: cannot express the actual type as metadata :-(
140        self.assertArgHasType(CFWriteStreamCreateWithBuffer, 1, b'n^v')
141        self.assertArgSizeInArg(CFWriteStreamCreateWithBuffer, 1, 2)
142        stream = CFWriteStreamCreateWithBuffer(None, a, 20)
143        self.assertIsInstance(stream, CFWriteStreamRef)
144        self.assertResultIsBOOL(CFWriteStreamOpen)
145        r = CFWriteStreamOpen(stream)
146        self.assertIs(r, True)
147        status = CFWriteStreamGetStatus(stream)
148        self.assertIsInstance(status, (int, long))
149        self.assertEqual(status, kCFStreamStatusOpen)
150
151        self.assertResultIsBOOL(CFWriteStreamCanAcceptBytes)
152        b = CFWriteStreamCanAcceptBytes(stream)
153        self.assertIs(b, True)
154        self.assertArgHasType(CFWriteStreamWrite, 1, b'n^v')
155        self.assertArgSizeInArg(CFWriteStreamWrite, 1, 2)
156        n = CFWriteStreamWrite(stream, b"0123456789ABCDE", 15)
157        self.assertEqual(n, 15)
158
159        if sys.version_info[0] == 3:
160            self.assertEqual(bytes(a[0:1]), b'0')
161            self.assertEqual(bytes(a[1:2]), b'1')
162            self.assertEqual(bytes(a[9:10]), b'9')
163        else:
164            self.assertEqual((a[0]), ord('0'))
165            self.assertEqual((a[1]), ord('1'))
166            self.assertEqual((a[9]), ord('9'))
167
168        n = CFWriteStreamWrite(stream, b"0123456789ABCDE", 15)
169        self.assertEqual(n, -1)
170
171        err = CFWriteStreamCopyError(stream)
172        self.assertIsInstance(err, CFErrorRef)
173        err = CFWriteStreamGetError(stream)
174        self.assertIsInstance(err, CFStreamError)
175        self.assertEqual(err.domain , kCFStreamErrorDomainPOSIX)
176        self.assertEqual(err.error , errno.ENOMEM)
177        status = CFWriteStreamGetStatus(stream)
178        self.assertIsInstance(status, (int, long))
179        self.assertEqual(status, kCFStreamStatusError)
180
181
182        del stream
183
184
185        self.assertResultIsCFRetained(CFWriteStreamCreateWithAllocatedBuffers)
186        stream = CFWriteStreamCreateWithAllocatedBuffers(None, None)
187        self.assertIsInstance(stream, CFWriteStreamRef)
188        r = CFWriteStreamOpen(stream)
189        self.assertIs(r, True)
190        n = CFWriteStreamWrite(stream, b"0123456789ABCDE", 15)
191        self.assertEqual(n, 15)
192
193        self.assertResultIsCFRetained(CFWriteStreamCopyProperty)
194        buf = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten)
195        self.assertIsInstance(buf, CFDataRef)
196        buf = CFDataGetBytes(buf, (0, CFDataGetLength(buf)), None)
197        self.assertIsInstance(buf, bytes)
198        self.assertEqual(buf, b'0123456789ABCDE')
199
200        CFWriteStreamClose(stream)
201        status = CFWriteStreamGetStatus(stream)
202        self.assertIsInstance(status, (int, long))
203        self.assertEqual(status, kCFStreamStatusClosed)
204
205        del stream
206
207
208        stream = CFWriteStreamCreateWithFile(None,
209                CFURLCreateWithString(None, u"file:///tmp/pyobjc.test.txt", None))
210        self.assertIsInstance(stream, CFWriteStreamRef)
211        r = CFWriteStreamOpen(stream)
212        self.assertIs(r, True)
213        n = CFWriteStreamWrite(stream, b"0123456789ABCDE", 15)
214        self.assertEqual(n, 15)
215
216        self.assertResultIsCFRetained(CFReadStreamCopyProperty)
217        val = CFReadStreamCopyProperty(stream, kCFStreamPropertyFileCurrentOffset)
218        self.assertEqual(val, 15)
219
220        self.assertResultIsBOOL(CFReadStreamSetProperty)
221        r = CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, 10)
222        self.assertIs(r, True)
223        val = CFReadStreamCopyProperty(stream, kCFStreamPropertyFileCurrentOffset)
224        self.assertEqual(val, 10)
225
226        CFWriteStreamClose(stream)
227        status = CFWriteStreamGetStatus(stream)
228        self.assertIsInstance(status, (int, long))
229        self.assertEqual(status, kCFStreamStatusClosed)
230
231        self.assertResultIsBOOL(CFWriteStreamSetProperty)
232        CFWriteStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, 0)
233
234        data = open('/tmp/pyobjc.test.txt', 'rb').read()
235        self.assertEqual(data, b'0123456789ABCDE')
236        os.unlink('/tmp/pyobjc.test.txt')
237
238    def testStreamPair(self):
239
240        self.assertArgIsOut(CFStreamCreateBoundPair, 1)
241        self.assertArgIsOut(CFStreamCreateBoundPair, 2)
242        readStream, writeStream = CFStreamCreateBoundPair(None, None, None, 1024*1024)
243        self.assertIsInstance(readStream, CFReadStreamRef)
244        self.assertIsInstance(writeStream, CFWriteStreamRef)
245        # Make sure we actually have streams instead of random pointers.
246        status = CFReadStreamGetStatus(readStream)
247        self.assertIsInstance(status, (int, long))
248        self.assertEqual(status, kCFStreamStatusNotOpen)
249
250        status = CFWriteStreamGetStatus(writeStream)
251        self.assertIsInstance(status, (int, long))
252        self.assertEqual(status, kCFStreamStatusNotOpen)
253
254        del readStream, writeStream
255
256    @onlyIf(onTheNetwork)
257    def testSockets(self):
258        sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
259        sd.connect(('www.apple.com', 80))
260
261        self.assertArgIsOut(CFStreamCreatePairWithSocket, 2)
262        self.assertArgIsOut(CFStreamCreatePairWithSocket, 3)
263        readStream, writeStream = CFStreamCreatePairWithSocket(None,
264                sd.fileno(), None, None)
265
266        status = CFReadStreamGetStatus(readStream)
267        self.assertIsInstance(status, (int, long))
268        self.assertEqual(status, kCFStreamStatusNotOpen)
269
270        status = CFWriteStreamGetStatus(writeStream)
271        self.assertIsInstance(status, (int, long))
272        self.assertEqual(status, kCFStreamStatusNotOpen)
273
274        del readStream, writeStream, sd
275
276        self.assertArgIsOut(CFStreamCreatePairWithSocketToHost, 3)
277        self.assertArgIsOut(CFStreamCreatePairWithSocketToHost, 4)
278        readStream, writeStream = CFStreamCreatePairWithSocketToHost(None,
279                "www.apple.com", 80, None, None)
280
281        status = CFReadStreamGetStatus(readStream)
282        self.assertIsInstance(status, (int, long))
283        self.assertEqual(status, kCFStreamStatusNotOpen)
284
285        status = CFWriteStreamGetStatus(writeStream)
286        self.assertIsInstance(status, (int, long))
287        self.assertEqual(status, kCFStreamStatusNotOpen)
288
289        del readStream, writeStream
290
291
292        # Note: I don't expect anyone to actually use this api, building
293        # struct sockaddr buffers by hand is madness in python.
294        ip = socket.gethostbyname('www.apple.com')
295        ip = map(int, ip.split('.'))
296
297        import struct
298        sockaddr = struct.pack('>BBHBBBB', 16, socket.AF_INET, 80, *ip)
299
300        if sys.version_info[0] == 3:
301            sockaddr_buffer = sockaddr
302        else:
303            sockaddr_buffer = buffer(sockaddr)
304
305        signature = CFSocketSignature(
306                protocolFamily=socket.AF_INET,
307                socketType=socket.SOCK_STREAM,
308                protocol=0,
309                address=sockaddr_buffer)
310
311        self.assertArgIsOut(CFStreamCreatePairWithPeerSocketSignature, 2)
312        self.assertArgIsOut(CFStreamCreatePairWithPeerSocketSignature, 3)
313        readStream, writeStream = CFStreamCreatePairWithPeerSocketSignature(
314                None, signature, None, None)
315
316        self.assertResultIsCFRetained(CFWriteStreamCopyError)
317        status = CFReadStreamGetStatus(readStream)
318        self.assertIsInstance(status, (int, long))
319        self.assertEqual(status, kCFStreamStatusNotOpen)
320
321        status = CFWriteStreamGetStatus(writeStream)
322        self.assertIsInstance(status, (int, long))
323        self.assertEqual(status, kCFStreamStatusNotOpen)
324
325    def testReadSocketASync(self):
326        rl = CFRunLoopGetCurrent()
327
328        strval = b"hello world"
329        readStream = CFReadStreamCreateWithBytesNoCopy(None,
330                strval, len(strval), kCFAllocatorNull)
331        self.assertIsInstance(readStream, CFReadStreamRef)
332        data = {}
333
334        state = []
335        def callback(stream, kind, info):
336            state.append((stream, kind, info))
337
338        status = CFReadStreamGetStatus(readStream)
339        self.assertIsInstance(status, (int, long))
340        self.assertEqual(status, kCFStreamStatusNotOpen)
341
342        CFReadStreamOpen(readStream)
343
344        ok = CFReadStreamSetClient(readStream,
345                kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
346                callback, data)
347        self.assertTrue(ok)
348
349        CFReadStreamScheduleWithRunLoop(readStream, rl, kCFRunLoopDefaultMode)
350        try:
351            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, True)
352            CFRunLoopWakeUp(rl)
353        finally:
354            CFReadStreamClose(readStream)
355            CFReadStreamUnscheduleFromRunLoop(readStream, rl, kCFRunLoopDefaultMode)
356            ok = CFReadStreamSetClient(readStream,
357                kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
358                callback, objc.NULL)
359            self.assertTrue(ok)
360
361        self.assertEqual(len(state) , 1)
362        self.assertIs(state[0][0], readStream)
363        self.assertIs(state[0][2], data)
364        self.assertEqual(state[0][1], kCFStreamEventHasBytesAvailable)
365
366
367    def testWriteSocketAsync(self):
368        rl = CFRunLoopGetCurrent()
369
370        import array
371        a = array.array('b', b" "*20)
372
373        writeStream = CFWriteStreamCreateWithBuffer(None, a, 20)
374        self.assertIsInstance(writeStream, CFWriteStreamRef)
375        r = CFWriteStreamOpen(writeStream)
376        self.assertIs(r, True)
377        data = {}
378        state = []
379        def callback(stream, kind, info):
380            state.append((stream, kind, info))
381
382        ok = CFWriteStreamSetClient(writeStream,
383                kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
384                callback, data)
385        self.assertTrue(ok)
386
387        CFWriteStreamScheduleWithRunLoop(writeStream, rl, kCFRunLoopDefaultMode)
388        try:
389            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, True)
390            CFRunLoopWakeUp(rl)
391        finally:
392            CFWriteStreamClose(writeStream)
393            CFWriteStreamUnscheduleFromRunLoop(writeStream, rl, kCFRunLoopDefaultMode)
394            ok = CFWriteStreamSetClient(writeStream,
395                kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
396                callback, objc.NULL)
397            self.assertTrue(ok)
398
399        self.assertEqual(len(state) , 1)
400        self.assertIs(state[0][0], writeStream)
401        self.assertIs(state[0][2], data)
402        self.assertEqual(state[0][1], kCFStreamEventCanAcceptBytes)
403
404if __name__ == "__main__":
405    main()
406