From cabea47de7287ea421317c92bc868c2d451cb757 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Wed, 7 Feb 2024 16:29:11 +0000 Subject: Tests: renamed test_python_procman.py since it's not Python-specific Python applications are used only to generate responses here and can be replaced by applications written in any other language. While the "_python" prefix is used to indicate that the file contains tests specific to the Python module. --- test/test_procman.py | 303 ++++++++++++++++++++++++++++++++++++++++++++ test/test_python_procman.py | 303 -------------------------------------------- 2 files changed, 303 insertions(+), 303 deletions(-) create mode 100644 test/test_procman.py delete mode 100644 test/test_python_procman.py (limited to 'test') diff --git a/test/test_procman.py b/test/test_procman.py new file mode 100644 index 00000000..b4378c4f --- /dev/null +++ b/test/test_procman.py @@ -0,0 +1,303 @@ +import re +import shutil +import subprocess +import time + +import pytest + +from unit.applications.lang.python import ApplicationPython +from unit.option import option + +prerequisites = {'modules': {'python': 'any'}} + +client = ApplicationPython() + + +@pytest.fixture(autouse=True) +def setup_method_fixture(temp_dir): + client.app_name = f'app-{temp_dir.split("/")[-1]}' + client.app_proc = f'applications/{client.app_name}/processes' + client.load('empty', client.app_name) + + +def pids_for_process(): + time.sleep(0.2) + + output = subprocess.check_output(['ps', 'ax']) + + pids = set() + for m in re.findall( + fr'.*unit: "{client.app_name}" application', output.decode() + ): + pids.add(re.search(r'^\s*(\d+)', m).group(1)) + + return pids + + +def conf_proc(conf, path=None): + if path is None: + path = client.app_proc + + assert 'success' in client.conf(conf, path), 'configure processes' + + +def stop_all(): + assert 'success' in client.conf({"listeners": {}, "applications": {}}) + + assert len(pids_for_process()) == 0, 'stop all' + + +@pytest.mark.skip('not yet') +def test_python_processes_idle_timeout_zero(): + conf_proc({"spare": 0, "max": 2, "idle_timeout": 0}) + + client.get() + assert len(pids_for_process()) == 0, 'idle timeout 0' + + +def test_python_prefork(): + conf_proc('2') + + pids = pids_for_process() + assert len(pids) == 2, 'prefork 2' + + client.get() + assert pids_for_process() == pids, 'prefork still 2' + + conf_proc('4') + + pids = pids_for_process() + assert len(pids) == 4, 'prefork 4' + + client.get() + assert pids_for_process() == pids, 'prefork still 4' + + stop_all() + + +@pytest.mark.skip('not yet') +def test_python_prefork_same_processes(): + conf_proc('2') + pids = pids_for_process() + + conf_proc('4') + pids_new = pids_for_process() + + assert pids.issubset(pids_new), 'prefork same processes' + + +def test_python_ondemand(): + conf_proc({"spare": 0, "max": 8, "idle_timeout": 1}) + + assert len(pids_for_process()) == 0, 'on-demand 0' + + client.get() + pids = pids_for_process() + assert len(pids) == 1, 'on-demand 1' + + client.get() + assert pids_for_process() == pids, 'on-demand still 1' + + time.sleep(1) + + assert len(pids_for_process()) == 0, 'on-demand stop idle' + + stop_all() + + +def test_python_scale_updown(): + conf_proc({"spare": 2, "max": 8, "idle_timeout": 1}) + + pids = pids_for_process() + assert len(pids) == 2, 'updown 2' + + client.get() + pids_new = pids_for_process() + assert len(pids_new) == 3, 'updown 3' + assert pids.issubset(pids_new), 'updown 3 only 1 new' + + client.get() + assert pids_for_process() == pids_new, 'updown still 3' + + time.sleep(1) + + pids = pids_for_process() + assert len(pids) == 2, 'updown stop idle' + + client.get() + pids_new = pids_for_process() + assert len(pids_new) == 3, 'updown again 3' + assert pids.issubset(pids_new), 'updown again 3 only 1 new' + + stop_all() + + +def test_python_reconfigure(): + conf_proc({"spare": 2, "max": 6, "idle_timeout": 1}) + + pids = pids_for_process() + assert len(pids) == 2, 'reconf 2' + + client.get() + pids_new = pids_for_process() + assert len(pids_new) == 3, 'reconf 3' + assert pids.issubset(pids_new), 'reconf 3 only 1 new' + + conf_proc('6', f'{client.app_proc}/spare') + + pids = pids_for_process() + assert len(pids) == 6, 'reconf 6' + + client.get() + assert pids_for_process() == pids, 'reconf still 6' + + stop_all() + + +def test_python_idle_timeout(): + conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) + + client.get() + pids = pids_for_process() + assert len(pids) == 1, 'idle timeout 1' + + time.sleep(1) + + client.get() + + time.sleep(1) + + pids_new = pids_for_process() + assert len(pids_new) == 1, 'idle timeout still 1' + assert pids_for_process() == pids, 'idle timeout still 1 same pid' + + time.sleep(1) + + assert len(pids_for_process()) == 0, 'idle timed out' + + +def test_python_processes_connection_keepalive(): + conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) + + (_, sock) = client.get( + headers={'Host': 'localhost', 'Connection': 'keep-alive'}, + start=True, + read_timeout=1, + ) + assert len(pids_for_process()) == 1, 'keepalive connection 1' + + time.sleep(2) + + assert len(pids_for_process()) == 0, 'keepalive connection 0' + + sock.close() + + +def test_python_processes_access(): + conf_proc('1') + + path = f'/{client.app_proc}' + assert 'error' in client.conf_get(f'{path}/max') + assert 'error' in client.conf_get(f'{path}/spare') + assert 'error' in client.conf_get(f'{path}/idle_timeout') + + +def test_python_processes_invalid(): + assert 'error' in client.conf( + {"spare": -1}, client.app_proc + ), 'negative spare' + assert 'error' in client.conf({"max": -1}, client.app_proc), 'negative max' + assert 'error' in client.conf( + {"idle_timeout": -1}, client.app_proc + ), 'negative idle_timeout' + assert 'error' in client.conf( + {"spare": 2}, client.app_proc + ), 'spare gt max default' + assert 'error' in client.conf( + {"spare": 2, "max": 1}, client.app_proc + ), 'spare gt max' + assert 'error' in client.conf( + {"spare": 0, "max": 0}, client.app_proc + ), 'max zero' + + +def test_python_restart(temp_dir): + shutil.copyfile( + f'{option.test_dir}/python/restart/v1.py', f'{temp_dir}/wsgi.py' + ) + + client.load( + temp_dir, + name=client.app_name, + processes=1, + environment={'PYTHONDONTWRITEBYTECODE': '1'}, + ) + + b = client.get()['body'] + assert b == "v1", 'process started' + + shutil.copyfile( + f'{option.test_dir}/python/restart/v2.py', f'{temp_dir}/wsgi.py' + ) + + b = client.get()['body'] + assert b == "v1", 'still old process' + + assert 'success' in client.conf_get( + f'/control/applications/{client.app_name}/restart' + ), 'restart processes' + + b = client.get()['body'] + assert b == "v2", 'new process started' + + assert 'error' in client.conf_get( + '/control/applications/blah/restart' + ), 'application incorrect' + + assert 'error' in client.conf_delete( + f'/control/applications/{client.app_name}/restart' + ), 'method incorrect' + + +def test_python_restart_multi(): + conf_proc('2') + + pids = pids_for_process() + assert len(pids) == 2, 'restart 2 started' + + assert 'success' in client.conf_get( + f'/control/applications/{client.app_name}/restart' + ), 'restart processes' + + new_pids = pids_for_process() + assert len(new_pids) == 2, 'restart still 2' + + assert len(new_pids.intersection(pids)) == 0, 'restart all new' + + +def test_python_restart_longstart(): + client.load( + 'restart', + name=client.app_name, + module="longstart", + processes={"spare": 1, "max": 2, "idle_timeout": 5}, + ) + + assert len(pids_for_process()) == 1, 'longstarts == 1' + + client.get() + + pids = pids_for_process() + assert len(pids) == 2, 'longstarts == 2' + + assert 'success' in client.conf_get( + f'/control/applications/{client.app_name}/restart' + ), 'restart processes' + + # wait for longstarted app + time.sleep(2) + + new_pids = pids_for_process() + assert len(new_pids) == 1, 'restart 1' + + assert len(new_pids.intersection(pids)) == 0, 'restart all new' diff --git a/test/test_python_procman.py b/test/test_python_procman.py deleted file mode 100644 index b4378c4f..00000000 --- a/test/test_python_procman.py +++ /dev/null @@ -1,303 +0,0 @@ -import re -import shutil -import subprocess -import time - -import pytest - -from unit.applications.lang.python import ApplicationPython -from unit.option import option - -prerequisites = {'modules': {'python': 'any'}} - -client = ApplicationPython() - - -@pytest.fixture(autouse=True) -def setup_method_fixture(temp_dir): - client.app_name = f'app-{temp_dir.split("/")[-1]}' - client.app_proc = f'applications/{client.app_name}/processes' - client.load('empty', client.app_name) - - -def pids_for_process(): - time.sleep(0.2) - - output = subprocess.check_output(['ps', 'ax']) - - pids = set() - for m in re.findall( - fr'.*unit: "{client.app_name}" application', output.decode() - ): - pids.add(re.search(r'^\s*(\d+)', m).group(1)) - - return pids - - -def conf_proc(conf, path=None): - if path is None: - path = client.app_proc - - assert 'success' in client.conf(conf, path), 'configure processes' - - -def stop_all(): - assert 'success' in client.conf({"listeners": {}, "applications": {}}) - - assert len(pids_for_process()) == 0, 'stop all' - - -@pytest.mark.skip('not yet') -def test_python_processes_idle_timeout_zero(): - conf_proc({"spare": 0, "max": 2, "idle_timeout": 0}) - - client.get() - assert len(pids_for_process()) == 0, 'idle timeout 0' - - -def test_python_prefork(): - conf_proc('2') - - pids = pids_for_process() - assert len(pids) == 2, 'prefork 2' - - client.get() - assert pids_for_process() == pids, 'prefork still 2' - - conf_proc('4') - - pids = pids_for_process() - assert len(pids) == 4, 'prefork 4' - - client.get() - assert pids_for_process() == pids, 'prefork still 4' - - stop_all() - - -@pytest.mark.skip('not yet') -def test_python_prefork_same_processes(): - conf_proc('2') - pids = pids_for_process() - - conf_proc('4') - pids_new = pids_for_process() - - assert pids.issubset(pids_new), 'prefork same processes' - - -def test_python_ondemand(): - conf_proc({"spare": 0, "max": 8, "idle_timeout": 1}) - - assert len(pids_for_process()) == 0, 'on-demand 0' - - client.get() - pids = pids_for_process() - assert len(pids) == 1, 'on-demand 1' - - client.get() - assert pids_for_process() == pids, 'on-demand still 1' - - time.sleep(1) - - assert len(pids_for_process()) == 0, 'on-demand stop idle' - - stop_all() - - -def test_python_scale_updown(): - conf_proc({"spare": 2, "max": 8, "idle_timeout": 1}) - - pids = pids_for_process() - assert len(pids) == 2, 'updown 2' - - client.get() - pids_new = pids_for_process() - assert len(pids_new) == 3, 'updown 3' - assert pids.issubset(pids_new), 'updown 3 only 1 new' - - client.get() - assert pids_for_process() == pids_new, 'updown still 3' - - time.sleep(1) - - pids = pids_for_process() - assert len(pids) == 2, 'updown stop idle' - - client.get() - pids_new = pids_for_process() - assert len(pids_new) == 3, 'updown again 3' - assert pids.issubset(pids_new), 'updown again 3 only 1 new' - - stop_all() - - -def test_python_reconfigure(): - conf_proc({"spare": 2, "max": 6, "idle_timeout": 1}) - - pids = pids_for_process() - assert len(pids) == 2, 'reconf 2' - - client.get() - pids_new = pids_for_process() - assert len(pids_new) == 3, 'reconf 3' - assert pids.issubset(pids_new), 'reconf 3 only 1 new' - - conf_proc('6', f'{client.app_proc}/spare') - - pids = pids_for_process() - assert len(pids) == 6, 'reconf 6' - - client.get() - assert pids_for_process() == pids, 'reconf still 6' - - stop_all() - - -def test_python_idle_timeout(): - conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) - - client.get() - pids = pids_for_process() - assert len(pids) == 1, 'idle timeout 1' - - time.sleep(1) - - client.get() - - time.sleep(1) - - pids_new = pids_for_process() - assert len(pids_new) == 1, 'idle timeout still 1' - assert pids_for_process() == pids, 'idle timeout still 1 same pid' - - time.sleep(1) - - assert len(pids_for_process()) == 0, 'idle timed out' - - -def test_python_processes_connection_keepalive(): - conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) - - (_, sock) = client.get( - headers={'Host': 'localhost', 'Connection': 'keep-alive'}, - start=True, - read_timeout=1, - ) - assert len(pids_for_process()) == 1, 'keepalive connection 1' - - time.sleep(2) - - assert len(pids_for_process()) == 0, 'keepalive connection 0' - - sock.close() - - -def test_python_processes_access(): - conf_proc('1') - - path = f'/{client.app_proc}' - assert 'error' in client.conf_get(f'{path}/max') - assert 'error' in client.conf_get(f'{path}/spare') - assert 'error' in client.conf_get(f'{path}/idle_timeout') - - -def test_python_processes_invalid(): - assert 'error' in client.conf( - {"spare": -1}, client.app_proc - ), 'negative spare' - assert 'error' in client.conf({"max": -1}, client.app_proc), 'negative max' - assert 'error' in client.conf( - {"idle_timeout": -1}, client.app_proc - ), 'negative idle_timeout' - assert 'error' in client.conf( - {"spare": 2}, client.app_proc - ), 'spare gt max default' - assert 'error' in client.conf( - {"spare": 2, "max": 1}, client.app_proc - ), 'spare gt max' - assert 'error' in client.conf( - {"spare": 0, "max": 0}, client.app_proc - ), 'max zero' - - -def test_python_restart(temp_dir): - shutil.copyfile( - f'{option.test_dir}/python/restart/v1.py', f'{temp_dir}/wsgi.py' - ) - - client.load( - temp_dir, - name=client.app_name, - processes=1, - environment={'PYTHONDONTWRITEBYTECODE': '1'}, - ) - - b = client.get()['body'] - assert b == "v1", 'process started' - - shutil.copyfile( - f'{option.test_dir}/python/restart/v2.py', f'{temp_dir}/wsgi.py' - ) - - b = client.get()['body'] - assert b == "v1", 'still old process' - - assert 'success' in client.conf_get( - f'/control/applications/{client.app_name}/restart' - ), 'restart processes' - - b = client.get()['body'] - assert b == "v2", 'new process started' - - assert 'error' in client.conf_get( - '/control/applications/blah/restart' - ), 'application incorrect' - - assert 'error' in client.conf_delete( - f'/control/applications/{client.app_name}/restart' - ), 'method incorrect' - - -def test_python_restart_multi(): - conf_proc('2') - - pids = pids_for_process() - assert len(pids) == 2, 'restart 2 started' - - assert 'success' in client.conf_get( - f'/control/applications/{client.app_name}/restart' - ), 'restart processes' - - new_pids = pids_for_process() - assert len(new_pids) == 2, 'restart still 2' - - assert len(new_pids.intersection(pids)) == 0, 'restart all new' - - -def test_python_restart_longstart(): - client.load( - 'restart', - name=client.app_name, - module="longstart", - processes={"spare": 1, "max": 2, "idle_timeout": 5}, - ) - - assert len(pids_for_process()) == 1, 'longstarts == 1' - - client.get() - - pids = pids_for_process() - assert len(pids) == 2, 'longstarts == 2' - - assert 'success' in client.conf_get( - f'/control/applications/{client.app_name}/restart' - ), 'restart processes' - - # wait for longstarted app - time.sleep(2) - - new_pids = pids_for_process() - assert len(new_pids) == 1, 'restart 1' - - assert len(new_pids.intersection(pids)) == 0, 'restart all new' -- cgit