1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3# vi:ts=4:et
4
5from . import localhost
6import flaky
7import pycurl
8import unittest, signal
9import time as _time
10
11from . import appmanager
12from . import util
13
14setup_module, teardown_module = appmanager.setup(('app', 8380))
15
16@flaky.flaky(max_runs=3)
17class PauseTest(unittest.TestCase):
18    def setUp(self):
19        self.curl = util.DefaultCurl()
20
21    def tearDown(self):
22        self.curl.close()
23
24    def test_pause_via_call(self):
25        self.check_pause(True)
26
27    def test_pause_via_return(self):
28        self.check_pause(False)
29
30    @util.only_unix
31    def check_pause(self, call):
32        # the app sleeps for 0.5 seconds
33        self.curl.setopt(pycurl.URL, 'http://%s:8380/pause' % localhost)
34        sio = util.BytesIO()
35        state = dict(paused=False, resumed=False)
36        if call:
37            def writefunc(data):
38                rv = sio.write(data)
39                if not state['paused']:
40                    self.curl.pause(pycurl.PAUSE_ALL)
41                    state['paused'] = True
42                return rv
43        else:
44            def writefunc(data):
45                if not state['paused']:
46                    # cannot write to sio here, because
47                    # curl takes pause return value to mean that
48                    # nothing was written
49                    state['paused'] = True
50                    return pycurl.READFUNC_PAUSE
51                else:
52                    return sio.write(data)
53        def resume(*args):
54            state['resumed'] = True
55            self.curl.pause(pycurl.PAUSE_CONT)
56        signal.signal(signal.SIGALRM, resume)
57        # alarm for 1 second which is 0.5 seconds more than the server side
58        # should sleep for
59        signal.alarm(1)
60        start = _time.time()
61        self.curl.setopt(pycurl.WRITEFUNCTION, writefunc)
62
63        m = pycurl.CurlMulti()
64        m.add_handle(self.curl)
65
66        # Number of seconds to wait for a timeout to happen
67        SELECT_TIMEOUT = 1.0
68
69        # Stir the state machine into action
70        while 1:
71            ret, num_handles = m.perform()
72            if ret != pycurl.E_CALL_MULTI_PERFORM:
73                break
74
75        # Keep going until all the connections have terminated
76        while num_handles:
77            # The select method uses fdset internally to determine which file descriptors
78            # to check.
79            m.select(SELECT_TIMEOUT)
80            while 1:
81                if _time.time() - start > 2:
82                    # test is taking too long, fail
83                    assert False, 'Test is taking too long'
84                ret, num_handles = m.perform()
85                if ret != pycurl.E_CALL_MULTI_PERFORM:
86                    break
87
88        # Cleanup
89        m.remove_handle(self.curl)
90        m.close()
91
92        self.assertEqual('part1part2', sio.getvalue().decode())
93        end = _time.time()
94        # check that client side waited
95        self.assertTrue(end-start > 1)
96
97        assert state['resumed']
98