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