如何为 Flutter devicelab 编写新的内存测试用例并接入 CI
【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter
Flutter 的 CI 系统会对每个 commit 在真实设备上运行 DeviceLab 测试,其中内存性能是高优先级指标。如果你的场景是"给某个 benchmark 应用新增一个内存测试,让 CI 持续测量其内存表现",操作路径在 如何为 Flutter 编写内存测试 中给出:为测试应用编写main入口、在 devicelab 的bin/tasks目录添加一个任务文件、并把任务注册进 CI。
这条路径的适用前提是:
- 宿主机装有 Flutter SDK,并且测试环境可以访问 ADB(Android 类测试要求);
- 本地运行 Android 测试时需要设置
ANDROID_SDK_ROOT环境变量。如果你有一份本地 engine 构建,Android SDK 位于.../engine/src/third_party/android_tools/sdk,也可以用flutter doctor -v找到自己的 Android SDK 位置(见 dev/devicelab/README.md)。
选择哪一类内存测试
文档给出了 DeviceLab 目前实际使用的三类内存测试,先按目标平台选定一类,再按该类步骤操作:
| 类型 | 位置 | 关键特性(摘自文档) | 主要限制(摘自文档) |
|---|---|---|---|
MemoryTest(直接轮询 adb) | perf_tests.dart 中的 MemoryTest 类 | 开销低;release 模式可用;测试自行控制测量起止 | 只有开始/结束两次读数;轮询 ADB 可能触发 Java heap GC;仅 Android;需要 ADB 环境和装好 Flutter SDK 的宿主机 |
| DevTools 内存测试 | perf_tests.dart 中的 DevToolsMemoryTest 类 | 约每秒一次读数,还能拿到 Dart VM 内存信息;可以把已有的速度类 driver 测试转成内存测试 | 对测量起止的控制较少;无 release 模式,profile/debug 模式可能带来额外内存开销;仅 Android |
| iOS 内存测试 | 由 driver 测试 + 任务文件中measureMemory: true触发(见 perf_tests.dart) | 采样频率可调;采样开销比调 adb 小;宿主机不需要 Flutter SDK | 仅 iOS;无 release 模式;内存轮询机制本身可能有额外开销 |
三类机制的区别:MemoryTest在可覆写的useMemory函数前后直接通过 adb 采样,默认的useMemory行为是应用以 release 模式运行并等待 logcat 中的 "done" 消息;DevTools 测试在普通 Flutter driver 测试运行期间通过 DevTools 轮询 adb 和 Dart VM;iOS 测试利用 iOS 嵌入层在运行时采样内存并写入 Dart timeline,事后从 timeline profile 中解析内存信息。
下面的主路径以MemoryTest(Android)为例。文档给出的完整示例可对照 complex_layout 的内存测试任务文件、macrobenchmarks 的大图滚动内存测试,以及它们的被测应用入口 dev/benchmarks/complex_layout/test_memory/scroll_perf.dart 和 dev/benchmarks/macrobenchmarks/test_memory/large_images.dart。
第一步:为测试应用写 main 入口
按文档要求,在 benchmark 工程的test_memory/目录下创建一个文件,例如test_memory/some_memory_perf.dart,其中包含测试应用的main函数。被测应用负责跑你想测量的操作,并在 logcat 中输出约定格式的标记日志(形如==== MEMORY BENCHMARK ==== <消息> ====),供MemoryTest通过prepareForNextMessage等待——例如READY表示应用启动完成,TAPPED表示点击已被确认。
MemoryTest的构造函数参数是:项目目录、main文件相对路径、Android 包名,以及可选的requiresTapToStart(启动后需要点击才开始的场景,如 complex_layout 示例)。
第二步:在 bin/tasks 下编写 devicelab 任务文件
在dev/devicelab/bin/tasks/目录添加与测试同名的some_memory_perf.dart。任务名就是该文件的 basename(不带.dart扩展名),CI 中引用的task_name与此一致。一个任务文件只允许一个task。文档示例(complex_layout 场景)的内容如下,可作为模板改写:
// 文件:dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart(仓库现有示例) import 'package:flutter_devicelab/framework/devices.dart'; import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart'; Future<void> main() async { deviceOperatingSystem = DeviceOperatingSystem.android; await task( MemoryTest( '${flutterDirectory.path}/dev/benchmarks/complex_layout', 'test_memory/scroll_perf.dart', 'com.yourcompany.complexLayout', requiresTapToStart: true, ).run, ); }写完后把三个占位信息替换为你自己的值:benchmark 工程路径(project)、test_memory/下 main 文件的相对路径(test)、被测应用的 Android 包名(package)。如果你的测试需要自定义测量过程(比如像 fast_scroll_large_images__memory.dart 那样覆写useMemory、覆写iterationCount),在该任务文件中定义一个继承MemoryTest的子类即可。
iOS 分支(可选):任务文件改为指定measureMemory: true的 driver 测试任务,例如 large_image_changer_perf_ios.dart;Android 的 DevTools 分支类似,任务是 large_image_changer_perf_android.dart 这类 driver 测试任务。
第三步:把任务注册进 CI
文档正文要求为新测试在dev/devicelab/manifest.yaml中添加一个条目。需要注意:在当前仓库快照中,dev/devicelab/下已没有manifest.yaml文件,CI 目标实际定义在仓库根目录的 .ci.yaml 中。因此当前有效的注册方式是按 dev/devicelab/README.md 的 "Adding tests to continuous integration" 一节操作:在.ci.yaml中新增一个 target,镜像一个已有的devicelab_dronerecipe target,task_name填你的任务名。
仓库中现有的真实 target 示例(可直接对照改写):
# .ci.yaml 中现有条目(Linux mokey benchmark 段) - name: Linux_mokey complex_layout_scroll_perf__memory recipe: devicelab/devicelab_drone presubmit: false timeout: 60 properties: tags: > ["devicelab","android","linux","mokey"] task_name: complex_layout_scroll_perf__memory dependencies: >- [ {"dependency": "open_jdk", "version": "version:21"} ]README 同时说明:如果测试需要在多个操作系统上运行,为每个系统创建独立的 target;presubmit 的 DeviceLab 容量有限,是否加入 presubmit 需要先提team-infraissue 论证可行性,不要直接加presubmit: true。
本地运行并验证
README 要求先本地通过再部署到 CI。以下副作用需要提前知道:本地运行 DeviceLab 测试会自动在你机器上启停 Gradle,并且设备在累计运行若干测试后会被自动重启(对应checkForRebootRequired()与device.reboot())。
在dev/devicelab目录下运行({NAME_OF_TEST}换成你的任务名,即bin/tasks文件 basename):
# from the .../flutter/dev/devicelab directory ../../bin/cache/dart-sdk/bin/dart bin/test_runner.dart test -t {NAME_OF_TEST}默认带自动重试(失败测试最多额外重试 2 次);想禁用重试复现问题时加--exit:
../../bin/cache/dart-sdk/bin/dart bin/test_runner.dart test --exit -t {NAME_OF_TEST}成功的判断方式来自 perf_tests.dart 中 MemoryTest.run 的实现:任务对每轮迭代的开始、结束和差值做统计,以TaskResult.success返回,指标键为start、end、diff三组统计数据。CI 侧的行为见 README:任务成功时 runner 上报成功并把性能指标上传到 Flutter 基础设施;失败会自动 rerun,某次 rerun 成功即整体记为成功并标记 flake;全部 rerun 失败才上报失败且不采集指标。
限制与边界
MemoryTest只适用于 Android 目标,且需要能访问 ADB 的环境;它的两次读数(begin/end)之外没有更细的测量。- DevTools 与 iOS 两类内存测试均没有 release 模式,profile 或 debug 模式下可能带来额外内存开销;iOS 侧的内存轮询机制本身也可能增加开销。
- 若你的测试还需要区分构建与测试阶段(例如在 host-only bot 上构建 apk 再上设备测试),README 提供了 build and test 模型的迁移路径(继承
BuildTestTask并覆写getBuildArgs、getTestArgs等),该模型下.ci.yamltarget 需镜像Linux_build_test或Mac_build_test平台的现有 target,并在 CI 验证通过后移除bringup: true正式启用。
参考文件
- 内存测试编写文档:三类内存测试的机制、优缺点与三步流程
- devicelab README:本地运行命令、注册 CI target、presubmit 与 build/test 模型
- MemoryTest / DevToolsMemoryTest 实现
- 现有任务文件目录
【免费下载链接】flutterFlutter makes it easy and fast to build beautiful apps for mobile and beyond项目地址: https://gitcode.com/GitHub_Trending/flutter41/flutter
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考