From 18b1579cfdeb2d038dbdec774ffbd80027114f69 Mon Sep 17 00:00:00 2001 From: Vladimir Druzhinin <128374224+StateOf-Vlado@users.noreply.github.com> Date: Tue, 22 Aug 2023 17:15:25 +0200 Subject: [PATCH] test(AUT Close) The closing AUT process by pid (#11955) #85 --- test/ui-pytest/configs/_local.py.ci | 1 - test/ui-pytest/configs/_local.py.default | 1 - test/ui-pytest/conftest.py | 1 - test/ui-pytest/driver/atomacos.py | 15 ++- test/ui-pytest/driver/aut.py | 14 +-- test/ui-pytest/driver/server.py | 17 +-- test/ui-pytest/scripts/utils/local_system.py | 112 +------------------ test/ui-test/src/drivers/aut.py | 15 +-- test/ui-test/src/drivers/context.py | 2 - test/ui-test/src/utils/local_system.py | 37 +----- 10 files changed, 35 insertions(+), 180 deletions(-) diff --git a/test/ui-pytest/configs/_local.py.ci b/test/ui-pytest/configs/_local.py.ci index d4d0f1e7dd..267eac0e17 100644 --- a/test/ui-pytest/configs/_local.py.ci +++ b/test/ui-pytest/configs/_local.py.ci @@ -2,7 +2,6 @@ import logging import os LOG_LEVEL = logging.DEBUG -LOCAL_RUN = True DEV_BUILD = False APP_DIR = os.getenv('APP_DIR') diff --git a/test/ui-pytest/configs/_local.py.default b/test/ui-pytest/configs/_local.py.default index 330ed57992..e3d99d1bd4 100644 --- a/test/ui-pytest/configs/_local.py.default +++ b/test/ui-pytest/configs/_local.py.default @@ -1,7 +1,6 @@ import logging LOG_LEVEL = logging.DEBUG -LOCAL_RUN = True DEV_BUILD = False APP_DIR = None diff --git a/test/ui-pytest/conftest.py b/test/ui-pytest/conftest.py index bc9ee87b1f..39f7b18fcb 100644 --- a/test/ui-pytest/conftest.py +++ b/test/ui-pytest/conftest.py @@ -62,6 +62,5 @@ def pytest_exception_interact(node): body=screenshot.read_bytes(), attachment_type=allure.attachment_type.PNG) driver.context.detach() - AUT().stop() except Exception as ex: _logger.debug(ex) diff --git a/test/ui-pytest/driver/atomacos.py b/test/ui-pytest/driver/atomacos.py index 73b53d7fce..92c5820b4e 100644 --- a/test/ui-pytest/driver/atomacos.py +++ b/test/ui-pytest/driver/atomacos.py @@ -4,6 +4,7 @@ from copy import deepcopy import configs.timeouts if configs.system.IS_MAC: + from atomacos._a11y import _running_apps_with_bundle_id import atomacos BUNDLE_ID = 'im.Status.NimStatusClient' @@ -13,7 +14,19 @@ BUNDLE_ID = 'im.Status.NimStatusClient' def attach_atomac(timeout_sec: int = configs.timeouts.UI_LOAD_TIMEOUT_SEC): - atomator = atomacos.getAppRefByBundleId(BUNDLE_ID) + def from_bundle_id(bundle_id): + """ + Get the top level element for the application with the specified + bundle ID, such as com.vmware.fusion. + """ + apps = _running_apps_with_bundle_id(bundle_id) + if not apps: + raise ValueError( + "Specified bundle ID not found in " "running apps: %s" % bundle_id + ) + return atomacos.NativeUIElement.from_pid(apps[-1].processIdentifier()) + + atomator = from_bundle_id(BUNDLE_ID) started_at = time.monotonic() while not hasattr(atomator, 'AXMainWindow'): time.sleep(1) diff --git a/test/ui-pytest/driver/aut.py b/test/ui-pytest/driver/aut.py index 40a480f907..1967c53e30 100644 --- a/test/ui-pytest/driver/aut.py +++ b/test/ui-pytest/driver/aut.py @@ -3,7 +3,7 @@ import squish import configs import driver -from configs.system import IS_WIN, IS_LIN +from configs.system import IS_LIN from driver import context from driver.server import SquishServer from scripts.utils import system_path, local_system @@ -23,7 +23,6 @@ class AUT: self.ctx = None self.pid = None self.aut_id = self.path.name if IS_LIN else self.path.stem - self.process_name = 'Status' if IS_WIN else 'nim_status_client' driver.testSettings.setWrappersForApplication(self.aut_id, ['Qt']) def __str__(self): @@ -49,12 +48,9 @@ class AUT: self.ctx = None return self - @allure.step('Close application by process name') + @allure.step('Close application') def stop(self): - if configs.LOCAL_RUN: - local_system.kill_process_by_pid(self.pid) - else: - local_system.kill_process_by_name(self.process_name) + local_system.kill_process(self.pid) @allure.step("Start application") def launch(self, *args) -> 'AUT': @@ -67,10 +63,6 @@ class AUT: f'"{self.path}"' ] + list(args) local_system.execute(command) - try: - local_system.wait_for_started(self.process_name) - except AssertionError: - local_system.execute(command, check=True) else: SquishServer().add_executable_aut(self.aut_id, self.path.parent) command = [self.aut_id] + list(args) diff --git a/test/ui-pytest/driver/server.py b/test/ui-pytest/driver/server.py index d5ebc2f8b4..4cf5feeec5 100644 --- a/test/ui-pytest/driver/server.py +++ b/test/ui-pytest/driver/server.py @@ -1,5 +1,6 @@ import logging import typing +from subprocess import CalledProcessError import configs.testpath from scripts.utils import local_system @@ -20,6 +21,7 @@ class SquishServer: self.config = configs.testpath.ROOT / 'squish_server.ini' self.host = host self.port = port + self.pid = None def start(self): cmd = [ @@ -28,19 +30,12 @@ class SquishServer: f'--host={self.host}', f'--port={self.port}', ] - local_system.execute(cmd) - try: - local_system.wait_for_started(_PROCESS_NAME) - except AssertionError as err: - _logger.info(err) - local_system.execute(cmd, check=True) + self.pid = local_system.execute(cmd) def stop(self): - local_system.kill_process_by_name(_PROCESS_NAME, verify=False) - try: - local_system.wait_for_close(_PROCESS_NAME, 2) - except AssertionError as err: - _logger.debug(err) + if self.pid is not None: + local_system.kill_process(self.pid) + self.pid = None # https://doc-snapshots.qt.io/squish/cli-squishserver.html def configuring(self, action: str, options: typing.Union[int, str, list]): diff --git a/test/ui-pytest/scripts/utils/local_system.py b/test/ui-pytest/scripts/utils/local_system.py index 6ae5c60cd2..a066235a7b 100644 --- a/test/ui-pytest/scripts/utils/local_system.py +++ b/test/ui-pytest/scripts/utils/local_system.py @@ -3,128 +3,24 @@ import os import signal import subprocess import time -from collections import namedtuple -from datetime import datetime import allure -import psutil import configs from configs.system import IS_WIN _logger = logging.getLogger(__name__) -process_info = namedtuple('RunInfo', ['pid', 'name', 'create_time']) - -@allure.step('Find process by name') -def find_process_by_name(process_name: str): - processes = [] - for proc in psutil.process_iter(): - try: - if process_name.lower().split('.')[0] == proc.name().lower().split('.')[0]: - processes.append(process_info( - proc.pid, - proc.name(), - datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f")) - ) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - return processes - - -@allure.step('Find process by pid') -def find_process_by_pid(pid): - for proc in psutil.process_iter(): - try: - if proc.pid == pid: - return process_info( - proc.pid, - proc.name(), - datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f") - ) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - - -@allure.step('Find process by port') -def find_process_by_port(port: int): - for proc in psutil.process_iter(): - try: - for conns in proc.connections(kind='inet'): - if conns.laddr.port == port: - return process_info( - proc.pid, - proc.name(), - datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f") - ) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - - -@allure.step('Kill process by name') -def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: int = 10): - _logger.info(f'Closing process: {process_name}') - processes = find_process_by_name(process_name) - for process in processes: - try: - os.kill(process.pid, signal.SIGILL if IS_WIN else signal.SIGKILL) - except PermissionError as err: - _logger.info(f'Close "{process}" error: {err}') - if verify and processes: - wait_for_close(process_name, timeout_sec) - - -@allure.step('Kill process by PID') -def kill_process_by_pid(pid, verify: bool = True, timeout_sec: int = 10): +@allure.step('Kill process') +def kill_process(pid): os.kill(pid, signal.SIGILL if IS_WIN else signal.SIGKILL) - if verify: - wait_for_close(pid=pid, timeout_sec=timeout_sec) - - -@allure.step('Kill process by port') -def kill_process_by_port(port: int): - proc = find_process_by_port(port) - if proc is not None and proc.pid: - kill_process_by_pid(proc.pid) - - -@allure.step('Wait for process start') -def wait_for_started(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC): - started_at = time.monotonic() - while True: - process = find_process_by_name(process_name) - if process: - _logger.info(f'Process started: {process_name}, start time: {process[0].create_time}') - return process[0] - time.sleep(1) - _logger.debug(f'Waiting time: {int(time.monotonic() - started_at)} seconds') - assert time.monotonic() - started_at < timeout_sec, f'Start process error: {process_name}' - - -@allure.step('Wait for process close') -def wait_for_close(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC, pid=None): - started_at = time.monotonic() - while True: - if process_name is not None: - process = find_process_by_name(process_name) - if not process: - break - elif pid is not None: - process = find_process_by_pid(pid) - if process is None: - break - else: - raise RuntimeError('Set process name or PID to find process') - time.sleep(1) - assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name or pid}' - _logger.info(f'Process closed: {process_name}') @allure.step('System execute command') def execute( command: list, - shell=True, + shell=False if IS_WIN else True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, check=False @@ -152,7 +48,7 @@ def execute( @allure.step('System run command') def run( command: list, - shell=True, + shell=False if IS_WIN else True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, timeout_sec=configs.timeouts.PROCESS_TIMEOUT_SEC, diff --git a/test/ui-test/src/drivers/aut.py b/test/ui-test/src/drivers/aut.py index 257ee5cfba..d66e591672 100644 --- a/test/ui-test/src/drivers/aut.py +++ b/test/ui-test/src/drivers/aut.py @@ -1,15 +1,14 @@ +import time from datetime import datetime -import configs import squish -import time import utils.FileManager as filesMngr -from utils import local_system from drivers.elements.base_window import BaseWindow from screens.main_window import MainWindow -from utils import local_system from utils.system_path import SystemPath +import configs +from scripts.utils import local_system from . import context @@ -34,7 +33,6 @@ class AbstractAut: pid = self.ctx.pid squish.currentApplicationContext().detach() assert squish.waitFor(lambda: not self.ctx.isRunning, configs.squish.APP_LOAD_TIMEOUT_MSEC) - assert squish.waitFor(lambda: pid not in local_system.get_pid(self.fp.name), configs.squish.APP_LOAD_TIMEOUT_MSEC) self.ctx = None return self @@ -44,19 +42,18 @@ class ExecutableAut(AbstractAut): def __init__(self, fp: SystemPath): super(ExecutableAut, self).__init__() self.fp = fp + self.pid = None def start(self, *args) -> 'ExecutableAut': cmd = ' '.join([self.fp.name] + list(args)) self.ctx = squish.startApplication(cmd) - local_system.wait_for_started(self.fp.stem) assert squish.waitFor(lambda: self.ctx.isRunning, configs.squish.APP_LOAD_TIMEOUT_MSEC) squish.setApplicationContext(self.ctx) + self.pid = self.ctx.pid return self def close(self): - # https://github.com/status-im/desktop-qa-automation/issues/85 - # local_system.kill_process(self.fp.name) - pass + local_system.kill_process(self.pid) class StatusAut(ExecutableAut): diff --git a/test/ui-test/src/drivers/context.py b/test/ui-test/src/drivers/context.py index 8235736f64..85a00e6e0b 100644 --- a/test/ui-test/src/drivers/context.py +++ b/test/ui-test/src/drivers/context.py @@ -25,5 +25,3 @@ def detach(timeout_sec: int = configs.squish.PROCESS_TIMEOUT_SEC): ctx.detach() while ctx.isRunning and time.monotonic() - started_at < timeout_sec: time.sleep(1) - - utils.local_system.kill_process(configs.path.AUT.name) diff --git a/test/ui-test/src/utils/local_system.py b/test/ui-test/src/utils/local_system.py index f533c61605..a38370377d 100644 --- a/test/ui-test/src/utils/local_system.py +++ b/test/ui-test/src/utils/local_system.py @@ -1,43 +1,10 @@ import os import signal import subprocess -import time - -import configs -def get_pid(process_name: str): - pid_list = [] - for line in os.popen("ps ax | grep " + process_name + " | grep -v grep"): - pid_list.append(int(line.split()[0])) - return pid_list - - -def kill_process(process_name: str, verify: bool = True, timeout_sec: int = configs.squish.PROCESS_TIMEOUT_SEC): - pid_list = get_pid(process_name) - for pid in pid_list: - os.kill(pid, signal.SIGKILL) - if verify: - wait_for_close(process_name, timeout_sec) - - -def wait_for_started(process_name: str, timeout_sec: int = configs.squish.PROCESS_TIMEOUT_SEC): - started_at = time.monotonic() - while True: - pid_list = get_pid(process_name) - if pid_list: - return pid_list - time.sleep(1) - assert time.monotonic() - started_at < timeout_sec, f'Start process error: {process_name}' - - -def wait_for_close(process_name: str, timeout_sec: int = configs.squish.PROCESS_TIMEOUT_SEC): - started_at = time.monotonic() - while True: - if not get_pid(process_name): - break - time.sleep(1) - assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name}' +def kill_process(pid): + os.kill(pid, signal.SIGKILL) def execute(