智能化功能测试
# Attribution :Chengdu Test Team
# Date :2021/08/20
仓库地址:https://gitlabcd.uniontech.com/autotest/cd-desktop-aitest
一、方案概述
基于深度学习智能识别应用的元素控件,用于在自动化测试操作过程中的元素定位以及判断结果时的断言处理;自动化测试过程中,测试机上的画面通过采集盒传输到服务器上,服务器对视频画面进行智能识别后,服务器智能判断用例是否执行成功,然后对测试机下发下一步操作指令,测试机接受并执行下一步操作。
服务器与测试机之间的链接:
- 服务器通过 USB 串口设备(USB-HID 协议)模拟鼠标键盘对测试机进行操作。
- 通过视频采集设备捕获测试机的实时画面传输给服务器。
服务器对采集的画面进行智能识别,识别其中的元素控件,并返回元素控件在屏幕中的坐标,服务器通过智能化分析,如果测试结果与预期一致将下一步操作指令通过串口模拟键鼠信号发送给测试机;如果测试结果与预期不一致将会进入中断处理及异常结果输出,继续执行后续场景,直至所有用例测试完成。
二、深度学习环境搭建
模型训练主要对 GPU 有要求,训练模型的机器显存不低于 6G,其他配置无特殊要求。
推荐性价比较高配置
操作系统: UnionTech OS Desktop 20 Professional Linux version 4.19.0-desktop-amd64 (deepin@deepin-PC) (Uos 8.3.0.3-3+rebuild)
处理器: Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz (八核 / 十六逻辑处理器)
主板: B460M-HDV(RD)
内存: 8GB(TF32D4U2S1MEH-8 DDR4 2933MHz (0.3ns))/8GB(TF32D4U2S1MEH-8 DDR4 2933MHz (0.3ns))
显示适配器: TU116 [GeForce GTX 1660]
1、虚拟环境安装
cd ~
wget -c https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
一路回车
第一次提示输入 yes/no :输入 yes
继续一路回车
第二次提示:输入 no
cd ~/miniconda3/bin
sudo chmod 777 activate
激活conda环境
. ./activate
添加公司内网源
conda config --add channels bioconda
conda config --add channels conda-forge
如果是外网添加外网源
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
如果要删除源
conda config --remove-key channels
2、安装依赖
(1)创建虚拟环境
conda remove --name mmlab --all # 移除所有虚拟环境
conda create -n mmlab python=3.7
conda activate mmlab
(2)安装 Pytorch
在mmlab虚拟环境中执行
pip install torch==1.7.0+cu101 torchvision==0.8.1+cu101 torchaudio==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
(3)安装 MMCV
在mmlab虚拟环境中执行
pip install mmcv-full==1.3.3 -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.7.0/index.html -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
(4)安装显卡驱动
根据你的显卡下载对应驱动,比如我的显卡为 GTX1660
,驱动版本为 430,驱动下载地址:https://www.nvidia.cn/Download/Find.aspx?lang=cn
CTRL+ALT+F2 进入tty
禁用 nouveau 驱动
sudo vim /etc/modprobe.d/blacklist-nouveau.conf
填入:
blacklist nouveau
options nouveau modeset=0
刷新配置文件
sudo update-initramfs -u
reboot 重启后再进入 tty
关闭图像界面,输入命令关闭图像界面
sudo service lightdm stop
安装驱动
sudo chmod a+x NVIDIA-Linux-x86_64-430.run
sudo ./NVIDIA-Linux-x86_64-430.run
reboot重启,nvidia-smi查看安装状态。注意看下cuda版本,10.1,driver版。
三、数据标注
1、数据标注
标注之前需要先转换图片大小,并且以数字命名,每组命名递增
(1)转换大小并重命名
rename_pic.py
import os
import sys
import cv2
import time
import getpass
username = getpass.getuser()
source_path = f"/home/{username}/Desktop/right_menu" # 图片路径
source_dest = os.path.join('/'.join(source_path.split("/")[:-1]), 'tmp')
if not os.path.exists(source_dest):
os.mkdir(source_dest)
start_name = sys.argv[1]
file_name_list = list()
for file in os.listdir(source_path):
if file.endswith('.png'):
file_name_list.append(file)
start_name = int(start_name)
for file in file_name_list:
os.rename(os.path.join(source_path,file), os.path.join(source_path, f"{str(start_name)}.png"))
start_name += 1
time.sleep(1)
for file in os.listdir(source_path):
image = os.path.join(source_path, str(file))
src = cv2.imread(image)
result = cv2.resize(src, (960, 540))
resizeImage = os.path.join(source_dest, str(file))
print(resizeImage)
cv2.imwrite(str(resizeImage), result)
cv2.waitKey(0)
cv2.destroyAllWindows()
time.sleep(1)
os.system(f'rm -rf {source_path}/*')
os.system(f'mv {source_dest}/* {source_path}')
os.system(f'rm -rf {source_dest}')
print("下一个序号:", start_name)
根据终端输出的下一个序号的提示,执行 Python 文件的时候传参。
python rename_pic.py 249
(2)工具标注
使用工具 labelImg 标注
sudo pip3 install PyQt5==5.13
sudo pip3 install labelImg
终端直接输入 labelImg,回车
标注模式选择:PascalVOC
2、智能标注
打开屏幕录制软件,录制手动操作一次测试用例,然后将录制的视频进行分帧,然后对比前两帧图标相似度,剔除相似度过高的图片,在保证素材多样性的前提下,剔除重复多余的图片生成待标注的素材集,然后取出控件模板数据集,通过 OpenCV 模板匹配获取控件在待标注的素材图片中所在的左上、左下、右上、右下四处坐标,从而根据模板数据集生成标注数据。参考目录 AnnotationMaterial
(1)模板数据集准备
1、在界面截取需要标注的元素控件,放在目录下 AnnotationMaterial/template/img
2、维护控件名与元素控件的键对,一个控件名可对应多个元素控件
menu:
- menu.jpg
- menu1.jpg
- menu2.jpg
- menu3.jpg
(2)素材集准备
录制视频或或屏幕截图放置目录 AnnotationMaterial/source
(3)开始标注
python3 AnnotationMaterial/main.py
生成的标注数据存放在 AnnotationMaterial/result
四、MMDetection
1、 MMDetection 代码
MMDetection 是一个有名的深度学习目标检测开源项目,也是 openMMlab
的招牌项目,为什么选择它,小孩儿没娘说来话长,感兴趣可以自行深入了解;
你可以直接在 GitHub 上克隆代码,也可以直接使用咱们仓库下 Train 目录中代码,Train 中的代码也是从 GitHub 上拉取的,不过我们根据项目需要做了一些二次开发,推荐直接使用它。
如果你想从 GitHub 上拉:
git clone https://github.com/open-mmlab/mmdetection.git
# 不能直接拉取主分支,建议使用2.12版本,不同的版本对应的mmcv版本是不同的
2、拉取 voc2coco 代码
git clone https://github.com/Tony607/voc2coco.git
3、转换 coco 数据集
将所有的图片和xml文件放入train2017,从中挑选几组放入val2017(测试集)
cd mmdetection/data/coco
python voc2coco.py train2017 annotations/instances_train2017.json
python voc2coco.py val2017 annotations/instances_val2017.json
生成json文件
4、修改配置
(1)读取模型名称
import json
module_name = []
with open('./instances_train2017.json', "r+") as f:
json_file = f.read()
json_dict = json.loads(json_file)
module_list = json_dict.get('categories')
for module_info in module_list:
name = module_info.get('name')
module_name.append(name)
print(module_name)
print("module_num:", len(module_name))
# 注意对比instances_train2017.json里面模型名称的顺序,与CLASSES和coco_classes里面的顺序保持一致。
(2)修改 faster_rcnn_r101_2x_coco.py
mmdetection/xianjin/faster_rcnn_r101_2x_coco.py
修改46行,num_clasess
的值,新增1个,就 +1;
(3)修改 coco.py
mmdetection/mmdet/datasets/coco.py
CLASSES = (),在里面添加模型名称;
(4)修改 class_names.py
mmdetection/mmdet/core/evaluation/class_names.py
coco_classes
里面添加模型名称;
5、缓存清理
删除 mmdetection/build 目录
python setup.py install
6、训练模型
(1)指定自己配置的训练模型;
python tools/train.py xianjin/faster_rcnn_r101_fpn_2x_coco.py --gpus 1
(2)查看训练结果的测试集结果;
python tools/train.py xianjin/faster_rcnn_r101_fpn_2x_coco.py xianjin/epoch_24.pth --show
(3)查看训练结果的准确度;
python tools/analysis_tools/analyze_logs.py plot_curve xianjin/20210530_011907.log.json --keys acc
7、快捷操作
python3 run.py
将以上 4 - 7 步操作整合成 run.py,一键完成。
五、硬件环境
1、设备清单
- 测试机至少一台;
- 服务端一台(目前仅支持AMD架构);
- 采集盒一个 (ACASIS hdmi视频采集卡,https://item.jd.com/10024310742602.html );
- USB串口转USB键鼠协议线(优胜电子科技USB串口转USB键鼠协议线B类,https://item.taobao.com/item.htm?spm=a1z10.3-c.w4002-1385258877.57.7ea037891D6nKh&id=611894882981 );
2、硬件环境搭建
- 采集盒,HDMI 端连接测试机, USB 端连接到服务器上;
- USB 串口线,白色端连接服务器,黑色端连接测试机;
- 如果需要可以接USB延长线;
- 测试机上可以不接显示器;
3、推荐配置
执行用例的服务端对配置没有特殊要求,如果条件允许配置越高,识别速度用例执行速度越快。
处理器: Intel(R) Core(TM) i3-10100 CPU @ 3.60GHz (四核 / 八逻辑处理器)
主板: B460-N2(J)
内存: 8GB(TF32D4U2S1MEH-8 DDR4 3200MHz (0.3ns))
显示适配器: UHD Graphics 630
存储设备: FORESEE P900F256GBH (256 GB)/ST1000DM003-1SB102 (1.00 TB)
测试机上不执行任何脚本,具体配置根据测试需要决定。
六、USB_MK串口驱动方法
位于control_method目录下的usb_mk.py文件
(1)列出可通信的端口
python3 -m serial.tools.list_ports -v
(2)修改串口的权限
sudo chmod 777 /dev/ttyACM0
(3)实例化USB_MK
import UsbMk
usb_mk = UsbMk("/dev/ttyACM0", 9600) # 设备默认9600为波特率,控制传输速率
(4)USB串口设备默认波特率为9600(经测试波特率9600准确性最好)。
1、键盘操作
(1)按下键盘按键
usb_mk.press_key("enter") # 按下键盘enter键
(2)按下键盘按键并且不放
usb_mk.press_key_down("enter") # 按下键盘enter键不放
(3)组合按键
usb_mk.hot_key("ctrl", "alt", "T") # 按下组合按键ctrl+alt+T调起终端
(4)组合按键不放
usb_mk.hot_key_down("ctrl", "alt", "T") # 按下组合按键ctrl+alt+T调起终端不放
(5)释放所有键盘按键
usb_mk.key_up() # 释放所有键盘按键
(6)键盘输入字符串(汉字会自动转成拼音输入)
usb_mk.hot_key("ctrl", "alt", "T") # 按下组合按键ctrl+alt+T调起终端
usb_mk.input_text("reboot") # 输入字符串reboot
usb_mk.press_key("enter") # 按下键盘enter键
2、鼠标操作
(1)按下鼠标左键
usb_mk.click() # 按下鼠标左键
(2)按下鼠标左键不放
usb_mk.mouse_down() # 按下鼠标左键不放
(3)释放鼠标所有按键
usb_mk.mouse_up() # 释放鼠标所有按键
(3)按下鼠标右键
usb_mk.right_click() # 释放鼠标所有按键
(4)鼠标左键双击
usb_mk.double_click() # 释放鼠标所有按键
(5)恢复鼠标至初始位置,默认左上角
usb_mk.move_to_init() # 恢复鼠标至初始位置,默认左上角
(6)移动鼠标至相对坐标
usb_mk.move_rel(100, 200) # 鼠标向左边移动100个像素,向下移动200个像素
(7)以屏幕左上角为圆心坐标移动鼠标至屏幕绝对坐标
usb_mk.move_to(100, 200) # 鼠标移动至屏幕坐标(100, 200)
# 注:因Linux系统不支持鼠标绝对路径,所以会鼠标会先移动到初始位置
(8)按下鼠标左键,拖动到绝对坐标位置
usb_mk.drag_to(100, 200) # 按下鼠标拖动至屏幕坐标(100, 200)
# 注:因Linux系统不支持鼠标绝对路径,所以会鼠标会先移动到初始位置
(9)按下鼠标左键,拖动到相对坐标位置
usb_mk.drag_rel(100, 200) # 鼠标向左边移动100个像素,向下移动200个像素
七、方法调用入参规则
1、基类
class Base:
def __init__(self):
self._obj = image
self._usb = usb_mk
def find_element_by_ai(self, element):
return self._obj.find_element(element)
2、入参说明
图像识别方法入口为 Base 类下的 find_element_by_ai 。仅接受一个参数 element,类型为 string。
可识别的 string 格式:
(1)大图标( 图标大于 40 像素):识别某个大图标,例:
Base().find_element_by_ai("window")
(2)大图标 / 小图标(图标小于 40 像素):识别某个小图标,例:window/search_btn
Base().find_element_by_ai("window/search_btn")
(3)大图标 / OCR:识别某个大图标内的文字,例:menu/属性
Base().find_element_by_ai("menu/属性")
(4)OCR:全屏识别文字
Base().find_element_by_ai("搜索")
八、隐藏鼠标
通过采集盒传输过来的视频流,在识别过程中可能受到鼠标影响,因此用例执行过程中需要隐藏鼠标。
屏蔽鼠标光标显示
/etc/lightdm/lightdm.conf
在[Seat:*]
下面添加参数
xserver-command=X -bs -core -nocursor
九、智能自动化的意义
1、改善 Acessibility 定位的稳定性问题
Acessibility 属性定位存在偶尔失效的情况
(1)开发在做新需求开发时可能涉及到对属性标签的修改或者层级的调整,可能造成自动化用例无法定位到元素。
- 比如1040阶段,音乐重构的时候,开发对部分属性和层级做了修改,导致自动化用例定位方法80%需要修改。
- 开发新功能的时候偶尔也会对属性做调整,遇到5次。
(2)目前主要使用Dogtail识别应用Acessibility属性进行定位,是获取应用当前界面的属性tree,在应用跳转后,Dogtail存在无法及时获取当前属性列表的情况。
- 多媒体调用文管窗口的时候,出现找不到文管里面元素的情况,目前出现10+次。
- 应用设置页面的属性偶尔存在找不到的情况,遇到5次。
(3)Dogtail工具本身存在问题,有时定位元素的时候很慢,让人无法接受,目前出现过很多次。
- 执行用例的时候,定位元素的时间比平常慢3倍,出现4次。
2、解决了使用绝对图像识别定位的容错性差的问题
部分控件无法添加 Acessibility 属性从而使用图像识别定位出现的容错性差的问题。
(1)图像识别定位方法需要维护大量的目标图片资源,在 UI 发生变化之后,会涉及到目标资源的替换,比较耗费人力。
(2)图像识别的定位方法对比精度比较搞,如果UI 的变更会造成无法准确定位到,自动化脚本健壮性不足。
3、使用机器分离,解决了自动化脚本对应用的影响,真实还原用户的使用场景
机器分离的架构设计,保证了测试机的测试环境完全等同于用户,排除测试脚本的影响。
(1)目前的功能自动化是在测试机上直接运行自动化测试脚本,加上用例执行过程中我们添加了一些进程监控、视频录制等功能,自动化脚本本身会消耗一些系统资源,无法还原真是的用户使用场景。
(2)性能测试需要排除其他程序的资源消耗影响,性能自动化采用机器分离,保证了性能数据的准确性。
以前的用例执行耗时没条在30秒左右,现在用例耗时在10秒。
4、解决了UI 调整导致定位失败的不稳定性问题
(1)解决了因 UI 调整导致控件位置变化或色差变化后定位不准确的问题,只要控件文案和控件外边框无变化均可准确定位;
(2)目前也会采用基于UI的定位方案,如果UI位置有调整,会导致元素无法定位。
5、简化了用例结构,提升编写友好度
(1)智能自动化识别元素的方法只有一个,就是基于我们训练的模型识别视频流中的元素,经过脚本封装之后,在自动化用例中所有定位的操作都只需要调用这一个方法就行了,编写自动化脚本的难度大大降低。
(2)也是由于调用方法简单,在 UI 自动化测试框架中的分层结构将会减少,多继承的情况会减少,自动化代码的调用结构也会变得简单,而且功能测试同学编写自动化用例会更加容易上手。
以前写用例由于需要结合不同的模块,调用不同的方法,编写一条用例并完成调试的时间平均在20-30分钟,现在写用例脚本能在5分钟左右完成。