xref: /unit/test/test_python_procman.py (revision 1453:71af60a59338)
1import re
2import time
3import subprocess
4import unittest
5from unit.applications.lang.python import TestApplicationPython
6
7
8class TestPythonProcman(TestApplicationPython):
9    prerequisites = {'modules': ['python']}
10
11    def setUp(self):
12        super().setUp()
13
14        self.app_name = "app-" + self.testdir.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('^\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        self.assertIn('success', self.conf(conf, path), 'configure processes')
34
35    def test_python_processes_idle_timeout_zero(self):
36        self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0})
37
38        self.get()
39        self.assertEqual(len(self.pids_for_process()), 0, 'idle timeout 0')
40
41    def test_python_prefork(self):
42        self.conf_proc('2')
43
44        pids = self.pids_for_process()
45        self.assertEqual(len(pids), 2, 'prefork 2')
46
47        self.get()
48        self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 2')
49
50        self.conf_proc('4')
51
52        pids = self.pids_for_process()
53        self.assertEqual(len(pids), 4, 'prefork 4')
54
55        self.get()
56        self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 4')
57
58        self.stop_all()
59
60    @unittest.skip('not yet')
61    def test_python_prefork_same_processes(self):
62        self.conf_proc('2')
63        pids = self.pids_for_process()
64
65        self.conf_proc('4')
66        pids_new = self.pids_for_process()
67
68        self.assertTrue(pids.issubset(pids_new), 'prefork same processes')
69
70    def test_python_ondemand(self):
71        self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1})
72
73        self.assertEqual(len(self.pids_for_process()), 0, 'on-demand 0')
74
75        self.get()
76        pids = self.pids_for_process()
77        self.assertEqual(len(pids), 1, 'on-demand 1')
78
79        self.get()
80        self.assertSetEqual(self.pids_for_process(), pids, 'on-demand still 1')
81
82        time.sleep(1)
83
84        self.assertEqual(
85            len(self.pids_for_process()), 0, 'on-demand stop idle'
86        )
87
88        self.stop_all()
89
90    def test_python_scale_updown(self):
91        self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1})
92
93        pids = self.pids_for_process()
94        self.assertEqual(len(pids), 2, 'updown 2')
95
96        self.get()
97        pids_new = self.pids_for_process()
98        self.assertEqual(len(pids_new), 3, 'updown 3')
99        self.assertTrue(pids.issubset(pids_new), 'updown 3 only 1 new')
100
101        self.get()
102        self.assertSetEqual(
103            self.pids_for_process(), pids_new, 'updown still 3'
104        )
105
106        time.sleep(1)
107
108        pids = self.pids_for_process()
109        self.assertEqual(len(pids), 2, 'updown stop idle')
110
111        self.get()
112        pids_new = self.pids_for_process()
113        self.assertEqual(len(pids_new), 3, 'updown again 3')
114        self.assertTrue(pids.issubset(pids_new), 'updown again 3 only 1 new')
115
116        self.stop_all()
117
118    def test_python_reconfigure(self):
119        self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1})
120
121        pids = self.pids_for_process()
122        self.assertEqual(len(pids), 2, 'reconf 2')
123
124        self.get()
125        pids_new = self.pids_for_process()
126        self.assertEqual(len(pids_new), 3, 'reconf 3')
127        self.assertTrue(pids.issubset(pids_new), 'reconf 3 only 1 new')
128
129        self.conf_proc('6', self.app_proc + '/spare')
130
131        pids = self.pids_for_process()
132        self.assertEqual(len(pids), 6, 'reconf 6')
133
134        self.get()
135        self.assertSetEqual(self.pids_for_process(), pids, 'reconf still 6')
136
137        self.stop_all()
138
139    def test_python_idle_timeout(self):
140        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
141
142        self.get()
143        pids = self.pids_for_process()
144        self.assertEqual(len(pids), 1, 'idle timeout 1')
145
146        time.sleep(1)
147
148        self.get()
149
150        time.sleep(1)
151
152        pids_new = self.pids_for_process()
153        self.assertEqual(len(pids_new), 1, 'idle timeout still 1')
154        self.assertSetEqual(
155            self.pids_for_process(), pids, 'idle timeout still 1 same pid'
156        )
157
158        time.sleep(1)
159
160        self.assertEqual(len(self.pids_for_process()), 0, 'idle timed out')
161
162    def test_python_processes_connection_keepalive(self):
163        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
164
165        (resp, sock) = self.get(
166            headers={'Host': 'localhost', 'Connection': 'keep-alive'},
167            start=True,
168            read_timeout=1,
169        )
170        self.assertEqual(
171            len(self.pids_for_process()), 1, 'keepalive connection 1'
172        )
173
174        time.sleep(2)
175
176        self.assertEqual(
177            len(self.pids_for_process()), 0, 'keepalive connection 0'
178        )
179
180        sock.close()
181
182    def test_python_processes_access(self):
183        self.conf_proc('1')
184
185        path = '/' + self.app_proc
186        self.assertIn('error', self.conf_get(path + '/max'))
187        self.assertIn('error', self.conf_get(path + '/spare'))
188        self.assertIn('error', self.conf_get(path + '/idle_timeout'))
189
190    def test_python_processes_invalid(self):
191        self.assertIn(
192            'error', self.conf({"spare": -1}, self.app_proc), 'negative spare',
193        )
194        self.assertIn(
195            'error', self.conf({"max": -1}, self.app_proc), 'negative max',
196        )
197        self.assertIn(
198            'error',
199            self.conf({"idle_timeout": -1}, self.app_proc),
200            'negative idle_timeout',
201        )
202        self.assertIn(
203            'error',
204            self.conf({"spare": 2}, self.app_proc),
205            'spare gt max default',
206        )
207        self.assertIn(
208            'error',
209            self.conf({"spare": 2, "max": 1}, self.app_proc),
210            'spare gt max',
211        )
212        self.assertIn(
213            'error',
214            self.conf({"spare": 0, "max": 0}, self.app_proc),
215            'max zero',
216        )
217
218    def stop_all(self):
219        self.conf({"listeners": {}, "applications": {}})
220
221        self.assertEqual(len(self.pids_for_process()), 0, 'stop all')
222
223
224if __name__ == '__main__':
225    TestPythonProcman.main()
226