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