xref: /unit/test/test_python_procman.py (revision 1775:4b4991514356)
1import re
2import subprocess
3import time
4
5import pytest
6from unit.applications.lang.python import TestApplicationPython
7from unit.option import option
8
9
10class TestPythonProcman(TestApplicationPython):
11    prerequisites = {'modules': {'python': 'any'}}
12
13    def setup_method(self):
14        self.app_name = "app-" + option.temp_dir.split('/')[-1]
15        self.app_proc = 'applications/' + self.app_name + '/processes'
16        self.load('empty', self.app_name)
17
18    def pids_for_process(self):
19        time.sleep(0.2)
20
21        output = subprocess.check_output(['ps', 'ax'])
22
23        pids = set()
24        for m in re.findall('.*' + self.app_name, output.decode()):
25            pids.add(re.search(r'^\s*(\d+)', m).group(1))
26
27        return pids
28
29    def conf_proc(self, conf, path=None):
30        if path is None:
31            path = self.app_proc
32
33        assert 'success' in self.conf(conf, path), 'configure processes'
34
35    @pytest.mark.skip('not yet')
36    def test_python_processes_idle_timeout_zero(self):
37        self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0})
38
39        self.get()
40        assert len(self.pids_for_process()) == 0, 'idle timeout 0'
41
42    def test_python_prefork(self):
43        self.conf_proc('2')
44
45        pids = self.pids_for_process()
46        assert len(pids) == 2, 'prefork 2'
47
48        self.get()
49        assert self.pids_for_process() == pids, 'prefork still 2'
50
51        self.conf_proc('4')
52
53        pids = self.pids_for_process()
54        assert len(pids) == 4, 'prefork 4'
55
56        self.get()
57        assert self.pids_for_process() == pids, 'prefork still 4'
58
59        self.stop_all()
60
61    @pytest.mark.skip('not yet')
62    def test_python_prefork_same_processes(self):
63        self.conf_proc('2')
64        pids = self.pids_for_process()
65
66        self.conf_proc('4')
67        pids_new = self.pids_for_process()
68
69        assert pids.issubset(pids_new), 'prefork same processes'
70
71    def test_python_ondemand(self):
72        self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1})
73
74        assert len(self.pids_for_process()) == 0, 'on-demand 0'
75
76        self.get()
77        pids = self.pids_for_process()
78        assert len(pids) == 1, 'on-demand 1'
79
80        self.get()
81        assert self.pids_for_process() == pids, 'on-demand still 1'
82
83        time.sleep(1)
84
85        assert len(self.pids_for_process()) == 0, 'on-demand stop idle'
86
87        self.stop_all()
88
89    def test_python_scale_updown(self):
90        self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1})
91
92        pids = self.pids_for_process()
93        assert len(pids) == 2, 'updown 2'
94
95        self.get()
96        pids_new = self.pids_for_process()
97        assert len(pids_new) == 3, 'updown 3'
98        assert pids.issubset(pids_new), 'updown 3 only 1 new'
99
100        self.get()
101        assert self.pids_for_process() == pids_new, 'updown still 3'
102
103        time.sleep(1)
104
105        pids = self.pids_for_process()
106        assert len(pids) == 2, 'updown stop idle'
107
108        self.get()
109        pids_new = self.pids_for_process()
110        assert len(pids_new) == 3, 'updown again 3'
111        assert pids.issubset(pids_new), 'updown again 3 only 1 new'
112
113        self.stop_all()
114
115    def test_python_reconfigure(self):
116        self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1})
117
118        pids = self.pids_for_process()
119        assert len(pids) == 2, 'reconf 2'
120
121        self.get()
122        pids_new = self.pids_for_process()
123        assert len(pids_new) == 3, 'reconf 3'
124        assert pids.issubset(pids_new), 'reconf 3 only 1 new'
125
126        self.conf_proc('6', self.app_proc + '/spare')
127
128        pids = self.pids_for_process()
129        assert len(pids) == 6, 'reconf 6'
130
131        self.get()
132        assert self.pids_for_process() == pids, 'reconf still 6'
133
134        self.stop_all()
135
136    def test_python_idle_timeout(self):
137        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
138
139        self.get()
140        pids = self.pids_for_process()
141        assert len(pids) == 1, 'idle timeout 1'
142
143        time.sleep(1)
144
145        self.get()
146
147        time.sleep(1)
148
149        pids_new = self.pids_for_process()
150        assert len(pids_new) == 1, 'idle timeout still 1'
151        assert self.pids_for_process() == pids, 'idle timeout still 1 same pid'
152
153        time.sleep(1)
154
155        assert len(self.pids_for_process()) == 0, 'idle timed out'
156
157    def test_python_processes_connection_keepalive(self):
158        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
159
160        (resp, sock) = self.get(
161            headers={'Host': 'localhost', 'Connection': 'keep-alive'},
162            start=True,
163            read_timeout=1,
164        )
165        assert len(self.pids_for_process()) == 1, 'keepalive connection 1'
166
167        time.sleep(2)
168
169        assert len(self.pids_for_process()) == 0, 'keepalive connection 0'
170
171        sock.close()
172
173    def test_python_processes_access(self):
174        self.conf_proc('1')
175
176        path = '/' + self.app_proc
177        assert 'error' in self.conf_get(path + '/max')
178        assert 'error' in self.conf_get(path + '/spare')
179        assert 'error' in self.conf_get(path + '/idle_timeout')
180
181    def test_python_processes_invalid(self):
182        assert 'error' in self.conf(
183            {"spare": -1}, self.app_proc
184        ), 'negative spare'
185        assert 'error' in self.conf({"max": -1}, self.app_proc), 'negative max'
186        assert 'error' in self.conf(
187            {"idle_timeout": -1}, self.app_proc
188        ), 'negative idle_timeout'
189        assert 'error' in self.conf(
190            {"spare": 2}, self.app_proc
191        ), 'spare gt max default'
192        assert 'error' in self.conf(
193            {"spare": 2, "max": 1}, self.app_proc
194        ), 'spare gt max'
195        assert 'error' in self.conf(
196            {"spare": 0, "max": 0}, self.app_proc
197        ), 'max zero'
198
199    def stop_all(self):
200        assert 'success' in self.conf({"listeners": {}, "applications": {}})
201
202        assert len(self.pids_for_process()) == 0, 'stop all'
203