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