Skip to content

Commit

Permalink
test(AUT Close) The closing AUT process by pid (#11955)
Browse files Browse the repository at this point in the history
  • Loading branch information
squ1sher authored Aug 22, 2023
1 parent 9716f9b commit 18b1579
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 180 deletions.
1 change: 0 additions & 1 deletion test/ui-pytest/configs/_local.py.ci
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import logging
import os

LOG_LEVEL = logging.DEBUG
LOCAL_RUN = True
DEV_BUILD = False

APP_DIR = os.getenv('APP_DIR')
1 change: 0 additions & 1 deletion test/ui-pytest/configs/_local.py.default
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging

LOG_LEVEL = logging.DEBUG
LOCAL_RUN = True
DEV_BUILD = False

APP_DIR = None
1 change: 0 additions & 1 deletion test/ui-pytest/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
15 changes: 14 additions & 1 deletion test/ui-pytest/driver/atomacos.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import configs.timeouts

if configs.system.IS_MAC:
from atomacos._a11y import _running_apps_with_bundle_id
import atomacos

BUNDLE_ID = 'im.Status.NimStatusClient'
Expand All @@ -13,7 +14,19 @@


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)
Expand Down
14 changes: 3 additions & 11 deletions test/ui-pytest/driver/aut.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

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
Expand All @@ -23,7 +23,6 @@ def __init__(
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):
Expand All @@ -49,12 +48,9 @@ def detach(self):
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':
Expand All @@ -67,10 +63,6 @@ def launch(self, *args) -> '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)
Expand Down
17 changes: 6 additions & 11 deletions test/ui-pytest/driver/server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import typing
from subprocess import CalledProcessError

import configs.testpath
from scripts.utils import local_system
Expand All @@ -20,6 +21,7 @@ def __init__(
self.config = configs.testpath.ROOT / 'squish_server.ini'
self.host = host
self.port = port
self.pid = None

def start(self):
cmd = [
Expand All @@ -28,19 +30,12 @@ def start(self):
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]):
Expand Down
112 changes: 4 additions & 108 deletions test/ui-pytest/scripts/utils/local_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,128 +3,24 @@
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
Expand Down Expand Up @@ -152,7 +48,7 @@ def _get_output(_process):
@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,
Expand Down
15 changes: 6 additions & 9 deletions test/ui-test/src/drivers/aut.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -34,7 +33,6 @@ def detach(self):
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

Expand All @@ -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):
Expand Down
2 changes: 0 additions & 2 deletions test/ui-test/src/drivers/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
37 changes: 2 additions & 35 deletions test/ui-test/src/utils/local_system.py
Original file line number Diff line number Diff line change
@@ -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(
Expand Down

0 comments on commit 18b1579

Please sign in to comment.