当前位置: 首页 > news >正文

os_util 工具类和方法的实现

一、前置说明

  • 总体目录:《从 0-1 搭建企业级 APP 自动化测试框架》
  • 上节回顾:在 init_appium_and_devices 的实现思路分析 小节中,分析了实现 init_appium_and_devices 的思路,梳理出了必要的工具类和方法。
  • 本节目标:完成 os_util 模块工具类和方法的编写,为具体实现做准备。
  • 其它说明:
    • 工具类和方法,是实现目的的手段,不是框架的重点;
    • 可以使用 ChatGPT 工具辅助实现工具类和方法,验证即可;
    • 对工具类和方法,进行分类整理,使用途更明确。

二、代码实现

utils/os_util.py

import os
import platform
import logging
import shutil
import subprocess
import timeimport psutil
import win32gui
import win32processlogger = logging.getLogger(__name__)class RunCMDError(Exception):...class OSName:@propertydef os_name(self):return platform.system()def is_windows(self):return self.os_name.lower() == 'windows'def is_linux(self):return self.os_name.lower() == 'linux'def is_mac(self):return self.os_name.lower() == 'darwin'osname = OSName()class CMDRunner:@staticmethoddef run_command(command, timeout=5, retry_interval=0.5, expected_text=''):"""执行命令,并等待返回结果。无论是执行成功还是执行失败,都会返回结果。:param command: 待执行的命令:param timeout: 超时时间该参数的作用说明:- 在连续执行多条命令时,会遇到:因为执行速度过快,上一条命令未执行完成就执行了下一条命令,导致下一条命令执行失败的问题;- 因此需要增加失败重跑的机制:只要未执行成功,就进入失败重跑,直到到达指定的超时时间为止。:param retry_interval: 失败重试的间隔等待时间:param expected_text: 期望字符串,如果期望字符串为真,则以"字符串是否在输出结果中"来判断是否执行成功;该参数的作用举例说明:- 如果你在命令行中输入:adb connect 127.0.0.1:7555- 返回的结果是:* daemon not running; starting now at tcp:5037* daemon started successfullycannot connect to 127.0.0.1:7555: 由于目标计算机积极拒绝,无法连接。 (10061)- 虽然命令已执行成功,但是并没有达到 "连接成功" 的预期;- 此时,你可以使用 run_command('adb connect 127.0.0.1:62001', timeout=60, expected_text='already connected to 127.0.0.1:7555'):return: (output, status), 返回输出结果和状态,无论是成功还是失败,都会返回输出结果和状态"""# 定义失败重跑的截止时间end_time = time.time() + timeoutattempts = 0while True:try:# subprocess.run() 方法用于执行命令并等待其完成,然后返回一个 CompletedProcess 对象,该对象包含执行结果的属性,# 它适用于需要等待命令完成并获取结果的情况。result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=timeout)if result.returncode == 0:# 如果 returncode==0,表示执行成功output = result.stdout.strip()# 如果期望字符串为真,则通过"字符串是否在输出结果中"来判断是否执行成功status = expected_text in output if expected_text else True# 通常情况下,执行成功时,命令行不会返回任何结果,此时result为'',因此添加这个逻辑output = output or 'successful'else:# 否则,从result.stderr.strip() 获取 outputoutput = result.stderr.strip()status = expected_text in output if expected_text else False# 如果 status 为真,则返回 (output, status), 否则进入 while 循环,进行失败重试if status:logger.debug(f"Execute adb command successfully: {command}"f"\nOutput is: "f"\n=========================="f"\n{output}"f"\n==========================")return output, statusexcept subprocess.TimeoutExpired as e:logger.warning(f"Command timed out: {e}")output, status = '', Falseexcept Exception as e:logger.warning(f"Unexpected error while executing command: {e}")output, status = '', False# 添加等待时间,等待上一条命令执行完成time.sleep(retry_interval)attempts += 1logger.debug(f'Execute adb command failure: {command}'f'\nRetrying... Attempt {attempts}')# 如果超过截止时间,则跳出循环if time.time() > end_time:breaklogger.warning(f"Execute adb command failure: {command}"f"\nOutput is: "f"\n=========================="f"\n{output}"f"\n==========================")return output, statusdef run_command_strict(self, command, timeout=5, retry_interval=0.5, expected_text=''):"""严格执行命令,如果执行失败,则抛出RunCMDError异常;如果执行成功,则返回输出结果.:param command: 待执行的命令:param timeout: 超时时间:param retry_interval: 失败重试的间隔等待时间:param expected_text: 期望字符串:return: output, 返回命令执行成功之后的输出结果"""output, status = self.run_command(command, timeout, retry_interval, expected_text)if not status:raise RunCMDError(output)return outputcmd_runner = CMDRunner()class PortManager:@staticmethoddef is_port_in_use(port) -> bool:"""判断指定端口是否被占用"""try:# 获取当前系统中所有正在运行的网络连接connections = psutil.net_connections()# 检查是否有连接占用指定端口for conn in connections:if conn.laddr.port == int(port):logger.debug(f"Port {port} is in use.")return Truelogger.debug(f"Port {port} is not in use.")return Falseexcept Exception as e:# 处理异常,例如权限不足等情况logger.error(f"Error checking port {port}: {e}")return Falsedef generate_available_ports(self, start_port, count):"""生成可用的端口号:param start_port: 起始端口号:param count: 生成端口的个数:return: 列表"""available_ports = []port = start_portwhile len(available_ports) < count:if not self.is_port_in_use(port):available_ports.append(port)port += 1return available_portsport_manager = PortManager()class ProcessManager:@staticmethoddef find_pid_by_window_name(window_name):"""根据窗口名称查找对应的进程ID列表"""pids = []  # 存储找到的进程ID列表system = platform.system()  # 获取操作系统类型if system == "Windows":# 使用 win32gui 库查找窗口def callback(hwnd, hwnd_list):try:if win32gui.IsWindowVisible(hwnd):  # 检查窗口是否可见window_text = win32gui.GetWindowText(hwnd)  # 获取窗口标题文本if window_name.lower() in window_text.lower():  # 如果窗口标题包含指定的窗口名称(不区分大小写)_, pid = win32process.GetWindowThreadProcessId(hwnd)  # 获取窗口对应的进程IDhwnd_list.append(pid)  # 将进程ID添加到列表中except:passreturn Truewin32gui.EnumWindows(callback, pids)  # 枚举所有窗口,并调用回调函数进行查找else:# 在 macOS 和 Linux 上使用 psutil 库查找进程 PIDfor proc in psutil.process_iter(['pid', 'name']):  # 遍历所有进程,并获取进程的PID和名称try:process_name = proc.info['name'].lower()  # 获取进程名称(转换为小写)if window_name.lower() in process_name:  # 如果进程名称包含指定的窗口名称(不区分大小写)pids.append(proc.info['pid'])  # 将进程ID添加到列表中except (psutil.NoSuchProcess, psutil.AccessDenied):passreturn pids@staticmethoddef find_pid_by_port(port):"""根据端口号查找对应的进程ID列表"""pids = []for conn in psutil.net_connections():try:if conn.laddr.port == port:pids.append(conn.pid)except (psutil.NoSuchProcess, psutil.AccessDenied):passreturn pids@staticmethoddef find_pid_by_process_name(process_name):"""根据进程名称查找对应的进程ID列表"""pids = []for proc in psutil.process_iter(['pid', 'name']):try:if process_name.lower() in proc.info['name'].lower():pids.append(proc.info['pid'])except (psutil.NoSuchProcess, psutil.AccessDenied):passreturn pids@staticmethoddef kill_process_by_pid(pid):"""根据进程PID查杀进程。此方法是 kill_process_by_port、kill_process_by_name、kill_process_by_window_name 的底层方法。"""try:process = psutil.Process(pid)process.kill()logger.debug(f"Killed process with PID {process.pid}")# 杀死进程时,可能会遇到权限等问题except Exception as e:logger.debug(f"Failed to kill process: {e}")def kill_process_by_port(self, port):"""根据端口号,查杀进程"""pids = self.find_pid_by_port(int(port))for pid in pids:self.kill_process_by_pid(pid)def kill_process_by_name(self, process_name):"""根据进程名,查杀进程"""pids = self.find_pid_by_process_name(process_name)for pid in pids:self.kill_process_by_pid(pid)def kill_process_by_window_name(self, window_name):"""根据窗口名称,查杀进程"""pids = self.find_pid_by_window_name(window_name)for pid in pids:self.kill_process_by_pid(pid)process_manager = ProcessManager()class ApplicationManager:@staticmethoddef check_app_installed(app_name):"""使用 shutil.which() 方法来检查应用是否已安装,可适用于所有平台。只会从Path环境变量中判断,如果已安装但未设置为环境变量,会返回False."""if shutil.which(app_name) is not None:return Trueelse:return False@staticmethoddef set_environment_variable(key, value):"""设置系统变量,比如设置: JAVA_HOME、ANDROID_HOME"""os.environ[key] = value@staticmethoddef set_path_env_variable(path):"""设置Path环境变量warning: 这种方式设置环境变量,在windows平台会遇到1024截断的问题, todo"""if 'PATH' in os.environ:os.environ['PATH'] = path + os.pathsep + os.environ['PATH']else:os.environ['PATH'] = path@staticmethoddef start_application(executable_path, is_run_in_command=False):"""启动应用程序,但不必等待其启动完成。:param executable_path: 可执行文件的路径,比如: .exe文件:param is_run_in_command: 是否要在命令行中启动,比如:在命令行中输入 `appium` 启动 appium server"""try:if osname.is_windows():if is_run_in_command:# 如果程序需要在命令行中启动,则需要需要使用 start 启动命令行窗口command = f"start {executable_path}"else:command = executable_path# subprocess.Popen() 方法用于启动命令,但不必等待其完成,# 这对于需要启动长时间运行的程序或不需要等待程序完成的情况非常有用, 例如等待命令完成、发送信号等。subprocess.Popen(command, shell=True)logger.debug(f"Started program '{executable_path}'")else:if is_run_in_command:# 类Unix系统使用nohup命令启动程序command = f"nohup {executable_path} &"else:command = executable_pathsubprocess.Popen(command, shell=True, preexec_fn=os.setsid)logger.debug(f"Started program '{executable_path}'")except Exception as e:logger.warning(f"Error starting program '{executable_path}': {str(e)}")application_manager = ApplicationManager()if __name__ == '__main__':logging.basicConfig(level=logging.DEBUG)# 按窗口名称查杀 uiautomatorviewerprocess_manager.kill_process_by_window_name('automator')# 按端口号杀进 appiumprocess_manager.kill_process_by_port(4723)# 按进程名称查杀夜神模拟器process_manager.kill_process_by_name('nox.exe')

三、要点小结

请注意:

  • 工具类和方法,是实现目的的手段,不是框架的重点内容;
  • 可以使用 ChatGPT 工具辅助实现工具类和方法,验证即可;
  • 对工具类和方法,进行分类整理,使用途更明确。
    • 以本节为例,以上所有方法都与系统操作相关,因此将模块文件命名为 os_util.py
    • 再将方法进行分类:CMDRunner、PortManager、ProcessManager、ApplicationManager

点击返回主目录

相关文章:

os_util 工具类和方法的实现

一、前置说明 总体目录&#xff1a;《从 0-1 搭建企业级 APP 自动化测试框架》上节回顾&#xff1a;在 init_appium_and_devices 的实现思路分析 小节中&#xff0c;分析了实现 init_appium_and_devices 的思路&#xff0c;梳理出了必要的工具类和方法。本节目标&#xff1a;完…...

uview表单校验带星号

uView表单校验带星号可以通过设置required属性来实现。在uView中&#xff0c;可以使用组件来实现表单校验&#xff0c;具体步骤如下&#xff1a; 1、在需要校验的表单元素上添加required属性&#xff0c;例如&#xff1a; <u-form :model"detailInfo" ref"d…...

vue+element实现动态表格:根据后台返回的属性名和字段动态生成可变表格

现有一个胡萝卜厂生产不同品种的胡萝卜&#xff0c;为了便于客户了解产品&#xff0c;现需在官网展示胡萝卜信息。现有的萝卜信息&#xff1a;编号&#xff08;id&#xff09;、名称&#xff08;name&#xff09;、保质期&#xff08;age&#xff09;、特点&#xff08;remark&…...

云渲染UE4像素流送搭建(winows、ubuntu单实例与多实例像素流送)

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…...

Unity VR Pico apk安装失败:INSTALL_FAILED_UPDATE_INCOMPATIBLE

我的报错&#xff1a; PICO4企业版。安装apk&#xff0c;报错“安装失败。&#xff08;所属的Unity项目打包的apk&#xff0c;被我在同一台pico4安装了20次&#xff09; 调试方法&#xff1a; PIco4发布使用UNITY开发的Vr应用&#xff0c;格式为apk&#xff0c;安装的时候发生…...

Prompt 提示工程学习笔记

一、Prompt设计的四个关键要素&#xff1a; 任务描述、输入数据、上下文信息、提示风格 &#xff08;1&#xff09;任务描述&#xff1a;描述想要让LLM遵循的指令。描述应详细清晰&#xff0c;可进一步使用关键词突出特殊设置&#xff0c;从而更好地指导LLM工作。 &#xff0…...

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…...

1861_什么是H桥

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/g_hardware_basic: You should learn some hardware design knowledge in case hardware engineer would ask you to prove your software is right when their hardware design is wrong! 1861_什么是H桥 H桥电路可以…...

【计算机四级(网络工程师)笔记】操作系统运行机制

目录 一、中央处理器&#xff08;CPU&#xff09; 1.1CPU的状态 1.2指令分类 二、寄存器 2.1寄存器分类 2.2程序状态字&#xff08;PSW&#xff09; 三、系统调用 3.1系统调用与一般过程调用的区别 3.2系统调用的分类 四、中断与异常 4.1中断 4.2异常 &#x1f308;嗨&#xff…...

Swagger快速入门

1、Swagger快速入门 1.1 swagger介绍 官网&#xff1a;https://swagger.io/ Swagger 是一个规范和完整的Web API框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 功能主要包含以下几点: A. 使得前后端分离开发更加方便&#xff0c;有利于团队协作…...

数据结构之<堆>的介绍

1.简介 堆是一种特殊的数据结构&#xff0c;通常用于实现优先队列。堆是一个可以被看作近似完全二叉树的结构&#xff0c;并且具有一些特殊的性质&#xff0c;根据这些性质&#xff0c;堆被分为最大堆&#xff08;或者大根堆&#xff0c;大顶堆&#xff09;和最小堆两种。 2.…...

使用Ubuntu22+Minikube快速搭建K8S开发环境

安装Vmware 这一步&#xff0c;可以参考我的如下课程。 安装Ubuntu22 下载ISO镜像 这里我推荐从清华镜像源下载&#xff0c;速度会快非常多。 下载地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/22.04.3/ 如果你报名了我的这门视频课程&#xf…...

【中小型企业网络实战案例 二】配置网络互连互通

​【中小型企业网络实战案例 一】规划、需求和基本配置-CSDN博客 热门IT技术视频教程&#xff1a;https://xmws-it.blog.csdn.net/article/details/134398330?spm1001.2014.3001.5502 配置接入层交换机 1.以接入交换机ACC1为例&#xff0c;创建ACC1的业务VLAN 10和20。 <…...

Azure Machine Learning - Azure OpenAI GPT 3.5 Turbo 微调教程

本教程将引导你在Azure平台完成对 gpt-35-turbo-0613 模型的微调。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&…...

运维大模型探索之 Text2PromQL 问答机器人

作者&#xff1a;陈昆仪&#xff08;图杨&#xff09; 大家下午好&#xff0c;我是来自阿里云可观测团队的算法工程师陈昆仪。今天分享的主题是“和我交谈并获得您想要的PromQL”。今天我跟大家分享在将AIGC技术运用到可观测领域的探索。 今天分享主要包括5个部分&#xff1a;…...

虚拟机VMware:变动ip修改固定ip

1、配置ip地址 vi /etc/sysconfig/network-scripts/ifcfg-ens33修改为&#xff1a; 修改如下&#xff1a;TYPE"Ethernet" # 网络类型为以太网 BOOTPROTO"static" # 手动分配ip NAME"ens33" # 网卡…...

Docker部署Nexus Maven私服并实现远程访问Nexus界面

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. Docker安装Nexus2. 本地访问Nexus3. Linux安装Cpolar4. 配置Nexus界面公网地址5. 远程访问 Nexus界面6. 固定N…...

苏州科技大学计算机817程序设计(java) 学习笔记

之前备考苏州科技大学计算机&#xff08;专业课&#xff1a;817程序设计&#xff08;java&#xff09;&#xff09;。 学习Java和算法相关内容&#xff0c;现将笔记及资料统一整理归纳移至这里。 部分内容不太完善&#xff0c;欢迎提议。 目录 考情分析 考卷题型 刷题攻略…...

虚幻学习笔记22—C++同步和异步加载

一、前言 之前提到的静态和动态加载都是同步的加载&#xff0c;同时其中的引用基本都是硬引用。如果资源比较大的话会出现卡顿的现象&#xff0c;下面将介绍一种异步加载的方式。同时&#xff0c;还将介绍一种区别与之前的Load的方法。 在说明同步和异步加载之前需要先讲一下虚…...

华清远见嵌入式学习——ARM——作业3

作业要求&#xff1a; 代码效果图&#xff1a; 代码&#xff1a; led.h #ifndef __LED_H__ #define __LED_H__#define RCC_GPIO (*(unsigned int *)0x50000a28) #define GPIOE_MODER (*(unsigned int *)0x50006000) #define GPIOF_MODER (*(unsigned int *)0x50007000) #defi…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...