xref: /unit/test/test_python_procman.py (revision 1019:61d90b4da691)
1import re
2import time
3import subprocess
4import unittest
5from unit.applications.lang.python import TestApplicationPython
6
7
8class TestPythonProcman(TestApplicationPython):
9    def setUpClass():
10        TestApplicationPython().check_modules('python')
11
12    def pids_for_process(self):
13        time.sleep(0.2)
14
15        output = subprocess.check_output(['ps', 'ax'])
16
17        pids = set()
18        for m in re.findall('.*' + self.app_name, output.decode()):
19            pids.add(re.search('^\s*(\d+)', m).group(1))
20
21        return pids
22
23    def setUp(self):
24        super().setUp()
25
26        self.app_name = "app-" + self.testdir.split('/')[-1]
27        self.load('empty', self.app_name)
28
29    def test_python_processes_access(self):
30        self.conf('1', 'applications/' + self.app_name + '/processes')
31
32        self.assertIn(
33            'error',
34            self.conf_get('/applications/' + self.app_name + '/processes/max'),
35            'max no access',
36        )
37        self.assertIn(
38            'error',
39            self.conf_get(
40                '/applications/' + self.app_name + '/processes/spare'
41            ),
42            'spare no access',
43        )
44        self.assertIn(
45            'error',
46            self.conf_get(
47                '/applications/' + self.app_name + '/processes/idle_timeout'
48            ),
49            'idle_timeout no access',
50        )
51
52    def test_python_processes_spare_negative(self):
53        self.assertIn(
54            'error',
55            self.conf(
56                {"spare": -1}, 'applications/' + self.app_name + '/processes'
57            ),
58            'negative spare',
59        )
60
61    def test_python_processes_max_negative(self):
62        self.assertIn(
63            'error',
64            self.conf(
65                {"max": -1}, 'applications/' + self.app_name + '/processes'
66            ),
67            'negative max',
68        )
69
70    def test_python_processes_idle_timeout_negative(self):
71        self.assertIn(
72            'error',
73            self.conf(
74                {"idle_timeout": -1},
75                'applications/' + self.app_name + '/processes',
76            ),
77            'negative idle_timeout',
78        )
79
80    def test_python_processes_spare_gt_max_default(self):
81        self.assertIn(
82            'error',
83            self.conf(
84                {"spare": 2}, 'applications/' + self.app_name + '/processes'
85            ),
86            'spare greater than max default',
87        )
88
89    def test_python_processes_spare_gt_max(self):
90        self.assertIn(
91            'error',
92            self.conf(
93                {"spare": 2, "max": 1, "idle_timeout": 1},
94                '/applications/' + self.app_name + '/processes',
95            ),
96            'spare greater than max',
97        )
98
99    def test_python_processes_max_zero(self):
100        self.assertIn(
101            'error',
102            self.conf(
103                {"spare": 0, "max": 0, "idle_timeout": 1},
104                'applications/' + self.app_name + '/processes',
105            ),
106            'max 0',
107        )
108
109    def test_python_processes_idle_timeout_zero(self):
110        self.conf(
111            {"spare": 0, "max": 2, "idle_timeout": 0},
112            'applications/' + self.app_name + '/processes',
113        )
114
115        self.get()
116        self.assertEqual(len(self.pids_for_process()), 0, 'idle timeout 0')
117
118    def test_python_prefork(self):
119        self.conf('2', 'applications/' + self.app_name + '/processes')
120
121        pids = self.pids_for_process()
122        self.assertEqual(len(pids), 2, 'prefork 2')
123
124        self.get()
125        self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 2')
126
127        self.conf('4', 'applications/' + self.app_name + '/processes')
128
129        pids = self.pids_for_process()
130        self.assertEqual(len(pids), 4, 'prefork 4')
131
132        self.get()
133        self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 4')
134
135        self.stop_all()
136
137    @unittest.expectedFailure
138    def test_python_prefork_same_processes(self):
139        self.conf('2', 'applications/' + self.app_name + '/processes')
140
141        pids = self.pids_for_process()
142
143        self.conf('4', 'applications/' + self.app_name + '/processes')
144
145        pids_new = self.pids_for_process()
146
147        self.assertTrue(pids.issubset(pids_new), 'prefork same processes')
148
149    def test_python_ondemand(self):
150        self.conf(
151            {"spare": 0, "max": 8, "idle_timeout": 1},
152            'applications/' + self.app_name + '/processes',
153        )
154
155        self.assertEqual(len(self.pids_for_process()), 0, 'on-demand 0')
156
157        self.get()
158        pids = self.pids_for_process()
159        self.assertEqual(len(pids), 1, 'on-demand 1')
160
161        self.get()
162        self.assertSetEqual(self.pids_for_process(), pids, 'on-demand still 1')
163
164        time.sleep(1)
165
166        self.assertEqual(
167            len(self.pids_for_process()), 0, 'on-demand stop idle'
168        )
169
170        self.stop_all()
171
172    def test_python_scale_updown(self):
173        self.conf(
174            {"spare": 2, "max": 8, "idle_timeout": 1},
175            'applications/' + self.app_name + '/processes',
176        )
177
178        pids = self.pids_for_process()
179        self.assertEqual(len(pids), 2, 'updown 2')
180
181        self.get()
182        pids_new = self.pids_for_process()
183        self.assertEqual(len(pids_new), 3, 'updown 3')
184        self.assertTrue(pids.issubset(pids_new), 'updown 3 only 1 new')
185
186        self.get()
187        self.assertSetEqual(
188            self.pids_for_process(), pids_new, 'updown still 3'
189        )
190
191        time.sleep(1)
192
193        pids = self.pids_for_process()
194        self.assertEqual(len(pids), 2, 'updown stop idle')
195
196        self.get()
197        pids_new = self.pids_for_process()
198        self.assertEqual(len(pids_new), 3, 'updown again 3')
199        self.assertTrue(pids.issubset(pids_new), 'updown again 3 only 1 new')
200
201        self.stop_all()
202
203    def test_python_reconfigure(self):
204        self.conf(
205            {"spare": 2, "max": 6, "idle_timeout": 1},
206            'applications/' + self.app_name + '/processes',
207        )
208
209        pids = self.pids_for_process()
210        self.assertEqual(len(pids), 2, 'reconf 2')
211
212        self.get()
213        pids_new = self.pids_for_process()
214        self.assertEqual(len(pids_new), 3, 'reconf 3')
215        self.assertTrue(pids.issubset(pids_new), 'reconf 3 only 1 new')
216
217        self.conf('6', 'applications/' + self.app_name + '/processes/spare')
218
219        pids = self.pids_for_process()
220        self.assertEqual(len(pids), 6, 'reconf 6')
221
222        self.get()
223        self.assertSetEqual(self.pids_for_process(), pids, 'reconf still 6')
224
225        self.stop_all()
226
227    def test_python_idle_timeout(self):
228        self.conf(
229            {"spare": 0, "max": 6, "idle_timeout": 2},
230            'applications/' + self.app_name + '/processes',
231        )
232
233        self.get()
234        pids = self.pids_for_process()
235        self.assertEqual(len(pids), 1, 'idle timeout 1')
236
237        time.sleep(1)
238
239        self.get()
240
241        time.sleep(1)
242
243        pids_new = self.pids_for_process()
244        self.assertEqual(len(pids_new), 1, 'idle timeout still 1')
245        self.assertSetEqual(
246            self.pids_for_process(), pids, 'idle timeout still 1 same pid'
247        )
248
249        time.sleep(1)
250
251        self.assertEqual(len(self.pids_for_process()), 0, 'idle timed out')
252
253    def test_python_processes_connection_keepalive(self):
254        self.conf(
255            {"spare": 0, "max": 6, "idle_timeout": 2},
256            'applications/' + self.app_name + '/processes',
257        )
258
259        (resp, sock) = self.get(
260            headers={'Host': 'localhost', 'Connection': 'keep-alive'},
261            start=True,
262            read_timeout=1,
263        )
264        self.assertEqual(
265            len(self.pids_for_process()), 1, 'keepalive connection 1'
266        )
267
268        time.sleep(2)
269
270        self.assertEqual(
271            len(self.pids_for_process()), 0, 'keepalive connection 0'
272        )
273
274        sock.close()
275
276    def stop_all(self):
277        self.conf({"listeners": {}, "applications": {}})
278
279        self.assertEqual(len(self.pids_for_process()), 0, 'stop all')
280
281
282if __name__ == '__main__':
283    TestPythonProcman.main()
284