mcpField
S
scripting-adb-for-ci

scripting-adb-for-ci

github.com/skydoves云与 DevOps33970 次安装22 周前更新
代码仓库

关于 scripting-adb-for-ci

面向 Android CI 的 ADB 脚本编写技能,帮助团队可靠地运行仪器测试并管理多台设备。内容涵盖退出码判断、并行分发、重试、超时及清理流程。

主要功能

  1. 01解析仪器测试状态码
  2. 02多设备并行与测试分片
  3. 03传输错误重试和超时控制
  4. 04端口转发及清理
  5. 05失败时采集诊断产物
  6. 06Test Orchestrator 配置

适用场景

  1. 01在 CI 中准确判定 Android 测试结果
  2. 02在多台设备上并行运行测试
  3. 03避免重试或清理遗漏导致作业失败

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/skydoves/android-testing-skills --skill scripting-adb-for-ci

安装后在 Claude Code 中输入/scripting-adb-for-ci确认已加载。

详细介绍

CI 中的 ADB 脚本——Bash 惯用写法、退出码与扇出

这项技能是将其他 ADB 技能串联成 CI 流水线的纽带:退出码传递、重试、超时、多设备并行扇出、端口转发、幂等设置、失败时捕获,以及 Test Orchestrator 接入。每个部分都会交叉链接到负责底层基本操作的技能。

何时使用这项技能

  • CI 任务调用 adb shell am instrument,即使测试失败也会“通过”——它根据 $? 判断结果,但 am instrument 总是以 0 退出;真正的状态位于 stdout 中的 INSTRUMENTATION_STATUS_CODE 行(而且只有加上 -w 才能获取这些 stdout)。
  • 团队在单个 Agent 上连接了多台实体设备,并希望并行运行分片测试。
  • 团队混淆了 adb forward 和 adb reverse——大多数团队都至少遇到过一次因参数顺序相反而导致的 Bug。
  • 遇到暂时性的 error: device not found / error: closed 错误时不会重试;任务会因第一次故障而失败。
  • 脚本缺少清理步骤,导致遗留的端口转发或修改过的 settings put global ... 影响下一次运行。
  • 团队使用 androidTestImplementation 接入 Test Orchestrator——构建不会报错,却会安装 orchestrator APK 失败。

何时不使用这项技能

  • 尚未了解单项基本操作(logcat、screencap、pull、push、settings、input、am instrument)。请先阅读相关的同级技能。
  • CI 本身(Gradle 插件、Firebase Test Lab 云配置)。这一层请参考 FTL 自己的文档。
  • Compose / instrumentation 测试机制。请使用 ../../../compose/synchronization/synchronizing-with-idle/SKILL.md 及相关文档。

前置条件

  • Bash(大部分操作也适用于 zsh;macOS 用户如果要使用 timeout,应运行 brew install coreutils 安装 gtimeout)。
  • 主机上安装 Platform Tools 24+。旧版工具链中,adb shell 的退出码不可靠。
  • 设备运行 API 24+,才能可靠地传递 shell 退出码。Nougat 以前的设备可在 stdout 中使用哨兵值 __ADB_RC=$?,再在主机端使用 grep 匹配。

退出码——adb 如何传递退出码

操作行为
adb push / adb pull成功时返回 0,传输或文件系统失败时返回非零值。务必检查。
adb install / adb uninstall成功时返回 0。现代 adb 在遇到 INSTALL_FAILED_* 时会返回非零值,但旧版本即使包管理器操作失败也会以 0 退出——请通过管道检查 stderr。
adb wait-for-device(以及 wait-for-usb-device、wait-for-local-recovery 等)阻塞直到达到指定状态,然后退出并返回 0。实用的启动门控:`adb wait-for-device shell getprop sys.boot_completed
adb shell <cmd>在 platform-tools ≥ 24 且设备 adbd ≥ Android 7.0(Nougat / API 24)时,会传递设备端命令的退出码。更旧的设备无论命令结果如何,始终返回 0。
adb -t <transport-id>传输 ID,不是超时时间。这是脚本中最常见的混淆点。
am instrument始终以 0 退出,无论测试通过还是失败——AOSP Instrument.java 会在 run() 末尾无条件调用 System.exit(0),而 AndroidJUnitRunner 不论测试结果如何都会调用 finish(Activity.RESULT_OK, …)。-w 会让 shell 等待 Runner 运行完成,以确保其 stdout 完整;不加 -w 时,shell 会立即返回,无法获得可用内容。无论哪种情况,都应根据解析后的 INSTRUMENTATION_STATUS_CODE 行(如下所示)来控制 CI,绝不要根据 $? 判断。

可以使用 -x 禁用 adb shell 退出码传递:

shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]
   -x: disable remote exit codes and stdout/stderr separation

在 CI 中几乎绝不会想要使用 -x。默认行为才是你需要的。

AndroidJUnitRunner 状态码

am instrument -w -r 会针对每项测试输出 INSTRUMENTATION_STATUS_CODE: <int>,随后输出最终的 INSTRUMENTATION_CODE: <int>:

代码符号名称含义
1REPORT_VALUE_RESULT_START测试已开始
0REPORT_VALUE_RESULT_OK测试通过
-1REPORT_VALUE_RESULT_ERROR意外错误(进程死亡、运行器故障)
-2REPORT_VALUE_RESULT_FAILURE断言失败
-3REPORT_VALUE_RESULT_IGNORED(AndroidJUnitRunner 扩展)被 @Ignore 忽略/过滤掉
-4REPORT_VALUE_RESULT_ASSUMPTION_FAILURE(AndroidJUnitRunner 扩展)JUnit assumeXxx 返回 false

不要将 -3 或 -4 视为失败——它们表示跳过;只有 -1 和 -2 是失败。末尾的 INSTRUMENTATION_CODE 行与测试结果无关:它是一个 Activity.RESULT_* 值(-1 = RESULT_OK = 运行器级别未崩溃;0 = RESULT_CANCELED = 运行器已退出),与 shell $?(始终为 0)一样,不反映测试是否通过。请在 stdout 中搜索每项测试的 INSTRUMENTATION_STATUS_CODE: -1 / -2(参见下方 WRONG/RIGHT 示例)。

端口转发——adb forward 与 adb reverse

参数顺序相反。 这是最常见的脚本错误。

adb forward [--no-rebind] LOCAL  REMOTE        # host → device.  LOCAL  REMOTE
adb reverse [--no-rebind] REMOTE LOCAL         # device → host.  REMOTE LOCAL
方向用例命令示例
adb forward主机需要驱动设备端的服务。adb forward tcp:9222 localabstract:chrome_devtools_remote
adb reverse设备需要访问仅在本地主机上运行的服务(CI 运行器、开发笔记本电脑)。adb reverse tcp:8080 tcp:8080(Metro/本地后端)

令牌语法(摘自 adb help):

令牌位置
tcp:<port>任意一端。LOCAL 端的 tcp:0 = 选择任意空闲端口;所选端口会输出到 stdout。
localabstract:<name>Linux 抽象 Unix 域套接字。供 Chrome DevTools / Stetho / Flipper 使用。
localreserved:<name>保留的 Unix 套接字命名空间。
localfilesystem:<name>基于文件系统的 Unix 套接字。
jdwp:<pid>仅限 REMOTE 端。指定设备端 pid 的 Java Debug Wire Protocol。
vsock:<CID>:<port>仅限 REMOTE 端。virtio-vsock(模拟器)。
dev:<char-device>字符设备直通。
acceptfd:<fd>仅限 LOCAL 端——在预先打开的 fd 上监听。

清理时使用相同的命令:--list、--remove、--remove-all。务必在 trap 中移除添加的转发,以免在同一 Agent 上重新运行时因“cannot bind: address already in use”而失败。

超时——使用 timeout 包装,而不是 adb -t

adb -t <transport-id> 与超时无关。主机端没有按命令设置 adb 超时的标志。请使用 GNU/BSD timeout 实用工具进行包装:

timeout 60s adb -s emulator-5554 shell am instrument -w -r ...
echo $?
# 124 = timeout reached (process killed); 137 = had to escalate to SIGKILL.
# 0 just means am instrument exited (it always does) — see the timeout pattern below for pass/fail parsing.

在 macOS 上,timeout 是来自 coreutils 的 gtimeout(brew install coreutils)。

对于后台捕获,请设置最长时长,以免卡住的任务占满磁盘:

( timeout 600s adb logcat -v threadtime > artifacts/logcat.txt ) &
LOGCAT_PID=$!
trap 'kill $LOGCAT_PID 2>/dev/null' EXIT

瞬时错误时重试

常见错误包括:error: device 'XYZ' not found、error: closed、error: protocol fault、error: device offline。执行 kill-server 并重新连接通常可以解决大多数问题。

retry() {
  local n=0 max=3 delay=2
  until "$@"; do
    n=$((n+1))
    [ "$n" -ge "$max" ] && return 1
    echo "[retry] $* failed (attempt $n); restarting adb"
    adb kill-server; adb start-server
    sleep "$delay"
  done
}

retry adb install -r app.apk
retry adb shell am instrument -w -r ...

对于 am instrument 重试,还应先对测试包执行 pm clear——否则瞬时状态会在多次尝试间残留。请注意,retry 只会在传输错误时重新运行(adb 主机二进制文件以非零值退出);真正的测试失败不会导致 am instrument 以非零值退出,因此不会触发重试——应通过解析输出而非退出代码来检测此类失败。

并行扇出到多台设备

adb devices \
  | tail -n +2 \
  | awk '$2=="device"{print $1}' \
  | xargs -I {} -P 4 adb -s {} <command>

关键片段:

  • tail -n +2 会跳过 List of devices attached 标题。
  • awk '$2=="device"' 会过滤掉 unauthorized、offline、recovery 等状态。
  • -P 4 表示并行运行 4 个任务;请根据物理 USB 集线器的数量调整。
  • adb -s {} 用于指定序列号。$ANDROID_SERIAL 是在启动的 shell 中设置的等效环境变量。

对于共享设备状态的测试(清除 logcat、安装应用),先串行执行准备工作,再并行运行:

SERIALS=$(adb devices | awk '$2=="device"{print $1}')
echo "$SERIALS" | xargs -I {} -P 0 adb -s {} install -r app-debug.apk
echo "$SERIALS" | xargs -I {} -P 0 adb -s {} install -r app-debug-androidTest.apk

# Distribute shards across devices (one shard per device)
i=0
for S in $SERIALS; do
  adb -s "$S" logcat -c
  adb -s "$S" shell am instrument -w -r \
       -e numShards $(echo "$SERIALS" | wc -l) -e shardIndex "$i" \
       com.example.test/androidx.test.runner.AndroidJUnitRunner \
       > "results-$S.txt" &
  i=$((i+1))
done
wait

# am instrument always exits 0 — derive pass/fail from the captured output:
if grep -qE '^INSTRUMENTATION_STATUS_CODE: -[12]$' results-*.txt; then
  echo "test failures detected"; exit 1
fi

(xargs -P 0 会根据输入项数量尽可能多地并发运行。)

通过 numShards / shardIndex 进行分片

“如果需要并行执行测试……请使用 -e numShards 选项指定要创建的独立分片数量,并使用 -e shardIndex 选项指定要运行哪个分片。”——AndroidJUnitRunner 文档。

测试按名称进行哈希分桶:在测试集相同的情况下,各次运行的结果是确定的。添加或移除测试会重新分配分桶。

按设备区分产物路径

始终使用序列号作为命名空间:

artifact_dir() { echo "artifacts/${1//[:_]/-}"; }
for S in $(adb devices | awk '$2=="device"{print $1}'); do
  D=$(artifact_dir "$S"); mkdir -p "$D"
  adb -s "$S" logcat -d -v threadtime > "$D/logcat.txt"
  adb -s "$S" shell screencap -p /sdcard/last.png
  adb -s "$S" pull /sdcard/last.png "$D/last.png"
done

使用 trap 实现幂等的设置与清理

切勿假设上次运行后状态是干净的。将清理逻辑纳入设置流程,并使用 trap 确保完成拆卸。

最简设置前置代码
adb wait-for-device
adb shell input keyevent 82                         # wake; no-op if already on
adb shell pm clear com.example.app                  # wipe app data + cache
adb shell pm clear com.example.app.test             # wipe test process state
adb shell am force-stop com.example.app

# Hermetic animations
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0

(有关 pm clear 与 am force-stop 的完整说明,请参阅 ../../control/injecting-input-and-state/SKILL.md。)

使用 trap 清理
cleanup() {
  adb shell am force-stop com.example.app                       || true
  adb shell pm clear     com.example.app                        || true
  adb uninstall          com.example.app                        || true
  adb forward --remove tcp:6100                                 || true
  adb reverse --remove-all                                      || true
  adb shell settings put global window_animation_scale 1        || true
  adb shell settings put global transition_animation_scale 1    || true
  adb shell settings put global animator_duration_scale 1       || true
}
trap cleanup EXIT

每个步骤都使用 || true——清理部分状态时绝不能掩盖原始失败退出码。

失败时捕获信息
set +e
./gradlew connectedDebugAndroidTest
RC=$?
set -e

if [ "$RC" -ne 0 ]; then
  mkdir -p artifacts
  adb shell screencap -p /sdcard/fail.png
  adb pull /sdcard/fail.png artifacts/fail.png
  adb logcat -d -v threadtime              > artifacts/logcat.txt
  adb logcat -d -b crash    -v threadtime  > artifacts/crash.txt
  adb logcat -d -b events   -v descriptive > artifacts/events.txt
  adb shell dumpsys activity               > artifacts/dumpsys-activity.txt
fi
exit $RC

(有关底层原语,请参阅 ../../capture/capturing-screenshots-and-screenrecord/SKILL.md 和 ../../observability/extracting-logs-with-logcat/SKILL.md。)

配置 Test Orchestrator

“Android Test Orchestrator 会在测试套件运行开始时收集 JUnit 测试,但随后会在独立的 Instrumentation 实例中分别执行每项测试。”——developer.android.com。

每个 @Test 都会在独立的 Instrumentation 调用中运行,因此各项测试之间的进程状态、静态变量和 Application 单例都会重置。

Gradle 配置——使用 androidTestUtil,而非 androidTestImplementation
android {
  defaultConfig {
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    testInstrumentationRunnerArguments clearPackageData: 'true'
  }
  testOptions {
    execution 'ANDROIDX_TEST_ORCHESTRATOR'
  }
}

dependencies {
  androidTestImplementation 'androidx.test:runner:1.7.0'
  androidTestUtil           'androidx.test:orchestrator:1.6.1'   // NOT androidTestImplementation
}

clearPackageData: 'true' 会让 Orchestrator 在测试之间运行 pm clear <pkg>,这是最彻底的隔离选项。

直接调用 ADB(非 Gradle)
adb install --force-queryable -r path/to/orchestrator-1.6.1.apk
adb install --force-queryable -r path/to/test-services-1.6.0.apk

adb shell 'CLASSPATH=$(pm path androidx.test.services) app_process / \
 androidx.test.services.shellexecutor.ShellMain am instrument -w -e \
 targetInstrumentation com.example.test/androidx.test.runner.AndroidJUnitRunner \
 androidx.test.orchestrator/.AndroidTestOrchestrator'

API 30+ 需要使用 --force-queryable,这样 Orchestrator 才能在遵循软件包可见性规则的情况下,通过 bindService 连接到目标应用。

权衡
  • 优点: 每项测试都从状态已知且正常的 Application 开始;一项测试崩溃不会导致其余测试无法运行;测试之间的 pm clear 是真正执行的,而不是“我在 @After 里清理了”。
  • 缺点: 每项测试启动进程会增加约 1–2 秒。一个包含 500 项测试的套件,耗时会从约 5 分钟增加到 15 分钟以上。仅在状态泄漏确实造成问题的集成测试套件中使用。

模式

模式:错误——依据 am instrument 的退出码判断 CI 是否通过
# WRONG
adb shell am instrument -w -r com.example.test/androidx.test.runner.AndroidJUnitRunner
RC=$?
[ "$RC" -eq 0 ] && echo "tests passed" || echo "tests failed (rc=$RC)"
# WRONG because: am instrument calls System.exit(0) (AOSP Instrument.java) and AndroidJUnitRunner's
# finish() reports RESULT_OK regardless. $? is 0 even when tests fail — with OR without -w.
# RIGHT — keep -w (so stdout is complete) and -r (raw, parseable), then parse the stream
adb shell am instrument -w -r com.example.test/androidx.test.runner.AndroidJUnitRunner \
  | tee instrument.log
# -1 = error, -2 = failure; -3 (ignored) and -4 (assumption failure) are skips, not failures.
if grep -qE '^INSTRUMENTATION_STATUS_CODE: -[12]$' instrument.log; then
  echo "tests failed"; exit 1
fi
echo "tests passed"
模式:错误——混淆 adb forward 和 adb reverse 的参数顺序
# WRONG — copy/pasted the forward syntax for reverse
adb reverse tcp:8080 tcp:8080      # ambiguous (works because both are tcp:8080)
adb reverse tcp:9000 tcp:8080      # this means: device port 9000 → host port 8080
# Developer expected: host service on 8080 reachable as 9000 on device.
# WRONG because: reverse argument order is REMOTE LOCAL. The device tries to dial 8080 on
# the host, but the dev meant to expose the local 8080 to the device on port 9000.
# RIGHT — adb reverse REMOTE LOCAL
# Expose host service on 8080 to the device as 9000:
adb reverse tcp:9000 tcp:8080
# In the app code: open a connection to localhost:9000 — it terminates at the host's :8080.

# Or, simpler, identical port:
adb reverse tcp:8080 tcp:8080      # device 8080 ⇆ host 8080
模式:错误——为 orchestrator APK 使用 androidTestImplementation
// WRONG
dependencies {
  androidTestImplementation 'androidx.test:orchestrator:1.6.1'
}
// WRONG because: the orchestrator is a SEPARATE APK, not a library. androidTestImplementation
// merges it into the test APK's classpath, which AGP rejects (or silently ignores). The
// correct configuration is androidTestUtil, which AGP installs as an additional APK.
// RIGHT
dependencies {
  androidTestImplementation 'androidx.test:runner:1.7.0'
  androidTestUtil           'androidx.test:orchestrator:1.6.1'
}
模式:为 instrumentation 调用设置超时
timeout 600s adb shell am instrument -w -r \
  com.example.test/androidx.test.runner.AndroidJUnitRunner | tee instrument.log
RC=${PIPESTATUS[0]}   # exit of `timeout`, not `tee`
case "$RC" in
  124) echo "timed out at 600s"; exit 124 ;;
  137) echo "timed out and had to be SIGKILLed"; exit 137 ;;
esac
# RC is 0 whether tests passed or failed (am instrument always exits 0); pass/fail comes from the stream:
if grep -qE '^INSTRUMENTATION_STATUS_CODE: -[12]$' instrument.log; then
  echo "failed"; exit 1
fi
echo "passed"
模式:失败后重启服务再重试
retry adb -s "$SERIAL" install -r app-debug.apk
retry adb -s "$SERIAL" shell am instrument -w -r \
  -e clearPackageData true \
  com.example.test/androidx.test.runner.AndroidJUnitRunner

retry 辅助函数会在发生瞬时故障时重启 adb 服务(参见上文“瞬时错误时重试”)。

模式:并行分片分发,并按设备区分产物
SERIALS=( $(adb devices | awk '$2=="device"{print $1}') )
N=${#SERIALS[@]}
mkdir -p artifacts

pids=()
for i in "${!SERIALS[@]}"; do
  S="${SERIALS[$i]}"
  D="artifacts/${S//[:_]/-}"
  mkdir -p "$D"
  (
    adb -s "$S" logcat -c
    adb -s "$S" shell pm clear com.example.app
    adb -s "$S" shell am instrument -w -r \
         -e numShards "$N" -e shardIndex "$i" \
         com.example.test/androidx.test.runner.AndroidJUnitRunner \
         > "$D/results.txt"
    # am instrument exits 0 regardless — pass/fail is in the captured stream.
    if grep -qE '^INSTRUMENTATION_STATUS_CODE: -[12]$' "$D/results.txt"; then
      adb -s "$S" shell screencap -p /sdcard/fail.png
      adb -s "$S" pull /sdcard/fail.png "$D/fail.png"
      adb -s "$S" logcat -d -v threadtime > "$D/logcat.txt"
      exit 1
    fi
  ) &
  pids+=($!)
done

rc=0
for p in "${pids[@]}"; do wait "$p" || rc=1; done   # fail the job if any shard failed
exit $rc

强制规则

  • 必须向 am instrument 传入 -w -r,这样 shell 才会等待运行器并输出原始、可解析的结果。不加 -w 时,shell 会在产生任何输出之前返回。
  • 禁止根据 am instrument 的 $? 判断 CI 是否通过——无论测试结果如何(无论是否加 -w),它都会调用 System.exit(0)。应通过在标准输出中搜索 INSTRUMENTATION_STATUS_CODE: -1(错误)和 -2(失败)来检测失败。
  • 必须在解析 INSTRUMENTATION_STATUS_CODE 时,将 -3(已忽略)和 -4(假设失败)与实际失败区分开来。-1 和 -2 才是失败代码。
  • 必须记住,adb forward 的参数顺序是 LOCAL REMOTE(主机在前),而 adb reverse 的顺序是 REMOTE LOCAL(设备在前)。两者的参数顺序相反。
  • 必须使用 timeout <N>s(macOS 上使用 gtimeout)包装长时间运行的 adb 命令。adb 没有内置的超时选项——adb -t 指的是传输 ID,不是超时。
  • 必须使用 androidTestUtil("androidx.test:orchestrator:1.6.1"),不要使用 androidTestImplementation。Orchestrator 是独立 APK,不是库。
  • 必须使用有界的 retry 辅助函数重试临时 adb 错误(device not found、closed、protocol fault),并在重试之间调用 adb kill-server; adb start-server。
  • 必须安装 trap cleanup EXIT,以清理端口转发(adb forward --remove-all、adb reverse --remove-all)、恢复动画缩放比例,并对待测应用执行 pm clear。每个步骤都要加上 || true,避免清理操作掩盖测试的退出代码。
  • 每次测试运行前都必须执行 pm clear <pkg>(不能只用 am force-stop)。参见 ../../control/injecting-input-and-state/SKILL.md。
  • 禁止依赖 API 24 以下设备上的 adb shell 退出代码传递。对于较旧的目标设备,应在标准输出中使用哨兵值 __ADB_RC=$?,或为测试基础设施指定最低 SDK 版本。
  • 禁止将 adb -t <transport-id> 与 timeout 混淆。两者毫无相同之处。
  • 禁止在未串行化安装/设置阶段的情况下并行执行——同时向同一设备执行 adb install 会产生竞争。
  • 优先使用: Test Orchestrator 搭配 clearPackageData: 'true' 实现隔离性测试;对于经过路由的产物,再搭配 useTestStorageService: 'true'(参见 ../../transfer/extracting-test-artifacts/SKILL.md)。
  • 优先采用: 按序列号为产物划分命名空间(artifacts/${SERIAL//[:_]/-}/),这样 CI 中的 archive artifacts/**/* 就能整洁地收集所有产物。

验证

  • 脚本/Gradle 中的每次 am instrument 调用都带有 -w -r,且没有脚本将其 $? 用作通过/失败信号——应通过解析标准输出中的 INSTRUMENTATION_STATUS_CODE: -1/-2 来检测失败。
  • adb forward 和 adb reverse 的参数顺序均符合文档说明;CI 会运行冒烟测试,确认主机能访问设备服务,反之亦然。
  • 使用 androidTestUtil('androidx.test:orchestrator:...')(而非 androidTestImplementation)——./gradlew :app:dependencies --configuration androidTestUtil 会显示 Orchestrator。
  • 顶层 CI 脚本中已安装 trap cleanup EXIT。
  • 所有长时间运行的 adb 命令都已使用 timeout 包装。
  • CI 运行完成 50 次迭代,且未出现“端口转发未清理”或“上次运行的动画缩放比例”导致的间歇性故障。
  • 状态代码解析会将 -3 和 -4 视为跳过,而非失败。
  • 按设备划分的产物目录均以序列号作为命名空间;CI 产物归档中显示的目录结构符合预期。
  • 遇到临时错误时,重试辅助函数会在重试之间执行 adb kill-server; adb start-server。

参考资料

  • developer.android.com/tools/adb — forward / reverse 语法、传输和服务器。
  • developer.android.com/studio/test/command-line — am instrument -w -r、-e numShards / -e shardIndex、AndroidJUnitRunner 参数、输出目录。
  • developer.android.com/training/testing/instrumented-tests — Test Orchestrator 概述。
  • developer.android.com/reference/android/app/Instrumentation — REPORT_VALUE_RESULT_* 常量(OK = 0、START = 1、ERROR = -1、FAILURE = -2)。
  • firebase.google.com/docs/test-lab/android/command-line — Firebase Test Lab CI 退出代码(0 通过、10 测试失败、15 状态不确定、18 不兼容、20 基础设施错误)。
  • 研究说明 tasks/research/A3-adb-observability-automation.md — 完整的 bash 惯用写法、退出代码传播矩阵、端口转发令牌语法、并行扇出、重试、失败时捕获、Test Orchestrator 接线。
  • 研究说明 tasks/research/A2-adb-shell-commands.md — am instrument 参数、状态代码、AndroidJUnitRunner 的 -3 / -4 扩展。
  • 同级技能:../../architecture/understanding-adb-architecture/SKILL.md — 服务器 / 守护进程 / adb kill-server。
  • 同级技能:../../devices/connecting-to-devices/SKILL.md — 设备状态、wait-for-device。
  • 同级技能:../../devices/connecting-over-wifi/SKILL.md — 用于无头 CI 的 adb pair / adb connect。
  • 同级技能:../../apps/installing-and-managing-apps/SKILL.md — pm install / pm uninstall / pm clear。
  • 同级技能:../../tests/running-instrumented-tests-via-adb/SKILL.md — 深入介绍 am instrument -w -r。
  • 同级技能:../../control/injecting-input-and-state/SKILL.md — hermetic 前置步骤(将动画设为 0、pm clear、am force-stop)。
  • 同级技能:../../capture/capturing-screenshots-and-screenrecord/SKILL.md — 失败时使用 screencap / screenrecord 捕获。
  • 同级技能:../../observability/extracting-logs-with-logcat/SKILL.md — 失败时使用 logcat -d 捕获。
  • 同级技能:../../transfer/extracting-test-artifacts/SKILL.md — pull / push / run-as / TestStorage。
  • 跨集合:../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md — Runner 内部机制。
  • 跨集合:../../../instrumentation/scenarios/launching-activities-with-activityscenario/SKILL.md — Instrumentation 下的 Activity 场景。
  • 跨集合:../../../fundamentals/strategies/applying-testing-strategies/SKILL.md — 何时应投入 CI 编排,而非使用更简单的工具。

评论

支持 Markdown