移动测试 | 解析 Totoro 无侵入、全场景截图及图像技术体系

本文涉及的产品
mPaaS订阅基础套餐,标准版 3个月
简介: Totoro 框架是由 蚂蚁金服终端工程技术部-实验平台技术组 自研的一套自动化解决方案。目前以实验 SDK 形式输出能力,及凭借蚂蚁云测 SLM 平台能力,目前已有阿里系域内 16+ 业务接入。

为提高业务研发的开发效率,解决业务基础工具技术痛点,让研发主要精力回归业务核心问题解决上,我们在不同的维度做了大量体系化的技术构建,为不同的自动化场景提供稳定、易用的自动化底盘。

本文主要分享 Totoro 框架在移动端截图及图像处理及在全链路体系上的技术方案和业务支持能力。

基础截图技术方案

一台手机上获取屏幕截图简单,但是想要兼容集群环境云真机平台上的大量移动设备,突破系统各种安全限制、安全页面(密码、转账等页面)反截图安全限制、及 ROM 差异性等不同维度的兼容性问题,且是非侵入方式,则会有一点技术小挑战。

这里从基础截图方案和突破系统限制达到截图能力全场景覆盖两方面分享 Totoro 的一些实践。

1. 一般常见截图方案选型及改造

在移动端自动化(测试)场景, 最长见的截图方法就是使用adb shell screencapuiautomator截图接口。然而 adb shell screencap 方式使用很不方便,需要将截图先保存到手机磁盘上,然后再pull到 PC 设备上。
Totoro 框架中采用了一次性的获取方式如下:

adb -s sn exec-out screencap -p >  name

但是即使采用adb exec-out方式也存在图片无法压缩导致的截图耗时较长的问题,并且 Java 项目中每次都要 Process 创建进程,遇到频繁截图情况,耗费系统资源,影响稳定性。所以在 Totoro 框架中提供的 SDK 默认 API 底层采用的的是 Uiautomator 提供的接口方法。

UI Automator 测试框架提供了一组 API,用于构建在用户应用和系统应用上执行交互的界面测试。通过 UI Automator API,您可以执行在测试设备中打开“设置”菜单或应用启动器等操作。

同样发现,UIAutomator 提供默认截图方法 生成图片文件也是非常大,压缩算法并没生效。导致截图链路上整体耗时会造成每次截图在平均 2-5s 左右,影响到了自动化整体时间。 考虑到单独引进一套压缩算法较重,且可能后续无资源维护,所以决定研究安卓源码,试图从根本解决压缩无效的问题。

    /**
     * Write a compressed version of the bitmap to the specified outputstream.
     * If this returns true, the bitmap can be reconstructed by passing a
     * corresponding inputstream to BitmapFactory.decodeStream(). Note: not
     * all Formats support all bitmap configs directly, so it is possible that
     * the returned bitmap from BitmapFactory could be in a different bitdepth,
     * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
     * pixels).
     *
     * @param format   The format of the compressed image
     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
     *                 small size, 100 meaning compress for max quality. Some
     *                 formats, like PNG which is lossless, will ignore the
     *                 quality setting
     * @param stream   The outputstream to write the compressed data.
     * @return true if successfully compressed to the specified stream.
     */
    public boolean compress(CompressFormat format, int quality, OutputStream stream) {
      ...
    }

public boolean takeScreenshot(File storePath, int quality) {
        Bitmap screenshot = mUiAutomation.takeScreenshot();
        ...
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(storePath));
                screenshot.compress(Bitmap.CompressFormat.PNG, quality, bos);
        }
  ...
        return true;
    }

根据以上安卓源码,跟踪到系统压缩方法。看注释,似乎发现了问题所在,看 takeScreenshot 方法, 调用了 Bitmap 的 Compress 压缩方法,传的参数写死了 Bitmap.CompressFormat.PNG,格式,根据注释说明,Compress 方法又对 png 格式图片的压缩忽略的,所以导致获取到的图片都很大。马上重写系统方法,直接调用压缩方法,传入 JPEG 去验证可行性,马上得出了解决方案。

找到问题所在,解决方案也就出来了,主要改造逻辑可以通过三个核心步骤完成:

  1. 自行获取 Bitmap 文件。
  2. 调用 Bitmap 压缩方法,指定 jpeg 格式,并传入自定义压缩值。
  3. 获取压缩流数据,直接 Base64 编码传递给 SDK 层。

通过以上优化,减少了两次文件读写,并且有效压缩了图片大小,整体一次截图时间控制在了 200-800ms,对比原来速度最少提升 2-3 倍以上。

2.突破系统安全限制做到截图全覆盖

Totoro 的最大业务需求 来自 钱包系 相关 App 的自动化业务,而支付宝属于安全隐私要求较高
App,各个 ROM 针对钱包或者钱包自身都会做大量安全保护,例如在密码输入页面或转账页面,会导致截图获取失败,并且我们发现有些限制级别较高页面,连系统自身的截图功能也无法使用。

但是在测试场景下,数据及安全级别是可控的,多个业务线强烈提出 能否通过技术手段 获取到这些被限制的页面截图,来满足业务验证逻辑的正确性,去除人工页面截图审核死角,来进一步提高研发效能质量。

由于 Totoro 框架是对 App 是非侵入式的,也给我们的技术实现添加来不小挑战。但是,本着业务价值首位考虑,为了保障开发者使用效率,我们开始调研安卓各种截图方案,跟踪安卓截图源码,试图采用Hook方式绕过安全校验机制。

针对安卓平台各种全局截图方法的深入研究,最后发现,各种入口类的系统接口调用到底层都会调用SurfaceControl类或对应的 c 层代码, SurfaceControl 类成了关键研究对象。然而,从 Java 层的 SurfaceControl 类中各种 Hook 接口调用尝试发现,只要被安全页面限制,该方案下的截图方案就会失败或截到一张黑屏。这意味着系统针对截图的安全限制可以覆盖到目前我们了解到的任何场景,让我们的解决方案一时陷入困境,失去了思路方向。

然而在做 SLM 云测平台设备远程租用过程中,发现录屏功能在这些截图限制页面依然是可以正常使用,这说明系统或安全限制只是针对截图相关 API,这给我们的截图提供了新的思路,通过录屏来获取屏幕截屏。所以开始研究系统的录屏功能,能否在录屏过程中拉一帧的画面,作为截图新的实现方式。

A.自研技术方案

实现原理很简单,通过直接解码 Surface 获取屏幕视频流,然后从视频中获取一张可用帧,转换为 JPEG 格式,最后回传给 PC 上的接口。该方案从 Java 层实现,兼容性强,成本底,下面是关键代码逻辑:

    MediaCodec mcodec = MediaCodec.createEncoderByType("video/avc");//创建解码器
    Surface surface = codec.createInputSurface();//构造目标Surface
    SurfaceControl.setDisplaySurface(display, surface);//通过反射SurfaceControl,将构造的Surface设置到系统
     int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);//开始解码
    //判断有效帧
    ...
    Bitmap  bitmap = mediaMetadataRetriever.getFrameAtTime(index);//获取屏幕bitmap值
    bitmap.compress(Bitmap.CompressFormat.JPEG, quailty, fos);//保存截图
    ...
    //截图回传到PC接口层

然后就是打包程序,利用系统 app_process 服务,可以避开各种权限弹窗问题,直接运行程序,即可获取到屏幕截图。
目前该方式已全面应用到 Totoro 底层截图方案,可以兼容安卓 5.0-10 版本,但是仍然有改进地方,后续还可从以下几点进一步优化:

  • 视频流有效帧判断逻辑优化,减少截图时间。
  • 目前仍然需要一次手机端的文件保持读写逻辑,后续可考虑读流方式,直接获取图片 Base64 值。
  • 可执行文件参数标准化,可满足多场景技术输出。
B.备用兜底方案

以上 A 方法主要针对 5.0+ 的安卓设备,且针对个别 OPPO、VIVO 设备开启了系统恶意录屏功能,就会造成录屏功能限制,引起截图失败。针对这部分的设备,我们设想 Android 能否像 Linux 系统一样,绕过 Java 层权限限制,直接从C层读取操作系统 framebuffer(Linux 设备/dev/graphics/fb0),来获取屏幕图像。

期间调研了开源的 minicap 录屏方案,发现 Surface 方案在个别设备不能兼容情况下, minicap 依然可以使用,其底层原理就是采用 NDK 开发的工具,直接读取操作系统 framebuffer,为了不重复造轮子,决定把 minicap 工具中的手机端的服务单独剥离出来集成到 Totoro,作为备用兜底方法提供截图能力。

String cmd = String.format("adb -s %s shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %sx%s@%sx%s/0 -Q 90 -s -t > %s", deviceId, w, h, w, h, localPathFile);

按照上面命令单独调用 minicap 工具截图,并从流中解析到图片保持到制定目录,经大量测试,该命令并不一定稳定,有时会报格式错误,有时会报流处理错误,所以我们添加了降级方案,如以下命令。

String cmd = String.format("adb -s %s shell \"LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P %sx%s@%sx%s/0 -Q 90 -s -t > /sdcard/.totoro/%s\"", deviceId, w, h, w, h, name);

//然后将图片Pull到PC上制定目录

两种技术方案相互降级搭配使用,这样正好弥补了 Java 层 Surface 方案的一些可能失败场景,作为兜底方法集成到了 Totoro 框架中,目前为止暂未发现两种方法都失败场景,保障了截图能力的底盘稳定性。

业务多场景支持

1.基础截图场景

通过以上相关多层技术方案兼容,Totoro 目前可以提供稳定的基础截图能力,业务层无需关注底层的具体技术实现方案或兜底方案,只需要调用一个 Totoro 实验 SDK 暴露的 API 即可。

    String path = BizUtils.saveScreen(name);

2.长截屏技术方案

长截图能力一直是业务方提出的痛点需求,一直到 19 年末我们才抽出时间弥补了 Totoro 这块能力缺失。
起先,先调研了行业内其他实现方案,基本思路都是滑动截图,然后合并多张截图,最后生成长截图。然而,难点在多张截图的完美合并,针对移动端的页面截图,存在页头、页尾、小 banner 更新、小红点等多重影响,给长截图的实现增加了不少难度。

可行方案分析:

  1. 直接利用第三方拼接工具。需要添加额外依赖,其在移动端个别极限场景拼接存在问题,维护成本高。
  2. 利用算法部门资源,特征值直接匹配。需要添加算法库,且要推动算法部门不断迭代更新,后续更新可能不及时。
  3. 自己实现一套,难度大,初期成本高。需要兼容移动端页面各种情况,但是后续维护成本低,升级方便且及时。

权衡利弊后,最终我们采用了方案 3,决定自己实现了一套针对移动端截图特征的拼接能力,采用了自研像素行灰度值末尾优先对比技术方案,核心逻辑思路是默认向上滑动生成的图片组,两两从底部优先开始对比行像素的灰度值,找到拼接点和裁剪点。为了减少噪点及个别红点、红线、小 banner、及底部导航栏影响,在不同的对比点及区域添加了对应的参数。其核心代码思路如下:

    public static File mergeImg(File imgA, File imgB, String mergedImgPathName) {
            int[][] listA = getPX(imgA);//获取像素数组
            int samePartEnd = 0, sameBottom = srcALen;//定义查找目标位置
            int samePart = mergeLen >> 2;//定义相似颗粒
            for (int al = Math.max(0, listA.length - mergeLen + (samePart >> 1)), l = listA.length - 1; l >= al; l--) {
                for (int x = mergeLen - (listA.length - l), y = Math.max(0, (samePart >> 1)); x >= y; x--) {
                    if (compareRowPx(listA[l], listB[x])) {// 对比行灰度相似度
                        //找到基础对比点
                        for (int i = 0, partLen = (l == listA.length - 1) ? samePart : samePart >> 1; i < partLen; i++) {
                            //寻找拼接点
                            if (compareRowPx(listA[curA], listB[curB])) {
                                if (i == partLen - 1) {
                                    //相同区域达到阀值,找到拼接点
                                    samePartEnd = x;
                                    sameBottom = l - 1;
                                    break;
                            } else {
                                //寻找底部相同区域,如底部导航Tab栏
                                break;
                            }
                        }
                        if (matchedBottom || samePartEnd > 0) {
                            //拼接参数找到,提取结束循环
                            break;
                        }
                    }
                }
            }

            String fileSuffixA = getFileRealExt(imgA);//读取二进制流,获取原始图文件真实后缀
            String fileSuffixB = getFileRealExt(imgB);
            String fileSuffixC = mergedImgPathName.substring(1 + mergedImgPathName.lastIndexOf("."));
            if (samePartEnd == mergeLen - 1) {
                //图片相同,直接返回第一张图片
                imgA.renameTo(file);
                return file;
            }
            //开始合并逻辑
            int newY = srcALen - (srcALen - sameBottom) + (mergeLen - samePartEnd);
            ...//读取原始图片值
            if (sameBottom > 0) {
                  //    去除底部导航tab栏
            } else {
                //没有底部操作tab栏
                newA = reader.read(0);
            }
            ...//重新拼接图片
            imgNew.setRGB(0, newA.getHeight(), width, newB.getHeight(), imgArrayB, 0, width);
            ImageIO.write(imgNew, fileSuffixC, file);
              ..//其他资源回收
            return file;
    }

效果图如下:

totoro - 长图.png

该方案目前已全量上线,并且满足了目前业务方需求,在遇到长截图拼接有重叠或缺少问题时,也可以通过调节对比参数,快速优化合并逻辑代码,达到了既定研发目标。

3.局部控件截图

在自动化过程中,有些业务需要获取某个控件的截图, iOS 端可以通过 WDA 相关接口实现,但是安卓端是没有现成方案的。因此,Totoro 采用了按照坐标截图切割方式,结合控件坐标信息,可以在 PC 端实现一套局部控件获取方法。该 API 可以同时适用到
iOS 和安卓双平台,局部控件截图核心实现思路:

    src = BizUtils.saveScreen(name);
    WebElement element = driver.findElement(By);
    ...//根据element获取到element的坐标信息
    ImageCheckUtils.imageCut( src,  dest,  x,  y,  width,  height);//根据坐标信息切图,获取到控件截图dest文件

4.多屏幕截屏

目前 Totoro 中只集成了 adb 原生方式,用来为一些支付 IoT 设备,提供多屏幕截图能力。

#附屏幕截屏
adb shell screencap -d /dev/graphics/fb1 fb1.png
#主屏幕截屏
adb shell screencap -d /dev/graphics/fb0 fb0.png

后续根据需求反馈,会调研采用 UIAutomater 或 Surface 方式实现。

图像智能算法分析

Totoro 借助兄弟团队(工程数据技术组)的算法能力,针对截图实现了页面智能分析、检测等能力,完成了图片相关技术体系小闭环。智能算法能力集成可以归类为以下三种。

1.控件分析

Totoro -图片2.png

如上截图所示,可以通过以下 API 获取页面控件信息,可识别控件类型如 button、image、label、progress、switch、more、editView、popUp、checkBox、return、close 等。

2.页面异常检测

异常检测能力可抽象分为通用异常和业务异常,具体说明如下:

  • 通用异常分析,如:黑白屏、加载失败、图片或文字截断,控件排列重叠等常见页面 UI 问题;
  • 业务异常分析,如:业务弹窗、授权弹窗阻断等,且更加不同的业务异常场景需求,可定制化服务。

Totoro 中可通过一个接口获取页面异常信息,业务中通过自动化的检测,完成了之前很多无法实现的自动化场景。

3.业务场景识别

为实现更加智能的页面分析,为后续自动化测试场景中自遍历或自填充提供基础能力支撑,依赖算法能力可以智能分析当前页面场景,例如可以通过算法相关接口来自动判断当前页面属于哪个业务流程,比如登录流程场景、输入场景等。

目前内部只开放了登录场景的识别,已上线并应用到小程序自动化注入及巡检场景,可以代替小程序登录繁杂脚本工作量,大大减少用例脚本维护成本。

4.图片相似度分析

totoro 效果对比.png

如上两张截图,由于存在大面积 Banner 变化和背景渐变颜色差异,如果单纯通过像素对比,很难分析出是一个页面上的截图,这里主要借助算法能力,通过提取页面特征值、图像指纹、颜色通道等多维度来精确计算出两个截图的相似度。

Totoro 图.jpeg

如以上截图所示,可以通过 Totoro 封装的 API 得出两张图相似度为 54,业务层代码可通过相似度应用到自己的实际业务场景。(只要相似度 >=50 则可认为两个截图是一个相似业务场景)

后续计划

针对很小的一个图片业务能力,都需要投入量巨大的努力 来保障 Totoro 的自动化底盘易用性和稳定性,业务不断扩张过程中,图片相关能力体系还是有多个细节仍需要不断调优,其中明确需要投入研究和后期开发计划有以下几点:

  • 截图技术方法模块化抽离,进一步优化调用链路,加快整体截图获取速度。
  • 长截图技术方案优化,泛化拼接方向,组件化包装,可单独灵活对外输出技术能力。
  • 多屏截图场景优化,调研更加便捷稳定的多屏截图方案,满足越来越多的 IoT 场景。
  • 进一步推动算法能力深化,通过埋点上报,加大算法数据闭环支持。
目录
相关文章
|
2天前
|
Web App开发 IDE 测试技术
自动化测试的利器:Selenium 框架深度解析
【10月更文挑战第2天】在软件开发的海洋中,自动化测试犹如一艘救生艇,让质量保证的过程更加高效与精准。本文将深入探索Selenium这一强大的自动化测试框架,从其架构到实际应用,带领读者领略自动化测试的魅力和力量。通过直观的示例和清晰的步骤,我们将一起学习如何利用Selenium来提升软件测试的效率和覆盖率。
|
3天前
|
Web App开发 安全 测试技术
软件测试的艺术:从代码审查到用户验收的全方位解析
【10月更文挑战第1天】本文旨在深入探讨软件测试的精髓,通过分析不同类型的测试方法—单元测试、集成测试、系统测试、性能测试和用户接受度测试,揭示其在软件开发生命周期中的重要性。我们将通过具体案例,展示如何运用这些测试技术来发现并修复关键缺陷,提高产品质量。同时,文章还将提供一系列最佳实践,帮助读者建立有效的测试策略,确保软件项目的成功交付。通过阅读本文,您将获得一套全面的软件测试知识体系,以及如何在实际应用中灵活运用这些知识,以提升软件开发的整体质量和效率。
ly~
|
4天前
|
域名解析 网络协议 Linux
如何测试 DNS 记录中的反向代理服务器是否生效?
本文介绍了三种测试反向代理服务器配置的方法。首先,通过命令行工具如 `ping`、`nslookup` 和 `dig` 检查域名解析是否指向正确的 IP 地址。其次,利用 Web 浏览器访问域名,验证页面加载正常且请求头信息无误。最后,借助网络抓包工具如 `Wireshark` 和 `tcpdump` 分析数据包,确保请求正确转发并返回预期响应。
ly~
17 2
|
8天前
|
机器学习/深度学习 存储 监控
深入解析软件测试中的自动化测试技术
本文旨在全面探讨软件测试中的自动化测试技术。通过对自动化测试的定义、优势、常见工具和实施步骤的详细阐述,帮助读者更好地理解和应用自动化测试。同时,本文还将讨论自动化测试的局限性及未来发展趋势,为软件测试人员提供有益的参考。
27 6
|
7天前
|
Java 测试技术 持续交付
软件测试的艺术:从代码审查到用户体验的全方位解析
在当今数字化时代,软件已成为我们生活中不可或缺的一部分。无论是社交媒体、在线购物还是移动支付,背后都离不开软件的支持。然而,随着软件功能的日益复杂和用户需求的不断提高,软件测试的重要性也愈发凸显。本文将探讨软件测试的各个方面,从代码审查到用户体验,全面解析如何确保软件质量,为用户提供更好的使用体验。
26 1
|
13天前
|
安全 测试技术 UED
软件测试的艺术:从代码审查到用户体验的全方位解析
在软件开发的宇宙中,测试是那颗最耀眼的星辰。它不仅仅是一种技术活动,更是一门艺术。本文将带你领略这门艺术的魅力,从细微的代码审查到宏大的用户体验设计,揭示软件测试如何塑造出更加完美的数字世界。
|
18天前
|
测试技术 持续交付 UED
软件测试的艺术与科学:平衡创新与质量的探索在软件开发的波澜壮阔中,软件测试如同灯塔,指引着产品质量的方向。本文旨在深入探讨软件测试的核心价值,通过分析其在现代软件工程中的应用,揭示其背后的艺术性与科学性,并探讨如何在追求技术创新的同时确保产品的高质量标准。
软件测试不仅仅是技术活动,它融合了创造力和方法论,是软件开发过程中不可或缺的一环。本文首先概述了软件测试的重要性及其在项目生命周期中的角色,随后详细讨论了测试用例设计的创新方法、自动化测试的策略与挑战,以及如何通过持续集成/持续部署(CI/CD)流程优化产品质量。最后,文章强调了团队间沟通在确保测试有效性中的关键作用,并通过案例分析展示了这些原则在实践中的应用。
47 1
|
24天前
|
机器学习/深度学习 人工智能 算法
软件测试的艺术:从代码审查到用户体验的全方位解析
在软件开发过程中,一个经常被低估的环节就是软件测试。许多人认为测试仅仅是“点击几下鼠标,看看是否有错误”。然而,真正的软件测试是一门集技术深度、策略规划和细致观察于一体的艺术。它不仅关系到产品的质量和稳定性,更直接影响到最终用户的满意度。本文将从多个角度深入探讨软件测试的重要性、方法和最佳实践,帮助你理解为什么说软件测试是一种艺术。
|
26天前
|
设计模式 人工智能 算法
PHP中的设计模式:策略模式的深入解析与实践软件测试中的人工智能革命:提升效率与准确性的新篇章
在PHP开发中,理解并运用设计模式是提升代码质量和可维护性的重要途径。本文聚焦于策略模式(Strategy Pattern),一种行为型设计模式,它允许在运行时选择算法或业务规则。通过本文,我们将深入探讨策略模式的定义、结构、使用场景以及如何在PHP项目中有效地实现和利用策略模式。不同于性能优化等技术性摘要,本文着重于提供对策略模式全面而实用的理解,助力开发者编写出更加灵活和可扩展的应用程序。 本文深入探讨了人工智能在软件测试领域的应用,揭示了其如何显著提高测试过程的效率和准确性。通过实际案例分析,展示了AI技术在自动化测试、缺陷检测及结果分析中的关键作用,并讨论了实施AI测试策略时面临的挑
18 3
|
26天前
|
敏捷开发 安全 测试技术
软件测试的艺术:从代码到用户体验的全方位解析
本文将深入探讨软件测试的重要性和实施策略,通过分析不同类型的测试方法和工具,展示如何有效地提升软件质量和用户满意度。我们将从单元测试、集成测试到性能测试等多个角度出发,详细解释每种测试方法的实施步骤和最佳实践。此外,文章还将讨论如何通过持续集成和自动化测试来优化测试流程,以及如何建立有效的测试团队来应对快速变化的市场需求。通过实际案例的分析,本文旨在为读者提供一套系统而实用的软件测试策略,帮助读者在软件开发过程中做出更明智的决策。

热门文章

最新文章

推荐镜像

更多